ld链接器错误 - 体系结构x86_64的未定义符号

use*_*544 1 macos x86 assembly linker x86-64

我正在尝试链接组装的单模块汇编语言程序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
Run Code Online (Sandbox Code Playgroud)

我实际上是半定期得到这个错误,所以我想这是一个相当普遍的问题,但不知怎的,似乎没有人有一个满意的答案.在有人说这是前一个问题的副本之前,是的,我知道.正如你可以看到类似标题问题的巨大文本墙,看到这是重复的,我也可以.

编译器错误:架构x86_64的未定义符号

不适用于我的问题.我不是用C++编写的,并且在那个问题中给出的解决方案对于那种语言来说是特殊的.

编译C程序时体系结构x86_64的未定义符号

也没有解决我的问题,因为我不是试图将多个目标文件链接在一起.

错误架构x86_64的未定义符号:

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

编译器错误:架构x86_64的未定义符号

解决方案涉及修复函数原型.由于明显的原因,这里不适用.

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

请帮我解决一下这个.我已经厌倦了一次又一次地得到这个错误而无法对它做任何事情,因为它的记录很差.恕我直言世界迫切需要一个GNU Dev Tools等效的MS-DOS错误代码参考手册.

附加信息:

操作系统: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
Run Code Online (Sandbox Code Playgroud)

目标文件的进制打印,显示出符号确实_startstart:

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
Run Code Online (Sandbox Code Playgroud)

Mic*_*tch 7

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
Run Code Online (Sandbox Code Playgroud)

建议您使用32位Linux教程.我得出这个结论,因为32位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
Run Code Online (Sandbox Code Playgroud)

组装和链接:

nasm -f macho testexit.asm
ld -macosx_version_min 10.7.0 -o testexit testexit.o
./testexit
echo $?
Run Code Online (Sandbox Code Playgroud)

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

Hello, world!
42
Run Code Online (Sandbox Code Playgroud)

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

  • 参数在堆栈中从右向左传递
  • int 0x80 并不需要有一个16字节对齐栈
  • 在推送参数之后和系统调用之前,需要在堆栈上分配额外的4个字节.例子:

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

  • 系统调用通过启动 int 0x80

Apple在其网站上记录了OS/X系统调用.


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

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

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

64位OS/X使用与32位OS/X 相同的系统调用号,但所有数字都必须添加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
Run Code Online (Sandbox Code Playgroud)

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

组装和链接:

nasm -f macho64 testexit64.asm
ld -macosx_version_min 10.7.0 -lSystem -o testexit64 testexit64.o
./testexit64 
echo $?
Run Code Online (Sandbox Code Playgroud)

它应该输出:

Hello, world!
42
Run Code Online (Sandbox Code Playgroud)

注意:其中一些信息在本质上与此OS/X教程类似,并修复了一些更正和编码错误.