【问题标题】:ld linker error - Undefined symbols for architecture x86_64ld 链接器错误 - 体系结构 x86_64 的未定义符号
【发布时间】:2016-11-26 03:00:22
【问题描述】:

我正在尝试链接使用yasm 组装的单模块汇编语言程序,但我从ld 收到以下错误:

Undefined symbols for architecture x86_64:
  "start", referenced from:
     implicit entry/start for main executable
     (maybe you meant: _start)
ld: symbol(s) not found for inferred architecture x86_64

我实际上是半定期收到此错误的,所以我想这是一个相当普遍的问题,但不知何故,似乎没有人有一个令人满意的答案。在有人说这是之前问题的重复之前,是的,我知道。就像您可以查看巨大的标题相似的问题文本墙并发现这是重复的一样,我也可以。

Compiler Error: Undefined symbols for architecture x86_64

不适用于我的问题。我不是用 C++ 编写代码,并且该问题中给出的解决方案是该语言特有的。

undefined symbol for architecture x86_64 in compiling C program

也不能解决我的问题,因为我不想将多个目标文件链接在一起。

Error Undefined symbols for architecture x86_64:

解决方案与高级语言中的特定框架有关。

Compiler Error: Undefined symbols for architecture x86_64

解决方案涉及修复函数原型。由于显而易见的原因,此处不适用。

...你明白了。我能找到的每一个过去的问题都是通过一些不适用于我的情况的特殊方法来解决的。

请帮我解决这个问题。我厌倦了一次又一次地遇到这个错误,却无能为力,因为它的文档记录太差了。恕我直言,世界迫切需要一个相当于 MS-DOS 错误代码参考手册的 GNU Dev Tools。

其他信息:

操作系统:Mac OS X El Capitain

来源列表:

segment .text
global _start

_start:
    mov     eax,1   ; 1 is the syscall number for exit
    mov     ebx,5   ; 5 is the value to return
    int     0x80    ; execute a system call

目标文件的Hexdump,显示符号确实是_start而不是start

00000000  cf fa ed fe 07 00 00 01  03 00 00 00 01 00 00 00  |................|
00000010  02 00 00 00 b0 00 00 00  00 00 00 00 00 00 00 00  |................|
00000020  19 00 00 00 98 00 00 00  00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  0c 00 00 00 00 00 00 00  d0 00 00 00 00 00 00 00  |................|
00000050  0c 00 00 00 00 00 00 00  07 00 00 00 07 00 00 00  |................|
00000060  01 00 00 00 00 00 00 00  5f 5f 74 65 78 74 00 00  |........__text..|
00000070  00 00 00 00 00 00 00 00  5f 5f 54 45 58 54 00 00  |........__TEXT..|
00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000090  0c 00 00 00 00 00 00 00  d0 00 00 00 00 00 00 00  |................|
000000a0  00 00 00 00 00 00 00 00  00 00 00 80 00 00 00 00  |................|
000000b0  00 00 00 00 00 00 00 00  02 00 00 00 18 00 00 00  |................|
000000c0  dc 00 00 00 01 00 00 00  ec 00 00 00 08 00 00 00  |................|
000000d0  b8 01 00 00 00 bb 05 00  00 00 cd 80 01 00 00 00  |................|
000000e0  0f 01 00 00 00 00 00 00  00 00 00 00 00 5f 73 74  |............._st|
000000f0  61 72 74 00                                       |art.|
000000f4

【问题讨论】:

  • 您没有展示如何在 OS/X 上组装和链接您的程序,但我可以告诉您,您的代码 int 0x80 似乎是 32 位 Linux ABI 调用约定,即使这组装和链接它不会按预期运行。 32 位 OS/X 将参数传递给堆栈上的 int 0x80,而不是寄存器(尽管 EAX 将包含系统调用)
  • 好的,所以它在不同的操作系统上不同的。我正在使用一本关于 Linux 的英特尔汇编编程的书,但我认为该代码也适用于 MacOS,因为它们都使用 x86-64 架构并且符合 POSIX。
  • 底层系统调用约定在大多数操作系统之间是不同的。同一操作系统上的 i386 和 x86-64 之间的约定也可能不同。 POSIX 没有定义用于调用内核的底层方法,这是一个实现细节。
  • 我会看看那个链接。谢谢。
  • 本网站的全部目的是消除 XKCD 现象。这是创建 Stack Overflow 的主要理由。所以使用引用实际上并没有完成任何事情。另外,正如罗斯所提到的,我们不需要为每个问题提供一个大背景故事,因为我们更像是一本百科全书,而不是一个传统的论坛。人们来这里是为了获得答案,而不是阅读这些答案背后的用户。此外,罗斯不是主持人。他只是一名普通的社区成员(尽管他已经存在一段时间并提供了很多有用的答案)帮助保持网站的清洁。

