glibc version
Una alternativa para la funcionalidad__malloc_hook en desuso de glibc (2)
Puede usar LD_PRELOAD & dlsym Consulte "Consejos para malloc y gratis" en http://www.slideshare.net/tetsu.koba/presentations
Estoy escribiendo un perfilador de memoria para C y estoy interceptando llamadas a las funciones malloc, realloc y free a través de malloc_hooks. Desafortunadamente, estos están en desuso debido a su comportamiento deficiente en entornos multiproceso. No pude encontrar un documento que describiera la solución alternativa de mejores prácticas para lograr lo mismo, ¿alguien me puede aclarar?
He leído que un simple #define malloc(s) malloc_hook(s)
haría el truco, pero eso no funciona con la configuración del sistema que tengo en mente, porque es demasiado intrusiva para la base del código original como para ser adecuado para usar en una herramienta de perfilado / rastreo. Tener que cambiar manualmente el código de la aplicación original es un asesino para cualquier perfilador decente. De manera óptima, la solución que estoy buscando debe habilitarse o deshabilitarse simplemente mediante el enlace a una biblioteca compartida opcional. Por ejemplo, mi configuración actual utiliza una función declarada con __attribute__ ((constructor))
para instalar los enlaces de interceptación de malloc.
Gracias
Después de probar algunas cosas, finalmente logré descubrir cómo hacer esto.
En primer lugar, en glibc
, malloc
se define como un símbolo débil, lo que significa que puede ser sobrescrito por la aplicación o una biblioteca compartida. Por lo tanto, LD_PRELOAD
no es necesariamente necesario. En cambio, implementé la siguiente función en una biblioteca compartida:
void*
malloc (size_t size)
{
[ ... ]
}
Que se llama por la aplicación en lugar de glibc
s malloc
.
Ahora, para ser equivalente a la funcionalidad __malloc_hook
, aún faltan algunas cosas.
1.) la dirección de la persona que llama
Además de los parámetros originales de malloc
, glibc
s __malloc_hook
s también proporcionan la dirección de la función de llamada, que en realidad es la dirección de retorno de donde regresaría malloc
. Para lograr lo mismo, podemos usar la función __builtin_return_address
que está disponible en gcc. No he investigado otros compiladores, porque de todos modos estoy limitado a gcc, pero si sabe cómo hacerlo de manera portátil, envíeme un comentario :)
Nuestra función malloc
ahora se ve así:
void*
malloc (size_t size)
{
void *caller = __builtin_return_address(0);
[ ... ]
}
2.) accediendo a glibc
s malloc desde dentro de su gancho
Como estoy limitado a glibc en mi aplicación, elegí usar __libc_malloc
para acceder a la implementación original de malloc. Alternativamente, se dlsym(RTLD_NEXT, "malloc")
, pero ante la posible trampa de que esta función use calloc
en su primera llamada, posiblemente resulte en un bucle infinito que conduzca a una segfault.
completar el gancho malloc
Mi función de enganche completa ahora se ve así:
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);
}
donde my_malloc_hook
ve así:
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;
}
Por supuesto, los ganchos para calloc
, realloc
y free
funcionan de manera similar.
enlace dinámico y estático
Con estas funciones, la vinculación dinámica funciona de la caja. Vincular el archivo .so que contiene la implementación de malloc hook resultará en todas las llamadas a malloc
desde la aplicación y también todas las llamadas a la biblioteca que se enrutarán a través de mi gancho. La vinculación estática es problemática sin embargo. Todavía no me he acostumbrado por completo, pero en el enlace estático malloc no es un símbolo débil, lo que da como resultado un error de definición múltiple en el tiempo del enlace.
Si necesita enlaces estáticos por cualquier razón, por ejemplo, traducir direcciones de funciones en bibliotecas de terceros a líneas de código mediante símbolos de depuración, puede vincular estas bibliotecas de terceros estáticamente mientras sigue enlazando dinámicamente los ganchos malloc, evitando el problema de definición múltiple. Todavía no he encontrado una solución mejor para esto, si conoce uno, siéntase libre de dejarme un comentario.
Aquí hay un pequeño ejemplo:
gcc -o test test.c -lmalloc_hook_library -Wl,-Bstatic -l3rdparty -Wl,-Bdynamic
3rdparty
se vinculará estáticamente, mientras que malloc_hook_library
se vinculará dinámicamente, dando como resultado el comportamiento esperado, y las direcciones de funciones en 3rdparty
para ser traducibles a través de símbolos de depuración en test
. Bastante limpio, ¿eh?
Conlusion
las técnicas anteriores describen un enfoque no __malloc_hook
, bastante equivalente, para __malloc_hook
s, pero con un par de limitaciones medias:
__builtin_caller_address
solo funciona con gcc
__libc_malloc
solo funciona con glibc
dlsym(RTLD_NEXT, [...])
es una extensión de GNU en glibc
los indicadores del enlazador -Wl,-Bstatic
y -Wl,-Bdynamic
son específicos de los binutils de GNU.
En otras palabras, esta solución es totalmente no portátil y las soluciones alternativas tendrían que agregarse si la biblioteca de ganchos fuera a ser portada a un sistema operativo que no sea GNU.