Альтернатива устаревшей функциональности __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)
{
  [ ... ]
}

Который вызывается приложением вместо glibcs malloc,

Теперь, чтобы быть эквивалентным __malloc_hookс функциональностью, пара вещей все еще отсутствует.

1.) адрес звонящего

В дополнение к исходным параметрам malloc, glibcs __malloc_hooks также предоставляют адрес вызывающей функции, который фактически является адресом возврата malloc вернется к. Чтобы достичь того же, мы можем использовать __builtin_return_address функция, которая доступна в GCC. Я не смотрел на другие компиляторы, потому что я все равно ограничен gcc, но если вам случится знать, как делать такие вещи переносимо, пожалуйста, оставьте мне комментарий:)

наш malloc функция теперь выглядит так:

void*
malloc (size_t size)
{
  void *caller = __builtin_return_address(0);
  [ ... ]
}

2.) доступ glibcS 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_hooks, но с парой средних ограничений:

__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);
Другие вопросы по тегам