本文学习386架构的可重定向目标文件中的重定向条目,以及符号解析。x86重定向类型有2种:
R_386_PC32:指令使用PC相对地址访问对象,所以链接器应该计算PC相对地址,填充到重定向处。
R_386_32:指令使用绝对地址访问对象,所以链接器应该计算绝对地址,填充到重定向处。
首先有一个源文件a.c,它访问外部定义的变量foo,和外部定义的函数bar()。
1 2 3 4 5 6 7 8 | herbert@herbert-pc:/work/code/link/sample2$ cat a.c extern int foo; int bar(); int function( void ) { return foo + bar(); } |
编译:
1 | herbert@herbert-pc: /work/code/link/sample2 $ cc -m32 -c a.c |
查看elf信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | herbert@herbert-pc:/work/code/link/sample2$ readelf -a a.o 。。。 Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 00000000 000034 000016 00 AX 0 0 1 [ 2] .rel.text REL 00000000 000180 000010 08 I 10 1 4 [ 3] .data PROGBITS 00000000 00004a 000000 00 WA 0 0 1 [ 4] .bss NOBITS 00000000 00004a 000000 00 WA 0 0 1 [ 5] .comment PROGBITS 00000000 00004a 000035 01 MS 0 0 1 [ 6] .note.GNU-stack PROGBITS 00000000 00007f 000000 00 0 0 1 [ 7] .eh_frame PROGBITS 00000000 000080 000038 00 A 0 0 4 [ 8] .rel.eh_frame REL 00000000 000190 000008 08 I 10 7 4 [ 9] .shstrtab STRTAB 00000000 000198 000057 00 0 0 1 [10] .symtab SYMTAB 00000000 0000b8 0000b0 10 11 8 4 [11] .strtab STRTAB 00000000 000168 000016 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific) Relocation section '.rel.text' at offset 0x180 contains 2 entries: Offset Info Type Sym.Value Sym. Name 00000007 00000902 R_386_PC32 00000000 bar 0000000e 00000a01 R_386_32 00000000 foo Relocation section '.rel.eh_frame' at offset 0x190 contains 1 entries: Offset Info Type Sym.Value Sym. Name 00000020 00000202 R_386_PC32 00000000 .text Symbol table '.symtab' contains 11 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 00000000 0 FILE LOCAL DEFAULT ABS a.c 2: 00000000 0 SECTION LOCAL DEFAULT 1 3: 00000000 0 SECTION LOCAL DEFAULT 3 4: 00000000 0 SECTION LOCAL DEFAULT 4 5: 00000000 0 SECTION LOCAL DEFAULT 6 6: 00000000 0 SECTION LOCAL DEFAULT 7 7: 00000000 0 SECTION LOCAL DEFAULT 5 8: 00000000 22 FUNC GLOBAL DEFAULT 1 function 9: 00000000 0 NOTYPE GLOBAL DEFAULT UND bar 10: 00000000 0 NOTYPE GLOBAL DEFAULT UND foo |
可以看到有一个重定向section .rel.text,这是代码段的重定向条目。
添加一个start.c,它定义了变量foo和函数bar(),并且包含入口_start,调用function()。
1 2 3 4 5 6 7 8 9 10 11 12 13 | herbert@herbert-pc:/work/code/link/sample2$ cat start.c int foo = 2; int bar() { return 3; } int function(); int _start() { return function(); } |
编译,并链接:
1 2 | herbert@herbert-pc:/work/code/link/sample2$ cc -m32 -c start.c herbert@herbert-pc:/work/code/link/sample2$ ld -o a.out a.o start.o -melf_i386 |
注意,编译出的a.out并不能很好的运行,因为它没有退出逻辑,程序会跑飞,这里仅仅是为了演示重定向与符号解析。
a.out的elf信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | herbert@herbert-pc:/work/code/link/sample2$ readelf -a a.out 。。。 Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 08048094 000094 00002d 00 AX 0 0 1 [ 2] .eh_frame PROGBITS 080480c4 0000c4 000078 00 A 0 0 4 [ 3] .data PROGBITS 0804913c 00013c 000004 00 WA 0 0 4 [ 4] .comment PROGBITS 00000000 000140 000034 01 MS 0 0 1 [ 5] .shstrtab STRTAB 00000000 00028a 00003a 00 0 0 1 [ 6] .symtab SYMTAB 00000000 000174 0000e0 10 7 7 4 [ 7] .strtab STRTAB 00000000 000254 000036 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific) There are no section groups in this file. Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x08048000 0x08048000 0x0013c 0x0013c R E 0x1000 LOAD 0x00013c 0x0804913c 0x0804913c 0x00004 0x00004 RW 0x1000 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10 Section to Segment mapping: Segment Sections... 00 .text .eh_frame 01 .data 02 There is no dynamic section in this file. There are no relocations in this file. The decoding of unwind sections for machine type Intel 80386 is not currently supported. Symbol table '.symtab' contains 14 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: 08048094 0 SECTION LOCAL DEFAULT 1 2: 080480c4 0 SECTION LOCAL DEFAULT 2 3: 0804913c 0 SECTION LOCAL DEFAULT 3 4: 00000000 0 SECTION LOCAL DEFAULT 4 5: 00000000 0 FILE LOCAL DEFAULT ABS a.c 6: 00000000 0 FILE LOCAL DEFAULT ABS start.c 7: 08048094 22 FUNC GLOBAL DEFAULT 1 function 8: 080480b4 13 FUNC GLOBAL DEFAULT 1 _start 9: 08049140 0 NOTYPE GLOBAL DEFAULT 3 __bss_start 10: 0804913c 4 OBJECT GLOBAL DEFAULT 3 foo 11: 08049140 0 NOTYPE GLOBAL DEFAULT 3 _edata 12: 08049140 0 NOTYPE GLOBAL DEFAULT 3 _end 13: 080480aa 10 FUNC GLOBAL DEFAULT 1 bar |
显然,没有重定向信息了,所有的符号已经解析了。
a.o的反汇编信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | herbert@herbert-pc:/work/code/link/sample2$ objdump -d a.o a.o: file format elf32-i386 Disassembly of section .text: 00000000 <function>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 08 sub $0x8,%esp 6: e8 fc ff ff ff call 7 <function+0x7> b: 89 c2 mov %eax,%edx d: a1 00 00 00 00 mov 0x0,%eax 12: 01 d0 add %edx,%eax 14: c9 leave 15: c3 ret |
a.out的反汇编信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | herbert@herbert-pc:/work/code/link/sample2$ objdump -d a.out a.out: file format elf32-i386 Disassembly of section .text: 08048094 <function>: 8048094: 55 push %ebp 8048095: 89 e5 mov %esp,%ebp 8048097: 83 ec 08 sub $0x8,%esp 804809a: e8 0b 00 00 00 call 80480aa <bar> 804809f: 89 c2 mov %eax,%edx 80480a1: a1 3c 91 04 08 mov 0x804913c,%eax 80480a6: 01 d0 add %edx,%eax 80480a8: c9 leave 80480a9: c3 ret 080480aa <bar>: 80480aa: 55 push %ebp 80480ab: 89 e5 mov %esp,%ebp 80480ad: b8 03 00 00 00 mov $0x3,%eax 80480b2: 5d pop %ebp 80480b3: c3 ret 080480b4 <_start>: 80480b4: 55 push %ebp 80480b5: 89 e5 mov %esp,%ebp 80480b7: 83 ec 08 sub $0x8,%esp 80480ba: e8 d5 ff ff ff call 8048094 <function> 80480bf: c9 leave 80480c0: c3 ret |
在a.o的反汇编中,调用bar()的指令为:
1 | 6: e8 fc ff ff ff call 7 <function+0x7> |
e8指令是相对PC函数调用指令[5],后面跟着4个字节的相对地址。所以在a.o的重定向条目:
1 | 00000007 00000902 R_386_PC32 00000000 bar |
类型是R_386_PC32,因为call指令的偏移是6,所以重定向要修改的内容的偏移是6 + 1 = 7。
在a.out的反汇编中,偏移值是0xb = 0x80480aa-0x804809f。0x80480aa是bar()函数的地址,0x804809f是call下一条指令的地址,最后的汇编指令为:
1 | 804809a: e8 0b 00 00 00 call 80480aa <bar> |
a.o中的重定向条目为:
1 | 0000000e 00000a01 R_386_32 00000000 foo |
其偏移是0xe,对应的指令为:
1 | d: a1 00 00 00 00 mov 0x0,%eax |
a.out中,foo符号的绝对地址为:
1 | 10: 0804913c 4 OBJECT GLOBAL DEFAULT 3 foo |
所以最后的汇编指令为:
1 | 80480a1: a1 3c 91 04 08 mov 0x804913c,%eax |
【1】PLT and GOT - the key to code sharing and dynamic libraries.
https://www.technovelty.org/linux/plt-and-got-the-key-to-code-sharing-and-dynamic-libraries.html
【2】https://www.packtpub.com/books/content/understanding-elf-specimen
【3】Resolving Symbols.
http://web.cse.ohio-state.edu/~reeves.92/CSE2421au12/SlidesDay52.pdf
【4】Study of ELF loading and relocs.
http://netwinder.osuosl.org/users/p/patb/public_html/elf_relocs.html
【5】x86 Instruction Set Reference CALL.
http://x86.renejeschke.de/html/file_module_x86_id_26.html