C++ 在MASM中调用标准库函数
我想以混合C++/汇编的方式开始使用MASM。 我现在试图从一个进程中调用一个标准库函数(例如PrtTf),然后调用C++。 在我的cpp文件中声明printf的签名后,我的代码开始工作。但我不明白我为什么要这样做,如果我能避免的话 我的cpp文件:C++ 在MASM中调用标准库函数,c++,visual-studio,assembly,x86,masm,C++,Visual Studio,Assembly,X86,Masm,我想以混合C++/汇编的方式开始使用MASM。 我现在试图从一个进程中调用一个标准库函数(例如PrtTf),然后调用C++。 在我的cpp文件中声明printf的签名后,我的代码开始工作。但我不明白我为什么要这样做,如果我能避免的话 我的cpp文件: #include <stdio.h> extern "C" { extern int __stdcall foo(int, int); } extern int __stdcall printf(const char*, .
#include <stdio.h>
extern "C" {
extern int __stdcall foo(int, int);
}
extern int __stdcall printf(const char*, ...); // When I remove this line I get Linker-Error "LNK2019: unresolved external symbol"
int main()
{
foo(5, 5);
}
#include <stdio.h>
extern "C" {
extern int __cdecl foo(int, int);
}
extern int __cdecl printf(const char*, ...); // omiting the extern results in a linker error
int main()
{
//printf("\0"); // this would replace the declaration
foo(5, 5);
return 0;
}
一些更新
作为对这些评论的回应,我试图重新编写代码以符合cdecl调用约定。不幸的是,这并没有解决问题(代码在extern
声明中运行良好,但抛出了一个错误,没有任何错误)
但通过反复试验,我发现,extern
似乎强制外部链接,即使不需要关键字,因为外部链接应该是函数声明的基础
我可以通过在我的cpp代码中使用函数来省略声明(即,如果在源文件中的某个地方添加一个printf(“\0”);
,则链接器可以使用它,并且一切正常
新的(但不是更好的)cpp文件:
#include <stdio.h>
extern "C" {
extern int __stdcall foo(int, int);
}
extern int __stdcall printf(const char*, ...); // When I remove this line I get Linker-Error "LNK2019: unresolved external symbol"
int main()
{
foo(5, 5);
}
#include <stdio.h>
extern "C" {
extern int __cdecl foo(int, int);
}
extern int __cdecl printf(const char*, ...); // omiting the extern results in a linker error
int main()
{
//printf("\0"); // this would replace the declaration
foo(5, 5);
return 0;
}
这确实有点毫无意义,不是吗 链接器通常是非常愚蠢的东西。他们需要被告知对象文件需要
printf
。链接器无法从缺少的printf
符号中看出这一点,这已经够愚蠢了
<> > C++编译器将告诉你在写代码“>代码> >外接int tsddLeLINK PRINTF(const char *,…);,或者,这是正常的方式,编译器会告诉链接器,所以当你真的调用<代码> Prtff<代码>时,C++的代码不调用它。
<>汇编程序也很笨。你的汇编程序显然无法告诉它需要从C++中链接到代码> Prtff<代码>的链接器。
一般的解决方案是不在汇编中做复杂的事情。汇编并不适合这样做。从C到汇编的调用通常工作得很好,以其他方式调用是有问题的。我最好的猜测是,这与Microsoft从VS2015开始重构C库,并且一些C库现在是内联的这一事实有关d(包括
printf
),实际上不在默认的.lib
文件中
我的猜测是这样的:
extern int __cdecl printf(const char*, ...);
<代码> ExtXu/<代码>强制将旧的遗留库包含在链接过程中。这些库包含非内联函数<代码> Prtff<代码>。如果C++代码不强制MS链接器包含遗留C库,则MASM代码使用的<代码> Prtff < /C>将不解决。< / P> < P> >我相信这与StAcExcel和2015有关。如果您想从C++代码中删除<代码> Extn int -CyDel-Prtuf(const char *,…);< /Cord> >,您可能希望考虑将这行添加到您的MASM代码:
includelib legacy_stdio_definitions.lib
如果使用CDECL调用约定并将C/C++与汇编语言混合使用,则MASM代码将如下所示:
.model flat, C ; Default to C language
includelib legacy_stdio_definitions.lib
EXTERN printf :PROC ; declare printf
.data
tstStr db "Mult: %i",0Ah,"Add: %i",0 ; 0Ah is the backslash - escapes are not supported
.code
foo PROC x:DWORD, y:DWORD
mov eax, x
mov ebx, y
add eax, ebx
push eax
mov eax, x
mul ebx
push eax
push OFFSET tstStr
call printf
ret
foo ENDP
END
<>你的C++代码是:
#include <stdio.h>
extern "C" {
extern int foo(int, int); /* __cdecl removed since it is the default */
}
int main()
{
//printf("\0"); // this would replace the declaration
foo(5, 5);
return 0;
}
使用EBX
告诉MASM生成额外的开场白和尾声代码,以便在开始时保存EBX,并在函数执行ret
指令时恢复EBX。生成的指令类似于:
函数声明前面的
extern
可能不会执行您认为它会执行的操作,因为它没有效果。@KonradRudolph:它没有效果是一个相当强的语句,因为代码使用该声明编译,而不使用该声明编译。对于纯(标准)函数,您的注释可能是正确的C++程序,但这是C++和汇编的混合。很明显,编译器、这个汇编程序和这个链接器之间的交互作用是:<代码> ExtNe>代码>必须做一些事情。@ MsAlter删除代码< ExtNe>代码>不会改变任何东西(这里是一个红色鲱鱼)。因为函数声明总是隐式的外部声明。所以,是的,它没有任何效果。@MSalters Konrad谈论的是外部声明,而不是整个声明。我自己确实有点困惑。因为包含
,所以已经有了:std::printf
或::printf
的声明,视情况而定。Bu澄清这一点确实是件好事。@AnonymousAnonymous,如果你只删除extern
,它仍然有效吗?不是我的反对票,我很久没有使用MSVC了,但是源代码中的声明真的告诉链接器什么了吗?在GCC/clangg/…这是错误的,因为符号声明和库链接大多是或不正确的“C++”源文件中的<代码> Prtff<代码>声明将不起作用。一方面,在任何库中,不存在代码“>>STDCALL PrTFF < /C>”,编译器应警告,变量函数参数不能与约定方面,它确实更改了导出的名称,但仍然是一个非常好的答案,谢谢!我不知道必须恢复ebx-我只是希望在cdecl的文档中说明这一点,而不是在其他页面上,列出所有调用约定…我的意思是,来吧Microsoft:)
foo PROC uses EBX x:DWORD, y:DWORD
mov eax, x
mov ebx, y
add eax, ebx
push eax
mov eax, x
mul ebx
push eax
push OFFSET tstStr
call printf
add esp, 12 ; Remove the parameters pushed on the stack for
; the printf call. The stack needs to be
; properly restored. If not done, the function
; prologue can't properly restore EBX
; (and any registers listed by USES)
ret
foo ENDP
0000 _foo:
0000 55 push ebp
0001 8B EC mov ebp,esp
0003 53 push ebx
0004 8B 45 08 mov eax,0x8[ebp]
0007 8B 5D 0C mov ebx,0xc[ebp]
000A 03 C3 add eax,ebx
000C 50 push eax
000D 8B 45 08 mov eax,0x8[ebp]
0010 F7 E3 mul ebx
0012 50 push eax
0013 68 00 00 00 00 push tstStr
0018 E8 00 00 00 00 call _printf
001D 83 C4 0C add esp,0x0000000c
0020 5B pop ebx
0021 C9 leave
0022 C3 ret