Alternatywa dla przestarzałej funkcji haka malloc w glibc
Piszę Profiler pamięci dla C i do tego przechwytuję połączenia do malloc
, realloc
i free
funkcji poprzez malloc_hooks. Niestety, są one przestarzałe ze względu na ich złe zachowanie w środowiskach wielowątkowych. Nie mogłem znaleźć dokumentu opisującego alternatywne rozwiązanie najlepszych praktyk, aby osiągnąć to samo, czy ktoś może mnie oświecić?
Czytałem, że prosty #define malloc(s) malloc_hook(s)
załatwiłby sprawę, ale to nie działa z konfiguracją systemu, którą mam na myśli, ponieważ jest zbyt inwazyjny dla oryginalnej bazy kodu, aby nadawał się do użycia w narzędziu do profilowania / śledzenia. Konieczność ręcznej zmiany oryginalnego kodu aplikacji jest zabójcza dla każdego przyzwoitego profilera. Optymalnie rozwiązanie, którego szukam, powinno być włączone lub wyłączone poprzez połączenie z opcjonalną biblioteką współdzieloną. Na przykład, moja bieżąca konfiguracja używa funkcji zadeklarowanej przez __attribute__ ((constructor))
, Aby zainstalować Hooki przechwytujące malloc
.
Dzięki
2 answers
Po wypróbowaniu kilku rzeczy, w końcu udało mi się wymyślić, jak to zrobić.
Po pierwsze, w glibc
, malloc
jest zdefiniowany jako słaby symbol, co oznacza, że może być nadpisany przez aplikację lub bibliotekę współdzieloną. Stąd, {[7] } nie jest koniecznie potrzebne. Zamiast tego zaimplementowałem następującą funkcję w bibliotece współdzielonej:
void*
malloc (size_t size)
{
[ ... ]
}
Który jest wywoływany przez aplikację zamiast glibc
s malloc
.
Teraz, aby być odpowiednikiem __malloc_hook
s funkcjonalności, kilka wciąż czegoś brakuje.
1.) adres rozmówcy
Oprócz oryginalnych parametrów do malloc
, glibc
s __malloc_hook
s dostarcza również adres funkcji wywołującej, który jest w rzeczywistości adresem zwrotnym where malloc
powróci do. Aby osiągnąć to samo, możemy użyć funkcji __builtin_return_address
, która jest dostępna w gcc. Nie zaglądałem do innych kompilatorów, bo i tak ograniczam się do gcc, ale jeśli zdarzy ci się wiedzieć, jak to zrobić, to proszę o upuść mnie a komentarz:)
Nasza malloc
funkcja wygląda teraz tak:
void*
malloc (size_t size)
{
void *caller = __builtin_return_address(0);
[ ... ]
}
2.) dostęp do glibc
s malloc z wewnątrz haka
Ponieważ jestem ograniczony do glibc w mojej aplikacji, zdecydowałem się użyć __libc_malloc
, aby uzyskać dostęp do oryginalnej implementacji malloc. Alternatywnie można użyć dlsym(RTLD_NEXT, "malloc")
, ale przy możliwej pułapce, którą ta funkcja używa calloc
podczas pierwszego wywołania, co może skutkować nieskończoną pętlą prowadzącą do segfault.
Kompletny hak malloc
Mój kompletny funkcja hooking wygląda teraz tak:
extern void *__libc_malloc(size_t size);
int malloc_hook_active = 0;
void*
malloc (size_t size)
{
void *caller = __builtin_return_address(0);
if (malloc_hook_active)
return my_malloc_hook(size, caller);
return __libc_malloc(size);
}
Gdzie my_malloc_hook
wygląda tak:
void*
my_malloc_hook (size_t size, void *caller)
{
void *result;
// deactivate hooks for logging
malloc_hook_active = 0;
result = malloc(size);
// do logging
[ ... ]
// reactivate hooks
malloc_hook_active = 1;
return result;
}
Oczywiście, haki do calloc
, realloc
i free
działają podobnie.
Dynamiczne i statyczne łączenie
Dzięki tym funkcjom dynamiczne łączenie działa od razu po uruchomieniu. Połączenie pliku. so zawierającego implementację Hooka malloc spowoduje wszystkie wywołania malloc
z aplikacji, a także wszystkie wywołania biblioteki, które mają być przekierowane przez mój hook. Statyczne linkowanie jest problematyczne chociaż. Nie owinąłem jeszcze całkowicie głowy, ale w linkowaniu statycznym malloc nie jest słabym symbolem, co powoduje błąd wielokrotnej definicji w czasie łącza.
Jeśli potrzebujesz statycznego linkowania z dowolnego powodu, na przykład tłumaczenia adresów funkcji w bibliotekach innych firm na linie kodu za pomocą symboli debugowania, możesz połączyć te biblioteki stron trzecich statycznie, jednocześnie łącząc Hooki malloc dynamicznie, unikając problemu z wieloma definicjami. Nie znalazłem jeszcze lepiej obejść to, jeśli znasz jedno, zostaw mi komentarz.
Oto krótki przykład:]}gcc -o test test.c -lmalloc_hook_library -Wl,-Bstatic -l3rdparty -Wl,-Bdynamic
3rdparty
będą połączone statycznie, podczas gdy malloc_hook_library
będą połączone dynamicznie, co spowoduje oczekiwane zachowanie, a adresy funkcji w 3rdparty
będą tłumaczone za pomocą symboli debugowania w test
. Nieźle, co?
Conlusion
Powyższe techniki opisują nieaktualne, prawie równoważne podejście do __malloc_hook
s, ale z kilkoma średnie ograniczenia:
__builtin_caller_address
działa tylko z gcc
__libc_malloc
działa tylko z glibc
dlsym(RTLD_NEXT, [...])
jest rozszerzeniem GNU w glibc
Flagi linkera -Wl,-Bstatic
i -Wl,-Bdynamic
są specyficzne dla GNU binutils.
Innymi słowy, to rozwiązanie jest całkowicie przenośne i trzeba by było dodać alternatywne rozwiązania, gdyby biblioteka hooków została przeniesiona na system operacyjny nie będący GNU.
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
2016-04-22 13:32:55
Możesz użyć LD_PRELOAD & dlsym Zobacz "Porady dla malloc i za darmo" w http://www.slideshare.net/tetsu.koba/presentations
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
2013-07-23 08:05:37