66

我在编译与静态库静态链接的共享对象时遇到了很多问题。此问题仅出现在 x84_64 平台上。在 x86_32 上进行相同的编译工作时,我没有任何问题。

也许这是特定于操作系统的 GCC 配置,但我的研究表明 GCC 在 x86_64 平台上是如何工作的。无论如何,我在 Ubuntu 10.04 x86_64 上使用 gcc 4.4.3。

问题是如何解决的?...确保所有静态库依赖项都使用 -fPIC 编译。

问题 1: -fpic 和 -fPIC 有什么区别(显然 -fPIC 在 x86 上生成更多指令)?为什么后一种类型在 x86_64 上下文中更相关?

问题 2:我的假设是,当您链接静态代码时,您在链接时将函数硬连接到二进制文件中,为什么它需要“位置无关代码”机制提供的间接级别?

问题 3:现在如果 x86 不需要 -fpic / -fPIC 将共享对象链接到静态档案,为什么 x86_64 需要它?

问题 4:即使需要,为什么不隐式提供?我认为突破性的改变应该是一个很大的禁忌

4

2 回答 2

57
  1. 见问题3544035。也在这里那里讨论。
  2. 这取决于您对静态库的用途。如果您只想将其链接到程序中,则不需要 PIC 代码(libtool 将其称为便利库,因为您几乎可以不用它,例如,它只是帮助您将编译过程调整到合理的大小)。否则,如果您打算将共享库链接到它,则需要静态库中的 PIC 代码。
  3. 请参阅问题3146744此处
  4. 它会使您的代码膨胀,因此它不是默认值。需要注意的一点是,当您编译单个目标文件时,GCC 不知道您是否要从中创建一个共享库。例如,在我的大多数小型项目中,我只是将几个目标文件链接在一起,并且不需要 PIC 代码。

另外,我的建议是:如果您需要担心这一点,那您就做错了(或者您喜欢以艰苦的方式学习,这很好,因为您将从经验中获得更多收益)。编译系统(libtool、cmake,无论你使用什么)都应该为你做到这一点。

于 2010-10-27T18:35:35.837 回答
0

典型的.a静态库只是常规.o对象的集合

因此,从概念上讲,您通常可以在命令行上将 替换为.a完全相同的.o文件列表,这些文件不需要可重定位。

例如,考虑这个最小的可运行示例

交流

#include "a.h"

int a(void) { return 1; }

#ifndef A_H
#define A_H

int a(void);

#endif

公元前

#include "b.h"

int b(void) { return 2; }

bh

#ifndef B_H
#define B_H

int b(void);

#endif

主程序

#include <assert.h>
#include <stdlib.h>

#include "a.h"
#include "b.h"

int main(void) {
    assert(a() == 1);
    assert(b() == 2);
    return EXIT_SUCCESS;
}

编译并运行:

gcc -ggdb3 -std=c89 -Wall -Wextra -pedantic-errors -fPIC -c 'main.c' -o 'main.o'
gcc -ggdb3 -std=c89 -Wall -Wextra -pedantic-errors -fPIC -c 'a.c' -o 'a.o'
gcc -ggdb3 -std=c89 -Wall -Wextra -pedantic-errors -fPIC -c 'b.c' -o 'b.o'
ar rcs ab.a a.o b.o
gcc -ggdb3 -std=c89 -Wall -Wextra -pedantic-errors main.o ab.a -o maina.out
./maina.out

从这里我们清楚地看到,ar简单地打包a.ob.o放入ab.a.

因此,以下命令也有效:

gcc -ggdb3 -std=c89 -Wall -Wextra -pedantic-errors main.o a.o b.o -o maina.out

由此应该希望清楚的是,通常不需要使.a存档内的目标文件与位置无关。

例如,如果您想将它们链接到共享库中,您可以使它们与位置无关。一切都像以前一样适用:.a只是包含它们而不修改它们。

这个答案可能也很有趣:gcc 和 ld 中与位置无关的可执行文件的 -fPIE 选项是什么?

在 Ubuntu 20.04 上测试。

于 2020-11-25T13:16:38.133 回答