从 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 位程序时有用。