【问题标题】:How can I run this assembly code on OS X?如何在 OS X 上运行此汇编代码?
【发布时间】:2013-09-12 10:06:08
【问题描述】:

开始学习汇编时,我收到了一些在 Linux 课程中创建的 Hello World 汇编代码。我想让它在 64 位 Mac OS X 上工作。

code.asm

SECTION .data       
    hola:   db "Hola!",10   
    tam:    equ $-hola      

SECTION .text       
    global main     

main:               

    mov edx,tam     
    mov ecx,hola        
    mov ebx,1       
    mov eax,4       
    int 0x80        

    mov ebx,0       
    mov eax,1       
    int 0x80        

这就是我的工作:

nasm -f macho32 -o object.o code.asm
gcc -m32 -o program object.o

这告诉我:

架构 i386 的未定义符号:“_main”,引用自: 从 crt1.10.6.o ld 开始:未找到架构 i386 的符号

搜索这个错误,发现了这个问题:nasm and gcc: 32 bit linking failed (64 bit Mac OS X)

一个回答说

您遇到的问题是您正在创建 32 位 Linux(ELF) 与 Mac OS X 对象格式不兼容的对象文件。 尝试将“-f elf”切换为“-f macho32”。

但我肯定在使用-f macho32。那么问题来了呢?

【问题讨论】:

  • Mac OS X 上的主汇编器似乎是as - Mac OS X Mach-O GNU-based assemblers。我不确定这意味着什么。

标签: macos gcc assembly nasm


【解决方案1】:

我也一直在尝试自学一些入门级的汇编编程,但我遇到了类似的问题。我最初是使用 nasmelf 编译的,但是当我尝试使用 ld 链接目标文件并创建可执行文件时,它不起作用。

我认为您的主要问题"what would the problem be then?" [to get this to run on 64bit MacOSX] 的答案是:您正在使用-f macho32 但希望它在64 位机器上运行,您需要将命令选项更改为-f macho64 当然,这并不能解决您的汇编代码是为不同架构编写的事实(稍后会详细介绍)。

我发现 this handy answer 在正确的命令上用于在此实例中编译和链接您的代码(在您重构汇编代码以使用正确的语法而不是 duskwuff 声明的 *nix 之后):nasm -f macho64 main.asm -o main.o && ld -e _main -macosx_version_min 10.8 -arch x86_64 main.o -lSystem

经过一番搜索,这是我学到的……

  1. 在 Mac 64 位上,使用 as 汇编程序而不是 nasm 可能会更好(如果您想要更原生的东西),但如果您想要更便携的代码(了解差异)。
  2. nasm 默认没有安装 macho64 输出类型
  3. 组装是一件很痛苦的事情(除了这个)

现在我的学习咆哮已经结束了......

这是应该在 MacOSX 64 上使用 nasm 运行的代码(如果您已将 nasm 更新为 macho64,归功于 Dustin Schultz):

section .data
hello_world     db      "Hello World!", 0x0a

section .text
global start

start:
mov rax, 0x2000004      ; System call write = 4
mov rdi, 1              ; Write to standard out = 1
mov rsi, hello_world    ; The address of hello_world string
mov rdx, 14             ; The size to write
syscall                 ; Invoke the kernel
mov rax, 0x2000001      ; System call number for exit = 1
mov rdi, 0              ; Exit success = 0
syscall                 ; Invoke the kernel

我与 MacOSX64 原生的 as 汇编程序一起使用的工作代码:

.section __TEXT,__text

.global start

start:
  movl $0x2000004, %eax           # Preparing syscall 4
  movl $1, %edi                   # stdout file descriptor = 1
  movq str@GOTPCREL(%rip), %rsi   # The string to print
  movq $100, %rdx                 # The size of the value to print
  syscall

  movl $0, %ebx
  movl $0x2000001, %eax           # exit 0
  syscall

.section __DATA,__data
str:
  .asciz "Hello World!\n"

编译命令:as -arch x86_64 -o hello_as_64.o hello_as_64.asm

链接命令:ld -o hello_as_64 hello_as_64.o

执行命令:./hello_as_64

我在旅途中发现的一些有用资源:

ASOSX 汇编器参考https://developer.apple.com/library/mac/documentation/DeveloperTools/Reference/Assembler/Assembler.pdf

