C语言的3种打桩方法总结

        前段时间项目组相对很久以前的工程做单元测试,由于比较久远内部逻辑复杂,外部依赖都比较多,所以想通过打桩的方式(不但要对外部接口还得可以对内部函数)进行函数级别的单元测试。调查了几个方法记录一下,以备以后参照。

  • google的cmockery框架实现

       优点很多,请大家自行google。

        但cmockery不能对内部函数进行打桩,所以不满足我们要求。

  • 利用C编译器预编译的特点,通过宏定义实现

       利用C编译器的预编译特点,通过宏定义替换需要打桩的函数。直接上代码。

        main.c

#include "test.h"

// 把func1替换成func1_stub_行号
#define func1 func1_(__LINE__)
#define func1_(line) func1__(line)
#define func1__(line) func1_stub_ ## line

// 把func2替换成func2_stub_行号
#define func2 func2_(__LINE__)
#define func2_(line) func2__(line)
#define func2__(line) func2_stub_ ## line

//test函数中func1为第5行
void func1_stub_5(void)
{
    printf("func1_stub\n");
}

//test函数中func1为第6行
void func2_stub_6(void)
{
    printf("func2_stub\n");
}

int main()
{
    test();
    return 0;
}

test.h

#include 

void test(void);
void func1(void);
void func2(void);

     test.c

#include "test.h"

void test(void)
{
    func1();
    func2();
}

void func1(void)
{
    printf("func1()\n");
}
void func2(void)
{
    printf("func2()\n");
}
  • 修改函数内存地址,通过Jump指令跳转到stub函数

     第一种

demo.c

#include 
#include 
#include 
#include 

void bar()
{
        printf("function bar\n");
}

void foo()
{
        printf("function foo\n");
}

void monkey_patch(void* old_function, void* new_function)
{
        void* page;

        page = (void*)((uint64_t)old_function & ~(getpagesize()-1));
        if(mprotect(page, getpagesize(), PROT_WRITE|PROT_READ|PROT_EXEC)) {
                perror("mprotect");
        }
        ((uint8_t*)old_function)[0] = 0xeb; /* jmp */
        /*
         * Number of bytes to jump relative to the eip which is 2 bytes past
         * the address of this instruction. Cannot jump more than 128 bytes.
         */
        ((uint8_t*)old_function)[1] = new_function - old_function - 2;
}

int main()
{
        foo();
        monkey_patch(foo, bar);
        foo();

    return 0;
}

      第二种

      stub.h    

#ifndef __STUB_H__
#define __STUB_H__

#ifdef __cplusplus
exern "C"
{
#endif

#define CODESIZE 5U

struct func_stub
{
    void *fn;
    unsigned char code_buf[CODESIZE];
};

int stub_init();
void stub_set(struct func_stub *pstub, void *fn, void *fn_stub);
void stub_reset(struct func_stub *pstub);

#ifdef __cplusplus
}
#endif

#endif

     stub.c   

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "stub.h"

static long pagesize = -1;

static inline void *pageof(const void* p)
{
        return (void *)((unsigned long)p & ~(pagesize - 1));
}

void stub_set(struct func_stub *pstub, void *fn, void *fn_stub)
{
    pstub->fn = fn;
    memcpy(pstub->code_buf, fn, CODESIZE);

    if (-1 == mprotect(pageof(fn), pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC))
    {
        perror("mprotect to w+r+x faild");
        exit(errno);
    }

        *(unsigned char *)fn = (unsigned char)0xE9;
    *(unsigned int *)((unsigned char *)fn + 1) = (unsigned char *)fn_stub - (unsigned char *)fn - CODESIZE;

    if (-1 == mprotect(pageof(fn), pagesize * 2, PROT_READ | PROT_EXEC))
    {
        perror("mprotect to r+x failed");
        exit(errno);
    }

    return;
}

void stub_reset(struct func_stub *pstub)
{
    if (NULL == pstub->fn)
    {
                return;
    }

    if (-1 == mprotect(pageof(pstub->fn), pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC))
    {
        perror("mprotect to w+r+x faild");
        exit(errno);
    }

        memcpy(pstub->fn, pstub->code_buf, CODESIZE);

    if (-1 == mprotect(pageof(pstub->fn), pagesize * 2, PROT_READ | PROT_EXEC))
    {
        perror("mprotect to r+x failed");
        exit(errno);
    }

        memset(pstub, 0, sizeof(struct func_stub));

    return;
}

int stub_init(void)
{
        int ret;

        pagesize = sysconf(_SC_PAGE_SIZE);

        ret = 0;
        if (pagesize < 0)
        {
                perror("get system _SC_PAGE_SIZE configure failed");
                ret = -1;
        }

        return ret;
}

    main.c

#include 
#include 
#include 

#include "stub.h"

void f1()
{
    printf("f1\n");

    return;
}

void f2()
{
    printf("f2\n");

    return;
}

void *_memset(void *s, int ch, size_t n)
{
    printf("-memset\n");

    return s;
}

int main()
{
    char ac[10] = {1};
    struct func_stub stub;

        if (-1 == stub_init())
        {
                printf("faild to init stub\n");
                return 0;
        }

    stub_set(&stub, (void *)memset, (void *)_memset);

    memset(ac, 0, 10);

        stub_reset(&stub);

    memset(ac, 0, 10);
    printf("ac[0] = %hhu\n", ac[0]);

        stub_set(&stub, (void *)f1, (void *)f2);

        f1();

        stub_reset(&stub);

        f1();

        return 0;
}

 

你可能感兴趣的:(C语言)