【问题标题】:Assembler MASM (64-bit) not recognizes entry point and throws error汇编程序 MASM(64 位)无法识别入口点并抛出错误
【发布时间】:2022-12-11 02:55:02
【问题描述】:

我正在研究 Windows 上 x86 系列处理器架构(32 位和 64 位)的汇编程序。并不是说我是新手,但我可能不是什么都懂,至少对 MASM 汇编程序的语法,就像它看起来的那样。我使用位于属于 Visual Studio 的文件夹中的 MASM 汇编器(用于 64 位程序):
..\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 位程序组装并工作。然后我将其代码翻译为 64 位架构(那里的代码几乎不需要更改)。但是,当用 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

我花了大约 2 周或一个月的时间寻找此错误的解决方案,但没有找到

一般来说,他曾经给出错误消息,据称有一些未解析的符号“WinMainCRTStartup”,但最近我有点意识到他做了这样的入口点,因为我没有在控制台中明确指定入口点(通过命令“@987654326 @”,在上面的控制台中),但是有关“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
includelib  "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.19041.0\um\x64\User32.lib"

extern      LoadLibraryA:near    ; I load from static libraries functions which 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, here, in 64-bit, i finally not put

        push    qword ptr nameOfDLL
        call    LoadLibraryA                    ; I dynamically connect DLL so that i can then take function from it
        
        mov     handlerToModule, eax
        
        push    qword ptr nameOfProcedureOfDLL
        push    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
        call    GetProcAddress
        
        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
        
        push    0
        push    qword ptr header
        push    qword ptr text
        push    0
        call    qword ptr 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
        
        push    qword ptr handlerToModule
        call    FreeLibrary

        push    0
        call    ExitProcess

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 functions which used in this program
extern      _GetProcAddress@8:near
extern      _FreeLibrary@4:near
extern      _ExitProcess@4:near

.model flat

; **** 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 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

【问题讨论】:

  • 不要只将 StartOfProgram 作为标签,而是将其声明为 StartOfProgram proc。您需要在结束前添加一个匹配的StartOfProgram endp
  • @David Wohlferd,我只想使用我的“StartOfProgram”标签显示的入口点,因为它在 32 位 MASM 中,以及此处。至少因为我怀疑它们在某种程度上是高级的,并且作为宏,它们可以在我的程序中制作我没有输入的代码。有没有办法不用 proc 和 endp 呢?
  • 由于某些原因- 可能是为了与 Windows 约定一致,即 C 名称前面加上前导下划线以获取 32 位代码中的 asm 符号名称,但不是 64 位代码中的。对于从未从 C 引用的符号名称,是的,任何一种方式都应该没问题。
  • proc/endp 不应该引入额外的说明,如果你不使用任何可以实现这种情况的 MASM 东西,所以至少试一试看看 David 的建议是否有效。如果这有效但一个简单的标签不起作用,那仍然是一个有趣的问题,即为什么 MASM 在 32 位中工作时以这种方式设计。
  • 虽然我不明白您对 proc/endp 的反对意见(正如 Peter 所说,他们没有添加任何说明),但如果您愿意,也可以只添加 public StartOfProgram。此外,虽然 LoadLibrary 可用于以这种方式调用函数,但还有其他选择。例如,查看您如何调用 LoadLibrary。这只是一个功能,对吧?您通过将符号声明为 extern 并在适当的库中链接来调用它。 Process Loader 负责为您加载 kernel32.dll 并查找地址。您应该能够对 MessageBoxA 执行相同的操作。

标签: windows assembly x86 x86-64 masm


【解决方案1】:

该问题已在 cmets 中得到解决。正如@Peter Cordes 和@David Wohlferd 所说,我需要通过指令“public”在我的程序中发布我的标签,然后写下标签的名称,或者使用指令“@987654322”重写我的入口点标签@" 和 "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

但是我的问题代码还有其他错误,但这不是这个问题的主题,所以,当我在我的程序中更正它时,我会在那里更正它。

【讨论】:

  • @Michael Petch,我们讨论的是该程序 64 位版本中的问题。只有 32 位版本的程序可以在“end”指令末尾使用入口点标签。在 64 位程序中,MASM 禁止这样做,并且只允许在末尾放置不带标签的“end”指令,然后它只允许在 MASM 的命令行中定义入口点,附加命令链接器“/entry:
  • 啊,这就是为什么你可以在 32 位版本中使用非导出 (public) 符号的原因,因为 end foo.obj 文件中使用代码中的适当点创建元数据。否则,您需要一个公共标签来创建一个可以在外部看到的符号,以便 /entry:foo 找到它。所以这一切都是有道理的。至少事后看来,这个问题应该提到 32 位根本没有使用 /entry: arg,因为使用了 end foo 的功能
  • 虽然 public 可能会“感觉”到更低的级别,但我怀疑它是否真的改变了输出。不过,如果“低级别”是您所追求的,也许可以看看this。它甚至不使用链接器,每个输出字节都由汇编器指定。虽然我不建议您以这种方式编写生产代码,但它很有趣并且很有教育意义。它还展示了如何在 x64 下调用 MessageBoxA,就是这样。请注意,通过指定一个导入表(链接器通常为您做的事情),它不需要 Loadlibrary/GetProcAddress。
  • @DavidWohlferd:我会说如果你想要更“低级”的东西而不会有汇编程序在你背后做神奇奇怪事情的风险,请使用 NASM!它根本没有proc/endp,只有标签(和global foo而不是public foo)。它具有更一致的语法,其中 [] 始终是内存引用,但没有 [] 的任何内容都不是。不像MASM的废话(Confusing brackets in MASM32)。 NASM 也更好地使用多字符常量作为整数文字,只按源字节顺序工作。
  • @PeterCordes:如果您查看我提供的链接,它使用的是 NASM。
猜你喜欢
  • 2016-01-20
  • 2023-02-01
  • 2015-06-14
  • 2012-05-29
  • 2018-02-08
  • 1970-01-01
  • 2011-09-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多