我目前正在为一个自定义编译器项目学习汇编语言。然而,我不能做一个单一的工作汇编程序。我有一个64位操作系统的AMD CPU和一个基于x64的处理器。我正在使用Windows 11。
我不想使用c库,因为它破坏了尽可能低层次编码的目的,这是我的目标。
我已经尝试了很多不同的程序和链接器。
Assembly Syscalls in 64-bit Windows这不包含任何工作代码。
Hello world在windows assembly中使用nasm我认为这是32位系统,但我不知道我需要改变什么。我把“eax”改成了“rax”,这个程序编译并运行了,但是没有打印任何东西。我还可以在代码中添加随机字母,没有任何错误,退出代码仍然为0。(仅一个GoLink警告)
Setting Exit Status in NASM for Windows同样的问题。我总是得到一个退出代码,编译器不关心任何语法错误。
我也试过使用gcc和ld,但没有成功。
我做错了什么?
编辑我试过这个:如何在Windows下用汇编语言编写Hello World?但是link.exe似乎只适用于visual studio(我正在使用vscode)。当我使用GoLink时,我得到以下错误:
Error!
The following symbol was not defined in the object file or files:-
MessageBoxA
Output file not made
Edit 2当我使用gcc时,我得到以下错误:
C:/msys64/ucrt64/bin/../lib/gcc/x86_64-w64-
mingw32/13.1.0/../../../../x86_64-w64-mingw32/bin/ld.exe:
out.obj:out.asm:(.text+0x1e): undefined reference to `MessageBoxA'
collect2.exe: error: ld returned 1 exit status
如果我删除消息打印代码,它工作正常,我终于得到了我选择的退出代码!MessageBoxA函数是在另一个库中还是什么?我该怎么解决这个问题?
编辑3我发现了这段代码并复制了它,但同样,它不工作。(编者按:它是32位调用约定的32位代码。)
global _main
extern GetStdHandle
extern WriteFile
extern ExitProcess
section .text
_main:
; DWORD bytes;
mov ebp, esp
sub esp, 4
; hStdOut = GetstdHandle( STD_OUTPUT_HANDLE)
push -11
call GetStdHandle
mov ebx, eax
; WriteFile( hstdOut, message, length(message), &bytes, 0);
push 0
lea eax, [ebp-4]
push rax
push (message_end - message)
push message ; ERROR
push rbx
call WriteFile
; ExitProcess(0)
xor rcx, rcx
call ExitProcess
; never here
hlt
message: db "Hello, World", 8
message_end:
当我运行以下命令时:
nasm -f win64 out.asm && gcc -o out.exe out.obj -nostdlib -lkernel32
.我得到一个截断错误(idk这意味着什么)
out.obj:out.asm:(.text+0x18): relocation truncated to fit:
IMAGE_REL_AMD64_ADDR32 against `.text'
collect2.exe: error: ld returned 1 exit status
我有点恼火,为什么这么难执行这样一个简单的程序。我只需要一个nasm的hello world程序,x64_64 windows,带有visual studio代码,没有c库。
1条答案
按热度按时间6vl6ewon1#
经过一番努力,我终于让它工作了。这里是这个答案的第一个版本中的代码的清理版本(由@PeterCordes)。(同样,我使用的是AMD基于x64的处理器的Windows。我在Visual Studio Code中编写代码。
用nasm和gcc组装链接:
在这个例子中省略:stack-unwind metadata用于更改RSP的指令(
sub rsp,40
)。只要没有SEH或C++异常发生,没有它也能正常运行,但是调试器从WriteFile
内部通过main
回溯到它的调用者是不起作用的。* Under what conditions do I need to set up SEH unwind info for an x86-64 assembly function? * 查看更多。如果你是asm的初学者,只是想了解“正常”的东西是如何工作的,并且不打算在生产代码中实际使用手写的asm,那么你根本不需要担心SEH和堆栈展开信息。关于这与 * How To Properly call 64 Bit Windows API In Assembly * 中的代码之间的差异的评论,它最初基于:
静态存储(
.data
/.bss
/.rdata
)中唯一需要的数据是字符串本身的ASCII字节。输出的字节数可以指向堆栈空间,我们传递给WriteFile的常量可以只是机器码中的立即数,而不是从静态存储(dq 14
)加载的。一般来说,have the assembler calculate a length instead of hard-coding it也不错。WriteFile
需要存储实际写入的字节数(因为返回值只是BOOL
,不幸的是,不像POSIXssize_t write()
,它返回负数的错误)。因为main
是一个函数,所以它有自己的阴影空间。(The从 * How To Properly call 64 Bit Windows API In Assembly * 复制的示例代码使用了像
NtlpNBytesWritten
这样的奇怪名称作为BSS中的变量,以指向WriteFile
的lpNumberOfBytesWritten
输出arg。这是令人困惑的,因为这些不是低级NT函数,它们是Win32 API。变量本身只是一个双字,所以如果你使用的是静态存储,它的名字中不应该有lp
。注解中的arg名称来自MS的
WriteFile
文档:https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile还有其他的Hello World示例,例如如何在Windows下的程序集中编写Hello World?- 使用
MessageBoxA
,它在user32.dll
中,而GetStdHandle
和WriteFile
在kernel32.dll
中