标签: macos assembly linker x86 x86-64


【解决方案1】:

32 位 OS/X 代码通过 int 0x80 进行系统调用

代码:

segment .text
global _start

_start:
    mov     eax,1   ; 1 is the syscall number for exit
    mov     ebx,5   ; 5 is the value to return
    int     0x80    ; execute a system call

建议您使用 32 位 Linux 教程。我得出这个结论是因为32-bit Linux ABI 使用寄存器通过int 0x80 将参数传递给内核。 OS/X 是不同的。您在堆栈上传递参数(从右到左传递它们)。在 32 位 OS/X 中,它看起来像:

global start

section .text
start:
    ; sys_write syscall
    ; See: https://opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master
    ; 4 AUE_NULL ALL { user_ssize_t write(int fd, user_addr_t cbuf, user_size_t nbyte); }
    push    dword msg.len  ; Last argument is length
    push    dword msg      ; 2nd last is pointer to string
    push    dword 1        ; 1st argument is File descriptor (1=STDOUT)
    mov     eax, 4         ; eax = 4 is write system call
    sub     esp, 4         ; On OS/X 32-bit code always need to allocate 4 bytes on stack
    int     0x80

    ; sys_exit
    ; 1 AUE_EXIT ALL { void exit(int rval); }
    push    dword 42       ; Return value
    mov     eax, 1         ; eax=1 is exit system call
    sub     esp, 4         ; allocate 4 bytes on stack
    int     0x80

section .rodata

msg:    db      "Hello, world!", 10
.len:   equ     $ - msg

组装和链接:

nasm -f macho testexit.asm
ld -macosx_version_min 10.7.0 -o testexit testexit.o
./testexit
echo $?

YASM 参数应与 NASM 相同。它应该输出:

Hello, world!
42

32 位 OS/X 代码中系统调用的经验法则:

  • 参数在堆栈上从右到左传递
  • int 0x80 不需要需要一个 16 字节对齐的堆栈
  • 在参数被压入之后和系统调用之前,需要在堆栈上分配额外的 4 个字节。例子:

    1. sub esp, 4
    2. push eax
  • EAX 寄存器中的系统调用号

  • 通过int 0x80发起的系统调用

Apple 在其website 上记录了 OS/X 系统调用。


64 位 OS/X 代码通过 SYSCALL 指令进行系统调用

64 位 OS/X 几乎使用与 64 位 Linux 相同的内核调用约定。 64-bit Linux System V ABI 适用于系统调用。特别是A.2 AMD64 Linux 内核约定部分。该部分有以下规则:

  1. 用户级应用程序用作整数寄存器来传递序列 %rdi、%rsi、%rdx、%rcx、%r8 和 %r9。内核接口使用 %rdi, %rsi、%rdx、%r10、%r8 和 %r9。
  2. 系统调用是通过系统调用指令完成的。内核破坏 注册 %rcx 和 %r11。
  3. 系统调用的编号必须在寄存器 %rax 中传递。
  4. 系统调用仅限于六个参数,没有参数直接传递 堆栈。
  5. 从系统调用返回,寄存器 %rax 包含 系统调用。 -4095 和 -1 之间的值表示错误, 它是 -errno。
  6. 只有 INTEGER 类或 MEMORY 类的值被传递给内核。

64 位 OS/X 使用与 32 位 OS/X 相同的 System Call numbers,但是所有数字都必须添加 0x02000000。上面的代码可以修改为 64 位 OS/X 程序:

global start
section .text

start:
    mov     eax, 0x2000004 ; write system call
    mov     edi, 1         ; stdout = 1
    mov     rsi, msg       ; address of the message to print
    ;lea     rsi, [rel msg]; Alternative way using RIP relative addressing
    mov     edx, msg.len   ; length of message
    syscall                ; Use syscall, NOT int 0x80

    mov     eax, 0x2000001 ; exit system call
    mov     edi, 42        ; return 42 when exiting
    syscall

section .rodata

msg:    db      "Hello, world!", 10
.len:   equ     $ - msg

请注意,在写入 32 位寄存器时,CPU 会自动将零扩展到 64 位寄存器。上面的代码通过写入 EAXEDI 等寄存器而不是 RAXRDI 来使用此功能。您可以使用 64 位寄存器,但使用 32 位寄存器会在代码中节省一个字节。

组装和链接:

nasm -f macho64 testexit64.asm
ld -macosx_version_min 10.7.0 -lSystem -o testexit64 testexit64.o
./testexit64 
echo $?

它应该输出:

Hello, world!
42

注意:其中一些信息在本质上与OS/X tutorial 相似,但已修复了一些更正和编码错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多