Альтернатива устаревшей функциональности __malloc_hook в glibc
Я пишу профилировщик памяти для C, и для этого перехватывает вызовы к malloc
, realloc
а также free
работает через malloc_hooks. К сожалению, они устарели из-за их плохого поведения в многопоточных средах. Я не мог найти документ, описывающий альтернативное решение передовой практики для достижения того же, может кто-то просветить меня?
Я прочитал это просто #define malloc(s) malloc_hook(s)
может помочь, но это не работает с настройкой системы, которую я имею в виду, потому что она слишком навязчива к исходной базе кода, чтобы ее можно было использовать в инструменте профилирования / трассировки. Необходимость вручную изменять исходный код приложения является убийцей для любого приличного профилировщика. Оптимально, решение, которое я ищу, должно быть включено или отключено, просто связавшись с дополнительной общей библиотекой. Например, моя текущая установка использует функцию, объявленную с __attribute__ ((constructor))
установить перехват malloc
крючки.
Спасибо
2 ответа
Попробовав некоторые вещи, мне наконец удалось выяснить, как это сделать.
Прежде всего, в glibc
, malloc
определяется как слабый символ, что означает, что он может быть перезаписан приложением или общей библиотекой. Следовательно, LD_PRELOAD
не обязательно нужно. Вместо этого я реализовал следующую функцию в общей библиотеке:
void*
malloc (size_t size)
{
[ ... ]
}
Который вызывается приложением вместо glibc
s malloc
,
Теперь, чтобы быть эквивалентным __malloc_hook
с функциональностью, пара вещей все еще отсутствует.
1.) адрес звонящего
В дополнение к исходным параметрам malloc
, glibc
s __malloc_hook
s также предоставляют адрес вызывающей функции, который фактически является адресом возврата malloc
вернется к. Чтобы достичь того же, мы можем использовать __builtin_return_address
функция, которая доступна в GCC. Я не смотрел на другие компиляторы, потому что я все равно ограничен gcc, но если вам случится знать, как делать такие вещи переносимо, пожалуйста, оставьте мне комментарий:)
наш malloc
функция теперь выглядит так:
void*
malloc (size_t size)
{
void *caller = __builtin_return_address(0);
[ ... ]
}
2.) доступ glibc
S 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, все вызовы 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.
Другими словами, это решение совершенно непереносимо, и необходимо было бы добавить альтернативные решения, если бы библиотека ловушек была перенесена в операционную систему не-GNU.
Вы можете использовать LD_PRELOAD & dlsym. См. "Советы по malloc и free" на http://www.slideshare.net/tetsu.koba/presentations
Просто удалось NDK построить код, содержащий __malloc_hook
,
Похоже, он был восстановлен в Android API v28, согласно https://android.googlesource.com/platform/bionic/+/master/libc/include/malloc.h, esp:
extern void* (*volatile __malloc_hook)(size_t __byte_count, const void* __caller) __INTRODUCED_IN(28);