关于-fPIC选项 和 GOT表

加上-fPIC参数后,编译后的文件和没有加这个参数的文件,有什么区别呢?
没有加这个参数的编译后的共享库,也可以使用,它和加了参数后的使用起来又有什么区别呢?

position independent(位置无关)
relocate(可重定位)

位置无关代码主要是在访问全局变量和全局函数的时候采用了位置无关的重定位方法,即依赖GOT和PLT来重定位。
普通的重定位方法需要修改代码段,比如偏移地址0x100处需要重定位,loader就修改代码段的0x100处的内容,通过查找重定位信息得到具体的值。这种方法需要修改代码段的内容,对于动态连接库来说,其初衷是让多个进程共享代码段,若对其进行写操作,就回引起COW,从而失去共享。
-fPIC选项告诉编绎器使用GOT和PLT的方法重定位,GOT是数据段,因此避免了COW,真正实现了共享。
如果不用-fPIC,动态连接库依然可以使用,但其重定位方法为一般方法,必然会引起COW,但也无所谓,除了性能在COW时稍微受些影响,其他也没啥。

GOT 是 data section,是一个 table,除专用的几个 entry,每个 entry 的内容可以再执行的时候修改;
PLT 是 text section,是一段一段的 code,执行中不需要修改。

http://www.linuxforum.net/forum/showflat.php?Cat=&Board=cpu&Number=688070&page=&view=&sb=&o=&vc=1

如何得到GOT表

汇编语言片段: 2f7: e8 00 00 00 00 call 2fc <ok+0xc>
2fc: 5b pop %ebx
2fd: 81 c3 b0 10 00 00 add $0x10b0,%ebx

这里的2f7中的call 2fc <ok+0xc>和2fc处的pop %ebx,得到运行时的eip地址,存入ebx寄存器中。

后面的add $0x10b0,%ebx又是什么用处?如果我们这里假定在内存中的地址是2fc,那加上10b0之后的值是0x13ac了,看在这里是什么呢?

Disassembly of section .got:
000013ac <.got>:
13ac: 34 13 xor $0x13,%al

可以看出这是一个got节。

-------------------------

一个例子

对于加了-fPIC选项的共享库,如:gcc -shared -fPIC -o libhello.so hello.c,
其.got.plt section中存放外部函数的地址,.got section中存放全局变量的地址。
.got.plt紧接在.got后面,ebx寄存器存放.got.plt地址,正偏移引用函数地址,负偏移引用数据地址。
对于static变量(不通过GOT表来引用),如static int ss; 可以用ss@GOTOFF(%ebx)来引用(相对于GOT表的偏移)。

// hello.c

#include <stdio.h>

#include <stdlib.h>

int count;
char ch;
struct _s {
int s1;
int s2;
} s;

void say_hello()
{
printf(“Hello:%xn”, ++count);
printf(“Hello:%xn”, ++ch);
printf(“Hello:%xn”, ++s.s1);
printf(“Hello:%xn”, ++s.s2);
}

jfo@lab:~/test$ gcc -ggdb -shared -fPIC -o libhello.so hello.c

jfo@lab:~/test$ readelf -x.got libhello.so

Hex dump of section ‘.got’:
0x000016a0 00000000 00000000 00000000 00000000 …………….
0x000016b0 00000000 00000000 ……..

jfo@lab:~/test$ readelf -x.got.plt libhello.so

Hex dump of section ‘.got.plt’:
0x000016b8 d8150000 00000000 00000000 9e030000 …………….
0x000016c8 ae030000 be030000 ……..

jfo@lab:~/test$ objdump -S -j.plt libhello.so

libhello.so: file format elf32-i386

Disassembly of section .plt:

00000388 <gmon_start@plt-0x10>:
388: ff b3 04 00 00 00 pushl 0x4(%ebx)
38e: ff a3 08 00 00 00 jmp *0x8(%ebx)
394: 00 00 add %al,(%eax)

00000398 <gmon_start@plt>:
398: ff a3 0c 00 00 00 jmp *0xc(%ebx)
39e: 68 00 00 00 00 push $0x0
3a3: e9 e0 ff ff ff jmp 388 <_init+0x30>

000003a8 <printf@plt>:
3a8: ff a3 10 00 00 00 jmp *0x10(%ebx)
3ae: 68 08 00 00 00 push $0x8
3b3: e9 d0 ff ff ff jmp 388 <_init+0x30>