在 Mac OSX 上编写 64 位程序集http://www.idryman.org/blog/2014/12/02/writing-64-bit-assembly-on-mac-os-x/

无法使用 ld 链接目标文件Can't link object file using ld - Mac OS X

OSX i386 SysCallshttp://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/mach/i386/syscall_sw.h

OSX 主系统调用定义http://www.opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master

OSX 系统调用https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/syscall.2.html

【讨论】:

  • 如果您还不了解 x86 的 Intel / NASM 和 AT&T 语法,我建议您先学习 NASM 语法。必须使用 gcc -S -masm=intelobjdump -M intel 以该语法输出不太方便,但 Intel insn ref 手册使用 Intel/NASM 语法。对于 3 和 4 操作数指令,颠倒它们的顺序并跟踪哪个可以是立即数或内存操作数的额外障碍可能是一种精神负担。另外,AT&T syntax has a bug with one of the x87 instructions.
  • 对于拆卸,我还是推荐 Agner Fog 的objconv。 (agner.org/optimize。如果您没有找到 OS X 二进制文件,请从源代码构建它。)在 OS X 上,请确保您拥有最新版本的 NASM,因为最近数据部分标签出现了一个令人讨厌的错误。有几个 SO 问题。我其实很喜欢 YASM。它被 x264 和 x265 视频编解码器使用,它们有大量的 asm,并且显然是不只是随机选择汇编程序的项目。
  • 另外,为了将常量移动到寄存器中,最好使用mov edi, 1 而不是mov rdi, 1,以防您的汇编程序认为您想要指令的REX 前缀版本来执行符号扩展mov r64, imm32,或更糟的是movabs r64, imm64。只需执行 32 位 mov r32, imm32 并在写入低位 32 时像往常一样让上位 32 归零即可。 (这就是为什么用xor eax,eax 将寄存器归零会短一个字节,否则相当于xor rax,rax)。有关更多类似内容,请参阅 Agner Fog 的指南。
  • 这个答案将真正受益于 OS X 系统调用表的链接,就像filippo.io/linux-syscall-table 用于 Linux 一样。 (Linux 中的数字常量在内核源代码的 C 头文件中的 #defines 中。)实际上,我应该将其添加到 x86 标记 wiki 信息页面。我还将链接到此 OS X 内容的答案。
  • @PeterCordes 我想我已经添加了你的建议。请让我知道这是否可以改善答案。我非常感谢您的意见,因为我刚刚学习汇编(甚至在使用参考材料编写它的第一个小时里,我就对这些东西感到震惊)。您的 cmets 提供了一些很好的反馈。
【解决方案2】:

您需要:

  1. 将标签名称从 main 更改为 _main(在这两个地方)。符号命名在 Mac OS X 下的工作方式略有不同。

  2. 更改将参数传递给系统调用的方式。 Mac OS X 使用与 Linux 不同的内核调用约定;此代码可移植!我不知道它是如何工作的,因为有任何官方文档说明它是如何工作的,但是在 GDB 中查看像 _exit() 这样的标准库函数的反汇编可能是有启发性的。

这是我系统上的_exit,例如:

    <_exit+0>:  mov    $0x40001,%eax
    <_exit+5>:  call   0x96f124c2 <_sysenter_trap>
    <_exit+10>: jae    0x96f10086 <_exit+26>
    <_exit+12>: call   0x96f1007d <_exit+17>
    <_exit+17>: pop    %edx
    <_exit+18>: mov    0x15a3bf9f(%edx),%edx
    <_exit+24>: jmp    *%edx
    <_exit+26>: ret
    <_exit+27>: nop

0x40001 中设置的额外位......很奇怪,但在这里可以放心地忽略。

调用_sysenter_trap 之后的内容用于错误处理。

_sysenter_trap 是:

    <_sysenter_trap+0>: pop    %edx
    <_sysenter_trap+1>: mov    %esp,%ecx
    <_sysenter_trap+3>: sysenter
    <_sysenter_trap+5>: nop

考虑到所有因素,您最好链接到 libSystem(在 OS X 中等效于 libc),而不是尝试直接调用内核。

【讨论】:

    猜你喜欢
    • 2013-01-28
    • 2011-01-29
    • 2011-04-24
    • 2013-12-21
    • 2012-01-04
    • 1970-01-01
    • 2015-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多