assembly 汇编程序MASM(64位)无法识别入口点并引发错误

fsi0uk1n  于 2022-12-13  发布在  其他
关注(0)|答案(1)|浏览(233)

我正在学习Windows上x86系列处理器架构(32位和64位)的汇编程序。这并不是说我是一个初学者,但我可能不知道一切,至少对MASM汇编程序的语法不了解。
我使用的MASM汇编程序(用于64位程序)位于Visual Studio的文件夹中:安装了“..\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\ml64.exe“Visual Studio 2019,我使用它的文件夹中的MASM汇编程序,我自己有Windows 7。
我的程序是为32位系统编写的,它通常是由MASM为32位程序汇编的,并且可以工作。(而且那里的代码需要做一些修改)。但是,当用MASM为64位程序组装它时,MASM给出了一个错误消息,声称有一些未解析的“StartOfProgram“符号。下面是控制台中的内容:

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

我花了大约两个星期或一个月的时间寻找这个错误的解决方案,但我没有找到。
通常,它会给予一个错误消息,声称存在一些未解析的符号“WinMainCRTStartup“,但最近我意识到它创建了这样一个入口点,因为我没有在控制台中显式指定入口点(通过命令“/entry:“,这是在控制台从上面),但关于“unresolved external symbol“的问题仍然存在,即使我在需要的地方设置了入口点(即在“StartOfProgram“上)。
下面是我的64位版本的程序的代码,它只需要在弹出窗口中输出“Hello world”:

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

下面是我的32位版本的程序代码(通常是汇编和工作的):

.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

下面是32位版本程序的结果:Result of 32-bit version of program

kh212irz

kh212irz1#

这个问题在注解中得到了解决。正如@Peter Cordes和@大卫Wohlferd所说,我需要通过指令“public“在我的程序中发布我的标签,然后写入标签的名称,或者使用指令“proc“和“endp“重写我的入口点标签,并在该指令的开头加上标签的名称。
我更喜欢通过“public“指令的解决方案,因为我认为它更接近底层编程。在这种情况下,我不得不使用“public“指令在程序中公开我的标签,然后在标签的末尾写入标签的名称,以便外部程序可以使用。显然,MASM汇编程序我给出了一个错误,因为它没有看到它可以从外部访问,因此不认为它是正确的分配它作为入口点,虽然它可以猜测,如果我指定它作为入口点,那么它可以从外部切换到它。显然,MASM的开发人员没有这样做。
下面是在我的程序中使用指令“public“的示例(我使用了指令“public“):public StartOfProgram
我注意到我可以把它放在代码的任何地方。
下面是在我的程序中使用指令“proc“和“endp“的示例:

StartOfProgram proc     ; - Beginning of this directivical procedure

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

StartOfProgram endp     ; - End of this directivical procedure

我在问题中的代码有其他错误,与这个问题的主题无关;我已经更正了。

相关问题