Подтвердить что ты не робот

Альтернатива устаревшей функциональности __malloc_hook glibc

Я пишу профилировщик памяти для C и для этого перехватываю вызовы для malloc, realloc и бесплатных функций через malloc_hooks. К сожалению, они устарели из-за их плохого поведения в многопоточных средах. Я не мог найти документ, описывающий альтернативное решение лучшей практики для достижения того же, может ли кто-нибудь просветить меня?

Я читал, что простой #define malloc(s) malloc_hook(s) будет делать трюк, но это не работает с настройкой системы, о которой я помню, потому что он слишком навязчив исходной кодовой базе, подходящей для использования в профилировании/трассировочный инструмент. Чтобы вручную изменить исходный код приложения, это убийца для любого достойного профилировщика. Оптимально, решение, которое я ищу, должно быть включено или отключено, просто связываясь с дополнительной разделяемой библиотекой. Например, моя текущая настройка использует функцию, объявленную с помощью __attribute__ ((constructor)), для установки перехватов перехватов malloc.

Спасибо

4b9b3361

Ответ 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.

Другими словами, это решение совершенно не переносимо, и альтернативные решения должны быть добавлены, если библиотека крючков должна быть перенесена в операционную систему, отличную от GNU.