This /ENTRY (MASM) document says in its Remark section:
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.
That does not seem to be entirely correct, as the code below works without a problem in VS2017.
.586
.MODEL flat, C
.stack 4096
.CODE
main PROC
mov eax, -1
main ENDP
END
where main
is defined as the code entry point in the linker option /ENTRY. Note that main
doesn't use an stdcall
calling convention.
Is this the case that the highlighted sentence refers only to codes written in C or C++?
Just for documentation, I provide below the linker command line used to run the code:
/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
It's partly correct. The actual requirements here are fair bit more complex and partly contradictory. For x86 targets, the calling convention used by the actual entry point code doesn't matter for executables, but needs to use the stdcall calling convention for DLLs. However, on x86 targets, the name passed with the /ENTRY
option is automatically decorated as if it were a function following the cdecl calling convention, regardless whether an executable or DLL is being built.
On x64 and ARM targets there isn't actually a stdcall calling convention so there's no special requirements and no inconsistency. The entry point code should follow the standard cdecl convention for these targets, and the name passed with the /ENTRY
option isn't decorated.
In the case of an executable, the entry point is called without arguments and so both the cdecl and stdcall calling convention are equivalent and so either can be used for the entry point code. In the case of a DLL, the entry point is called with three arguments, the same arguments passed to DllMain, and these arguments are passed using the stdcall calling convention. On x86 targets, a DLL's entry point code should pop these arguments using a ret 12
instruction when returning.
On x86 targets (and not x64 and ARM targets) the /ENTRY
linker option automatically prefixes the the symbol passed with an underscore _
, following the cdecl convention for symbol names. It doesn't add the stdcall @##
suffix, even if you're building a DLL. It will then fuzzy match this symbol against the symbols in the code being linked. If you use /ENTRY:foo
then it will first try to find a symbol named _foo
, and if it doesn't find it, it will look for a symbol named foo
or one starting with _foo@
or foo@
.
Note, because you're using .MODEL flat, C
in your example code MASM will automatically prefix the symbol main
defined in your code with an underscore _
, following the x86 cdecl calling convention. Conveniently, this matches the behaviour of /ENTRY
as described above. However, it's not the best idea to name your entry point main
as this would imply that it's supposed to be the C function of the same name. Since your entry entry point doesn't get passed arguments, but the C function main
does it can be misleading to call your entry point main
. I'd suggest naming it start
instead.
Finally the .STACK directive does nothing useful when building 32-bit or 64-bit PECOFF programs. It's only useful when creating 16-bit programs.