作为一个练习来更准确地学习c程序如何工作以及程序能够使用libc必须存在的最低内容级别,我自己尝试使用gas和ld主要在x86程序集中进行编程。
作为一个有趣的小挑战,我已成功组装并链接了几个链接到不同自制动态库的程序,但是我无法从头编写程序以使用libc函数调用而无需直接使用gcc。
我了解单个c库函数的调用约定,并通过使用objdump和readelf彻底检查了gcc编译的程序,但是在气体汇编文件中包含哪些信息以及要调用的参数方面都没有得到任何结果。在ld中成功链接到libc。有人对此有任何见解吗?
我在x86机器上运行Linux。
要成功将libc与动态链接一起使用,至少需要做三件事:
/usr/lib/crt1.o
,其中包含_start
,它将成为ELF二进制文件的入口点;/usr/lib/crti.o
(在libc之前)和/usr/lib/crtn.o
(之后),它们提供了一些初始化和最终化代码;/lib/ld-linux.so
。例如:
$ cat hello.s
.text
.globl main
main:
push %ebp
mov %esp, %ebp
pushl $hw_str
call puts
add $4, %esp
xor %eax, %eax
leave
ret
.data
hw_str:
.asciz "Hello world!"
$ as -o hello.o hello.s
$ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -lc hello.o /usr/lib/crtn.o
$ ./hello
Hello world!
$
如果在程序集中定义main
马修的答案很好地告诉你最低要求。
让我向您展示如何在系统中找到这些路径。跑:
gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'
然后拿起Matthew提到的文件。
gcc -v
为您提供GCC使用的确切链接器命令。
collect2是GCC用作链接器前端的内部可执行文件,它具有与ld
类似的接口。
在Ubuntu 14.04 64位(GCC 4.8)中,我最终得到:
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
/usr/lib/x86_64-linux-gnu/crt1.o \
/usr/lib/x86_64-linux-gnu/crti.o \
-lc hello_world.o \
/usr/lib/x86_64-linux-gnu/crtn.o
您可能还需要-lgcc
和-lgcc_s
。另见:Do I really need libgcc?
如果在程序集中定义_start
如果我定义了_start
,glibc的hello world只需:
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc hello_world.o
我不确定这是否健壮,即如果可以安全地跳过crt
初始化来调用glibc函数。另见:Why does an assembly program only work when linked with crt1.o crti.o and crtn.o?
如果您确实使用_start
而不是main
(如上面的一些评论中所述),您还需要更改程序退出的方式,否则您将遇到seg错误:
.text
.globl _start
_start:
mov $hw_str, %rdi
call puts
movl $0,%ebx # first argument: exit code.
movl $1,%eax # system call number: sys_exit.
int $0x80 # call kernel.
.data
hw_str: .asciz "Hello world!"
在Kubuntu 18.04.2(gcc(Ubuntu 7.3.0-27ubuntu1~18.04)7.3.0):
$ as -o hello.o hello.s
$ ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello hello.o -lc
另外,找出系统中动态链接器的一个简单方法是编译一个小的C程序,然后在二进制文件上运行ldd
:
test.c的:
int main() { return 0; }
针对可执行文件编译并运行ldd:
$ gcc -o test test.c
$ ldd test
linux-vdso.so.1 (0x00007ffd0a182000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff24d8e6000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff24ded9000)
我认为这样的事情应该有效: