私はCのメモリプロファイラーを作成しています。そのため、malloc_hooksを介したmalloc
、realloc
、およびfree
関数の呼び出しをインターセプトしています。残念ながら、マルチスレッド環境での動作が悪いため、これらは非推奨です。同じことを達成するための代替のベストプラクティスソリューションを説明するドキュメントが見つかりませんでした。誰かに教えてもらえますか?
単純な#define malloc(s) malloc_hook(s)
でうまくいくと読みましたが、これは、私が考えているシステム設定では機能しません。これは、元のコードベースに侵入しすぎて、プロファイリング/トレースツール。元のアプリケーションコードを手動で変更する必要があることは、適切なプロファイラーにとってはキラーです。理想的には、私が探しているソリューションは、オプションの共有ライブラリにリンクするだけで有効または無効にする必要があります。たとえば、現在のセットアップでは、__attribute__ ((constructor))
で宣言された関数を使用して、インターセプトmalloc
フックをインストールしています。
ありがとう
いくつかのことを試した後、私はようやくこれを行う方法を理解することができました。
まず、glibc
では、malloc
がウィークシンボルとして定義されています。これは、アプリケーションまたは共有ライブラリによって上書きできることを意味します。したがって、_LD_PRELOAD
_は必ずしも必要ではありません。代わりに、共有ライブラリに次の関数を実装しました。
_void*
malloc (size_t size)
{
[ ... ]
}
_
glibc
s malloc
の代わりにアプリケーションによって呼び出されます。
さて、___malloc_hook
_ s機能と同等にするために、いくつかのものがまだ不足しています。
malloc
への元のパラメーターに加えて、glibc
s ___malloc_hook
_ sは呼び出し元の関数のアドレスも提供します。これは実際にはmalloc
が返す場所の戻りアドレスですに。同じことを達成するために、gccで使用できる___builtin_return_address
_関数を使用できます。私はとにかくgccに限定されているため、他のコンパイラーについては調べていませんが、そのようなことを移植性のある方法で行う方法を知っている場合は、コメントを投稿してください:)
malloc
関数は次のようになります。
_void*
malloc (size_t size)
{
void *caller = __builtin_return_address(0);
[ ... ]
}
_
glibc
s mallocにアクセスするアプリケーションではglibcに限定されているため、元のmalloc実装にアクセスするために___libc_malloc
_を使用することを選択しました。または、dlsym(RTLD_NEXT, "malloc")
を使用できますが、この関数が最初の呼び出しでcalloc
を使用する可能性のある落とし穴で、segfaultにつながる無限ループが発生する可能性があります。
私の完全なフッキング関数は次のようになります。
_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
のフックも同様に機能します。
これらの関数を使用すると、動的リンクはそのまま使用できます。 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オペレーティングシステムに移植する場合は、代替ソリューションを追加する必要があります。
LD_PRELOADとdlsymを使用できます http://www.slideshare.net/tetsu.koba/presentations の「mallocとfreeのヒント」を参照してください
__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);