我正在尝试在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位代码,我也遇到同样的问题)
ld
对于i386的默认动态链接器是/usr/lib/libc.so.1
,这在当今大多数Linux系统上都是错误的。您确实尝试覆盖它,但是给出的路径也不正确。两个选项:
ld hello.o -o hello -dynamic-linker /lib/ld-linux.so.2 -lc -m elf_i386
gcc
进行链接,as fuz mentioned:gcc -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?。