Ответ 1
После некоторых попыток мне удалось выяснить, как это сделать.
Прежде всего, в glibc
, malloc
определяется как слабый символ, а это означает, что он может быть перезаписан приложением или разделяемой библиотекой. Следовательно, LD_PRELOAD
необязательно необходимо. Вместо этого я реализовал следующую функцию в общей библиотеке:
void*
malloc (size_t size)
{
[ ... ]
}
который вызывается приложением вместо glibc
malloc
.
Теперь, чтобы быть эквивалентным функциональности __malloc_hook
, осталось несколько вещей.
1.) адрес вызывающего абонента
В дополнение к исходным параметрам malloc
, glibc
__malloc_hook
также предоставляет адрес вызывающей функции, которая на самом деле является обратным адресом, в который будет возвращаться malloc
. Для достижения такой же цели мы можем использовать функцию __builtin_return_address
, доступную в gcc. Я не рассматривал другие компиляторы, потому что я ограничен gcc, но если вы случайно знаете, как это сделать, переносите мне комментарий:)
Наша функция malloc
теперь выглядит следующим образом:
void*
malloc (size_t size)
{
void *caller = __builtin_return_address(0);
[ ... ]
}
2.) доступ к glibc
malloc из вашего крюка
Поскольку я ограничен glibc в своем приложении, я решил использовать __libc_malloc
для доступа к исходной реализации malloc. В качестве альтернативы можно использовать dlsym(RTLD_NEXT, "malloc")
, но при возможной ловушке эта функция использует calloc
при первом вызове, что может привести к бесконечному циклу, ведущему к segfault.
полный крюк malloc
Теперь моя полная функция подключения выглядит следующим образом:
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
выглядит следующим образом:
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;
}
Конечно, крючки для calloc
, realloc
и free
работают аналогично.
динамическое и статическое связывание
С помощью этих функций динамическая компоновка работает из коробки. Связывание файла .so, содержащего реализацию malloc hook, будет вызвано всеми вызовами malloc
из приложения, а также всеми вызовами библиотеки, которые будут маршрутизироваться через мой крючок. Однако статическая связь является проблематичной. Я еще не обернул вокруг себя полностью, но в статической привязке malloc не является слабым символом, что приводит к ошибке множественного определения во время соединения.
Если вам нужна статическая привязка по какой-либо причине, например, перевод адресов функций в сторонних библиотеках на строки кода через символы отладки, то вы можете связать эти сторонние библиотеки статически, сохраняя связь между динамиками malloc динамически, избегая проблемы с множественным определением, Я еще не нашел лучшего обходного пути для этого, если вы его знаете, не стесняйтесь оставлять мне комментарий.
Вот краткий пример:
gcc -o test test.c -lmalloc_hook_library -Wl,-Bstatic -l3rdparty -Wl,-Bdynamic
3rdparty
будет связан статически, а malloc_hook_library
будет динамически связываться, что приведет к ожидаемому поведению, а адреса функций в 3rdparty
будут переводиться через символы отладки в test
. Довольно аккуратно, да?
Conlusion
описанные выше методы описывают не-устаревший, довольно эквивалентный подход к __malloc_hook
s, но с несколькими средними ограничениями:
__builtin_caller_address
работает только с gcc
__libc_malloc
работает только с glibc
dlsym(RTLD_NEXT, [...])
является расширением GNU в glibc
флаги компоновщика -Wl,-Bstatic
и -Wl,-Bdynamic
относятся к GNU binutils.