GCC -fPIC选项

时间:2011-03-15 12:12:17

标签: c++ gcc options fpic

我已经阅读了GCC's Options for Code Generation Conventions,但无法理解“生成与位置无关的代码(PIC)”的作用。请举例说明一下这是什么意思。

6 个答案:

答案 0 :(得分:447)

与位置无关的代码意味着生成的机器代码不依赖于位于特定地址以便工作。

E.g。跳跃会产生为相对而非绝对。

伪组件:

PIC:无论代码是在地址100还是1000

,这都可以
100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL CURRENT+10
...
111: NOP

非PIC:仅当代码位于地址100

时才会起作用
100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL 111
...
111: NOP

编辑:回应评论。

如果您的代码是使用-fPIC编译的,那么它适合包含在库中 - 库必须能够从其在内存中的首选位置重新定位到另一个地址,在您的库的地址可能还有另一个已加载的库喜欢。

答案 1 :(得分:49)

我会尝试以更简单的方式解释已经说过的内容。

每当加载共享库时,加载器(操作系统上的代码加载您运行的任何程序)都会根据加载对象的位置更改代码中的某些地址。

在上面的例子中,非PIC代码中的“111”由加载程序在第一次加载时写入。

对于非共享对象,您可能希望它是这样的,因为编译器可以对该代码进行一些优化。

对于共享对象,如果另一个进程想要“链接”到该代码,则必须将其读取到相同的虚拟地址,否则“111”将没有意义。但该虚拟空间可能已在第二个过程中使用。

答案 2 :(得分:35)

内置于共享库中的代码通常应该是与位置无关的代码,这样共享库可以很容易地(或多或少)加载到内存中的任何地址。 -fPIC选项确保GCC生成此类代码。

答案 3 :(得分:17)

进一步添加...

每个进程都有相同的虚拟地址空间(如果通过在linux OS中使用标志来停止虚拟地址的随机化) (有关详细信息Disable and re-enable address space layout randomization only for myself

因此,如果它的一个exe没有共享链接(假设场景),那么我们总是可以给同一个asm指令提供相同的虚拟地址而不会有任何伤害。

但是当我们想要将共享对象链接到exe时,我们不确定分配给共享对象的起始地址,因为它将取决于共享对象链接的顺序。这就是说,asm指令里面.so将始终具有不同的虚拟地址,具体取决于其链接的进程。

因此,一个进程可以在其自己的虚拟空间中将.so的起始地址作为0x45678910给出,同时其他进程可以给出起始地址0x12131415,如果它们不使用相对寻址,则.so根本不起作用。

因此他们总是必须使用相对寻址模式,因此使用fpic选项。

答案 4 :(得分:12)

在加载库或运行时解析动态库中函数的链接。因此,程序运行时,可执行文件和动态库都会加载到内存中。 无法确定加载动态库的内存地址 提前,因为固定地址可能与另一个需要相同地址的动态库冲突。

有两种常用的方法可以解决这个问题:

1.Relocation。如有必要,将修改代码中的所有指针和地址以适合实际的加载地址。重定位由链接器和加载器完成。

2.与位置无关的代码。代码中的所有地址都与当前位置有关。类Unix系统中的共享对象默认使用与位置无关的代码。如果程序运行很长时间,这比重定位效率低,特别是在32位模式下。

名称“与位置无关的代码”实际上意味着以下内容:

  • 代码部分不包含需要重定位的绝对地址,但只包含自相关地址 地址。因此,代码段可以加载到任意内存地址,并在多个进程之间共享。

  • 数据部分不在多个进程之间共享,因为它通常包含 可写数据。因此,数据部分可能包含指针或地址 需要搬迁。

  • 可以在Linux中覆盖所有公共函数和公共数据。如果一个功能 在主可执行文件中具有与共享对象中的函数相同的名称,然后是 main中的版本优先,不仅在从main调用时,还在何时调用 从共享对象调用。同样,当main中的全局变量具有相同的变量时 将name命名为共享对象中的全局变量,然后main中的实例将为 即使从共享对象访问时也使用。

这种所谓的符号插入旨在模仿静态库的行为。

共享对象有一个指向其函数的指针表,称为过程链接表(PLT)和表 指向其变量的指针称为全局偏移表(GOT),以实现此“覆盖”功能。 对函数和公共变量的所有访问都通过这些表。

P.S。在无法避免动态链接的地方,有各种方法可以避免与位置无关的代码的耗时功能。

您可以阅读以下文章中的更多内容:http://www.agner.org/optimize/optimizing_cpp.pdf

答案 5 :(得分:5)

对已经发布的答案的一个小补充:未编译为位置无关的目标文件是可重定位的;它们包含重定位表条目。

这些条目允许加载程序(将程序加载到内存中的那一段代码)重写绝对地址,以调整虚拟地址空间中的实际加载地址。

操作系统将尝试与链接到同一共享对象库的所有程序共享加载到内存中的“共享对象库”的单个副本。

由于代码地址空间(与数据空间的部分不同)不需要是连续的,并且因为链接到特定库的大多数程序具有相当固定的库依赖性树,所以这在大多数情况下都成功。在极少数存在差异的情况下,是的,可能需要在内存中有两个或更多共享对象库副本。

显然,任何尝试在程序和/或程序实例之间随机化库的加载地址(以减少创建可利用模式的可能性)都会使这种情况变得常见,而不是罕见的,因此在系统启用的情况下这个功能,应该尽一切努力将所有共享对象库编译为位置无关。

由于从主程序主体调用这些库也将可以重定位,因此不太可能需要复制共享库。