[英]weak linking in shared object not working as expected
I'm trying to use the cmocka unit test framework which suggests to use weak linking to be able to select a user-defined implementation over the actual implementation of a function.我正在尝试使用cmocka单元测试框架,该框架建议使用弱链接来选择用户定义的实现而不是函数的实际实现。 In my environment I have a shared object which I want to unit test.在我的环境中,我有一个要进行单元测试的共享对象。 I've implemented the unit tests in a separate file which I compile and link to the shared object.我在一个单独的文件中实现了单元测试,我编译并链接到共享对象。 My problem is that calling a function bar
in the shared object which in turn calls a function foo
in that shared object always leads to the real implementation of foo
and not the custom one.我的问题是,调用共享对象中的函数bar
反过来调用该共享对象中的函数foo
总是会导致foo
的真正实现,而不是自定义实现。 I've created a simplified implementation of the shared object and of the unit test.我创建了共享对象和单元测试的简化实现。
The shared library, ac
:共享库ac
:
#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();
}
The unit test, bc
:单元测试, bc
:
#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;
}
And finally, the Makefile
:最后, 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
Running a.out
produces the following output:运行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
From main, the direct call to foo
results in __wrap_foo
being called, as expected.正如预期的那样,从 main 直接调用foo
导致调用__wrap_foo
。
Next, I call bar
from main which correctly results in __wrap_bar
being called, where I redirect the call to the real implementation of bar
( __real_bar
).接下来,我从 main 调用bar
,它正确地导致__wrap_bar
被调用,在那里我将调用重定向到bar
( __real_bar
) 的实际实现。 bar
then calls foo
but the real implementation is used, not the wrapped one. bar
然后调用foo
但使用了真正的实现,而不是包装的实现。 Why isn't the wrapped implementation of foo
called in this case?为什么在这种情况下不调用foo
的包装实现? It looks like the issue is related to from where the function call originates.看起来问题与函数调用的来源有关。
In function bar
, if I replaced the called to foo
with __wrap_foo
I do get the expected behaviour however I don't think that this is an elegant solution.在函数bar
,如果我用__wrap_foo
替换了对foo
的调用,我确实得到了预期的行为,但是我认为这不是一个优雅的解决方案。
I've managed to bypass this problem using normal linking and dlopen(3)
and friends however I'm curious as to why weak linking isn't working in my case.我已经设法使用普通链接和dlopen(3)
和朋友绕过了这个问题,但是我很好奇为什么弱链接在我的情况下不起作用。
Ld's --wrap
works by replacing calls to real functions by calls to their wrappers only in files which are linked with this flag. Ld 的--wrap
工作原理是通过仅在与此标志链接的文件中调用包装器来替换对实际函数的调用。 Your library isn't, so it just does plain foo
and bar
calls.你的图书馆不是,所以它只是简单的foo
和bar
调用。 So they get resolved to where they are implemented ( a.so
).所以他们解决了他们实施的地方( a.so
)。
As for weak symbols, dynamic linker ignores their weakness and treats them as normal symbols (unless you run with LD_DYNAMIC_WEAK
which isn't recommended).至于弱符号,动态链接器会忽略它们的弱点并将它们视为普通符号(除非您使用不推荐的LD_DYNAMIC_WEAK
运行)。
In your particular case (overriding symbols in shared library) you'd probably need to apply --wrap
to a.so
as well.在您的特定情况下(覆盖共享库中的符号),您可能还需要将--wrap
应用于a.so
。 Or - you could use standard symbol interposition.或者 - 您可以使用标准符号插入。 Say if library-under-test is in libsut.so
and you want to replace some functions with custom implementations in libstub.so
, link them to driver program in a proper order:假设被测库是否在libsut.so
并且您想用libstub.so
自定义实现替换某些函数,请按正确顺序将它们链接到驱动程序:
LDFLAGS += -lstub -lsut
Then definitions in libstub.so
will prevail over libsut.so
ones.那么libstub.so
定义将优先于libsut.so
的定义。
One error is that attribute syntax is not correct.一个错误是属性语法不正确。 Correct ones:正确的:
void foo(void) __attribute__((weak));
void bar(void) __attribute__((weak));
An alternative solution would be the standard Linux function interposition:另一种解决方案是标准的 Linux 函数插入:
// a.c
#include <stdio.h>
void foo(void) {
printf("called real foo\n");
}
void bar(void) {
printf("called real bar calling\n");
foo();
}
// b.c
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdbool.h>
bool orig_foo;
bool orig_bar;
void foo(void) {
printf("in foo wrapper\n");
static void(*__real_foo)(void);
if(!__real_foo)
__real_foo = dlsym(RTLD_NEXT, "foo");
if (orig_foo)
__real_foo();
else
printf("called wrapped foo\n");
}
void bar() {
printf("in bar wrapper\n");
static void(*__real_bar)(void);
if(!__real_bar)
__real_bar = dlsym(RTLD_NEXT, "bar");
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;
}
$ gcc -o a.so -shared -Wall -Wextra -fPIC a.c
$ gcc -o b -Wall -Wextra b.c -L. -l:a.so -Wl,-rpath='$ORIGIN' -ldl
$ ./b
calling foo from main
in foo wrapper
called wrapped foo
calling bar from main
in bar wrapper
called real bar calling
in foo wrapper
called wrapped foo
In bar()
the linker does not store a reference to foo()
but to an offset in the text
section of the translation unit.在bar()
,链接器不存储对foo()
的引用,而是存储翻译单元text
部分中的偏移量。 Therefore the name is lost.因此,名称丢失。
The "weak" attribute does not help here. “弱”属性在这里没有帮助。
Also it does not help to use -ffunction_sections
because the reference will be to an offset of the section of foo()
.此外,使用-ffunction_sections
因为引用将指向foo()
部分的偏移量。
One straight forward way to get the desired result is to separate all functions in their own translation unit.获得所需结果的一种直接方法是将所有函数分离到它们自己的翻译单元中。 You don't need separated source files for this, some conditional compilation will also help.为此,您不需要单独的源文件,一些条件编译也会有所帮助。 But it makes the source ugly.但它使源变得丑陋。
You might like to look into my answer to the question "Rename a function without changing its references" , too.您可能也想查看我对“重命名函数而不更改其引用”这个问题的回答。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.