Developer
Developer

Reputation: 25

Assembler MASM (64-bit) does not recognize the entry point and throws an error

I am studying assembler for the x86 family of processor architectures (32-bit and 64-bit) on Windows. It is not to say that I'm quite a beginner, but I probably don't know everything, at least about the syntax of the MASM assembler, as it seems.

I use the MASM assembler (for 64-bit programs) located in folders belonging to Visual Studio: "..\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\ml64.exe" Visual Studio 2019 is installed, and I use the MASM assembler from its folder. I have Windows 7 myself.

I made my program for a 32-bit system, and it was normally assembled by MASM for 32-bit programs and worked. Then I translated its code for a 64-bit architecture (and there are a few changes needed in the code there). But, when assembling it with MASM for 64-bit programs, MASM gave an error message that there was allegedly some unresolved "StartOfProgram" symbol. Here's what's in the console:

C:\Assembler>cd "C:\Assembler"

C:\Assembler>"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\ml64.exe" "C:\Assembler\Main.asm" /link /subsystem:windows /entry:StartOfProgram
Microsoft (R) Macro Assembler (x64) Version 14.29.30138.0
Copyright (C) Microsoft Corporation.  All rights reserved.

 Assembling: C:\Assembler\Main.asm
Microsoft (R) Incremental Linker Version 14.29.30138.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/OUT:Main.exe
Main.obj
/subsystem:windows
/entry:StartOfProgram
LINK : error LNK2001: unresolved external symbol StartOfProgram.
Main.exe : fatal error LNK1120: unresolved external symbols: 1

I spent about two weeks or month searching for solution to this error, but I didn't find it.

In general, it used to give an error message that allegedly there is some unresolved symbol "WinMainCRTStartup", but recently I kind of realized that it made such an entry point, because I did not explicitly specify entry point in the console (via the command "/entry:", which is in the console from above), but the problem about "unresolved external symbol" remained, even though I set the entry point where I needed it (that is, on "StartOfProgram").


Here is the code of my 64-bit version of the program that just has to output "Hello world" in a pop-up window:

option  casemap:none    ; As far as i understand, functions from Windows API without case sensitivity not works

; **** Importing what needs ****

includelib  "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x64\kernel32.lib"   ; Downloading main static library to use main functions of Windows API

extern      LoadLibraryA:near    ; I load from static libraries functions used in this program
extern      GetProcAddress:near
extern      FreeLibrary:near
extern      ExitProcess:near

; **** Declaring memory segment ****

.data

        text                    db  'Hello world', 0            ; Text in "Text Box"'s window
        header                  db  'Title of hello world', 0   ; Header of "Text Box"'s window
        
        nameOfDLL               db  'user32.dll', 0
        nameOfProcedureOfDLL    db  'MessageBoxA', 0

        handlerToModule         dd  0
        addressOfProcedureOfDLL dq  0   ; In 64-bit operating system, addresses are 64-bit, so size of memory area that this label points to - is quad word (dq) (that is 64 bits)

.code

; **** Entry point to program ****

StartOfProgram:    ; For some reason, MASM assembler recommends putting "_" sign before label of entry point to program, if it is 32-bit. Therefore in 64-bit I don't.

        mov     rcx, offset nameOfDLL
        sub     rsp, 40                         ; Pointer shifting for alignment of stack and plus "shadow space" in stack. It needed by x64 calling convention
        call    LoadLibraryA                    ; I dynamically connect DLL so that i can then take function from it
        add     rsp, 40
        
        mov     qword ptr handlerToModule, rax
        
        mov     rcx, rax                        ; Functions from Windows API use stdcall convention. stdcall is agreement to pass function parameters to stack backwards, so rax is last. Rax still contains Windows' DLL address (Microsoft call it "handler") (after recent call to Loadlibrary function), so it's better to use register, processor works faster with registers
        mov     rdx, offset nameOfProcedureOfDLL
        sub     rsp, 40
        call    GetProcAddress
        add     rsp, 40
        
        mov     addressOfProcedureOfDLL, rax    ; I save address of procedure that i took from GetProcAddress. In 64-bit operating system, addresses are 64-bit, so needs to transfer rax register and not eax
        
        mov     rcx, 0
        mov     rdx, offset text
        mov     r8, offset header
        mov     r9, 0
        sub     rsp, 40
        call    addressOfProcedureOfDLL         ; It is better to immediately pass address of function through memory address label and not through register containing this address, because computer will still have to go to this address later and there is no point in wasting time reading from  register of same address
        add     rsp, 40        

        mov     rcx, offset handlerToModule
        sub     rsp, 40
        call    FreeLibrary
        add     rsp, 40

        mov     rcx, 0
        sub     rsp, 40
        call    ExitProcess
        add     rsp, 40