000003b8 <__cxa_finalize@plt>:
3b8: ff a3 14 00 00 00 jmp *0x14(%ebx)
3be: 68 10 00 00 00 push $0x10
3c3: e9 c0 ff ff ff jmp 388 <_init+0x30>

jfo@lab:~/test$ objdump -S -j.text libhello.so

void say_hello()
{
48c: 55 push %ebp
48d: 89 e5 mov %esp,%ebp
48f: 53 push %ebx
490: 83 ec 14 sub $0x14,%esp
493: e8 ef ff ff ff call 487 <__i686.get_pc_thunk.bx>
498: 81 c3 20 12 00 00 add $0x1220,%ebx
printf(“Hello:%xn”, ++count);
49e: 8b 83 f8 ff ff ff mov -0x8(%ebx),%eax
4a4: 8b 00 mov (%eax),%eax
4a6: 8d 50 01 lea 0x1(%eax),%edx
4a9: 8b 83 f8 ff ff ff mov -0x8(%ebx),%eax
4af: 89 10 mov %edx,(%eax)
4b1: 8b 83 f8 ff ff ff mov -0x8(%ebx),%eax
4b7: 8b 00 mov (%eax),%eax
4b9: 89 44 24 04 mov %eax,0x4(%esp)
4bd: 8d 83 fc ee ff ff lea -0x1104(%ebx),%eax
4c3: 89 04 24 mov %eax,(%esp)
4c6: e8 dd fe ff ff call 3a8 <printf@plt>
printf(“Hello:%xn”, ++ch);
4cb: 8b 83 f4 ff ff ff mov -0xc(%ebx),%eax
4d1: 0f b6 00 movzbl (%eax),%eax
4d4: 8d 50 01 lea 0x1(%eax),%edx
4d7: 8b 83 f4 ff ff ff mov -0xc(%ebx),%eax
4dd: 88 10 mov %dl,(%eax)
4df: 8b 83 f4 ff ff ff mov -0xc(%ebx),%eax
4e5: 0f b6 00 movzbl (%eax),%eax
4e8: 0f be c0 movsbl %al,%eax
4eb: 89 44 24 04 mov %eax,0x4(%esp)
4ef: 8d 83 fc ee ff ff lea -0x1104(%ebx),%eax
4f5: 89 04 24 mov %eax,(%esp)
4f8: e8 ab fe ff ff call 3a8 <printf@plt>
printf(“Hello:%xn”, ++s.s1);
4fd: 8b 83 e8 ff ff ff mov -0x18(%ebx),%eax
503: 8b 00 mov (%eax),%eax
505: 8d 50 01 lea 0x1(%eax),%edx
508: 8b 83 e8 ff ff ff mov -0x18(%ebx),%eax
50e: 89 10 mov %edx,(%eax)
510: 8b 83 e8 ff ff ff mov -0x18(%ebx),%eax
516: 8b 00 mov (%eax),%eax
518: 89 44 24 04 mov %eax,0x4(%esp)
51c: 8d 83 fc ee ff ff lea -0x1104(%ebx),%eax
522: 89 04 24 mov %eax,(%esp)
525: e8 7e fe ff ff call 3a8 <printf@plt>
printf(“Hello:%xn”, ++s.s2);
52a: 8b 83 e8 ff ff ff mov -0x18(%ebx),%eax
530: 8b 40 04 mov 0x4(%eax),%eax
533: 8d 50 01 lea 0x1(%eax),%edx
536: 8b 83 e8 ff ff ff mov -0x18(%ebx),%eax
53c: 89 50 04 mov %edx,0x4(%eax)
53f: 8b 83 e8 ff ff ff mov -0x18(%ebx),%eax
545: 8b 40 04 mov 0x4(%eax),%eax
548: 89 44 24 04 mov %eax,0x4(%esp)
54c: 8d 83 fc ee ff ff lea -0x1104(%ebx),%eax
552: 89 04 24 mov %eax,(%esp)
555: e8 4e fe ff ff call 3a8 <printf@plt>
}

jfo@lab:~/test$ readelf -a libhello.so

