メモリ リークを通知するために、C用free
およびC用のラッパー関数を作成しようとしています。malloc
これらの関数を宣言する方法を知っている人はいますか?私が呼び出すmalloc()
とfree()
、標準ライブラリ関数ではなくカスタム関数が呼び出されますか?
10 に答える
いくつかのオプションがあります:
GLIBC 固有のソリューション (主に Linux)。コンパイル環境が の場合
glibc
、gcc
推奨される方法はmalloc フックを使用することです。malloc
カスタムおよびを指定できるだけでなくfree
、スタック上の戻りアドレスによって呼び出し元を識別します。POSIX 固有のソリューション。
malloc
およびを実行可能ファイル内の元の割り当てルーチンのラッパーとして定義free
します。これにより、libc のバージョンが「オーバーライド」されます。ラッパー内では、元のmalloc
実装を呼び出すことができます。これは、ハンドルを使用dlsym
して検索できます。RTLD_NEXT
ラッパー関数を定義するアプリケーションまたはライブラリは、 とリンクする必要があります-ldl
。#define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> void* malloc(size_t sz) { void *(*libc_malloc)(size_t) = dlsym(RTLD_NEXT, "malloc"); printf("malloc\n"); return libc_malloc(sz); } void free(void *p) { void (*libc_free)(void*) = dlsym(RTLD_NEXT, "free"); printf("free\n"); libc_free(p); } int main() { free(malloc(10)); return 0; }
Linux 固有。
LD_PRELOAD
環境変数で指定することにより、ダイナミック ライブラリの関数を非侵襲的にオーバーライドできます。LD_PRELOAD=mymalloc.so ./exe
Mac OSX 固有。
DYLD_INSERT_LIBRARIES
環境変数を使用することを除いて、Linux と同じです。
前に示した例と同様に、LD_PRELOAD を使用してラッパーと「上書き」機能を実行できます。
LD_PRELOAD=/path.../lib_fake_malloc.so ./app
ただし、これを「少し」賢く行うことをお勧めします。つまり、dlsym を1回呼び出すことを意味します。
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
void* malloc(size_t size)
{
static void* (*real_malloc)(size_t) = NULL;
if (!real_malloc)
real_malloc = dlsym(RTLD_NEXT, "malloc");
void *p = real_malloc(size);
fprintf(stderr, "malloc(%d) = %p\n", size, p);
return p;
}
ここで見つけた例: http://www.jayconrod.com/cgi/view_post.py?23 Jay Conrod による投稿。
しかし、このページで私が本当にクールだと思ったのは、GNU リンカが便利なオプション--wrapを提供していることです。「man ld」を確認すると、次の例があります。
void *
__wrap_malloc (size_t c)
{
printf ("malloc called with %zu\n", c);
return __real_malloc (c);
}
「些細な例」であることに同意します:)。dlsym も必要ありません。
私の「man ld」ページのもう1つの部分を引用しましょう。
--wrap=symbol
Use a wrapper function for symbol.
Any undefined reference to symbol will be resolved to "__wrap_symbol".
Any undefined reference to "__real_symbol" will be resolved to symbol.
説明が完全であり、それらの使用方法を示していることを願っています。
私の場合、malloc の下に memalign/aligned_malloc をラップする必要がありました。他のソリューションを試した後、以下にリストされているソリューションを実装することになりました。うまくいっているようです。
/*
* Link-time interposition of malloc and free using the static
* linker's (ld) "--wrap symbol" flag.
*
* Compile the executable using "-Wl,--wrap,malloc -Wl,--wrap,free".
* This tells the linker to resolve references to malloc as
* __wrap_malloc, free as __wrap_free, __real_malloc as malloc, and
* __real_free as free.
*/
#include <stdio.h>
void *__real_malloc(size_t size);
void __real_free(void *ptr);
/*
* __wrap_malloc - malloc wrapper function
*/
void *__wrap_malloc(size_t size)
{
void *ptr = __real_malloc(size);
printf("malloc(%d) = %p\n", size, ptr);
return ptr;
}
/*
* __wrap_free - free wrapper function
*/
void __wrap_free(void *ptr)
{
__real_free(ptr);
printf("free(%p)\n", ptr);
}
これは、解放されていないメモリ、複数回解放されたメモリ、解放されたメモリへの参照、バッファオーバーフロー/アンダーフロー、およびメモリの解放を検出するために、私が何年も使用していたラッパー関数のセットです (C に浸ったときもまだ使用しています)。割り当てられませんでした。
ftp://ftp.digitalmars.com/ctools.zip
彼らは 25 年間存在し、その実力を証明してきました。
マクロ プリプロセッサを使用して malloc を再定義し、mem パッケージのものを自由に使用できますが、strdup のようにライブラリ呼び出しを malloc にリダイレクトしないため、お勧めしません。
C では、私が使用した方法は次のようなものでした。
#define malloc(x) _my_malloc(x, __FILE__, __LINE__)
#define free(x) _my_free(x)
これにより、メモリが割り当てられた行とファイルをそれほど問題なく検出できました。クロスプラットフォームである必要がありますが、マクロが既に定義されている場合は問題が発生します (これは、別のメモリ リーク検出機能を使用している場合にのみ発生するはずです)。
同じことを C++ で実装する場合、手順はもう少し複雑になりますが、同じトリックを使用します。
メモリ リークをなくすことが目的の場合、 Valgrind (無料) やPurify (有料) などのツールを使用すると、より簡単で邪魔にならない方法になります。
malloc() および free() に独自の関数を定義し、それをアプリケーションに明示的にリンクする場合は、ライブラリ内の関数よりも関数を優先して使用する必要があります。
ただし、「malloc」と呼ばれる関数は、ライブラリの malloc 関数を呼び出すことはできません。これは、「c」には個別の名前空間の概念がないためです。つまり、malloc の内部を実装して、自分自身を解放する必要があります。
もう 1 つの方法は、標準ライブラリーを呼び出す関数 my_malloc() および my_free() を作成することです。これは、my_xxx 関数を呼び出すために、malloc を呼び出すコードを変更する必要があることを意味します。
自分が管理しているメモリ、つまり自分で malloc して解放するメモリについてのみ話している場合は、rmdebugを参照してください。おそらくそれはあなたが書き込もうとしているものなので、いつか保存することができます。それがあなたにとって重要であるならば、それは非常に自由なライセンスを持っています。
私は個人的にプロジェクトでそれを使用して、メモリ リークを探します。良い点は、valgrind よりもはるかに高速であることですが、それほど強力ではないため、完全な呼び出しスタックを取得することはできません。
Linux を使用している場合は、malloc_hook() (GNU glibc を使用) を使用できます。この関数を使用すると、実際の malloc を呼び出す前に、malloc で関数を呼び出すことができます。man ページには、使用方法の例があります。