end

Here is the code of my 32-bit version of this program (which was normally assembled and worked):

.386    ; There indicates processor with minimal set of functions (since new Intel processors (in "x86" family of architectures) are compatible (so far) with instructions of old Intel processors of same family of architectures)

option  casemap:none    ; As far as i understand, functions from Windows API without case sensitivity not works

; **** Importing what needs ****

includelib  "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x86\kernel32.lib"   ; Downloading main static library to use main functions of Windows API
;includelib  "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x86\User32.lib"

extern      _LoadLibraryA@4:near    ; I load from static libraries a functions used in this program
extern      _GetProcAddress@8:near
extern      _FreeLibrary@4:near
extern      _ExitProcess@4:near

.model flat

; **** Declaring a memory segment ****

.data

        text                    db  'Hello world', 0            ; Text in "Text Box"'s window
        header                  db  'Title of hello world', 0   ; Header of "Text Box"'s windowокна

        nameOfDLL               db  'user32.dll', 0
        nameOfProcedureOfDLL    db  'MessageBoxA', 0

        handlerToModule         dd  0
        addressOfProcedureOfDLL dd  0

.code

; **** Entry point to program ****

_StartOfProgram:    ; For some reason, MASM assembler recommends putting "_" sign before label of entry point to program, if it is 32-bit

        push    offset nameOfDLL
        call    _LoadLibraryA@4                 ; I dynamically connect DLL so that i can then take function from it

        mov     handlerToModule, eax

        push    offset nameOfProcedureOfDLL
        push    eax                             ; Functions from Windows API use stdcall convention. stdcall is agreement to pass function parameters to stack backwards, so eax is last. Eax still contains Windows' DLL address (Microsoft call it "handler") (after recent call to Loadlibrary function), so it's better to use register, processor works faster with registers
        call    _GetProcAddress@8

        mov     addressOfProcedureOfDLL, eax    ; I save address of procedure that i took from GetProcAddress

        push    0
        push    offset header
        push    offset text
        push    0
        call    addressOfProcedureOfDLL

        push    handlerToModule
        call    _FreeLibrary@4

        push    0
        call    _ExitProcess@4

end _StartOfProgram

And here is result of 32-bit version of program: Result of 32-bit version of program

Upvotes: 0

Views: 1300

Answers (1)

Developer
Developer

Reputation: 25

The problem was been solved in comments. As said by @Peter Cordes and @David Wohlferd, I needed to publish my label in my program by directive "public" and then writing the name of the label, or rewrite my entry-point-label with using directive "proc" and "endp" with name of label at beginning of this directives.


I prefer a solution through the "public" directive, because I think it is closer to low-level programming. In this case, I had to make my label public in my program using the "public" directive, and then write the name of the label at the end of it, to become available to external programs. The MASM assembler, apparently, gave an error due to fact that it did not see it accessible from the outside and therefore did not consider it correct to assign it as the entry point, although it could guess that if I specify it as entry point, then it is available for switching to it from the outside. Apparently, the developers of MASM didn't do this.

Here is an example of using directive "public" in my program (I used directive "public"): public StartOfProgram

And I noticed that I can put it anywhere in my code.

Here is an example of using directive "proc" and "endp" in my program:

StartOfProgram proc     ; - Beginning of this directivical procedure

; ... there may be the code itself inside this directivical procedure

StartOfProgram endp     ; - End of this directivical procedure

My code in the question had other errors, separate from the theme of this question; I've corrected it there.

Upvotes: 1

Related Questions