【问题标题】:Hello World using x86 assembler on Mac 0SX在 Mac 0SX 上使用 x86 汇编程序的 Hello World
【发布时间】:2011-05-16 09:25:05
【问题描述】:

我正在尝试在我的 Mac 上进行一些 x86 汇编编程,但无法生成可执行文件。问题似乎出在链接阶段。

helloWorld.s:

.data

    HelloWorldString:
    .ascii "Hello World\n"

.text

.globl _start

_start:
    # load all the arguments for write()
    movl $4, %eax
    movl $1, %ebx
    movl $HelloWorldString, %ecx
    movl $12, %edx
    # raises software interrupt to call write()
    int $0x80

    # call exit()
    movl $1, %eax
    movl $0, %ebx
    int $0x80

组装程序:

as -o helloWorld.o helloWorld.s

链接目标文件:

ld -o helloWorld helloWorld.o

此时我得到的错误是:

ld: could not find entry point "start" (perhaps missing crt1.o) for inferred architecture x86_64

任何关于我做错/遗漏的建议都会非常有帮助。谢谢

【问题讨论】:

  • 我知道我来晚了,但您需要将_start 更改为start

标签: macos linker x86 assembly


【解决方案1】:

您可能会发现使用 gcc 构建比尝试对汇编器和链接器进行微观管理更容易,例如

$ gcc helloWorld.s -o helloWorld

(如果你走这条路,你可能想要将_start 更改为_main。)

顺便说一句,从一个工作的 C 程序开始,并从中研究生成的 asm 可能是有益的。例如

#include <stdio.h>

int main(void)
{
    puts("Hello world!\n");

    return 0;
}

使用gcc -Wall -O3 -m32 -fno-PIC hello.c -S -o hello.S 编译时生成:

    .cstring
LC0:
    .ascii "Hello world!\12\0"
    .text
    .align 4,0x90
.globl _main
_main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $LC0, (%esp)
    call    _puts
    xorl    %eax, %eax
    leave
    ret
    .subsections_via_symbols

您可能需要考虑将其用作您自己的“Hello world”或其他实验性 asm 程序的模板,特别是考虑到它已经构建并运行:

$ gcc -m32 hello.S -o hello
$ ./hello 
Hello world!

最后一条评论:谨防从面向 Linux 的 asm 书籍或教程中获取示例并尝试在 OS X 下应用它们 - 存在重要差异!

【讨论】:

  • 知道为什么我在执行gcc -m32 hello.S -o hello 时会收到此错误吗? ld:警告:PIE 已禁用。在代码签名的 PIE 中不允许使用绝对寻址(可能是 -mdynamic-no-pic),但在来自 /var/folders/9b/n3lsk87513d57pzh0qvxjmz00000gn/T/hello-r4fQK2.o 的 _main 中使用。要修复此警告,请勿使用 -mdynamic-no-pic 编译或使用 -Wl,-no_pie 链接
  • 您使用的是不寻常的平台吗?
  • @michaelsnowden 代码是用 -fno-PIC 生成的,所以如果你想编译它,你需要使用gcc -m32 -Wl,-no_pie hello.S -o hello。或者,生成不带-fno-PIC 的代码。这将为您提供稍微复杂的样板,但您可以编译它而无需处理 PIE 问题。
【解决方案2】:

试试:

ld -e _start -arch x86_64 -o HelloWorld HelloWorld.S

然后:

./HelloWorld

信息:

-e <entry point>
-arch <architecture>, You can check your architecture by uname -a 
-o <output file>

【讨论】:

  • 是的,它将组装+链接,但问题中的代码对于 x86-64 MacOS 是 not。它适用于 i386 Linux。修复后(请参阅 NoOffenceIntended 的答案),然后此答案将起作用。如果您为入口点提供start: 而不是_start: 的标准(对于MacOS)标签,则可以省略-e _start 部分
【解决方案3】:

你好.asm

.data

    HelloWorldString:
    .ascii "Hello World!\n"

.text

.globl start

start:
    ; load all the arguments for write()
    movl $0x2000004, %eax
    movl $1, %ebx
    movq HelloWorldString@GOTPCREL(%rip), %rsi
    movq $100, %rdx
    ; raises software interrupt to call write()
    syscall

    ; call exit()
    movl $0x2000001, %eax
    movl $0, %ebx
    syscall

然后运行:

$ as -arch x86_64  -o hello.o hello.asm
$ ld -o hello hello.o
$ ./hello

这是一个适用于 Mac OS X Mach-0 基于 GNU 的汇编程序的有效解决方案

