不调用main函数的情况下使用glibc进行静态链接

4

我使用NASM创建了一个简单的hello world程序,该程序调用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

I create the object file like this

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 world”程序,并使用静态链接方式编译,它可以正常运行,因此显然在我的系统上可以实现与glibc的静态链接。那么,我该如何将我的汇编代码与glibc进行静态链接呢? 如果我链接到glibc的替代品,比如musl-libc,它也可以正常工作。
ld hello.o -static /usr/local/musl/lib/libc.a -melf_x86_64

我正在使用Ubuntu 14.04操作系统,eglibc 2.19 C库以及GCC 4.9.1编译器。
1个回答

9

Glibc拥有一个巨大的初始化序列,因为它旨在在多线程系统中工作。此外,GLIBC正确处理一些GNU扩展,如构造器属性。在启动时,它在TLS中缓存了很多东西,包括语言环境信息,初始化同步对象等。

你的vprintf出现的确切问题是未初始化的语言环境访问。

当您动态链接时,所有这些工作都在加载时完成,并且一切正常。

静态链接的glibc需要调用__libc_init_first来初始化其所有所需内容。在此调用之前,您需要__dl_tls_setup来正确设置TLS,在此调用之后,您将需要__libc_csu_init来正确调用所有全局构造函数。

所有这些内容高度依赖于版本,而且几乎没有文档记录。严格地说,没有安全的方法可以静态链接到glibc,跳过或修改其正常的_start序列。

另一方面,像musl或newlib这样针对嵌入式的库不那么限制初始化、多线程和语言环境。


谢谢,这个答案比我预期的要好。是时候放弃并接受静态链接到glibc应该被避免了。 - Z boson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接