我花了一整天的时间试图编译一些简单的程序,但到目前为止运气很小。我要做的是编译和运行以nasm汇编语言编写的程序。
我已升级到最新的nasm(v2.10.09)。现在让我进入代码,因为我对这些事情还不了解。这是一块汇编代码,它们在Linux上以elf
格式和链接的gcc gcc运行(评论是我对正在发生的事情的理解):
bits 32
extern printf
global main
section .data
message db "Hello world!", 10, 0
section .text
main:
pushad ;push all registers on stack
push dword message ;push the string on stack
call printf ;call printf
add esp, 4 ;clear stack
popad ;pop all registers back
ret ;return to whoever called me
没什么大的。但是,我该如何在OS X上使用它呢?我什至无法以任何方式来编译/链接。如果它编译了,我就无法链接它(关于i386和x86的东西不能链接在一起(我知道,但是如何解决?)。我已经尝试了十二种方法,但是都没有运气。
此外,如何在OS X组件上printf
和scanf
?
这是scanf
和printf
值返回的另一种徒劳尝试(实际上是编译和链接-甚至运行!):
[bits 32] ; why the []?
section .data
input_string db "Enter limit: %d", 0
output_string db "Value %d", 10, 0
counter dd 10
limit dd 0
;nasm -f macho -o test.o test.asm
;ld -lc -o test -arch i386 test.o -macosx_version_min 10.7
section .text
global start
extern _printf
extern _scanf
extern _exit
start:
push ebp ;push stack base
mov ebp, esp ;base is now current top
and esp, 0xFFFFFFF0 ;align the stack - WHY? I just googled this?
sub esp, 16 ;16 bytes for variables - 16 to keep the stack "aligned". WHY?
mov dword[esp], input_string ;ordinary push just isint good nuff for mac... WHY?
mov dword[esp + 4], limit
call _scanf ;does scan something but doesnt print the message, it just scans and then prints the message
mov eax, [limit] ;since i cant push this lets put it in eax first
mov dword[esp + 8], output_string ;push the output string. WHY again MOV?
mov dword[esp + 12], eax ;and the previusly "scanned" variable
call _printf ;print it out
mov dword[esp], 0 ;return value
call _exit ;return
[用:nasm -f macho -o test.o test.asm
编译,并用d -lc -o test -arch i386 test.o -macosx_version_min 10.7
链接。无法正常工作。在linux上,此scanf和printf东西超级容易。怎么了可以简化吗?
我不想在这个问题上添加更多的内容,因为人们有时会看到一个大问题,并且“嗯,太长了,不会读”。但是,如果有人需要更多信息,我会尽力而为。
请帮我,因为我不知道。
编辑第一个使用nasm -f macho -o out.o test.asm
进行编译,但没有使用gcc -o test out.o
或通过使用ld -lc -o test -arch i386 out.o -macosx_version_min 10.7
进行链接,并附加平面-arch i386
也无法解决。如果可以编写“类似于Linux的程序集”,我将很高兴,因为我不必担心堆栈对齐和类似的问题。gcc错误说:
ld: warning: ignoring file out.o, file was built for i386 which is not the architecture being linked (x86_64): out.o
Undefined symbols for architecture x86_64:
"_main", referenced from:
start in crt1.10.6.o
ld: symbol(s) not found for architecture x86_64
和ld错误如下:
Undefined symbols for architecture i386:
"printf", referenced from:
main in out.o
"start", referenced from:
-u command line option
ld: symbol(s) not found for architecture i386
请帮助。
您在询问有关代码的很多问题,而您确实不了解其中的汇编代码。
首先,由于您编写代码的方式,main
例程将成为C样式程序的入口点。由于mac os x链接的工作方式;您必须将其命名为_main
,以匹配链接程序在生成可执行文件时将其拉入/usr/lib/crt1.o
时作为默认程序入口点的链接器所寻找符号的名称(如果您执行在文件中,您将看到类似U _main
的条目。类似地,所有库例程都以前导下划线开头,因此如果要使用它们,则必须使用该前缀。
[其次,MAC OS调用约定要求所有call
的堆栈都对齐16字节,这意味着您必须确保堆栈指针在每个点都进行了相关对齐。在主例程的入口点,您已经知道由于返回地址存储在堆栈中以便从main返回而错位了。这意味着,即使您要进行单个调用,也必须将堆栈向下移动至少12个字节才能进行调用。
有了这些信息,我们将省略ebp
的内容,而仅出于代码目的而使用esp。
这是假设的序言:
bits 32 extern _printf global _main section .data message db "Hello world!", 10, 0 section .text _main:
[进入
_main
时,重新对齐堆栈:
sub esp, 12
接下来,我们将消息的地址存储到esp指向的地址中:
mov dword[esp], message
然后我们称为printf:
call _printf
然后我们恢复堆栈:
add esp, 12
设置
main
的返回码,并返回:
mov eax, 0 ret
MAC OS X的ABI使用
eax
作为例程的返回码,只要它适合寄存器即可。编译并链接代码后:
nasm -f macho -o test.o test.asm ld -o test -arch i386 test.o -macosx_version_min 10.7 -lc /usr/lib/crt1.o
它运行并打印消息,并以
0
退出。
接下来,我们将使用您的扫描和打印示例。
首先,scanf仅扫描,您不能在其中看到提示;它根本无法正常工作,因此您必须将提示与扫描分开。我们已经向您展示了如何进行打印,现在我们需要显示的是scanf。
在数据部分中设置变量:
存储在scan_string db "%d", 0 limit dd 0
首先将
scan_string
的地址存储在esp
中,然后将限制的地址
esp + 4
中,然后调用scanf:mov dword[esp], scan_string mov dword[esp + 4], limit call _scanf
我们现在应该将扫描的值存储在极限存储位置中。
下一步打印此消息:
output_string db "Value %d", 10, 0
接下来,我们将output_string的地址放在堆栈上:
mov dword[esp], output_string
将极限地址的值读入eax寄存器,并将其放入
esp + 4
-即printf的第二个参数:
mov eax, [limit] mov dword[esp + 4], eax call _printf
[下一步,我们正在调用exit,因此我们必须将退出代码存储在堆栈中并调用
_exit
函数-这与简单的打印变量不同,因为我们实际上是在调用exit,而不是简单地返回。
mov dword[esp], 0 call _exit
关于某些问题:
为什么对齐?
因为这是Mac OS X的工作方式
为什么推力不够好?
是,但是我们在例程开始时对齐了堆栈,对齐的堆栈是一个正常运行的堆栈,通过推动和弹出操作您弄乱了对齐方式。这是使用
ebp
寄存器而不是esp
寄存器的目的之一。如果使用
ebp
寄存器,函数序言将如下所示:
push ebp mov ebp, esp sub esp, 8 ; in this case to obtain alignment
和函数结尾类似:
add esp, 8 pop ebp
您也可以在其中放入对称的pusha / popa调用,但是如果您不使用寄存器,为什么要使堆栈复杂化。
OS X Developer guide上有32位函数调用机制的更好概述,ABI函数调用指南提供了有关参数传递和返回方式的更多详细信息。它基于AT&T System V ABI for the i386,列出了一些区别: