【问题标题】:Linking a shared library against a static library: must the static library be compiled differently than if an application were linking it?将共享库链接到静态库:静态库的编译方式必须与应用程序链接它的方式不同吗?
【发布时间】:2010-12-08 01:54:51
【问题描述】:

至少在 Linux 和 Solaris 上,静态库实际上只是一堆已编译的 .o 文件被扔进一个大文件中。编译静态库时,通常会省略 -fpic 标志,因此生成的代码是位置相关的。

现在假设我的静态库是 B。我已经构建了它并得到了生成的 .a 文件,它实际上只是所有与位置相关的 .o 文件的全局。现在我有一个我想构建的共享库A,我希望它静态链接B。当我构建A时,我自然会使用-fpic标志来使生成的代码位置独立。但是如果我链接到 B,我不是混合了位置相关和位置无关的目标文件吗?

除非我还指定 -mimpure-text,否则我会收到很多文本重定位错误,我认为这可能是原因。看来我编译一个库的时候,真的需要编译3次,一个共享版本,一个静态版本,一个static-that-c​​an-be-used-by-shared-libs版本.我对吗?我可以继续使用 -mimpure-text 但是 g++ 手册页说,如果你这样做,对象实际上并没有最终被共享(不清楚它是全部未共享还是只是静态链接的部分,有人知道吗?) .

【问题讨论】:

  • 问题陈述正确,他在第一段中'省略'-fpic,导致位置相关代码,然后在第二段中使用它,导致位置无关代码。跨度>

标签: c linker shared-libraries static-libraries fpic


【解决方案1】:

您不必在共享对象中使用 PIC 代码(正如您所发现的,您可以使用 -mimpure-text 选项来允许这样做)。

也就是说,共享对象中的非 PIC 代码更重量级。使用 PIC 代码,内存中的文本页面只是磁盘上文本页面的直接内存映射。这意味着如果多个进程正在使用共享对象,它们可以共享内存页面。

但如果您没有 PIC 代码,当运行时链接器加载共享对象时,它必须对文本页面应用修正。这意味着每个使用共享对象的进程都将拥有它自己的任何文本页面的唯一版本,该文本页面上有一个修复(即使共享对象加载到与写时复制相同的地址,也只会注意到该页面是修改,而不是以相同的方式修改)。

对我来说,重要的问题是您是否会同时运行多个进程,每个进程都加载共享对象。如果你这样做了,绝对值得确保 SO 中的所有代码都是 PIC。

但如果不是这种情况,并且只有一个进程加载了共享对象,那么它就没有那么重要了。

【讨论】:

  • 实际上,如果您知道只有一个进程会加载它,那么在没有 pic 的情况下进行编译难道不是一种性能优势吗?因为保存了一个寄存器。
  • @JosephGarvin PIC 使用 32 位 x86 代码的寄存器,但不使用 64 位代码。 x64 指令集支持程序计数器相对寻址 - 加载与当前执行指令(下一条指令之后)的内存地址偏移 NNNN 个字节的数据。 x64 中的 PIC 代码不需要 GOT 指针寄存器。
  • 感谢您的精彩解释!
【解决方案2】:

我在链接阶段为静态库的共享对象库版本执行以下操作:g++ -shared -o libshared.so -Wl,--whole-archive -fPIC -lstatic -Wl,--no-whole -档案。由于 --whole-archive 将静态库(形式为 libstatic.a)的(列表)中的每个对象链接起来,我相信在(列表)之前使用 -fPIC 是 OP 需要做的所有事情。

【讨论】:

    【解决方案3】:

    作为一种替代方法,提供两个库:共享库和链接的静态库。它们应该正确链接到最终的可执行文件。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-08
      • 1970-01-01
      • 1970-01-01
      • 2019-04-19
      相关资源
      最近更新 更多