如何在Ubuntu中动态链接到glibc

问题描述 投票:2回答:1

我正在尝试在Linux(Ubuntu 18.04 LTS)中汇编并链接这个微小的x86汇编代码:

;hello.asm
global _start

extern scanf, printf, exit

section .data
    read_name db '%255s', 0
    msg db 'Hello, %s', 0

section .text
_start:
    sub esp, 256
    push esp
    push read_name
    call scanf
    add esp, 8
    push esp
    push msg
    call printf
    add esp, 264
    push dword 0
    call exit

我正在使用nasm进行组装,并使用ld进行链接。您可能知道,该代码使用C函数,因此必须将其链接到glibc。由于我的代码使用的是_start,而不是main,因此我决定链接到共享库会更好,因为如果链接了二进制文件,则C运行时需要一些启动代码才能在_start中运行静态地。

问题是我无法链接我的代码,很可能是因为我没有使用正确的glibc .so。这是我组装和链接的方式:

nasm -f elf32 hello.asm
ld hello.o -o hello -dynamic-linker /lib/libc.so.6 -lc -m elf_i386

输出文件被创建,但是当我尝试运行它时,这就是我得到的:

./hello
bash: ./hello: No such file or directory

进行快速搜索后,发现这些都是我计算机上的所有libc .so

locate libc.so
/lib/x86_64-linux-gnu/libc.so.6
/snap/core/8268/lib/i386-linux-gnu/libc.so.6
/snap/core/8268/lib/x86_64-linux-gnu/libc.so.6
/snap/core/8689/lib/i386-linux-gnu/libc.so.6
/snap/core/8689/lib/x86_64-linux-gnu/libc.so.6
/snap/core18/1668/lib/i386-linux-gnu/libc.so.6
/snap/core18/1668/lib/x86_64-linux-gnu/libc.so.6
/usr/lib/x86_64-linux-gnu/libc.so

有人可以告诉我如何链接到glibc吗? (对于64位代码,我也遇到同样的问题)

assembly nasm ld glibc
1个回答
3
投票

ld对于i386的默认动态链接器是/usr/lib/libc.so.1,这在当今大多数Linux系统上都是错误的。您确实尝试覆盖它,但是给出的路径也不正确。两个选项:

  1. 链接时手动传递正确的一个:ld hello.o -o hello -dynamic-linker /lib/ld-linux.so.2 -lc -m elf_i386
  2. 使用gcc进行链接,as fuz mentionedgcc -nostartfiles -m32 -o hello hello.o

[如果您想知道我如何知道选项1的正确动态链接器,我首先做一次选项2并检查使用了哪个选项,就做到了。

另请参阅Red Hat's Bug 868662 - /lib/ld64.so.1: bad ELF Interpreter: No such file or directory,其他人基本上遇到了与您完全相同的问题(但由于某些原因,他们收到比您更有用的错误消息)。


编辑:您的代码还有另外两个潜在问题,可能会导致实际代码出现问题,但在这个小例子中却没有发生:

首先,作为Employed Russian pointed out in a comment,glibc期望其crt中自己的初始化代码将在您的应用程序代码开始调用其函数之前运行,但是通过自己定义_start,您可以颠覆它。如果要使用glibc,最安全的方法是使用main代替_start作为入口,然后使用gcc -nostartfiles -m32 -o hello hello.o进行链接(请注意,我们不再使用-nostartfiles) 。从理论上讲,您仍然可以使用ld进行链接,但是一旦将glibc引入混合中,就真的不值得尝试。

第二,您没有正确对齐堆栈。 call其他功能之前,您需要确保它与16字节边界对齐。在_start的开头(如果由于某些原因仍要使用它),堆栈将已经像这样对齐,因此您只需维护它即可。在main或任何其他函数的开头,将推入4字节的返回地址,因此您需要再推12个字节才能将其重新对齐。

使用以上两个修复程序,这是您的新hello.asm

;hello.asm
global main

extern scanf, printf, exit

section .data
    read_name db '%255s', 0
    msg db 'Hello, %s', 0

section .text
main:
    sub esp, 260
    push esp
    push read_name
    call scanf
    add esp, 8
    push esp
    push msg
    call printf
    add esp, 260
    push dword 0
    call exit

而且,现在您正在使用main而不是_start,您可以直接从中返回而不是调用exit。您只需要确保将堆栈指针放回开始时的位置即可。为此,将call printf之后的所有内容替换为:

    add esp, 268
    xor eax, eax
    ret

最后注:如果您想知道为什么我选择xor eax, eax而不是mov eax, 0,请参阅What is the best way to set a register to zero in x86 assembly: xor, mov or and?

© www.soinside.com 2019 - 2024. All rights reserved.