【讨论】:

  • 这对我有用,但必须删除 .asm 文件中的注释代码。将-macosx_version_min 10.6 传递给链接器命令也可以避免警告,尽管它仍然可以编译。
  • 当您的字符串较短时打印 100 个字节将在输出中包含一堆垃圾。可能所有0 字节对终端没有任何影响,因此您没有修复此错误。此外,exit() 的参数来自 EDI,而不是 EBX。也许您正在考虑 32 位 Linux int 0x80 ABI?哦,那是从问题中复制的。此外,无需从内存(从 GOT)中获取指针来访问您自己的静态数据。只需像@NoOffenceIntended 的答案那样直接使用相对于 RIP 的 LEA 进行计算。
  • 另外,; 不是 x86-64 的 as 的注释字符。它将语句/指令分开在同一行,因此汇编器将尝试将您的 cmets 解析为代码。如果您对此进行了测试,我认为是在添加 cmets 之前。
【解决方案4】:

问题中的代码看起来适用于 32 位 Linux,使用 int $0x80 ABI 和 EBX、ECX、EDX 中的参数。

MacOS 上的 x86-64 代码使用 syscall 指令,其 arg 传递和返回值类似于 与 x86-64 System V ABI for Linux 中记录的内容。它与int $0x80 完全不同,唯一的相似之处是在EAX/RAX 中传递了索书号。但电话号码不同:https://sigsegv.pl/osx-bsd-syscalls/ ORed 与 0x2000000

Args 进入与函数调用相同的寄存器。 (除了 R10 而不是 RCX。)

另见basic assembly not working on Mac (x86_64+Lion)?How to get this simple assembly to run?


我认为这是另一个答案中建议的更简洁和直观的版本。

OS X 使用start,而不是_start,作为进程入口点。

.data
str:
  .ascii "Hello world!\n"
  len = . - str                  # length = start - end.   . = current position

.text
.globl start
start:
    movl   $0x2000004, %eax
    movl   $1, %edi
    leaq   str(%rip), %rsi  
    movq   $len, %rdx          
    syscall                       # write(1, str, len)

    movl   $0x2000001, %eax 
    movl   $0, %edi
    syscall                       # _exit(0)

通常,当寄存器暗示操作数大小后缀时,您会省略它。并使用xor %edi,%edi 将 RDI 归零。

并使用mov $len, %edx,因为您知道大小小于 4GB,因此更高效的 32 位零扩展 mov-immediate 将起作用,就像您将 RAX 设置为索书号一样。

注意使用 RIP 相对 LEA 将静态数据的地址放入寄存器。 MacOS 上的 x86-64 代码不能使用 32 位绝对寻址,因为您的可执行文件将被映射的基地址高于 2^32。

32 位绝对地址没有重定位类型,因此您不能使用它们。 (而且您需要相对于 RIP,而不是绝对的 64 位,即使它也受支持。)

【讨论】:

  • 是的,这段代码更好。它仍然有一个我修复的错误,现在我认为这是一个很好的例子。我还添加了一些解释性文字。我没有解决浪费代码大小的低效率问题(我留下了与记录的系统调用 arg 宽度相匹配的操作数大小,而不是使用隐式零扩展)。而且我没有将只读字符串数据放入只读数据部分。 (就像 Linux 上的 .rodata 一样,IDK 是 OS X 所称的。)
【解决方案5】:

要在 MacOS 10.15 上组装和链接 @NoOffenceIntended's answer 中的代码,需要进行以下更改:

.global _start 更改为.global main,并将_start: 更改为main:

要组装和链接代码,请使用:

as -arch x86_64 -o hello.o hello.asm
ld -arch x86_64 -o hello hello.o -lSystem

这是假设正在使用“as”的“Apple clang version 12.0.0”并使用相应的“ld”。

【讨论】:

  • 您在说哪个“代码之上”?代码还有多个其他答案,如果您指的是其中之一,请包含指向它的链接(并可能提及作者的姓名)。或者,如果您指的是问题中的代码,它使用 32 位 Linux int $0x80 系统调用,并在每个 Linux 32 位 ABI 的寄存器中使用 args; MacOS/Darwin 将 int $0x80 的参数放在堆栈上。但是 IIRC,10.15 根本不支持 32 位代码,所以我怀疑你在谈论这个问题。
  • 很抱歉。我所指的代码由@NoOffenceIntended 于 2019 年 7 月 9 日 3:39 发布,Peter Cordes 于 2019 年 7 月 9 日 4:24 发布/编辑。 (我不确定如何直接链接到该代码示例。)
  • 每个答案下方都有一个“分享”链接,您可以使用该链接直接获取该链接。我对此进行了编辑以添加链接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-06-10
  • 2015-05-04
  • 2023-03-16
  • 1970-01-01
  • 2013-03-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多