您的位置:首页 > 编程语言

位置无关代码

2014-05-20 09:54 141 查看
首先是书:

《深入理解计算机系统》《程序员的自我休养》这两本书中都有详细地讲到位置无关代码。





位置无关代码(PIC)

==============================================

什么是位置无关代码(Position Independent Code —— PIC)?

理解这个,首先需要理解代码加载域和运行域的概念。

加载域是代码存放的地址,运行域是代码运行时的地址。

在一些场合,一些代码并不在存放这部分代码的地址上执行地址。比如说,放在 NorFlash 中的代码可能最终是放在 RAM 中运行,那么 NorFlash 中的地址就是加载域,而在 RAM 中的地址就是运行域。

在汇编代码中我们常常会看到一些跳转指令,比如说 b、bl 等,这些指令后面是一个相对地址而不是绝对地址,比如说b main,这个指令应该怎么理解呢?main这里究竟是一个什么东西呢?

这时候就需要涉及到链接地址的概念了。链接地址实际上就是链接器对代码中的变量名、函数名等东西进行一个地址的编排,赋予这些抽象的东西一个地址,然后在程序中访问这些变量名、函数名就是在访问一些地址。

一般所说的链接地址都是指链接这些代码的起始地址,代码必须放在这个地址开始的地方才可以正常运行,否则的话当代码去访问、执行某个变量名、函数名对应地址上的代码时就会找不到,接着程序无疑就是跑飞。

但是上面说的那个b main的情形有点特殊,b、bl等跳转指令并不是一个绝对跳转指令,而是一个相对跳转指令,什么意思呢?

就是说,这个main标签最后得到的地址并不是main被链接器编排后的绝对地址,而是main的绝对地址减去当前的这个指令的绝对地址所得到的值。也就是说b、bl访问到的是一个相对地址,不是绝对地址。

因此,包括这个语句和main在内的代码段无论是否放在它的运行域,这段代码都能正常运行。这就是所谓的位置无关代码。

由上面的论述可以得知,如果你的这段代码需要实现位置无关,那么你就不能使用绝对寻址指令,否则的话就是位置有关了。

==============================================

(1) 可重定位代码(Relocatable code):所谓代码的重定位,就是把可执行代码移动到内存中的另外一个地址去。OS一般会把内核从硬盘COPY到内存中去执行,就是用到了重定位这个技术。

生成动态库时假定它被加载在地址 0 处。加载时它会被加载到一个地址(base),这时要进行一次重定位(Relocation),把代码、数据段中所有的地址加上这个 base 的值。这样代码运行时就能使用正确的地址了。

(2) 位置无关代码(Position Independent Code):使用 -fPIC 的 Linux SO。

这样的代码本身就能被放到线性地址空间的任意位置,无需修改就能正确执行。通常的方法是获取指令指针(如 IA32 的 EIP 寄存器)的值,加上一个偏移得到全局变量/函数的地址。

==============================================

PIC vs. Relocatable:

(1) PIC 的缺点主要就是代码有可能长一些。例如 IA32,由于不能直接使用 [EIP+constant] 这样的寻址方式,甚至不能直接将 EIP 的值交给其他寄存器,要用到 GOT(Global Offset Table)来定位全局变量和函数。这样导致代码的效率略低。

(2) PIC 的加载速度稍快,因为不需要做重定位。

(3) 多个进程引用同一个 PIC 动态库时,可以共用内存。这一个库在不同进程中的虚拟地址不同,但操作系统显然会把它们映射到同一块物理内存上。对于可重定位代码,则必须为每个库都在物理内存中复制一份副本,因为需要修改其中的地址。当然,主流现代操作系统都启用了分页内存机制,这使得重定位时可以使用 COW(Copy On Write)来节省内存(32 位 Windows 就是这样做的);然而,页面的粒度还是比较大的(例如 IA32 上是 4KiB),至少对于代码段来说能节省的相当有限。

==============================================
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: