位置独立代码

位置独立代码(Position-independent code),是在PowerPC环境代码生成技术的名字,它允许动态链接器在不固定的虚拟内存地址中加载一个区域代码。除了位置独立代码生成的一些形式之外,操作系统会把你希望共享的代码放置在虚拟内存固定地址中,这会导致维护操作系统时相当困难。例如,它基本不会支持shared libraries和frameworks,因为他们都需要预先分配一个永远不会变的地址。
Mach-O位置独立代码的设置是基于观察到__DATA segment通常定位在__TEXT segment常量偏移的位置。因此,动态加载器在加载任何Mach-O文件时,从来不会移动一个文件的__TEXT segment相对于__DATA segment的位置(即偏移量都是常量)。因此,一个函数能使用它当前的地址加上一个固定的偏移量去决定它想要获取数据的位置。在Mach-O文件里的所有segment,不仅仅是__TEXT和__DATA segment,相对于其他segments来说都有一个固定的偏移量。

Eliminating Position-Independent Code Reference

位置独立代码通常被用于shared libraries 和 bundles,他们会允许动态加载器在加载期间重新把它们定位到不同的地址空间。然而,它没有必要用于通常属于虚拟内存相同地址的应用程序。GCC3.1介绍一个新的选择-mdynamic-no-pic。这个选项同时减少应用程序执行代码的大小,并且通过消除位置独立代码的引用从而提升性能,并且会保存shared libraries的间接调用和间接引导到undefined symbols。如果你使用xcode去创建程序,这个选项默认被选中。

Listing 1 C source code example for position-independent code

1
2
3
4
5
6
7
8
struct s { int member1; int member2; };

struct s bar = {1,2};

int foo(void)
{
return bar.member2;
}

Listing 2 Position-independent code generated from the C example (with addresses in the left column)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.text
; The function foo
.align 2
.globl _foo
0x0 _foo: mflr r0 ; save the link register (LR)
0x4 bcl 20,31,L1$pb ; Use the branch always instruction
; that does not affect the link
; register stack to get the address
; of L1$pb into the LR.
0x8 L1$pb: mflr r10 ; then move LR to r10
0xc mtlr r0 ; restore the previous LR
; bar is located at L1$pc + distance
0x10 addis r9,r10,ha16(_bar-L1$pb); L1$pb plus high 16 bits of distance
0x14 la r9,lo16(_bar-L1$pb)(r9) ; plus low 16 of distance
; => r9 now contains address of bar
0x18 lwz r3,4(r9) ; return bar.member2
0x1c blr
.data
; The initialized structure bar
.align 2
.globl _bar
0x20 _bar: .long 1 ; member1’s initialized value
0x24 .long 2 ; member2’s initialized value
To calculate the address of _bar, the generated code adds the address of the L1$pb symbol (0x8) to the distance to bar. The distance to bar from the address of L1$pb is the value of the expression _bar - L1$pb, which is 0x18 (0x20 - 0x8).

为了计算_bar的地址,产生出来的代码会添加L1\$pb symbol(0x8)的地址到bar的距离。从L1\$pb的地址到bar的距离是表达式 _bar - L1$pb的值,为 0x18 (0x20 - 0x8).

Relocating Position-Independent Code

为了支持中间对象文件的重定位, Mach-O支持一个不同section的重定位入口格式。重定位的入口在OS X ABI Mach-O File Format Reference被描述

每一个 add-immediate指令都代表了两个重定位的入口。 对于addis指令,下面这张表列举了两个重定位入口。第一个重定向的入口是:

r_scattered 1—true
r_pcrel 0—false
r_length 2—indicating 4 bytes
r_type PPC_RELOC_HA16_SECTDIFF
r_address 0x10—the address of the addis instruction
r_value 0x20—the address of the symbol _bar

The values of the second relocation entry are:

r_scattered 1—true
r_pcrel 0—false
r_length 2—indicating 4 bytes
r_type PPC_RELOC_PAIR
r_address 0x18—the low 16 bits of the expression (_bar - L1$pb)
r_value 0x8—the address of the symbol L1$pb

The first relocation entry for the la instruction (at address 0x14 in the example) is:

r_scattered 1—true
r_pcrel 0—false
r_length 2—indicating 4 bytes
r_type PPC_RELOC_LO16_SECTDIFF
r_address 0x14—the address of the addi instruction
r_value 0x20—the address of the symbol _bar

The values of the second relocation entry are:

r_scattered 1—true
r_pcrel 0—false
r_length 2—indicating 4 bytes
r_type PPC_RELOC_PAIR
r_address 0x0—the high 16 bits of the expression (_bar - L1$pb)
r_value 0x8—the address of the symbol L1$pb