web-dev-qa-db-ja.com

Cでmallocおよびfreeのラッパー関数を作成する

私はCでfreemallocのラッパー関数を作成して、メモリリークを通知できるようにしています。誰かがこれらの関数を宣言する方法を知っているので、malloc()およびfree()を呼び出すと、標準のlib関数ではなく、カスタム関数が呼び出されますか?

39
Dan Snyder

いくつかのオプションがあります:

  1. GLIBC固有のソリューション(主にLinux)。コンパイル環境がglibcgccの場合、推奨される方法は mallocフック を使用します。カスタムmallocfreeを指定できるだけでなく、スタック上の戻りアドレスによって呼び出し元を識別します。

  2. POSIX固有のソリューションです。mallocおよびfreeを、実行可能ファイルの元の割り当てルーチンのラッパーとして定義します。 libcからのバージョンを「オーバーライド」します。ラッパー内では、元のmalloc実装を呼び出すことができます。これは、_RTLD_NEXT_ハンドルで dlsym を使用してルックアップできます。ラッパー関数を定義するアプリケーションまたはライブラリは、_-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;
    }
    _
  3. Linux固有。_LD_PRELOAD_環境変数で指定することにより、ダイナミックライブラリの関数を非侵襲的にオーバーライドできます。

    _LD_PRELOAD=mymalloc.so ./exe
    _
  4. Mac OSX固有

    Linuxと同じですが、_DYLD_INSERT_LIBRARIES_環境変数を使用します。

75
Alex B

LD_PRELOADでラッパーと「上書き」機能を実行できます-前に示した例と同様です。

LD_PRELOAD=/path.../lib_fake_malloc.so ./app

しかし、これを「少し」スマートに行うことをお勧めします。つまり、一度dlsymを呼び出すことを意味します。

#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?2 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.

説明が完全であり、それらの使い方を示していることを願っています。

17

7歳の投稿を再開して申し訳ありません。

私の場合、mallocの下でmemalign/aligned_mallocをラップする必要がありました。他のソリューションを試した後、私は最終的に以下にリストされたものを実装しました。正常に動作しているようです。

mymalloc.c

/* 
 * 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);
}
9
user9869932

これは、未解放のメモリ、複数回解放されたメモリ、解放されたメモリへの参照、バッファのオーバーフロー/アンダーフロー、およびメモリの解放を検出するために私が何年も使用した(そしてCに浸ったときにも使用する)ラッパー関数のセットです割り当てられませんでした。

ftp://ftp.digitalmars.com/ctools.Zip

彼らは25年間存在し、彼ら自身を証明しています。

マクロプリプロセッサを使用してmallocを再定義し、無料でmemパッケージを使用することもできますが、strdupが行うようにライブラリ呼び出しをmallocにリダイレクトしないため、これはお勧めしません。

5
Walter Bright

Cでは、私が使用した方法は次のようなものでした。

#define malloc(x) _my_malloc(x, __FILE__, __LINE__)
#define free(x) _my_free(x)

これにより、メモリが割り当てられた場所の行とファイルをそれほど簡単に検出できました。クロスプラットフォームである必要がありますが、マクロが既に定義されている場合は問題が発生します(別のメモリリークディテクタを使用している場合にのみ該当します)。

同じことをC++で実装する場合、手順は少し complex ですが、同じトリックを使用します。

5

目標がメモリリークの解消である場合は、 Valgrind (無料)または Purify (高価)などのツールを使用する方が簡単で煩わしくない方法です。

4
Don Wakefield

Malloc()およびfree()に独自の関数を定義し、それをアプリケーションに明示的にリンクする場合は、ライブラリ内の関数よりも優先して関数を使用する必要があります。

ただし、「c」には個別の名前空間の概念がないため、「malloc」と呼ばれる関数はライブラリmalloc関数を呼び出すことができません。言い換えれば、mallocの内部を実装して自分を解放する必要があります。

もう1つのアプローチは、標準ライブラリを呼び出す関数my_malloc()およびmy_free()を記述することです。つまり、mallocを呼び出すコードは、my_xxx関数を呼び出すように変更する必要があります。

2
Roddy

自分が管理しているメモリについてのみ話している場合、つまり自分でmallocして解放する場合は、 rmdebug を確認できます。多分それはあなたがとにかく書こうとしているものなので、あなたはいつか節約することができます。それがあなたにとって重要であるならば、それは非常に自由なライセンスを持っています。

私は個人的にプロジェクトで使用していますが、メモリリークを探すために、valgrindよりもはるかに高速ですが、強力ではないため、完全な呼び出しスタックを取得できません。

0
quinmars

Linuxを使用している場合は、malloc_hook()(GNU glibc)を使用できます。この関数を使用すると、実際のmallocを呼び出す前にmallocで関数を呼び出すことができます。manページには例がありますそれを使用する方法について。

0
Sonja