【问题标题】:What's the best way to create shared libraries with circular dependencies?创建具有循环依赖关系的共享库的最佳方法是什么?
【发布时间】:2021-02-13 02:09:21
【问题描述】:

有没有创建两个相互依赖的库 liba.so 和 libb.so 的好方法?除非两个库都已存在,否则以下内容将不起作用:

g++ -fPIC -L. -shared -lb a.cpp -o liba.so
g++ -fPIC -L. -shared -la b.cpp -o libb.so

我可以创建没有依赖关系的 liba.so,然后根据 liba.so 创建 libb.so,然后重新链接 liba.so 或使用 patchelf 修复它。有没有更好的办法?

或者所有库都没有依赖关系会更好吗?只是确保主应用程序链接所有递归需要的东西?

背景:这是一个捆绑的应用程序,用户只会使用主应用程序,而不是库本身。我们目前正在使用静态库,但正在研究共享库,以避免增量构建的链接时间过长,并减少多个应用程序的总大小,这些应用程序都使用一组通用的库函数。

【问题讨论】:

  • 如果您的库相互依赖,那么它们应该是一个库。如果您总是必须同时加载两个,则没有理由拥有两个。
  • 问题是,如果它们是一个库,它可能会是 10GB,我们不想为每个增量构建重新链接。此外,我们有许多产品,每个产品都使用库的一个子集。尽管存在循环依赖,但并非所有 100 个库都依赖于所有其他库。

标签: linux linker shared-libraries


【解决方案1】:

依赖关系不一定是坏的,但它们在循环时有时会很混乱,导致在“愚蠢的”链接器中出现这样的情况,当它们在命令行上遇到时,它们只会从库中提取未定义的符号。

-llib1 -llib2 -llib1 # or, progressively worse:
-llib1 -llib2 -llib3 -llib1 -llib2 -llib1

我通常只是重构库,以便依赖是一种方式。例如,拆分lib1,因此依赖链是一种方式(并非总是可能,但这将是一个非常复杂的链):

lib1a (depends on) lib2 (depends on) lib1b

【讨论】:

  • 这需要两次列出库是否也适用于共享库?我以为它只适用于静态库。
【解决方案2】:

如果你将它们链接三次,它会起作用,第一次是没有依赖关系的库,然后是第二次和第三次相互链接。

第一步,您可以使用cc -shared -o libname.so -xc /dev/null 将库创建为虚拟库。

示例有两个库 A.soB.so 相互调用,以及两个使用它们的示例程序:

$ cat A.c
#include <stdio.h>
extern void B(int); void A(int n){ printf("%s\n", __func__); if(n--) B(n); }

$ cat B.c
#include <stdio.h>
extern void A(int); void B(int n){ printf("%s\n", __func__); if(n--) A(n); }

$ cat mainA.c
extern void A(int); int main(void){ A(3); }

$ cat mainB.c
extern void B(int); int main(void){ B(3); }

$ cc A.c -shared -o A.so      # or cc -shared -o A.so -xc /dev/null
$ cc -Wl,-rpath=. B.c A.so -shared -o B.so
$ cc -Wl,-rpath=. A.c B.so -shared -o A.so

$ ldd A.so
   ...
        B.so => ./B.so (0x00007fd1149d0000)
$ ldd B.so
   ...
        A.so => ./A.so (0x00007f6504c50000)

$ cc -Wl,-rpath=. mainA.c A.so -o mainA
$ cc -Wl,-rpath=. mainB.c B.so -o mainB

$ ./mainA
A
B
A
B
$ ./mainB
B
A
B
A

它同样适用于 e.g. -L. -lA -shared -o libB.so,改编这个愚蠢的例子留给读者作为练习;-)

【讨论】:

  • 是的,但我想知道是否有更直接的方法。我想我也可以在 tmp 目录中有一个名为 libB.so 的空库,我在第一步中链接到 libA,以避免生成 libA 两次。
  • 这是最直接最明显的方式。不,你不能有一个空的库——链接器不会接受一个空文件作为对象参数。即使它接受了它,你仍然必须创建它。
  • 您当然可以首先使用cc -shared -o libA.so -xc /dev/null 创建libA.so 作为一个虚拟库,但这只会混淆恕我直言。不过,我也会提到这一点。
【解决方案3】:

我希望ld 有一个选项可以允许链接一个不存在的库,但我找不到这样的库。我决定最干净的解决方案是使用 patchelf,它允许在不检查存在的情况下添加依赖项。

g++ -fPIC -L. -shared a.cpp -o liba.so
patchelf liba.so --add-needed libb.so

【讨论】:

    猜你喜欢
    • 2010-10-24
    • 2010-12-21
    • 2011-07-22
    • 1970-01-01
    • 2011-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-08
    相关资源
    最近更新 更多