Relocation section ‘.rel.dyn’ at offset 0x308 contains 7 entries:
Offset Info Type Sym.Value Sym. Name
000016d0 00000008 R_386_RELATIVE
000016a0 00000806 R_386_GLOB_DAT 000016dc s //offset -0x18(ebx)
000016a4 00000106 R_386_GLOB_DAT 00000000 gmon_start //offset -0x14(ebx)
000016a8 00000206 R_386_GLOB_DAT 00000000 _Jv_RegisterClasses //offset -0x10(ebx)
000016ac 00000d06 R_386_GLOB_DAT 000016e4 ch //offset -0xc(ebx)
000016b0 00000906 R_386_GLOB_DAT 000016e8 count //offset -0x8(ebx)
000016b4 00000406 R_386_GLOB_DAT 00000000 __cxa_finalize //offset -0x4(ebx)
//Offset000016a0、000016ac、000016b0位于.got section
//Sym.Value000016dc、000016e4、000016e8对应.bss段变量的地址

Relocation section ‘.rel.plt’ at offset 0x340 contains 3 entries:
Offset Info Type Sym.Value Sym. Name
000016c4 00000107 R_386_JUMP_SLOT 00000000 gmon_start //offset 0xc(ebx)
000016c8 00000307 R_386_JUMP_SLOT 00000000 printf //offset 0x10(ebx)
000016cc 00000407 R_386_JUMP_SLOT 00000000 __cxa_finalize //offset 0x14(ebx)
//Offset000016c4、000016c8、000016cc位于.got.plt section

There are no unwind sections in this file.

Symbol table ‘.dynsym’ contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 NOTYPE WEAK DEFAULT UND gmon_start
2: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
3: 00000000 54 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.0 (2)
4: 00000000 267 FUNC WEAK DEFAULT UND cxa_finalize@GLIBC_2.1.3 (3)
5: 0000048c 212 FUNC GLOBAL DEFAULT 11 say_hello
6: 000016ec 0 NOTYPE GLOBAL DEFAULT ABS _end
7: 000016d4 0 NOTYPE GLOBAL DEFAULT ABS _edata
8: 000016dc 8 OBJECT GLOBAL DEFAULT 22 s
9: 000016e8 4 OBJECT GLOBAL DEFAULT 22 count
10: 000016d4 0 NOTYPE GLOBAL DEFAULT ABS
bss_start
11: 00000358 0 FUNC GLOBAL DEFAULT 9 _init
12: 00000598 0 FUNC GLOBAL DEFAULT 12 _fini
13: 000016e4 1 OBJECT GLOBAL DEFAULT 22 ch

Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .hash HASH 000000b4 0000b4 00004c 04 A 3 0 4
[ 2] .gnu.hash GNU_HASH 00000100 000100 000048 04 A 3 0 4
[ 3] .dynsym DYNSYM 00000148 000148 0000e0 10 A 4 1 4
[ 4] .dynstr STRTAB 00000228 000228 000091 00 A 0 0 1
[ 5] .gnu.version VERSYM 000002ba 0002ba 00001c 02 A 3 0 2
[ 6] .gnu.version_r VERNEED 000002d8 0002d8 000030 00 A 4 1 4
[ 7] .rel.dyn REL 00000308 000308 000038 08 A 3 0 4
[ 8] .rel.plt REL 00000340 000340 000018 08 A 3 10 4
[ 9] .init PROGBITS 00000358 000358 000030 00 AX 0 0 4
[10] .plt PROGBITS 00000388 000388 000040 04 AX 0 0 4
[11] .text PROGBITS 000003d0 0003d0 0001c8 00 AX 0 0 16
[12] .fini PROGBITS 00000598 000598 00001c 00 AX 0 0 4
[13] .rodata PROGBITS 000005b4 0005b4 00000a 00 A 0 0 1
[14] .eh_frame PROGBITS 000005c0 0005c0 000004 00 A 0 0 4
[15] .ctors PROGBITS 000015c4 0005c4 000008 00 WA 0 0 4
[16] .dtors PROGBITS 000015cc 0005cc 000008 00 WA 0 0 4
[17] .jcr PROGBITS 000015d4 0005d4 000004 00 WA 0 0 4
[18] .dynamic DYNAMIC 000015d8 0005d8 0000c8 08 WA 4 0 4
[19] .got PROGBITS 000016a0 0006a0 000018 04 WA 0 0 4
[20] .got.plt PROGBITS 000016b8 0006b8 000018 04 WA 0 0 4
[21] .data PROGBITS 000016d0 0006d0 000004 00 WA 0 0 4
[22] .bss NOBITS 000016d4 0006d4 000018 00 WA 0 0 4
[23] .comment PROGBITS 00000000 0006d4 00005a 00 0 0 1
[24] .debug_aranges PROGBITS 00000000 000730 000070 00 0 0 8
[25] .debug_pubnames PROGBITS 00000000 0007a0 000037 00 0 0 1
[26] .debug_info PROGBITS 00000000 0007d7 0001a8 00 0 0 1
[27] .debug_abbrev PROGBITS 00000000 00097f 0000aa 00 0 0 1
[28] .debug_line PROGBITS 00000000 000a29 00013c 00 0 0 1
[29] .debug_frame PROGBITS 00000000 000b68 000030 00 0 0 4
[30] .debug_str PROGBITS 00000000 000b98 000083 01 MS 0 0 1
[31] .debug_loc PROGBITS 00000000 000c1b 00002c 00 0 0 1
[32] .debug_ranges PROGBITS 00000000 000c48 000040 00 0 0 8
[33] .shstrtab STRTAB 00000000 000c88 00013b 00 0 0 1
[34] .symtab SYMTAB 00000000 001364 000430 10 35 54 4
[35] .strtab STRTAB 00000000 001794 00019e 00 0 0 1

