我正在尝试使用cmocka单元测试框架,该框架建议使用弱链接来选择用户定义的实现而不是函数的实际实现。在我的环境中,我有一个共享对象,我想对其进行单元测试。我在一个单独的文件中实现了单元测试,我编译并链接到共享对象。我的问题是,在共享对象中调用一个函数,而该函数bar
又在该共享对象中调用一个函数foo
,总是会导致真正的实现,foo
而不是自定义的。我创建了共享对象和单元测试的简化实现。
共享库,a.c
:
#include <stdio.h>
void foo(void); __attribute__((weak))
void bar(void); __attribute__((weak))
void foo(void) {
printf("called real foo\n");
}
void bar(void) {
printf("called real bar calling\n");
foo();
}
单元测试,b.c
:
#include <stdio.h>
#include <stdbool.h>
bool orig_foo;
bool orig_bar;
void __wrap_foo(void) {
printf("in foo wrapper\n");
if (orig_foo)
__real_foo();
else
printf("called wrapped foo\n");
}
void __wrap_bar() {
printf("in bar wrapper\n");
if (orig_bar)
__real_bar();
else
printf("called wrapped bar\n");
}
int main(void) {
orig_bar = true;
orig_foo = false;
printf("calling foo from main\n");
foo();
printf("\n");
printf("calling bar from main\n");
bar();
return 0;
}
最后,Makefile
:
all: a.out
a.out: b.c a.so
gcc -Wall b.c a.so -Wl,--wrap=foo -Wl,--wrap=bar
a.so: a.c
gcc -Wall -c a.c -shared -o a.so
clean:
rm -f a.so a.out
运行a.out
产生以下输出:
# ./a.out
calling foo from main
in foo wrapper
called wrapped foo
calling bar from main
in bar wrapper
called real bar
called real foo
正如预期的那样,从 main 直接调用会foo
导致被调用。__wrap_foo
接下来,我bar
从正确导致__wrap_bar
被调用的 main 调用,我将调用重定向到bar
( __real_bar
) 的实际实现。bar
然后调用foo
,但使用真正的实现,而不是包装的。在这种情况下,为什么不foo
调用包装的实现?看起来问题与函数调用的来源有关。
在 functionbar
中,如果我将被调用替换为foo
我__wrap_foo
确实会得到预期的行为,但我认为这不是一个优雅的解决方案。
我已经设法使用普通链接和dlopen(3)
朋友绕过了这个问题,但是我很好奇为什么弱链接在我的情况下不起作用。