# 桩函数 在做单元测试时,经常需要将特定函数替换成测试需要模拟的函数,以便产生特定的输入或输出。这个过程称为打桩(stub),替换后的函数称为桩函数。 打桩的方式有多种,这里给出的方式比较常用: stub.h: ```c #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: ```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: ```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 memset 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 f1 stub_set(&stub, (void *)f1, (void *)f2); f1(); stub_reset(&stub); f1(); return 0; } ``` # 参考 * [C语言的3种打桩方法总结](C语言的3种打桩方法总结) * [cpp-stub](https://github.com/coolxv/cpp-stub) * [stub in DianH3C](https://github.com/DianH3C/INFO/blob/master/lifan/Project/INFO/ut/stub/stub.c)