Programowanie w systemie Linux Jacek Lach Zakład Oprogramowania Instytut Informatyki Politechnika Śląska
Plan Biblioteki Narzędzia pomocnicze Biblioteki statyczne Biblioteki dynamiczne Interfejs dynamicznego ładowania bibliotek
Narzędzia nm wypisanie symboli z pliku obiektowego Oznaczenia: x/x symbol lokalny / globalny B niezainicjalizowana sekcja danych D zainicjalizowana sekcja danych T symbol w sekcji kodu U symbol niezdefiniowany Opcje: C rozwikłanie nazw u tylko niezdefiniowane symbole l numery wierszy w kodzie źródłowym
Narzędzia #include <stdio.h> int zg; int zs=1; void hello(void) { printf("hello world\n"); int main(void) { hello(); return 0;
Narzędzia $ nm main 080483d0 T hello 080483e4 T main U printf@@glibc_2.0 080495e4 B zg 08049470 D zs $ nm -u main libc_start_main@@glibc_2.0 register_frame_info@@glibc_2.0 printf@@glibc_2.0
Narzędzia ar obsługa archiwów ar p[mod] archive [member...] p: d, m, q, p, r, x mod: a dodaj za b dodaj przed c utwórz archiwum s zapisz index u uzupełnij archiwum tylko nowszymi elementami
Narzędzia ranlib generacja indeksu zawartości archiwum ldd wypisanie listy bibliotek współdzielonych wymaganych przez program $ ldd main linux-gate.so.1 => (0xb80c8000) libc.so.6 => /lib/libc.so.6 (0xb7f59000) /lib/ld-linux.so.2 (0xb80c9000)
linux gate? int 0x80 sysenter/sysexit cat /proc/self/maps dd if=/proc/self/mem of=linux-gate.bin bs=1 skip=3086897152 count=4096 file linux-gate.bin linux-gate.bin: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped
Biblioteki statyczne Tradycyjne rozszerzenie:.a Kolekcja plików obiektowych Tworzenie biblioteki statycznej: Utworzenie plików obiektowych zawierających wymagany kod Utworzenie archiwum zawierającego skompilowany kod [Generacja indeksu] Udostępnienie nagłówków
Biblioteki statyczne hello.c: #include <stdio.h> int hello(void) { printf("hello world\n"); hello.h: #ifndef _HELLO_H_ #define _HELLO_H_ int hello(void); #endif
Biblioteki statyczne $ gcc -c hello.c $ ar crv libhello.a hello.o a - hello.o [$ ranlib libhello.a] $ nm -s libhello.a Archive index: hello in hello.o hello.o: 00000000 t gcc2_compiled. 00000000 T hello U printf
Biblioteki statyczne hello lib.c: #include <stdio.h> #include "hello.h" int main(void) { hello(); return 0; $ gcc main-lib.c -o main-lib -L. -lhello $ ldd main-lib linux-gate.so.1 => (0xb7fe9000) libc.so.6 => /lib/libc.so.6 (0xb7e7a000) /lib/ld-linux.so.2 (0xb7fea000)
Biblioteki dynamiczne + Szybsze działanie (jednokrotne ładowanie) + Mniejsze zużycie zasobów Konieczność rekompilacji w przypadku istotnych zmian w bibliotece Program dynamicznego ładowania bibliotek zawarty jest w bibliotece ld (ld.so, ld linux.so) Konfiguracja: /etc/ld.so.conf Narzędzie do generowania pliku cache: ldconfig Cache zawiera mapowania soname na nazwy plików bibliotek w systemie plików
Biblioteki dynamiczne LD_LIBRARY_PATH /lib/ld linux.so.2 library path PATH EXEC Ostrożnie! rpath (gcc) LD_DEBUG bindings libs versions files
Biblioteki dynamiczne Nazwy: soname libname.so.x name nazwa x wersja real name libname.so.x.y.z y podwersja ldname z wydanie libname.so
Biblioteki dynamiczne Tworzenie biblioteki dynamicznej: Utworzenie kodu biblioteki Kompilacja z przełącznikami: fpic, shared, soname Nie używać: fomit frame pointer Jawne łączenie z biblioteką C
Biblioteki dynamiczne void attribute ((constructor)) my_init(void); void attribute ((destructor)) my_fini(void);
Biblioteki dynamiczne $ gcc -g -c -fpic hello.c -o hello.o $ gcc -g -shared -Wl,-soname,libhello.so hello.o -o libhello.so.1.0.0 -lc $ ln -s libhello.so.1.0.0 libhello.so.1 $ ln -s libhello.so.1.0.0 libhello.so $ gcc main-lib.c -o main-lib -L. -lhello $ ldd main-lib linux-gate.so.1 => (0xb7fe9000) libhello.so => not found libc.so.6 => /lib/libc.so.6 (0xb7e7a000) /lib/ld-linux.so.2 (0xb7fea000) $ LD_LIBRARY_PATH=`pwd` ldd main-lib linux-gate.so.1 => (0xb7f33000) libhello.so =>../ex/libhello.so (0xb7f30000) libc.so.6 => /lib/libc.so.6 (0xb7dc2000) /lib/ld-linux.so.2 (0xb7f34000)
Biblioteki dynamiczne LD_LIBRARY_PATH=. LD_DEBUG=files./main-lib 31828: 31828: file=libhello.so [0]; needed by./main-lib [0] 31828: file=libhello.so [0]; generating link map 31828: dynamic: 0xb7fe3878 base: 0xb7fe2000 size: 0x000019a8 31828: entry: 0xb7fe26c0 phdr: 0xb7fe2034 phnum: 3 31828: 31828: file=libc.so.6 [0]; needed by./main-lib [0] 31828: file=libc.so.6 [0]; generating link map 31828: dynamic: 0xb7fbed7c base: 0xb7e8d000 size: 0x00135630 31828: entry: 0xb7ea34f0 phdr: 0xb7e8d034 phnum: 10 31828: 31828: calling init: /lib/libc.so.6 31828: calling init:./libhello.so 31828: initialize program:./main-lib 31828: transferring control:./main-lib hello world 31828: calling fini:./main-lib [0] 31828: calling fini:./libhello.so [0] 31828: calling fini: /lib/libc.so.6 [0]
Interfejs dl (dynamic loading) Umożliwia dynamiczne wczytanie biblioteki, nie dołączonej podczas kompilacji, w czasie działania programu. Możliwość implementacji mechanizmu wtyczek Umożliwia wczytanie, przeszukiwanie oraz zwolnienie dynamicznego obiektu Zaimplementowany w postaci biblioteki (libdl) Deklaracje: <dlfcn.h>
Wykorzystanie obiektów współdzielonych Wczytanie: void *dlopen(const char *filename, int flag); Odnalezienie symbolu: void *dlsym(void *handle, char *symbol); Zwolnienie: void dlclose(void *handle); Obsługa błędów: const char *dlerror(void);
Wykorzystanie obiektów współdzielonych #include <stdio.h> #include <stdlib.h> #include <dlfcn.h> int main(void) { void *handle; int (*hello_fun)(void); const char *errmsg; handle = dlopen("./libhello.so", RTLD_NOW); if(handle == NULL) { fprintf(stderr, "dlopen: %s\n",dlerror()); exit(1); dlerror(); hello_fun = dlsym(handle, "hello"); if((errmsg = dlerror())!= NULL ) { fprintf(stderr, "dlsym: %s\n", errmsg); exit(1); hello_fun(); dlclose(handle); return(0);
Wykorzystanie obiektów współdzielonych $ gcc main-dl.c -o main-dl -ldl $ ldd main-dl linux-gate.so.1 => (0xb80a7000) libdl.so.2 => /lib/libdl.so.2 (0xb807b000) libc.so.6 => /lib/libc.so.6 (0xb7f34000) /lib/ld-linux.so.2 (0xb80a8000)
Interfejs dl i C++ Jak odwołać się do funkcji? Jak utworzyć obiekt? C++ przeszkody i mechanizmy pomocne: wikłanie nazw (name mangling) extern C dziedziczenie polimorfizm
base.h #include <iostream.h> #include <stdlib.h> class parent { public: virtual void hello(void) {; ; extern "C" { parent *maker(void);
child.h #include "base.h" class child: public parent { public: virtual void hello(void); ; extern "C" { parent *maker(void);
child.cpp #include "child.h" void child::hello(void) { cout << "child" << endl; parent *maker(void) { child *c = new child; return((parent *)c);
main.cpp #include <stdio.h> #include "child.h" #include <stdlib.h> #include <dlfcn.h> int main(void) { void *handle = 0; parent *(*pmaker)(void) = 0; const char* error = 0; handle = dlopen("./child.so", RTLD_LAZY); if (!handle ) { error = dlerror(); if (!error ) fprintf(stderr, "dlopen()\n"); exit (EXIT_FAILURE); pmaker = (parent *(*)(void))dlsym(handle, "maker"); if (!pmaker ) { error = dlerror(); if (!error ) { fprintf(stderr, "dlsym"); exit (EXIT_FAILURE); parent *p = (*pmaker)(); p->hello(); dlclose(handle); return (EXIT_SUCCESS);