为什么所有代码​​的编译位置都不独立?


84

在gcc中编译共享库时,-fPIC选项将代码编译为与位置无关。是否有任何原因(无论是性能还是其他原因)为何您都不会独立编译所有代码位置?


2
但是,哇并不完全正确。许多函数调用和跳转都使用相对跳转,因此在移动后甚至不需要跳转表。
未知

查看生成的汇编代码,似乎函数的地址已加载,而非fpic代码似乎只是跳转。我是不是误会了你的说法?
ojblass

@ojblass我的意思是,有些跳转就像“在此处跳转50条指令”或“向后跳转5条指令”,而不是“跳转至0x400000”。因此,每次使用-fPIC都必须加载地址并不是完全正确的。
未知

维基百科文章提供了很好的描述。基本上,在某些体系结构上,没有直接跳转到相对地址的方法。因此,在这些架构上使用PIC更加昂贵。有关更多信息,请参见@EvanTeran的答案。
Alexei Sholik 2013年

Answers:


67

它添加了一个间接。使用位置无关的代码,您必须加载函数的地址,然后跳转到该地址。通常,函数的地址已经存在于指令流中。


33

本文介绍了PIC的工作原理,并将其与备用加载时间重定位进行了比较。我认为这与您的问题有关。


16
@尼克:我不同意。如果它对询问者有帮助,那就是答案。指向一两篇相关文章可以提供大量信息。
Eli Bendersky

5
这篇文章没有结论,只是一篇文章的链接。甚至没有线索表明由于性能问题,默认情况下不使用PIC。
尼克

10
虽然此链接可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。如果链接的页面发生更改,仅链接的答案可能会失效。
罗布2015年

4
@Rob:富有成效的事情是建议进行编辑,而不用抱怨。这个答案是4岁。当时,SO对于答案的外观没有那么严格的规定
Eli Bendersky

6
此帖子显示在“评论”下,要求我这样做,但我确实做到了。有人标记了它。“抱怨评论”是由SO而非我自动生成的。
罗布2015年

27

是的,有性能原因。一些访问实际上在另一层间接访问之下,以获取内存中的绝对位置。

还有一个GOT(全局偏移量表),用于存储全局变量的偏移量。在我看来,这就像一个IAT修正表,维基百科和其他一些来源将其归类为与位置有关。

http://en.wikipedia.org/wiki/Position_independent_code


23

除了接受的答案。严重影响PIC代码性能的一件事是x86上缺少“ IP相对寻址”。使用“ IP相对寻址”,您可以从当前指令指针中请求X字节的数据。这将使PIC代码更加简单。

跳转和调用通常与EIP有关,因此这些并没有真正造成问题。但是,访问数据将需要一些额外的技巧。有时,寄存器将被临时保留为代码所需数据的“基本指针”。例如,一种常见的技术是滥用x86上调用的工作方式:

call label_1
.dd 0xdeadbeef
.dd 0xfeedf00d
.dd 0x11223344
label_1:
pop ebp            ; now ebp holds the address of the first dataword
                   ; this works because the call pushes the **next**
                   ; instructions address
                   ; real code follows
mov eax, [ebp + 4] ; for example i'm accessing the '0xfeedf00d' in a PIC way

此技术和其他技术为数据访问添加了一个间接层。例如,gcc编译器使用的GOT(全局偏移表)。

X86-64新增了“相对RIP”模式,这使得事情很多简单。


1
IIRC MIPS除了相对跳转以外,也没有PC相对寻址
phuclv 2014年

1
这是Shellcode中用于获取其执行地址的常用技术。我已经在一些CTF解决方案中使用了它。
sherrellbc

2

因为实现完全独立于位置的代码会给代码生成器增加一个约束,这可能会阻止使用更快的操作,或者添加额外的步骤来保留该约束。

在没有虚拟内存系统的情况下进行多处理可能是一个可以接受的折衷方案,在这种情况下,您相信进程不会侵入彼此的内存,并且可能需要在任何基址上加载特定的应用程序。

在许多现代系统中,性能之间的取舍是不同的,并且重新定位的加载器通常比优化器具有自由统治权时所能做到的最好要便宜(它在任何第一次加载代码时都会花费)。同样,虚拟地址空间的可用性首先掩盖了位置独立性的大部分动机。


1

同样,大多数现代处理器(大多数现代OS使用)中的虚拟内存硬件意味着大量代码(所有用户空间应用,对mmap的过分使用等)不需要位置独立。每个程序都有自己的地址空间,它认为该地址空间从零开始。


4
但是即使使用VM-MMU,也需要使用PIC代码来确保相同的.so库在被不同的可执行文件使用时仅加载一次到内存中。
mmmmmmmm

1

position-independent code 在大多数架构上都有性能开销,因为它需要额外的寄存器。

因此,这是出于性能目的。


0

如今,操作系统和编译器默认将所有代码作为位置无关代码。尝试在不使用-fPIC标志的情况下进行编译,代码可以正常编译,但您只会得到警告。OS之类的Windows使用称为内存映射的技术来实现此目的。


-4

该问题的日期为2009年。十年过去了,现在所有代码实际上都是与位置无关的。现在由操作系统和编译器强制执行。无法退出。所有代码都使用PIE强制编译,并且-no-pic / -no-pie标志被忽略,这是该ASLR借口的一部分。这样做的原因是,以提高安全性为幌子,放慢了以前快速运行的应用程序的速度,并出售了更新的硬件。这是完全不合理的,因为现在大容量的内存使我们完全摆脱了动态链接的麻烦,可以静态地编译所有应用程序。

以前也发生过同样的情况,人们默默地接受实模式,而其他自由正在被夺走。我想请您注意,由于上下文切换和地址转换延迟,MMU的速度大大降低。您不会在性能至关重要的系统中找到MMU,就像科学家用来采样物理实验的系统一样。

您无需抱怨,因为您甚至都不知道所有这些培训轮子都在妨碍您的代码。我能说什么 立即使用其PIC享受慢2倍的软件!甚至随着LLVM的到来,很快将强制实施JIT(托管代码),而无法访问x86内联汇编,这将进一步减慢任何C / C ++代码的速度。“为安全而牺牲自由的人不应该得到任何回报。”


那只是事实的陈述:10年前,PIC是可选的,但今天它是默认的和强制性的。我怀疑在将来的OS版本中将支持非PIE代码。就像在Windows 9x之后放弃了实模式支持一样。因此,使用或不使用PIC的问题更多地是理论上的计算机科学主题,除非您以某种方式解锁操作系统并重新启用对此的支持。人们需要了解的关于PIC的最重要的事情是,它的速度足够慢,直到现在为止编译器都支持静态编译,而且大多数DLL都有静态版本。
SmugLispWeenie

1
您的前几句话只是事实陈述。剩下的就是意见,与阴谋接轨。
Mitch Lindgren

好吧,只要与人们交谈,征求他们的意见。我个人发现,PIC与非PIC也成为意识形态问题。PIC与Communism等效,在此程序中,代码是批量生产的,每个人都得到相同的副本。非PIC等同于资本主义,在编程中,相同代码有许多竞争版本。因此,拥有更多左派思想的人们下意识地支持PIC,以证明他们最喜欢的思想至少可以在计算中起作用的观点。这些人会建议您不要使用个人修改的libpng。
SmugLispWeenie

2
我们能否在编程网站上没有政治言论,请谢谢
Ryan McCampbell,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.