Search code examples
assemblyx86masm

Is the sentence highlighted below, obtained from the /ENTRY page in MASM docs, correct?


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

Solution

  • 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.

    Entry point assembly code requirements

    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.

    How /ENTRY handles its argument

    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@.


    Comments on your example code

    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.