main函数中对于libhello.so中的函数say_hello的调用,会建立plt表(可减少relocation的次数,不需要对每个say_hello调用处都relocate,只需将GOT表中对应entry relocate);
而对于count的使用,直接分配了固定地址!.rel.dyn节里标记为R_386_COPY类型,并将count变量的地址直接copy到libhello.so的GOT表对应的entry里。
参见:【zz】从程序员角度看ELF::启动过程::静态的初始化

// test.c

#include <stdio.h>

#include <stdlib.h>

extern int count;

int main()
{
say_hello();
printf(“count is %xn”, count);

return 0;
}

jfo@lab:~/test$ gcc -ggdb -o test test.c -L. -lhello -Wl,-rpath,.

jfo@lab:~/test$ objdump -S test

int main()
{
80484e4: 8d 4c 24 04 lea 0x4(%esp),%ecx
80484e8: 83 e4 f0 and $0xfffffff0,%esp
80484eb: ff 71 fc pushl -0x4(%ecx)
80484ee: 55 push %ebp
80484ef: 89 e5 mov %esp,%ebp
80484f1: 51 push %ecx
80484f2: 83 ec 14 sub $0x14,%esp
say_hello();
80484f5: e8 02 ff ff ff call 80483fc <say_hello@plt>

printf(“count is %xn”, count);
80484fa: a1 08 97 04 08 mov 0x8049708,%eax //变量cout的地址固定!!!
80484ff: 89 44 24 04 mov %eax,0x4(%esp)
8048503: c7 04 24 e0 85 04 08 movl $0x80485e0,(%esp)
804850a: e8 0d ff ff ff call 804841c <printf@plt>

return 0;
804850f: b8 00 00 00 00 mov $0x0,%eax
}
8048514: 83 c4 14 add $0x14,%esp
8048517: 59 pop %ecx
8048518: 5d pop %ebp
8048519: 8d 61 fc lea -0x4(%ecx),%esp
804851c: c3 ret
804851d: 90 nop
804851e: 90 nop
804851f: 90 nop

jfo@lab:~/test$ readelf -a test

Relocation section ‘.rel.dyn’ at offset 0x37c contains 2 entries:
Offset Info Type Sym.Value Sym. Name
080496e0 00000106 R_386_GLOB_DAT 00000000 gmon_start
08049708 00000905 R_386_COPY 08049708 count

Relocation section ‘.rel.plt’ at offset 0x38c contains 4 entries:
Offset Info Type Sym.Value Sym. Name
080496f0 00000107 R_386_JUMP_SLOT 00000000 gmon_start
080496f4 00000307 R_386_JUMP_SLOT 00000000 say_hello
080496f8 00000407 R_386_JUMP_SLOT 00000000 __libc_start_main
080496fc 00000507 R_386_JUMP_SLOT 00000000 printf