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

Author: Guy Avraham, 2013-07-23

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, glibcs __malloc_hooks 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 glibcs 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_hooks, 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.

 36
Author: Andreas Grapentin,
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

 2
Author: vinayak,
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