我使用NASM创建了一个简单的hello世界,它从libc调用printf
和_exit
但不使用main
。
extern printf
extern _exit
section .data
hello: db 'Hello world!',10
section .text
global _start
_start:
xor eax, eax
mov edi, hello
call printf
mov rax, 0
jmp _exit
我像这样创建对象文件
nasm -felf64 hello.asm
然后我可以使用像glibc这样的动态链接来链接它
ld hello.o -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc -melf_x86_64
这样运行正常,没有错误。但现在我想静静地做。我做了
ln -s `gcc -print-file-name=libc.a`
ln -s `gcc -print-file-name=libgcc_eh.a`
ld hello.o -static libc.a libgcc_eh.a libc.a -melf_x86_64
这链接但是当我运行代码时,我遇到了分段错误。使用gdb
我看到它给出了
Program received signal SIGSEGV, Segmentation fault.
0x0000000000401004 in vfprintf ()
如果我在C中编写一个简单的hello世界并在运行中使用static编译,那么显然可以将静态链接到我的系统上的glibc。 如何使用glibc与汇编代码进行静态链接?
如果我链接到glibc的替代品,例如musl-libc,它可以正常工作
ld hello.o -static /usr/local/musl/lib/libc.a -melf_x86_64
我使用的是Ubuntu 14.04,eglibc 2.19和GCC 4.9.1
答案 0 :(得分:6)
Glibc有一个巨大的初始化序列,因为它有很强的意图在多线程系统中工作。此外,GLIBC正确处理一些GNU扩展,如构造函数属性。在启动时,它在TLS中缓存很多,包括区域设置信息,初始化同步对象等等。
vprintf的确切问题是未初始化的区域设置访问。
当你动态链接它时,所有这些工作都是在加载时完成的,一切正常。
静态链接的glibc需要调用__libc_init_first
来初始化它所需要的一切。在此次通话之前,您需要__dl_tls_setup
才能正确设置TLS,在此次通话后,您需要__libc_csu_init
来正确调用所有全局构造函数。
所有这些东西都是高度依赖版本的,几乎没有记录。严格地说,没有安全的方法可以静态链接到glibc,跳过或修改其正常的_start
序列。
另一方面,像musl或newlib这样的嵌入式库对初始化,多线程和语言环境没有那么严格的限制。