【问题标题】:Linking Linux x86-64 assembly hello world program with ld fails [duplicate]将 Linux x86-64 程序集 hello world 程序与 ld 链接失败 [重复]
【发布时间】:2013-03-06 03:56:31
【问题描述】:

我最近一直在 linux 上玩 x86 64 位汇编,在编译了一个看似简单的程序后,我只能摸不着头脑:P

虽然我编译和链接它不会抛出任何错误并产生一个 linux ELF 当我尝试运行它时,我得到:

.:[ h4unt3r@sp3ctr4l-h0st asm ]:.
#(0)> ./hello 
bash: ./hello: No such file or directory

我假设它产生了一个无效的 ELF 文件,这就是为什么它报告 hello 不存在,即使它是。不知道为什么-我可能会继续玩它,只是好奇这是否可以通过简单的方法解决^_^

这是我的编译/链接命令行:

nasm -f elf64 hello.s -g
ld -o hello hello.o -lc

代码如下:

section .data
    msg: db "Hello, world!",0xa,0

section .text
    extern printf
    global main

main:
    push rbp
    mov rbp, rsp

    mov rdi, msg
    xor rax, rax
    call printf
    xor rax, rax

    pop rbp
    ret

编辑——我不想使用 gcc :)

【问题讨论】:

  • 如果它有任何价值,如果你删除 -lc 标志并注释掉 printf 的 extern,文件就可以工作。
  • 你怎么看? :) 那么 printf 是未定义的符号...
  • 其实我也把 printf 调用注释掉了,但我的意思是,如果文件没有与 libc 链接,它可以正常工作。
  • 你又是怎么想的? :) 你在使用 ld 吗?因为 gcc 为你做了所有正确的链接......就像 incldugin libs 所以如果你做 -nostdlib 或 -nodefaultlibs 它将失败并出现未定义的符号
  • 这是我用来链接的命令。可能链接不完整导致程序无法正确加载。也许有更好的知识的人可以解释一下。 ld -o hello /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o hello.o /usr/lib/gcc/x86_64-linux -gnu/5/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o -lc --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn。 o -dynamic-linker /lib64/ld-linux-x86-64.so.2

标签: linux assembly 64-bit x86-64 nasm


【解决方案1】:

我正在运行 32 位硬件,无法测试 64 位的东西。我在 32 位代码中看到了这个“没有这样的文件”错误。 ld 默认使用“/lib/ld-linux.so.1” - 您可以在可执行文件中以纯文本形式查看此字符串。这是不存在的文件(显然“你好”就在那里!)。解决办法是告诉ld-I/lib/ld-linux.so.2。我怀疑类似的解决方案适用于 64 位,但我不知道您需要什么“解释器”或“动态链接器”。尝试在可执行文件中查找类似的字符串,并在您的库中查找类似的 .so。你不应该“需要”使用 gcc ......但是 gcc 知道在哪里可以找到这些东西!可能更容易使用它。真是个令人困惑的错误,不是吗?

(如果您打算这样做,我希望您的入口点是_start,而不是main。您将无法从此ret - 使用sys_exit 或exit()。 )

我不熟悉 nrz 提到的关于“符号表 0”的错误。当然不是故意改变 Nasm! Nasm 开发人员在http://www.nasm.us 上或附近闲逛,很高兴在那里听到反馈和错误报告。 (好吧,也许不是对错误报告感到“高兴”。)我会看看我是否能找到任何东西......

FWIW,Nasm 默认使用 -g 开关“刺伤”调试信息。要启用“矮人”调试信息,-F dwarf...应该会更好...

【讨论】:

  • 谢谢!你完全明白了:)
【解决方案2】:

首先,要使用printf,您需要使用gcc而不是ld进行链接:

gcc -o hello hello.o

那么,另一个问题可能和我自己遇到的一样。我不确定这是 NASM 中的错误还是预期的更改:

user@computer:~/code/asm$ nasm -f elf64 hello.asm -g; gcc -o 你好你好.o /usr/bin/ld: 错误: 重定位节 9 使用了意外的符号表 0 collect2:错误:ld 返回 1 个退出状态

我已经通过转移到YASM 解决了这个问题,并以这种方式进行组装和链接:

yasm -f elf64 hello.s -g dwarf2
gcc -o hello hello.o

生成具有预期输出的可执行文件:

./hello
Hello, world!

【讨论】:

  • 好吧,就是这样...我不想使用 gcc 进行链接,我想使用 ld :) gcc 如果我理解之后调用 ld...
  • @h4unt3r 你说得对,问题是缺少标准库
  • 不,因为符号 printf 在 stdlib 中,我将它与我的 ld 标志 -lc 明确链接
【解决方案3】:

把代码稍微修改一下。

section .data
        msg:    db "Hello, world!",0xa,0
section .text
        extern  printf
 global _start
_start:
        ; RSP already 16-byte aligned, ready for a function call.
        mov     rdi,msg           ; or better, lea rdi, [rel msg]
        xor     eax, eax          ; AL=0  - no XMM args
        call    printf

        mov     rax,60  ; use _exit syscall
        mov     rdi,0   ; exit code 0
        syscall         ; call kernel

并使用此链接设置正确的现代 ld.so 路径,而不是现代 GNU/Linux 系统上不存在的 ld 默认值。

ld hello.o -o hello -lc --dynamic-linker /lib64/ld-linux-x86-64.so.2

请注意,这仅适用于动态链接的可执行文件。如果你静态链接,你需要手动调用 glibc 的 init 函数来初始化它的数据结构(比如 stdio 缓冲区),然后像 printf 这样的函数才能工作。

【讨论】:

  • int 0x80 不适用于 64 位代码(可以生成不兼容 32 位代码的 Linux 内核)。首选方法是通过 SYSCALL 指令(注意,系统调用号和数据传递方式与int 0x80 不同)。如果与 C 运行时链接,ret 的使用不是问题,因为 main 是一个函数,并且堆栈上有一个返回地址返回到 C i> 调用它的运行时函数(只需确保它们遵循保存调用者/被调用者的寄存器规则)
  • @MichaelPetch 根据您的输入修改为系统调用。谢谢!
猜你喜欢
  • 2015-05-04
  • 2017-06-10
  • 2014-05-17
  • 2019-05-24
  • 2013-09-04
  • 1970-01-01
  • 2020-07-17
  • 2010-12-18
  • 2014-08-07
相关资源
最近更新 更多