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
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_hook
s, 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
, glibc
s __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);
}
Où 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.
Vous pouvez utiliser LD_PRELOAD & dlsym Voir "Conseils pour malloc et gratuit" à http://www.slideshare.net/tetsu.koba/presentations