在Mac OSX上编译NASM

5

我在学校编写编译器,最后一个里程碑是生成汇编代码。尝试学习NASM。从一开始开始,http://www.cs.lmu.edu/~ray/notes/nasmexamples/,尝试编译Hello World。

; ----------------------------------------------------------------------------
; helloworld.asm
;
; This is a Win32 console program that writes "Hello, World" on one line and
; then exits.  It needs to be linked with a C library.
; ----------------------------------------------------------------------------

    global  _main
    extern  _printf

    section .text
_main:
    push    message
    call    _printf
    add     esp, 4
    ret
message:
    db      'Hello, World', 10, 0

在Windows下,组装、链接并运行此程序的方法如下:
nasm -fwin32 helloworld.asm
gcc helloworld.obj
a

在Linux下,您需要从函数名中删除前导下划线,并执行以下操作:
nasm -felf helloworld.asm
gcc helloworld.o
./a.out

但是我使用的是OSX操作系统。找到了这个小资源:http://salahuddin66.blogspot.com/2009/08/nasm-in-mac-os-x.html。在Mac OS X中,我们应该使用macho格式...

nasm -f macho -o hello.o hello.asm

...对于链接器(我们需要指定入口点)...

ld -e main -o hello hello.o

但是当我这样做时......
Undefined symbols:
    "printf", referenced from:
        _main in hello.o
ld: symbol(s) not found for inferred architecture i386

抱歉,我知道这是一篇很长的阅读内容。而且我怀疑这里没有太多NASM编程人员,但还是值得一试对吧?我会非常感激任何帮助。


那页笔记已更新至http://cs.lmu.edu/~ray/notes/nasmtutorial/。现在包含更多关于macOS的信息。 - Ray Toal
2个回答

6

你的示例程序是一个32位的Windows程序。如今,编写64位程序可能更好。

要将其转换为64位macOS程序,您应该确保拥有最新版本的nasm,并安装了gcc。

程序现在应该看起来像这样:

; ----------------------------------------------------------------------------------------
; This is an macOS console program that writes "Hola, mundo" on one line and then exits.
; It uses puts from the C library.  To assemble and run:
;
;     nasm -fmacho64 hola.asm && gcc hola.o && ./a.out
; ----------------------------------------------------------------------------------------

          global    _main
          extern    _puts

          section   .text
_main:    push      rbx                     ; Call stack must be aligned
          lea       rdi, [rel message]      ; First argument is address of message
          call      _puts                   ; puts(message)
          pop       rbx                     ; Fix up stack before returning
          ret

          section   .data
message:  db        "Hola, mundo", 0        ; C strings need a zero byte at the end

您会注意到一些不同之处:
  • 在64位环境下,第一个参数在RDI中,而不是在栈上
  • 在调用之前,堆栈必须对齐到16字节边界。当进入main函数时,操作系统已经将main函数的返回地址(8个字节)放在了堆栈上,因此在调用puts函数之前推送rbx可以使堆栈重新对齐。
  • 此外,在macOS上使用nasm需要使用rel。

+1 展示如何使用 gcc 而不是 ld。出于某种原因,对我来说,gcc 在编译 xxx.o 方面的效果要好得多。 - Shades

3

函数printf在一些C库中定义(在Linux上,它可能在/lib/libc.so.6/lib/x86_64-linux-gnu/libc.so.6中),因此您需要链接到该库(我不知道MacOSX上是什么)。

您可以直接进行系统调用,即syscalls(我不知道MacOSX的细节,也不知道它们是否公开可用)。在Linux上,Linux汇编指南提供了详细信息。您需要找到相应操作系统的等效详细信息。

(顺便说一句,对于这样的任务,完全使用免费软件肯定更容易,因为它们的规范和源代码是可用的;对于像MacOSX这样的专有软件,您需要从软件提供商那里获得,有时非常昂贵)


请注意,GUI 及其工具称为 MacOS X 是专有的,但真正的系统称为 Darwin,是开源的。我同意 Linux 在这方面有更好的文档,但在 Mac 上汇编绝不是一件“封闭”的事情。规格说明都在那里,也许缺乏流行的教程只是因为对汇编的兴趣不够。大多数时候这并不值得(除非你正在编写编译器 :-) 如果你熟悉 BSD 中的汇编(与 Linux 内核有很多差异),MacOS X 的汇编就是一个简单的跳跃,有时只需要检查头文件即可。 - sidyll
1
抱歉,我对此一无所知。是标准库stdio.h吗?如果我找到了这个库,我该如何链接它? - savinger
1
stdio.h是一个头文件,而不是库。我猜你需要在MacOSX上使用一些lib*.dylib库。 - Basile Starynkevitch
stdio.h 只在 C 和 C++ 中有用和意义(然后编译器应该知道如何找到,可能需要额外的 -I 选项)。在汇编中不需要它。 - Basile Starynkevitch
要了解在编译helloworld.cgcc正在做什么,请使用gcc -v运行它。 - Basile Starynkevitch
OS X使用免费软件来完成这些任务,即LLVM及其前端clang。 - no92

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接