Clang 使用链接的共享库来决定导出哪些符号?

问题描述 投票:0回答:1

将此玩具代码用作可执行文件和共享库:

// main.c
void foo() {}
int main() { return 0; }

// bar.c
void foo();
void bar() { foo(); }

让我们在不进行优化的情况下构建,首先使用 gcc:

$ gcc -fpic -shared -o libbar_gcc.so bar.c
$ gcc -L. -lbar_gcc -o prog_gcc main.c

并观察到

foo
没有出现在
.dynsyms
部分 - 即,没有从可执行文件中导出:

$ readelf --dyn-syms prog_gcc | grep foo
$

现在尝试用 clang 进行同样的操作,看看它确实导出

foo
:

$ clang -fpic -shared -o libbar_clang.so bar.c
$ clang -L. -lbar_clang -o prog_clang main.c
$ readelf --dyn-syms prog_clang | grep foo
    6: 0000000000001130     6 FUNC    GLOBAL DEFAULT   13 foo

如果我们不链接到

libbar_clang
foo
isn't 导出:

$ clang -fpic -shared -o libbar_clang.so bar.c
$ clang -o prog_clang main.c
$ readelf --dyn-syms prog_clang | grep foo
$

更令人惊讶的是,如果我们恢复针对 libbar 的链接,但将

bar
的源更改为 not 调用
foo
:

// bar.c
void bar() { }

我们会再次看到

foo
未导出!

$ clang -fpic -shared -o libbar_clang.so bar.c
$ clang -L. -lbar_clang -o prog_clang main.c
$ readelf --dyn-syms prog_clang | grep foo
$

因此,修改依赖共享库中的源代码 - 不更改可执行文件的源代码或链接 - 会改变 clang 关于从可执行文件导出内容的决定。这是什么法术??

我使用的是 Ubuntu 20、clang 14.0.6 和 gcc 9.4.0。两者都使用

ld

 链接器。

c gcc linker clang elf
1个回答
0
投票
因此,在比较 clang 和 gcc 的详细命令行时取得了突破:结果默认情况下 gcc 使用

--as-needed

 而 clang 使用 
--no-as-needed
。即,gcc 驱动程序引导链接器仅链接到实际定义所需符号(尚未定义)的共享库,而 clang 驱动程序引导链接器愉快地链接到命令行上出现的任何共享库。

但这如何导致问题中出现明显的差异呢?

我目前的理解是,在链接可执行文件时,默认链接器逻辑仅导出链接器

可以看到实际需要的符号以满足其他地方的链接。如果链接器可以看到某些依赖库依赖于可执行文件来实现某些符号,则该符号将被导出而无需进一步的用户干预。

有时,链接器无法看到库需要某个符号 - 比如说它是否在运行时由

dlopen

 加载,并且首先对链接器不可见。在这种情况下,用户必须进行干预,可能是将 
-rdynamic
 添加到链接行 - 从而强制从可执行文件导出所有公共符号。

无论如何,在原始案例中:

  1. libbar.so
     不直接从可执行文件中使用(直到我们
    添加来自 
    bar()
    main()
     呼叫!),所以 -
  2. 它被 gcc 从链接中删除,默认为
  3. --as-needed
    ,并且 
  4. 链接器无法看到
  5. libbar.so
     需要符号 
    foo
     并且不会导出它。

我将其放在这里,以防有人对此感兴趣,还请注意这很大程度上是一个假设。我还是很好奇是否有人可以证实或反驳这一点。

© www.soinside.com 2019 - 2024. All rights reserved.