Jak połączyć program do montażu gazu, który korzysta z biblioteki standardowej C z ld bez użycia gcc?
Jako ćwiczenie, aby dowiedzieć się dokładniej, jak działają programy w języku c i jaki minimalny poziom zawartości musi istnieć, aby program mógł korzystać z libc, wziąłem na siebie próbę zaprogramowania głównie w x86 assembly przy użyciu gazu i ld.
Jako zabawne małe wyzwanie, z powodzeniem zmontowałem i połączyłem kilka programów powiązanych z różnymi własnymi dynamicznymi bibliotekami, ale nie udało mi się zakodować programu od podstaw, aby używać wywołań funkcji libc bez bezpośrednio przy użyciu gcc.
Rozumiem konwencje wywoływania poszczególnych funkcji biblioteki c i dokładnie sprawdzałem programy skompilowane z gcc poprzez użycie objdump i readelf, ale nie doszedłem do tego, jakie informacje włączyć do pliku montażu gazu i jakie parametry wywołać w ld, aby pomyślnie połączyć się z libc. Ktoś ma w tym jakiś wgląd?
Używam Linuksa, na maszynie x86.
4 answers
Są co najmniej trzy rzeczy, które musisz zrobić, aby z powodzeniem używać libc z dynamicznym łączeniem:
- Link
/usr/lib/crt1.o
, który zawiera_start
, który będzie punktem wejścia dla pliku binarnego ELF; - Link
/usr/lib/crti.o
(przed libc) i/usr/lib/crtn.o
(Po), które zapewniają kod inicjalizacji i finalizacji; - Powiedz linkerowi, że binarny użyje linkera dynamicznego,
/lib/ld-linux.so
.
Na przykład:
$ cat hello.s
.text
.globl main
main:
push %ebp
mov %esp, %ebp
pushl $hw_str
call puts
add $4, %esp
xor %eax, %eax
leave
ret
.data
hw_str:
.asciz "Hello world!"
$ as -o hello.o hello.s
$ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -lc hello.o /usr/lib/crtn.o
$ ./hello
Hello world!
$
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-26 23:56:02
Jeśli zdefiniujesz main
w zbiorze
Odpowiedź Mateusza świetnie mówi o minimalnych wymaganiach.
Pozwól, że pokażę Ci, jak znaleźć te ścieżki w Twoim systemie. Run:
gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'
A potem odbierz akta, o których wspominał Matthew.
gcc -v
podaje dokładne polecenie linkera, którego używa GCC.
Collect2 to wewnętrzny plik wykonywalny używany przez GCC jako front-end linkera, który ma podobny interfejs do ld
.
W Ubuntu 14.04 64-bit (GCC 4.8), skończyło się na:
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
/usr/lib/x86_64-linux-gnu/crt1.o \
/usr/lib/x86_64-linux-gnu/crti.o \
-lc hello_world.o \
/usr/lib/x86_64-linux-gnu/crtn.o
Możesz również potrzebować -lgcc
i -lgcc_s
. Zobacz także: czy naprawdę potrzebuję libgcc?
Jeśli zdefiniujesz _start
w zbiorze
Jeśli zdefiniowałem _start
, hello world z glibc działało z just:
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc hello_world.o
Nie jestem pewien, czy jest to solidne, tzn. czy inicjalizacje crt
można bezpiecznie pominąć w celu wywołania funkcji glibc. Zobacz także: Dlaczego program do montażu działa tylko wtedy, gdy jest połączony z crt1.o crti.o i crtn.o?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:03
Myślę, że coś takiego powinno działać:
- Stwórz prosty program C
- Plik gcc-S.c
- Edycja pliku.s Plik gazowy.s
- plik ld.o-LC crt1.o-o myprog
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-26 19:26:22
Jeśli używasz _start
zamiast main
(Jak wspomniano w niektórych komentarzach powyżej), musisz również zmienić sposób wyjścia programu, lub dostaniesz błąd seg:
.text
.globl _start
_start:
mov $hw_str, %rdi
call puts
movl $0,%ebx # first argument: exit code.
movl $1,%eax # system call number: sys_exit.
int $0x80 # call kernel.
.data
hw_str: .asciz "Hello world!"
Na Kubuntu 18.04.2 (GCC (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0):
$ as -o hello.o hello.s
$ ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello hello.o -lc
Ponadto, jednym z łatwych sposobów, aby dowiedzieć się, co jest dynamicznym linkerem w Twoim systemie, jest skompilowanie małego programu w języku C, a następnie uruchomienie ldd
na binarnym:
Test.c:
int main() { return 0; }
Skompiluj i uruchom ldd z wykonywalnym:
$ gcc -o test test.c
$ ldd test
linux-vdso.so.1 (0x00007ffd0a182000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff24d8e6000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff24ded9000)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-28 15:09:33