Mac OS X nasm程序集无法正常工作

时间:2013-11-19 21:27:51

标签: c macos gcc assembly nasm

我花了整整一天试图编写一些简单的程序,但到目前为止运气很少。我想要做的是编译和运行用nasm汇编编写的程序。

我已升级到最新的nasm(v2.10.09)。所以,让我跳到代码中,因为我对这些事情还不太了解。下面是一大堆汇编代码,它使用elf格式在linux上运行并链接了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程序集的printfscanf怎么办?

这是scanfprintf值的另一个徒劳的尝试(这个实际上编译和链接 - 甚至运行!):

[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

使用以下代码编译:{{1​​}}并将其与nasm -f macho -o test.o test.asm相关联。不能正常工作。在linux上它超级容易到这个scanf和printf thingie。什么在这里?可以更简单吗?

我不想在这个问题上添加更多东西,因为人们有时会看到一个大问题和事情,呃,太久了,不会阅读"。但如果有人要求更多信息,我会尽我所能。

请帮助我,因为我无法弄明白。

修改 第一个使用d -lc -o test -arch i386 test.o -macosx_version_min 10.7进行编译,但使用nasm -f macho -o out.o test.asm或使用gcc -o test out.o进行doest链接,并且追加展平ld -lc -o test -arch i386 out.o -macosx_version_min 10.7也无法解决。我很乐意,如果我能写出那样的" linux就像"因为我不必担心堆栈对齐和类似的东西。 gcc错误说:

-arch i386

和ld错误如下:

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

请帮忙。

1 个答案:

答案 0 :(得分:18)

你问了很多关于代码的问题,而你真的不明白那里的汇编代码。

首先,由于您编写代码的方式,main例程将成为C风格程序的入口点。由于mac os x linkages的工作方式;您必须将其命名为_main,以便在生成可执行文件时({1}}生成可执行文件时将链接器查找的符号名称与默认程序入口点相匹配(如果这样做)在文件的nm处你会看到如下条目:/usr/lib/crt1.o。类似地,所有库例程都以前导下划线开头,因此如果要使用它们,则必须使用该前缀。

其次,MAC OS调用约定要求所有U _main的堆栈的16字节对齐,这意味着您必须确保堆栈指针在每个点处相关对齐。在主例程的入口点,您已经知道由于返回地址存储在堆栈中以便从main返回,因此您未对齐。这意味着如果你想进行一次调用,你将不得不将堆栈向下移动至少12个字节来进行调用。

有了这条信息,我们将省略call,并且只为了代码的目的而使用esp。

这是假设的序言:

ebp

进入bits 32 extern _printf global _main section .data message db "Hello world!", 10, 0 section .text _main: 后,重新对齐堆栈:

_main

接下来,我们将消息的地址存储到esp:

指向的地址中
sub esp, 12

然后我们打电话给printf:

mov dword[esp], message

然后我们恢复堆栈:

call _printf

设置add esp, 12 的返回码,并返回:

main

MAC OS X的ABI使用mov eax, 0 ret 作为例程的返回码,只要它适合寄存器即可。一旦编译并链接了代码:

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 退出。

接下来我们将讨论您的扫描和打印示例。

首先,scanf只扫描,你不能在那里有提示;它根本不起作用,所以你必须从扫描分割提示。我们已经向您展示了如何进行打印,现在我们需要显示的是scanf。

在数据部分设置变量:

0

首先将scan_string db "%d", 0 limit dd 0 的地址存储在scan_string中,然后将限制的地址存储在esp中,然后调用scanf:

esp + 4

我们现在应该将扫描的值存储在限制内存位置。

接下来打印此消息:

mov dword[esp], scan_string
mov dword[esp + 4], limit
call _scanf

接下来,我们将output_string的地址放在堆栈上:

output_string   db  "Value %d", 10, 0

将限制地址的值读入eax寄存器并将其放入mov dword[esp], output_string - 即printf的第二个参数:

esp + 4

接下来,我们调用exit,因此我们必须将退出代码存储在堆栈中并调用mov eax, [limit] mov dword[esp + 4], eax call _printf 函数 - 这与简单的print变体不同,因为我们实际上是在调用exit,而不是只是回来。

_exit

至于一些问题:

为什么要对齐?

  

因为Mac OS X就是这样做的

为什么推得不够好?

  

确实如此,但是我们在例程的开始处对齐堆栈,并且对齐的堆栈是一个正常运行的堆栈,通过推送和弹出你正在弄乱对齐。这是使用mov dword[esp], 0 call _exit 寄存器而不是ebp寄存器的目的之一。

如果我们使用esp寄存器,则函数prolog将如下所示:

ebp

和函数epilog看起来像:

push ebp
mov ebp, esp
sub esp, 8 ; in this case to obtain alignment

你也可以在那里放入对称的pusha / popa调用,但是如果你没有使用寄存器,为什么要使栈复杂化。

OS X Developer guide上更好地概述了32位函数调用机制,ABI函数调用指南提供了有关参数传递和返回方法的更多详细信息。它基于AT&T System V ABI for the i386,列出了一些差异:

  • 返回结构的不同规则
  • 在函数调用
  • 处,堆栈是16字节对齐的
  • 大数据类型(大于4个字节)保持自然对齐
  • 大多数浮点运算使用SSE单元而不是x87 FPU执行,除非在长双精度值上运行。 (IA-32环境默认为x87 FPU的64位内部精度。)