二流エンジニアの逆襲

二流エンジニアが一流エンジニアになるまでの記録である

標準関数のスタブ化

標準関数をスタブ化し、尚且つスタブと通常動作の切り替えを行えるようにした時のメモ。


手順

  1. nm コマンドでスタブにしたい関数のシンボルが入っている共有ライブラリを探す。
  2. 1で得た共有ライブラリをdlopen()で開く。戻り値としてライブラリハンドルが返ってくるので受け取る。
  3. スタブにしたい関数の関数ポインタを static で定義しておく。
  4. スタブにしたい関数を実装する。
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

void* handle = NULL;
static void SharedLibraryOpenForStub()
{
        handle = dlopen("/usr/lib/libc.so.4.6", RTLD_LAZY);
        if(!handle)
        {
                fprintf(stderr, "%s\n", dlerror());
                exit(EXIT_FAILURE);
        }
}        

//-----------------------------------------------------------------
static int (*fclosePtr)(FILE* stream) = NULL;

//-----------------------------------------------------------------
static short int STUB_ENABLE_TABLE = 0;


//-----------------------------------------------------------------

enum STUB_ENABLE_TABLE
{
        FCLOSE_STUB = 0x1
}
//-----------------------------------------------------------------
static int FCLOSE_DO_STUB_NUM = 0;
        
//-----------------------------------------------------------------


int fclose(FILE* stream)
{              
        if((STUB_ENABLE_TABLE & FCLOSE_STUB ) && (--FCLOSE_DO_STUB_NUM <= 0))
        {       // スタブを実行
                FCLOSE_DO_STUB_NUM = 0;
                printf("do fclose stub\n");
                return -1;
        }
        else
        {       // 標準関数を実行
                if(!handle)
                {
                        SharedLibraryOpenForStub();
                }
                
                *(void **) (&fclosePtr) = dlsym(handle, "fclose");
                return fclosePtr(stream);
        }
}

以上をスタブを使用したいソース内でインクルードさせれば良い。
使うときは、

 
FCLOSE_DO_STUB_NUM  = 5; // fclose が5回目に呼ばれた時にスタブを実行。
STUB_ENABLE_TABLE |= FCLOSE_STUB; // fclose のスタブを有効にする。
for(int i = 0; i < 10 ; i++)
{
         if(fclose(fp) == -1)
         {
                  printf("fclose は%d回目に失敗しました。\n", i + 1);
                  break;
         }
}
STUB_ENABLE_TABLE = 0;   // fclose のスタブを無効にする。

こんな感じだったと思う。
実際には、fclose()を包含し、fclose()が何度か行われる関数を単体テストするときくらいしか、カウンタは使わないと思う。

実装する関数によっては、

extern "C"{}

が必要。リンケージがナンタラカンタラというエラーが出たらいると思われ。