Статическая связь с glibc без вызова main
Я создал простой привет мир с помощью NASM, который вызывает 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
Я создаю объектный файл, как это
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 ()
Если я напишу простой hello world на C и скомпилирую со static в run, то все нормально, так что, возможно, можно статически связать 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
1 ответ
У Glibc есть огромная последовательность инициализации, потому что это сделано с сильным намерением работать в многопоточных системах. Также GLIBC правильно обрабатывает некоторые расширения GNU, такие как атрибуты конструктора. При запуске он много кеширует внутри TLS, включая информацию о локали, инициализирует объекты синхронизации и так далее.
Точная проблема с вашим vprintf - неинициализированный доступ к локали.
Когда вы ссылаетесь на него динамически, вся эта работа выполняется при загрузке, и все работает.
Статически связанный glibc требует __libc_init_first
вызываться для инициализации всего, что нужно. Перед этим звонком нужно __dl_tls_setup
правильно настроить TLS и после этого звонка вам понадобится __libc_csu_init
правильно вызывать все глобальные конструкторы.
Все это сильно зависит от версии и практически не документировано. Строго говоря, не существует безопасного способа статической ссылки на glibc, пропуская или изменяя его нормальное состояние. _start
последовательность.
С другой стороны, встраиваемые библиотеки, такие как musl или newlib, не ограничивают инициализацию, многопоточность и локали.