Une alternative à la fonctionnalité obsolète malloc hook de glibc

J'écris un profileur de mémoire pour C et pour cela j'intercepte les appels au malloc, realloc et free fonctions via malloc_hooks. Malheureusement, ceux-ci sont obsolètes en raison de leur mauvais comportement dans les environnements multi-threads. Je n'ai pas pu trouver un document décrivant la solution alternative de meilleure pratique pour atteindre la même chose, quelqu'un peut-il m'éclairer?

J'ai lu qu'un simple #define malloc(s) malloc_hook(s) ferait l'affaire, mais cela ne fonctionne pas avec la configuration du système que j'ai en tête, car il est trop intrusif par rapport à la base de code d'origine pour pouvoir être utilisé dans un outil de profilage / traçage. Avoir à changer manuellement le code d'application d'origine est un tueur pour tout profileur décent. De manière optimale, la solution que je recherche devrait être activée ou désactivée simplement en créant un lien vers une bibliothèque partagée facultative. Par exemple, ma configuration actuelle utilise une fonction déclarée avec __attribute__ ((constructor)) pour installer les crochets d'interception malloc.

Merci

22
demandé sur Guy Avraham 2013-07-23 11:02:34

2 réponses

Après avoir essayé certaines choses, j'ai enfin réussi à comprendre comment faire.

Tout d'Abord, dans glibc, malloc est défini comme un faible le symbole, ce qui signifie qu'il peut être remplacé par l'application ou une bibliothèque partagée. Par conséquent, LD_PRELOAD n'est pas nécessairement nécessaire. Au lieu de cela, j'ai implémenté la fonction suivante dans une bibliothèque partagée:

void*
malloc (size_t size)
{
  [ ... ]
}

Qui est appelé par l'application au lieu de glibc s malloc.

Maintenant, pour être équivalent à la fonctionnalité __malloc_hooks, un couple de les choses sont toujours portées disparues.

1.) l'adresse de l'appelant

En plus des paramètres d'origine pour malloc, glibcs __malloc_hook S fournissent également l'adresse de la fonction appelante, qui est en fait l'adresse de retour de l'endroit où malloc retournerait. Pour obtenir la même chose, nous pouvons utiliser la fonction __builtin_return_address disponible dans gcc. Je n'ai pas regardé dans d'autres compilateurs, parce que je suis limité à gcc de toute façon, mais si vous savez comment faire une telle chose de manière portable, veuillez me laisser tomber un commentaire :)

Notre fonction malloc ressemble maintenant à ceci:

void*
malloc (size_t size)
{
  void *caller = __builtin_return_address(0);
  [ ... ]
}

2.) accéder à glibc s malloc depuis votre crochet

Comme je suis limité à la glibc dans mon application, j'ai choisi d'utiliser __libc_malloc pour accéder à l'implémentation malloc d'origine. Alternativement, dlsym(RTLD_NEXT, "malloc") peut être utilisé, mais au piège possible que cette fonction utilise calloc lors de son premier appel, entraînant éventuellement une boucle infinie menant à un segfault.

Crochet malloc complet

Mon complet la fonction d'accrochage ressemble maintenant à ceci:

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);
}

my_malloc_hook ressemble à ceci:

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;
}

Bien sûr, les crochets pour calloc, realloc et free fonctionnent de la même façon.

Liaison dynamique et statique

Avec ces fonctions, la liaison dynamique fonctionne hors de la boîte. Lier le fichier. so contenant l'implémentation du hook malloc résultera de tous les appels à malloc de l'application et aussi de tous les appels de bibliothèque à acheminer via mon hook. La liaison statique est problématique bien. Je n'ai pas encore complètement enveloppé ma tête, mais dans la liaison statique, malloc n'est pas un symbole faible, ce qui entraîne une erreur de définition multiple au moment du lien.

Si vous avez besoin d'une liaison statique pour une raison quelconque, par exemple en traduisant des adresses de fonction dans des bibliothèques tierces en lignes de code via des symboles de débogage, vous pouvez lier ces bibliothèques tierces statiquement tout en reliant dynamiquement les crochets malloc, en évitant le problème de définition multiple. Je n'ai pas encore trouvé une meilleure solution pour cela, si vous en connaissez un,n'hésitez pas à me laisser un commentaire.

Voici un petit exemple:

gcc -o test test.c -lmalloc_hook_library -Wl,-Bstatic -l3rdparty -Wl,-Bdynamic

3rdparty sera lié statiquement, tandis que malloc_hook_library sera lié dynamiquement, ce qui entraînera le comportement attendu, et les adresses des fonctions dans 3rdparty pourront être traduites via des symboles de débogage dans test. Très soigné, hein?

Conlusion

Les techniques ci-dessus décrivent une approche non dépréciée, à peu près équivalente à __malloc_hook s, mais avec quelques limites moyennes:

__builtin_caller_address fonctionne uniquement avec les gcc

__libc_malloc fonctionne uniquement avec les glibc

dlsym(RTLD_NEXT, [...]) est une extension GNU dans glibc

Les drapeaux de l'éditeur de liens -Wl,-Bstatic et -Wl,-Bdynamic sont spécifiques aux binutils GNU.

En d'autres termes, Cette solution est totalement non portable et des solutions alternatives devraient être ajoutées si la bibliothèque hooks devait être portée sur un système d'exploitation non GNU.

36
répondu Andreas Grapentin 2016-04-22 13:32:55

Vous pouvez utiliser LD_PRELOAD & dlsym Voir "Conseils pour malloc et gratuit" à http://www.slideshare.net/tetsu.koba/presentations

2
répondu vinayak 2013-07-23 08:05:37