从 MASM 文档中的 /ENTRY 页面获得的下面突出显示的句子是否正确?
Is the sentence highlighted below, obtained from the /ENTRY page in MASM docs, correct?
此 /ENTRY (MASM) 文档在其备注部分中说:
Remarks
The /ENTRY option specifies an entry point function as the starting
address for an .exe file or DLL.
The function must be defined to use the __stdcall calling convention.
这似乎并不完全正确,因为下面的代码在 VS2017 中没有问题。
.586
.MODEL flat, C
.stack 4096
.CODE
main PROC
mov eax, -1
main ENDP
END
其中 main
被定义为链接器选项 /ENTRY 中的代码入口点。请注意 main
不使用 stdcall
调用约定。
这是不是高亮句只指用C或C++写的代码?
仅供参考,我在下面提供了用于 运行 代码的链接器命令行:
/OUT:"C:\Users\xxxx\Documents\Visual Studio 2017\Projects\Assemblies\Debug\A_test.exe" /MANIFEST
/NXCOMPAT /PDB:"C:\Users\xxxx\Documents\Visual Studio 2017\Projects\Assemblies\Debug\A_test.pdb"
/DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib"
"shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DEBUG:FASTLINK
/MACHINE:X86 /ENTRY:"main" /INCREMENTAL /PGD:"C:\Users\xxxx\Documents\Visual Studio
2017\Projects\Assemblies\Debug\A_test.pgd" /MANIFESTUAC:"level='asInvoker' uiAccess='false'"
/ManifestFile:"Debug\A_test.exe.intermediate.manifest" /ERRORREPORT:PROMPT /NOLOGO /TLBID:1
部分正确。这里的实际要求相当复杂,并且部分矛盾。对于 x86 目标,实际入口点代码使用的调用约定对于可执行文件无关紧要,但需要使用 DLL 的 stdcall 调用约定。但是,在 x86 目标上,使用 /ENTRY
选项传递的名称会自动修饰,就好像它是遵循 cdecl 调用约定的函数一样,无论正在构建的是可执行文件还是 DLL。
在 x64 和 ARM 目标上,实际上没有 stdcall 调用约定,因此没有特殊要求,也没有不一致之处。入口点代码应遵循这些目标的标准 cdecl 约定,并且使用 /ENTRY
选项传递的名称未修饰。
入口点汇编代码要求
在可执行文件的情况下,调用入口点时不带参数,因此 cdecl 和 stdcall 调用约定是等效的,因此都可以用于入口点代码。在 DLL 的情况下,使用三个参数调用入口点,传递给 DllMain 的相同参数,并且使用 stdcall 调用约定传递这些参数。在 x86 目标上,DLL 的入口点代码应在返回时使用 ret 12
指令弹出这些参数。
/ENTRY 如何处理它的参数
在 x86 目标(而不是 x64 和 ARM 目标)上,/ENTRY
链接器选项自动为传递的符号加上下划线前缀 _
,遵循符号名称的 cdecl 约定。它不会添加 stdcall @##
后缀,即使您正在构建 DLL。然后,它会将此符号与所链接代码中的符号进行模糊匹配。如果你使用 /ENTRY:foo
那么它会首先尝试找到一个名为 _foo
的符号,如果没有找到,它会寻找一个名为 foo
或以 [ 开头的符号=19=] 或 foo@
.
对您的示例代码的评论
请注意,因为您在示例代码中使用了 .MODEL flat, C
,MASM 会自动在代码中定义的符号 main
前加上下划线 _
,在 x86 cdecl 调用之后惯例。方便的是,这与上述 /ENTRY
的行为相匹配。但是,将入口点命名为 main
并不是最好的主意,因为这意味着它应该是同名的 C 函数。由于您的入口点没有获得传递的参数,但 C 函数 main
确实如此,因此调用您的入口点 main
可能会产生误导。我建议将其命名为 start
。
最后 构建 32 位或 64 位 PECOFF 程序时。它仅在创建 16 位程序时有用。
此 /ENTRY (MASM) 文档在其备注部分中说:
Remarks
The /ENTRY option specifies an entry point function as the starting address for an .exe file or DLL.
The function must be defined to use the __stdcall calling convention.
这似乎并不完全正确,因为下面的代码在 VS2017 中没有问题。
.586
.MODEL flat, C
.stack 4096
.CODE
main PROC
mov eax, -1
main ENDP
END
其中 main
被定义为链接器选项 /ENTRY 中的代码入口点。请注意 main
不使用 stdcall
调用约定。
这是不是高亮句只指用C或C++写的代码?
仅供参考,我在下面提供了用于 运行 代码的链接器命令行:
/OUT:"C:\Users\xxxx\Documents\Visual Studio 2017\Projects\Assemblies\Debug\A_test.exe" /MANIFEST
/NXCOMPAT /PDB:"C:\Users\xxxx\Documents\Visual Studio 2017\Projects\Assemblies\Debug\A_test.pdb"
/DYNAMICBASE "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib"
"shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DEBUG:FASTLINK
/MACHINE:X86 /ENTRY:"main" /INCREMENTAL /PGD:"C:\Users\xxxx\Documents\Visual Studio
2017\Projects\Assemblies\Debug\A_test.pgd" /MANIFESTUAC:"level='asInvoker' uiAccess='false'"
/ManifestFile:"Debug\A_test.exe.intermediate.manifest" /ERRORREPORT:PROMPT /NOLOGO /TLBID:1
部分正确。这里的实际要求相当复杂,并且部分矛盾。对于 x86 目标,实际入口点代码使用的调用约定对于可执行文件无关紧要,但需要使用 DLL 的 stdcall 调用约定。但是,在 x86 目标上,使用 /ENTRY
选项传递的名称会自动修饰,就好像它是遵循 cdecl 调用约定的函数一样,无论正在构建的是可执行文件还是 DLL。
在 x64 和 ARM 目标上,实际上没有 stdcall 调用约定,因此没有特殊要求,也没有不一致之处。入口点代码应遵循这些目标的标准 cdecl 约定,并且使用 /ENTRY
选项传递的名称未修饰。
入口点汇编代码要求
在可执行文件的情况下,调用入口点时不带参数,因此 cdecl 和 stdcall 调用约定是等效的,因此都可以用于入口点代码。在 DLL 的情况下,使用三个参数调用入口点,传递给 DllMain 的相同参数,并且使用 stdcall 调用约定传递这些参数。在 x86 目标上,DLL 的入口点代码应在返回时使用 ret 12
指令弹出这些参数。
/ENTRY 如何处理它的参数
在 x86 目标(而不是 x64 和 ARM 目标)上,/ENTRY
链接器选项自动为传递的符号加上下划线前缀 _
,遵循符号名称的 cdecl 约定。它不会添加 stdcall @##
后缀,即使您正在构建 DLL。然后,它会将此符号与所链接代码中的符号进行模糊匹配。如果你使用 /ENTRY:foo
那么它会首先尝试找到一个名为 _foo
的符号,如果没有找到,它会寻找一个名为 foo
或以 [ 开头的符号=19=] 或 foo@
.
对您的示例代码的评论
请注意,因为您在示例代码中使用了 .MODEL flat, C
,MASM 会自动在代码中定义的符号 main
前加上下划线 _
,在 x86 cdecl 调用之后惯例。方便的是,这与上述 /ENTRY
的行为相匹配。但是,将入口点命名为 main
并不是最好的主意,因为这意味着它应该是同名的 C 函数。由于您的入口点没有获得传递的参数,但 C 函数 main
确实如此,因此调用您的入口点 main
可能会产生误导。我建议将其命名为 start
。
最后