【问题标题】:Static linkage with glibc without calling main与 glibc 的静态链接,无需调用 main
【发布时间】:2014-12-09 02:06:55
【问题描述】:

我使用 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

我这样创建目标文件

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 和 GCC 4.9.1

【问题讨论】:

    标签: gcc assembly nasm glibc


    【解决方案1】:

    Glibc 有一个庞大的初始化序列,因为它的完成目的是为了在多线程系统中工作。 GLIBC 还可以正确处理一些 GNU 扩展,例如构造函数属性。启动时,它会在 TLS 中缓存很多内容,包括语言环境信息、初始化同步对象等。

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

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

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

    所有这些东西都高度依赖于版本并且实际上没有记录。严格来说,没有安全的方法可以静态链接到 glibc,跳过或修改其正常的_start 序列。

    另一方面,musl 或 newlib 等面向嵌入式的库在初始化、多线程和语言环境方面没有那么严格。

    【讨论】:

    • 谢谢,这是一个比我预期的更好的答案。现在是我认输并接受应该避免静态链接到 glibc 的时候了。
    猜你喜欢
    • 1970-01-01
    • 2013-01-12
    • 2012-05-09
    • 1970-01-01
    • 1970-01-01
    • 2010-10-01
    • 1970-01-01
    • 2016-08-21
    相关资源
    最近更新 更多