プログラミングなどなど

プログラミングに関係しそうな内容を

GCC:LD_PRELOADを使用したmallocの動作切替

今回もC言語の関数単体テスト系の話題です。mallocを利用する関数を試験する際にmallocのスタブを作成して単体試験を実行しようとすると単体試験のツール自体がmallocを使用していたりしてうまく動かないケースがあります。

このような場合、GDBを使用してmallocの戻り値を操作したりしていたのですが、LD_PRELOADを使用してmallocの動作を切り替えることができましたので紹介します。ネタもとは「Binary Hacks ―ハッカー秘伝のテクニック100選」のHACK#61「LD_PRELOADで既存の関数をラップする」です。

使用したのは以下のソースになります。

サンプルソースコンパイル

malloc_stub.c
#define _GNU_SOURCE

#include <dlfcn.h>
#include <stdlib.h>

static void *(*malloc0)(size_t size);

void __attribute__((constructor))
init_malloc0(void)
{
  malloc0 = dlsym(RTLD_NEXT, "malloc");
}

void *malloc(size_t size)
{
  if( !getenv("MALLOC_STUB") )
  {
    /* 環境変数未定義時 */
    return malloc0(size);
  }
  else
  {
    /* 環境変数定義時 */
    return NULL;
  }
}

環境変数MALLOC_STUBが定義されている場合はNULLを返すことにします。
このモジュールは共有ライブラリとしてコンパイルします。

$ gcc -shared -fPIC -o malloc_stub.so malloc_stub.c -ldl
use_malloc.c

mallocを使用するモジュール

#include <stdlib.h>
#include <stdio.h>

int main(void)
{
  void *vp = malloc(1024);

  if( vp )
  {
    printf("通常\n");
  }
  else
  {
    printf("NULL\n");
  }

  return 0;
}

こちらは普通にコンパイルします。

$ gcc -Wall use_malloc.c -o use_malloc

実行

通常の実行
$ ./use_malloc 
通常
LD_PRELOAD指定かつ環境変数未定義
$ LD_PRELOAD=./malloc_stub.so ./use_malloc 
通常
LD_PRELOAD指定かつ環境変数定義
$ LD_PRELOAD=./malloc_stub.so MALLOC_STUB=1 ./use_malloc 
NULL