使用main和scanf/printf的Mac OS X 32位nasm汇编程序?
我花了一整天的时间试图编译一些简单的程序,但到目前为止运气很差。我想做的是编译和运行用nasm汇编编写的程序 我已升级到最新的nasm(v2.10.09)。所以让我跳进代码,因为我还不太了解这些东西。下面是一段在linux上运行的汇编代码,使用使用main和scanf/printf的Mac OS X 32位nasm汇编程序?,c,macos,gcc,assembly,nasm,C,Macos,Gcc,Assembly,Nasm,我花了一整天的时间试图编译一些简单的程序,但到目前为止运气很差。我想做的是编译和运行用nasm汇编编写的程序 我已升级到最新的nasm(v2.10.09)。所以让我跳进代码,因为我还不太了解这些东西。下面是一段在linux上运行的汇编代码,使用elf格式和链接的witch gcc(注释是我对发生的事情的理解): 没什么太大的。然而,我该如何在OSX上实现这一点呢?我甚至无法以任何方式编译/链接它。如果它编译了,我就不能链接它(关于i386和x86的一些东西不能链接在一起(我知道,但如何修复它?)
elf
格式和链接的witch gcc(注释是我对发生的事情的理解):
没什么太大的。然而,我该如何在OSX上实现这一点呢?我甚至无法以任何方式编译/链接它。如果它编译了,我就不能链接它(关于i386和x86的一些东西不能链接在一起(我知道,但如何修复它?)。我试过十几种方法,但都没有成功
进一步了解如何在OS X组件上printf
和scanf
下面是另一个徒劳的scanf
和printf
返回值的尝试(这一个实际编译和链接,甚至运行!):
使用:nasm-f macho-o test.o test.asm编译它,并将它与d-lc-o test-arch i386 test.o-macosx\u version\u 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\u 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风格程序的入口点。因为MacOSX连接的工作方式;在生成可执行文件时,当链接器拉入/usr/lib/crt1.o
时,必须将其命名为\u main
,以匹配链接器正在查找的作为默认程序入口点的符号名称(如果对文件执行nm操作,您将看到一个条目,如:U\U main
。类似地,所有库例程都以一个前导下划线开头,因此如果要使用它们,您必须使用该前缀
其次,MAC OS调用约定要求所有调用的堆栈对齐为16字节,这意味着您必须确保堆栈指针在每个点上都相应对齐。在主例程的入口点,您已经知道,由于堆栈中存储的返回地址用于返回,因此未对齐这意味着,如果你想进行一次调用,你必须将堆栈向下移动至少12个字节才能进行调用
有了这条信息,我们就不必再使用ebp了,只需将esp专门用于代码目的
假设序言为:
bits 32
extern _printf
global _main
section .data
message db "Hello world!", 10, 0
section .text
_main:
进入\u main
时,重新对齐堆栈:
sub esp, 12
add esp, 12
mov dword[esp], output_string
接下来,我们将消息的地址存储到esp指向的地址中:
mov dword[esp], message
然后我们称之为printf:
call _printf
然后我们恢复堆栈:
sub esp, 12
add esp, 12
mov dword[esp], output_string
设置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\u string
的地址存储在esp
中,然后将limit的地址存储在esp+4
中,然后调用scanf:
mov dword[esp], scan_string
mov dword[esp + 4], limit
call _scanf
我们现在应该将扫描的值存储在限制内存位置
打印此邮件的旁边:
output_string db "Value %d", 10, 0
接下来,我们将输出字符串的地址放在堆栈上:
sub esp, 12
add esp, 12
mov dword[esp], output_string
将限制地址的值读入eax寄存器,并将其放入esp+4
-即printf的第二个参数:
mov eax, [limit]
mov dword[esp + 4], eax
call _printf
接下来,我们调用exit,因此我们必须将exit代码存储在堆栈中并调用\u exit
函数-这与简单的print变量不同,因为我们实际上是在调用exit,而不是简单地返回
mov dword[esp], 0
call _exit
至于一些问题:
为什么要对齐
因为Mac OS X就是这样做的
为什么推得不够好
是的,但我们在例程开始时对齐了堆栈,对齐的堆栈是一个正常运行的堆栈,通过按下和弹出您正在扰乱对齐。这是使用ebp
寄存器而不是esp
寄存器的目的之一
如果我们使用ebp
寄存器,函数prolog将如下所示:
push ebp
mov ebp, esp
sub esp, 8 ; in this case to obtain alignment
add esp, 8
pop ebp
epilog的功能如下所示:
push ebp
mov ebp, esp
sub esp, 8 ; in this case to obtain alignment
add esp, 8
pop ebp
您也可以在其中放入对称pusha/popa调用,但是如果您不使用寄存器,为什么要使堆栈复杂化呢
有关32位函数调用机制的更好概述,请参阅,ABI函数调用指南提供了有关参数的更多详细信息