C 共享库中的符号查找

C 共享库中的符号查找,c,gcc,function-interposition,library-interposition,C,Gcc,Function Interposition,Library Interposition,我在下面测试了这样一个简单的程序 /* a shared library */ dispatch_write_hello(void) { fprintf(stderr, "hello\n"); } extern void print_hello(void) { dispatch_write_hello(); } 我的主要程序如下: extern void dispatch_write_hello(void) { fprintf(stderr,

我在下面测试了这样一个简单的程序

/* a shared library */
dispatch_write_hello(void)
{
        fprintf(stderr, "hello\n");
}

extern void
print_hello(void)
{
        dispatch_write_hello();
}
我的主要程序如下:

extern void
dispatch_write_hello(void)
{
        fprintf(stderr, "overridden\n");
}

int
main(int argc, char **argv)
{
        print_hello();
        return 0;
}
程序的结果是“覆盖的”。
为了弄清楚为什么会发生这种情况,我使用了gdb。呼叫链是这样的:
\u dl\u运行时\u解析->\u dl\u修复->\u dl\u查找\u符号\u x

我发现glibc中
\u dl\u lookup\u symbol\u x
的定义是

搜索加载对象的符号表以查找符号UNDEF_名称的定义,可能需要符号的请求版本


因此,我认为当试图查找符号
dispatch\u write\u hello
时,它首先在主对象文件中查找,然后在共享库中查找。这就是这个问题的原因。我的理解正确吗?非常感谢您的时间。

鉴于您提到了
\u dl\u runtime\u resolve
,我假设您使用的是Linux系统(感谢@Olaf对此的澄清)

对您的问题的简短回答-是的,在符号插入期间,动态链接器将首先查看可执行文件内部,然后才扫描共享库。因此,将以
dispatch\u write\u hello
的定义为准

编辑

如果您想知道为什么运行时链接器需要将
print\u hello
中对
dispatch\u write\u hello
的调用解析为同一翻译单元中除
dispatch\u write\u hello
之外的任何内容-这是由GCC中所谓的语义插入支持引起的。默认情况下,编译器将库代码(即使用
-fPIC
编译的代码)内的任何调用视为在运行时可以插入,除非您通过
-fvisibility hidden
-Wl,-Bsymbolic
-fno语义插入
\uu属性((可见性(“隐藏”))
明确告诉它不要插入。这已经在网上讨论过很多次了,比如在臭名昭著的


作为旁注,与其他编译器(Clang、Visual Studio)相比,此功能会带来显著的性能损失。

哪个“动态链接器”?这取决于平台。没有这一点,我们只有C标准。@Olaf OP提到了
\u dl\u runtime\u resolve
,这意味着他在Linux或Android(或Solaris?)上,它们的行为都是相同的wrt共享库和插入。解释得不错,但我不相信。在共享库中,dispatch_write_hello()没有什么奇怪的地方。为什么在调用dispatchxxx()的同一文件中打印_hello()需要解析符号?对我来说,有一件事是不清楚的。@linuxfan,因为在默认情况下,共享libs在Linux上就是这样工作的。您可以使用
-fvisibility hidden
-b符号
-fno语义插入
来更改此行为,但前提是您真正了解自己在做什么。有关Linux shlibs的入门教程,请参阅网上众多教程中的任何一个,例如臭名昭著的。@yugr@linuxfan通过设置
LD_DEBUG=symbols,bindings
,我们可以验证是否首先在可执行文件中查找了
dispatch\u write\u hello