web-dev-qa-db-ja.com

glibcの非推奨の__malloc_hook機能の代替

私はCのメモリプロファイラーを作成しています。そのため、malloc_hooksを介したmallocrealloc、およびfree関数の呼び出しをインターセプトしています。残念ながら、マルチスレッド環境での動作が悪いため、これらは非推奨です。同じことを達成するための代替のベストプラクティスソリューションを説明するドキュメントが見つかりませんでした。誰かに教えてもらえますか?

単純な#define malloc(s) malloc_hook(s)でうまくいくと読みましたが、これは、私が考えているシステム設定では機能しません。これは、元のコードベースに侵入しすぎて、プロファイリング/トレースツール。元のアプリケーションコードを手動で変更する必要があることは、適切なプロファイラーにとってはキラーです。理想的には、私が探しているソリューションは、オプションの共有ライブラリにリンクするだけで有効または無効にする必要があります。たとえば、現在のセットアップでは、__attribute__ ((constructor))で宣言された関数を使用して、インターセプトmallocフックをインストールしています。

ありがとう

34

いくつかのことを試した後、私はようやくこれを行う方法を理解することができました。

まず、glibcでは、mallocがウィークシンボルとして定義されています。これは、アプリケーションまたは共有ライブラリによって上書きできることを意味します。したがって、_LD_PRELOAD_は必ずしも必要ではありません。代わりに、共有ライブラリに次の関数を実装しました。

_void*
malloc (size_t size)
{
  [ ... ]
}
_

glibcs mallocの代わりにアプリケーションによって呼び出されます。

さて、___malloc_hook_ s機能と同等にするために、いくつかのものがまだ不足しています。

1.)発信者アドレス

mallocへの元のパラメーターに加えて、glibcs ___malloc_hook_ sは呼び出し元の関数のアドレスも提供します。これは実際にはmallocが返す場所の戻りアドレスですに。同じことを達成するために、gccで使用できる___builtin_return_address_関数を使用できます。私はとにかくgccに限定されているため、他のコンパイラーについては調べていませんが、そのようなことを移植性のある方法で行う方法を知っている場合は、コメントを投稿してください:)

malloc関数は次のようになります。

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

2.)フック内からglibcs mallocにアクセスする

アプリケーションではglibcに限定されているため、元のmalloc実装にアクセスするために___libc_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;
}
_

もちろん、callocreallocfreeのフックも同様に機能します。

動的および静的リンク

これらの関数を使用すると、動的リンクはそのまま使用できます。 mallocフック実装を含む.soファイルをリンクすると、アプリケーションからのmallocへのすべての呼び出しと、すべてのライブラリー呼び出しが私のフックを介してルーティングされます。静的リンクには問題があります。私はまだ完全に頭を覆っていませんが、静的リンクではmallocは弱いシンボルではないため、リンク時に複数の定義エラーが発生します。

何らかの理由で静的リンクが必要な場合、たとえばサードパーティライブラリの関数アドレスをデバッグシンボルを介してコード行に変換する場合は、mallocフックを動的にリンクしながら、これらのサードパーティライブラリを静的にリンクして、複数定義の問題を回避できます。まだこれ以上の回避策はありません。ご存知の場合は、遠慮なくコメントを残してください。

以下に短い例を示します。

_gcc -o test test.c -lmalloc_hook_library -Wl,-Bstatic -l3rdparty -Wl,-Bdynamic
_

_3rdparty_は静的にリンクされますが、_malloc_hook_library_は動的にリンクされます。その結果、期待される動作と、_3rdparty_の関数のアドレスがtestのデバッグシンボルを介して翻訳可能になります。かなりきちんとね?

結論

上記の手法は、___malloc_hook_ sへの非推奨ではない、ほぼ同等のアプローチを説明していますが、平均的な制限がいくつかあります。

___builtin_caller_address_はgccでのみ機能します

___libc_malloc_はglibcでのみ機能します

dlsym(RTLD_NEXT, [...])はGNU glibcの拡張機能です

リンカフラグ_-Wl,-Bstatic_および_-Wl,-Bdynamic_は、GNU binutilsに固有です。

言い換えれば、このソリューションは完全に移植不可能であり、フックライブラリを非GNUオペレーティングシステムに移植する場合は、代替ソリューションを追加する必要があります。

50

LD_PRELOADとdlsymを使用できます http://www.slideshare.net/tetsu.koba/presentations の「mallocとfreeのヒント」を参照してください

2
vinayak

__malloc_hookを含むNDKビルドコードを管理しました。

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);
0
evandrix