与glibc静态链接而不调用main

Z b*_*son 3 assembly gcc glibc nasm

我创建使用NASM它调用一个简单的Hello World printf,并_exit从libc的,但不使用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
Run Code Online (Sandbox Code Playgroud)

我像这样创建对象文件

nasm -felf64 hello.asm
Run Code Online (Sandbox Code Playgroud)

然后我可以使用动态链接与glibc这样链接它

ld hello.o -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc -melf_x86_64
Run Code Online (Sandbox Code Playgroud)

这运行正确,没有错误.但现在我想静静地做.我做

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
Run Code Online (Sandbox Code Playgroud)

这链接,但当我运行代码时,我得到一个分段错误.使用gdb我看到它给出

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401004 in vfprintf ()
Run Code Online (Sandbox Code Playgroud)

如果我在C中编写一个简单的hello世界并在运行中使用static编译,那么显然可以在我的系统上静态链接到glibc.如何使用glibc与汇编代码进行静态链接?

如果我链接到glibc的替代品,如musl-libc,它可以正常工作

ld hello.o -static /usr/local/musl/lib/libc.a -melf_x86_64
Run Code Online (Sandbox Code Playgroud)

我使用的是Ubuntu 14.04,eglibc 2.19和GCC 4.9.1

Kon*_*rov 6

Glibc有一个巨大的初始化序列,因为它有很强的意图在多线程系统中工作.此外,GLIBC正确处理一些GNU扩展,如构造函数属性.在启动时,它在TLS中缓存很多,包括区域设置信息,初始化同步对象等等.

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

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

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

所有这些东西都是高度依赖版本的,几乎没有文档记录.严格地说,没有安全的方法可以静态链接到glibc,跳过或修改它的正常_start序列.

另一方面,像musl或newlib这样的嵌入式库对初始化,多线程和语言环境没有那么严格的限制.