2010-06-18 20 views
6

Jak zmienić bibliotekę, z której ładuje się funkcja podczas pracy?Zmienianie kolejności ładowania biblioteki w czasie wykonywania (np. LD_PRELOAD, ale podczas wykonywania)

Na przykład, chcę zastąpić standardową funkcję printf czymś nowym, mogę napisać własną wersję i skompilować ją do biblioteki współdzielonej, a następnie umieścić "LD_PRELOAD =/my/library.so" w środowisku przed uruchamianie mojego pliku wykonywalnego.

Ale powiedzmy, że zamiast tego chcę zmienić to powiązanie z poziomu samego programu. Z pewnością to musi być możliwe ... prawda?

EDIT
I nie, następujące nie działa (ale jeśli możesz mi powiedzieć jak to działało, to byłoby wystarczające).

void* mylib = dlopen("/path/to/library.so",RTLD_NOW); 
printf = dlsym(mylib,"printf"); 

Odpowiedz

4

AFAIK, że nie jest to możliwe. Ogólna reguła jest taka, że ​​jeśli ten sam symbol pojawia się w dwóch bibliotekach, ld.so będzie faworyzować bibliotekę, która została załadowana jako pierwsza. LD_PRELOAD działa, upewniając się, że określone biblioteki są załadowane przed wszelkimi niejawnie załadowanymi bibliotekami.

Po uruchomieniu programu wszystkie wczytane biblioteki zostaną załadowane, a zatem jest za późno, aby załadować bibliotekę przed nimi.

0

istnieje zmienna środowiskowa LD_LIBRARY_PATH gdzie wyszukuje łącznikowe do niszczenia bibliotekach poprzedzić ścieżka do LD_LIBRARY_PATH, mam nadzieję, że będzie działać

0

Nie można tego zmienić. Ogólnie * pojęcie łączenia NIX (lub raczej brak koncepcji) jest wybierane z pierwszego obiektu, w którym został znaleziony. (Z wyjątkiem dziwnego systemu AIX, który domyślnie działa jak OS/2.)

Programowo można zawsze wypróbować: dlsym(RTLD_DEFAULT) i dlsym(RTLD_NEXT). man dlsym, aby uzyskać więcej informacji. Chociaż szybko wymyka się spod kontroli. Dlaczego rzadko jest używany.

+0

Czy możesz wyjaśnić, co masz na myśli z 'dlsym()'? Zobacz moją edycję na oryginalne pytanie, aby uzyskać więcej informacji. – tylerl

+1

nie możesz 'printf = dlsym (mylib," printf ");' oczywiście powinieneś być w stanie, chociaż ze zmienną globalną 'int (* myprintf) (const char * fmt, ...) = dlsym (mylib, "printf") ', a następnie' #define printf myprintf' w potrzebnych jednostkach kompilacji. Myślałem, że 'printf' jest bardziej przykładem w twoim przypadku. Jeśli '' printf' jest konkretnie twoim celem (możesz także zastąpić w czasie łącza), możesz również spróbować powiedzieć linkerowi, żeby nie łączyć standardowych bibliotek, najpierw umieść bibliotekę z niestandardowym printf() na liście bibliotek - przed ręcznym określone biblioteki standardowe, w których znajduje się orig printf(). – Dummy00001

+0

BTW, LD_PRELOAD to metoda dodawania biblioteki * przed * standardowymi bibliotekami. Po prostu robi to podczas pracy. W czasie łączenia, kolejność bibliotek, które dajesz linkerowi, określa priorytet symboli: symbole będą wyszukiwane od pierwszej lib do ostatniej. Zazwyczaj standardowe biblioteki są najpierw umieszczane na liście linków (przez interfejsy takie jak cc). Ale możesz go wyłączyć (-nostdlib) i określić ręcznie w żądanej kolejności, np. umieszczając swoją bibliotekę libmy.so przed wszystkim innym. Następnie printf() z libmy.so może * zastąpić * printf z libc. – Dummy00001

1

Należy powiedzieć, że próba zastąpienia funkcji z biblioteki libc w aplikacji ma niezdefiniowane zachowanie zgodnie z ISO C/POSIX, niezależnie od tego, czy robisz to statycznie, czy dynamicznie. Może działać (i w dużej mierze będzie działać na GNU/Linux), ale niemądrze jest polegać na tym, że działa. Jeśli chcesz tylko użyć nazwy "printf", ale musisz zrobić coś niestandardowego w swoim programie, najlepszym sposobem na to jest #undef printf i #define printf my_printf AFTER, w tym wszelkie nagłówki systemowe. W ten sposób nie zakłócasz żadnego wewnętrznego użycia funkcji przez biblioteki, z których korzystasz ... a twoja implementacja my_printf może nawet wywołać printf systemu, jeśli/kiedy to konieczne.

Z drugiej strony, jeśli twoim celem jest ingerowanie w to, co robią biblioteki, to gdzieś na dole prawdopodobnie pojawią się problemy ze zgodnością. Lepszym podejściem byłoby prawdopodobnie zastanowienie się, dlaczego biblioteka nie zrobi tego, czego chcesz, bez ponownego zdefiniowania używanych funkcji, łatania ich i wysyłania poprawek, jeśli będą odpowiednie.

-2

Zapisz wynik dlsym() w tabeli odnośników (tablica, tablica skrótów itp.). Następnie #undef print i #define print użyć swojej wersji tabeli odnośników.

+1

To nie działa w języku C. – shoosh

+0

Działa to dobrze. To, w jaki sposób QT używa OpenSSL, a także mój kod, który ma łączenie w czasie wykonywania zamiast czasu kompilacji/ładowania. –

2

Nie ma czystego rozwiązania, ale jest to możliwe. Widzę dwie opcje:

  1. nadpisywania funkcji printf prolog ze skoku do funkcji zastępczej.

    Jest to dość popularne rozwiązanie do funkcji hookowania w MS Windows. Możesz znaleźć przykłady funkcji podpinania przez przepisywanie kodu w Google.

  2. Przepisz tablice przeniesień ELF.

    To prawie dokładnie to, o co prosisz, ale tylko w zakresie modułów ed dlopen(). W twoim przypadku chcesz również edytować swój główny (zazwyczaj nie-PIC) moduł. Nie próbowałem, ale może jej tak proste jak wywołanie kodu wyposażone w:

    void* handle = dlopen(NULL, RTLD_LAZY); 
    void* original; 
    original = elf_hook(argv[0], LIBRARY_ADDRESS_BY_HANDLE(handle), printf, my_printf); 
    

    Jeśli to nie musisz czytać źródła dynamicznego linkera, aby dowiedzieć się, co musi zostać dostosowany.

Powiązane problemy