【发布时间】:2023-12-23 10:36:01
【问题描述】:
我正在尝试制作一个打印“Hello!”的简单汇编程序。一次,等待一秒钟,然后再次打印。由于 sleep 函数在汇编中相对复杂,而且我不太擅长,所以我决定使用 C++ 来制作 Sleep 子例程。这是 C++ 程序:
// Sleep.cpp
#include <thread>
#include <chrono>
void Sleep(int TimeMs) {
std::this_thread::sleep_for(std::chrono::milliseconds(TimeMs));
}
然后我使用“gcc -S Sleep.cpp”将此睡眠函数编译为汇编程序,然后使用“gcc -c Sleep.s”将其编译为目标文件
我正在尝试从程序集中调用这个 C++ 子例程。我听说您通过将 C++ 子例程推入堆栈来为它们提供参数,这是我目前的汇编代码:
global _main
extern _puts
extern Sleep
section .text
_main:
push rbp
mov rbp, rsp
sub rsp, 32
;Prompt user:
lea rdi, [rel prompt] ; First argument is address of message
call _puts ; puts(message)
push 1000 ; Wait 1 second (Sleep time is in milliseconds)
call Sleep
lea rdi, [rel prompt] ; Print hello again
call _puts
xor rax, rax ; Return 0
leave
ret
section .data
prompt:
db "Hello!", 0
这两个文件都保存到桌面/程序。我正在尝试使用 NASM 和 GCC 编译它,我的编译器调用是:
nasm -f macho64 Program.asm && gcc Program.o Sleep.s -o Program && ./Program
但我得到了错误:
"Sleep", referenced from:
_main in Program.o
(maybe you meant: __Z5Sleepi)
"std::__1::this_thread::sleep_for(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000000000l> > const&)", referenced from:
void std::__1::this_thread::sleep_for<long long, std::__1::ratio<1l, 1000l> >(std::__1::chrono::duration<long long, std::__1::ratio<1l, 1000l> > const&) in Sleep-7749e0.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
将代码更改为“extern __Z5Sleepi”并调用“__Z5Sleepi”而不是 Sleep 似乎无法解决问题。 (我收到相同的错误消息,只是没有“也许你的意思是 __Z5Sleepi”位。我还尝试使用 _Sleep 而不是 Sleep,但没有成功。)我做错了什么?如何正确使用此 C++ 子例程并将其与我的汇编程序链接?到目前为止,我使用的方法是否完全错误?
非常感谢任何帮助,浏览堆栈溢出,似乎有很多关于此的问题,但实际上没有一个问题进入链接过程。 (而且他们似乎在询问将程序集与 C++ 链接,而不是 C++ 与程序集。)我正在使用 NASM 和 GCC 进行编译,我的平台是 Mac OSX。
【问题讨论】:
-
首先,使用
g++进行链接。要摆脱名称混淆,请在您的 c++ 源代码中使用extern C。 -
@Jester 抓紧最后一条评论。我的 asm 文件仍在使用 __Z5Sleepi 而不是 Sleep。现在,使用 _Sleep 程序编译并执行。除了现在,当它应该暂停 1 秒时,我得到一个分段错误。知道为什么会这样吗?
-
你可能知道(因为你正确地将它用于
puts)你必须把参数放在rdi而不是堆栈上。因此push 1000应该是mov edi, 1000。 -
arg-passing 部分是What are the calling conventions for UNIX & Linux system calls on i386 and x86-64 的副本。在某处也可能存在名称修改部分的副本。是的,
extern "C"How to call C++ functions in my assembly code -
即使你得到了这个工作,我强烈建议你用 C++ 编写你的 main 函数并让它调用你的汇编函数。也使用 g++ 链接,@Jester 也推荐。这将确保正确初始化 C++ 运行时库。