GCC -fPIC选项

℡╲_俬逩灬. 提交于 2020-02-26 08:18:56

我已经阅读了GCC的代码生成约定选项 ,但无法理解“生成与位置无关的代码(PIC)”的作用。 请举例说明一下这是什么意思。


#1楼

进一步添加......

每个进程都有相同的虚拟地址空间(如果通过在linux OS中使用标志来停止虚拟地址的随机化)(更多详细信息仅为我自己禁用并重新启用地址空间布局随机化

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

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

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

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


#2楼

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

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

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

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

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

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


#3楼

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


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

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

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


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

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

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

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


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

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

ps在无法避免动态链接的情况下,有多种方法可以避免与位置无关的代码的耗时功能。

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


#4楼

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

例如,跳跃将生成为相对而非绝对。

伪汇编:

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编译的,那么它适合包含在库中 - 库必须能够从其在内存中的首选位置重新定位到另一个地址,在您的库更喜欢的地址处可能有另一个已加载的库。


#5楼

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!