使用cmocka框架Mock/Wrap被测非静态函数调用的静态函数遇运行问题
我之前也碰到过一模一样的问题!别着急,咱们一步步排查,大概率是这几个环节出了问题:
问题根源与解决办法
1. 静态函数的文件内作用域限制
静态函数默认是文件级私有的,如果被测函数和静态函数在同一个.c文件里,编译器很可能会直接将静态函数内联到被测函数中,或者直接做内部调用优化,完全跳过链接器的符号解析环节——这时候--wrap标志根本没机会生效。
解决办法:
- 如果代码架构允许,把静态函数拆分到单独的
.c文件中,让链接器能处理它的符号。 - 给被测文件的编译添加
-fno-inline和-fno-builtin选项,强制编译器禁止内联和内置优化,保留静态函数的外部符号入口。
2. Wrap函数的定义必须严格符合要求
Wrap函数的签名和命名规则不能出错:
- 函数名必须是
__wrap_原静态函数名,比如原函数是static void hw_write(int val),Wrap函数就得是void __wrap_hw_write(int val)。 - Wrap函数不能是静态的,必须是全局可见的,否则链接器找不到它。
- 参数类型、返回值必须和原函数完全一致,哪怕是
const修饰符都不能少。
3. 链接器标志的位置要正确
很多人会把--wrap=xxx加到编译选项里,但这是链接器专属选项,必须确保它被传给链接器:
- 用GCC编译时,要写成
-Wl,--wrap=xxx(-Wl,是把后续参数传递给链接器的标志)。 - 如果用CMake构建,要把选项加到
target_link_options或LINK_FLAGS里,不能放在compile_options中。
4. 编译优化等级过高导致内联
如果测试代码开启了-O2及以上的优化,编译器会自动内联静态函数,让Wrap失效。解决办法:
- 测试代码的编译优化等级设为
-O0。 - 针对被测文件单独添加
-fno-inline选项,强制保留静态函数的独立调用入口。
5. 验证符号是否被正确替换
可以用nm命令检查测试可执行文件的符号表,确认原静态函数是否被Wrap函数替换:
nm test_executable | grep 原静态函数名
如果看到原函数的符号是小写t(本地符号),说明链接器没替换成功;如果出现__wrap_xxx的大写T(全局符号),才说明替换生效了。
示例代码与编译命令
举个完整的例子,帮你快速验证:
被测文件hw.c
// 静态硬件交互函数 static void hw_send(int data) { // 真实硬件操作 } // 被测非静态函数 void process_input(int input) { hw_send(input * 2); }
测试文件test_hw.c
#include <stdarg.h> #include <stddef.h> #include <setjmp.h> #include <cmocka.h> // Wrap函数,全局可见 void __wrap_hw_send(int data) { check_expected(data); // 验证传入参数是否符合预期 } static void test_process_input(void **state) { expect_value(__wrap_hw_send, data, 4); // 预期传入4 process_input(2); } int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_process_input), }; return cmocka_run_group_tests(tests, NULL, NULL); }
编译命令
# 编译被测文件,禁用内联和优化 gcc -c hw.c -o hw.o -O0 -fno-inline # 编译测试文件 gcc -c test_hw.c -o test_hw.o -O0 -I/path/to/cmocka/include # 链接时添加Wrap标志和cmocka库 gcc -o test_hw test_hw.o hw.o -L/path/to/cmocka/lib -lcmocka -Wl,--wrap=hw_send
如果还是不行,可以试试在被测文件中给静态函数添加__attribute__((noinline))属性,强制编译器不内联它。
内容的提问来源于stack exchange,提问作者Iniesta8
相关产品推荐
相关产品推荐

