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