2023-08-05 10:00:07

GCC -fPIC选项

我读过GCC的代码生成约定选项,但不明白“生成位置无关代码(PIC)”是做什么的。请举个例子给我解释一下是什么意思。


当前回答

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

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

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

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

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

由于从主程序主体对这些库的调用也可以被重新定位,这使得必须复制共享库的可能性大大降低。

其他回答

我将试着用更简单的方式解释已经说过的话。

无论何时加载共享库,加载器(在操作系统上加载任何你运行的程序的代码)都会根据对象加载到的位置更改代码中的一些地址。

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

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

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

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

添加更多…

每个进程都有相同的虚拟地址空间(如果在linux操作系统中使用标志停止虚拟地址的随机化) (有关详细信息,仅为自己禁用和重新启用地址空间布局随机化)

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

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

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

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

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

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

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

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

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

由于从主程序主体对这些库的调用也可以被重新定位,这使得必须复制共享库的可能性大大降低。

位置独立代码意味着生成的机器代码不依赖于位于特定地址才能工作。

例如,跳跃是相对的,而不是绝对的。

Pseudo-assembly:

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