早上阅读的第一篇牛文章,实践派,不愧是gdb开发者,gdb玩得真厉害。 asmwarrior发送自手机 On May 13, 2012 6:39 PM, "Yao Qi" <qiyaoltc@xxxxxxxxx> wrote: 我在你的文章里边,加了一些东西,我是在火车上写的,所以没有按照comments的方式加进去。 我在第五步的时候,加入了我的两个问题,第一个问题,最好回答一下,第二个问题有些超 范围,可以在这个文章里边跳过。 如果别人没有问题,就post到博客上吧。 首先,为了方便理解这些枯燥的spec,我们结合一个实际的例子,来理解这些spec。 例子很简单, #include <stdio.h> int main (void) { printf ("hellogcc\n"); return 0; } 后边我们会看到一些汇编程序和一些地址,为了搞清楚这些地址的含义,我们先列出一些段的地址范围, (gdb) maintenance info sections Exec file: `/home/yao/SourceCode/plt.exe', file type elf32-i386. 0x80481d4->0x8048224 at 0x000001d4: .dynsym ALLOC LOAD READONLY DATA HAS_CONTENTS 0x8048224->0x804826e at 0x00000224: .dynstr ALLOC LOAD READONLY DATA HAS_CONTENTS 0x8048298->0x80482a0 at 0x00000298: .rel.dyn ALLOC LOAD READONLY DATA HAS_CONTENTS 0x80482a0->0x80482b8 at 0x000002a0: .rel.plt ALLOC LOAD READONLY DATA HAS_CONTENTS 0x80482b8->0x80482e8 at 0x000002b8: .init ALLOC LOAD READONLY CODE HAS_CONTENTS 0x80482e8->0x8048328 at 0x000002e8: .plt ALLOC LOAD READONLY CODE HAS_CONTENTS 0x8048330->0x804849c at 0x00000330: .text ALLOC LOAD READONLY CODE HAS_CONTENTS 0x804849c->0x80484b8 at 0x0000049c: .fini ALLOC LOAD READONLY CODE HAS_CONTENTS 0x80484b8->0x80484c9 at 0x000004b8: .rodata ALLOC LOAD READONLY DATA HAS_CONTENTS 0x80484cc->0x80484d0 at 0x000004cc: .eh_frame ALLOC LOAD READONLY DATA HAS_CONTENTS 0x8049f0c->0x8049f14 at 0x00000f0c: .ctors ALLOC LOAD DATA HAS_CONTENTS 0x8049f14->0x8049f1c at 0x00000f14: .dtors ALLOC LOAD DATA HAS_CONTENTS 0x8049f1c->0x8049f20 at 0x00000f1c: .jcr ALLOC LOAD DATA HAS_CONTENTS 0x8049f20->0x8049ff0 at 0x00000f20: .dynamic ALLOC LOAD DATA HAS_CONTENTS 0x8049ff0->0x8049ff4 at 0x00000ff0: .got ALLOC LOAD DATA HAS_CONTENTS 0x8049ff4->0x804a00c at 0x00000ff4: .got.plt ALLOC LOAD DATA HAS_CONTENTS 0x804a00c->0x804a014 at 0x0000100c: .data ALLOC LOAD DATA HAS_CONTENTS 0x804a014->0x804a01c at 0x00001014: .bss ALLOC 有了这些,我们开始看看 SYS V ABI 怎么说的吧。 原文摘自SYSTEM V APPLICATION BINARY INTERFACE。 Figure 5-7: Position-Independent Procedure Linkage Tabl... 这个图是spec中给的,我们看看实际程序中,我们 PLT section里边的内容是什么? (gdb) disassemble 0x80482e8,0x8048328 Dump of assembler code from 0x80482e8 to 0x8048328: 0x080482e8: pushl 0x8049ff8 0x080482ee: jmp *0x8049ffc 0x080482f4: add %al,(%eax) 0x080482f6: add %al,(%eax) 0x080482f8 <__gmon_start__@plt+0>: jmp *0x804a000 0x080482fe <__gmon_start__@plt+6>: push $0x0 0x08048303 <__gmon_start__@plt+11>: jmp 0x80482e8 0x08048308 <__libc_start_main@plt+0>: jmp *0x804a004 0x0804830e <__libc_start_main@plt+6>: push $0x8 0x08048313 <__libc_start_main@plt+11>: jmp 0x80482e8 0x08048318 <puts@plt+0>: jmp *0x804a008 0x0804831e <puts@plt+6>: push $0x10 0x08048323 <puts@plt+11>: jmp 0x80482e8 我们看到了,puts的plt entry,是 plt 3,前边的0 1 和 2都已经被占用了。这些都是系统 保留的entry。不同的体系结构,这里可能占用不同的书目的entry。plt 0会在本文中介绍 到,但是 plt 1 和 2 的作用,没有在本文介绍。 Following the steps below, the dynamic linker and the program ‘‘cooperate’’ to resolve symbolic ref... 0x08048318 <puts@plt+0>: jmp *0x804a008 // -> jmp *(_GLOBAL_OFFSET_TABLE_+20) 0x0804831e <puts@plt+6>: push $0x10 // push relocation offset. 我们可以看到 0x804a008 落在的 .got.plt 的范围, 0x8049ff4->0x804a00c at 0x00000ff4: .got.plt ALLOC LOAD DATA HAS_CONTENTS (gdb) x/4x 0x804a008 0x804a008 <_GLOBAL_OFFSET_TABLE_+20>: 0x0804831e 0x00000000 0x00000000 0x00000000 第一条指令跳转到全局偏移表项中name1的地址。初始的时候,全局偏移表中存放的是pushl指令之后的地址,而不是name1的实际地址。 5 . Consequently, the program ... 因此,程序将一个重定位偏移量(offset)压入栈中 (see the insn on 0x0804831e: push 0x10)。重定位偏移量为一个32位,非负的,重定位表的字节偏移。其所指定的重定位项将具有R_386_JMP_SLOT类型,并且它的偏移量指定了在之前jmp指令中会用到的全局偏移表项。 Relocation section '.rel.plt' at offset 0x2a0 contains 3 entries: Offset Info Type Sym.Value Sym. Name 0804a000 00000107 R_386_JUMP_SLOT 00000000 __gmon_start__ 0804a004 00000207 R_386_JUMP_SLOT 00000000 __libc_start_main 0804a008 00000307 R_386_JUMP_SLOT 00000000 puts 我们可以看到,这里有一个reloc R_386_JUMP_SLOT,对应的地址是0x804a008,其实就是 puts对应的 .got.plt 的entry。 重定位项还包含了一个符号表索引,因此告诉了动态链接器哪个符号在被引用。在该例子中,为name1. 这里的offset (就是 0x10)还是不是很清楚,它什么是 puts在.rel.plt 段里边的那个记录的偏移吗? .rel.plt 段的大小是 0x16,里边包含三个一样类型的记录,那么第三个应该就是offset 0x10。 0x80482a0->0x80482b8 at 0x000002a0: .rel.plt ALLOC LOAD READONLY DATA HAS_CONTENTS 这些是我的猜测。 第二个问题,dynamic linker对R_386_JUMP_SLOT是怎么处理的,都干了些什么? 6 . After pushing the relocation offset, the program then jumps to .PLT0, the first entry in the pr... 0x08048323 <puts@plt+11>: jmp 0x80482e8 // jump to start of .plt section. .PLT0: 0x080482e8: pushl 0x8049ff8 0x080482ee: jmp *0x8049ffc pushl指令将全局偏移表的第二个表项(got_plus_4 or 4(%ebx))压入栈中,因此给了动态链接器一个字的标识信息。 ^^^^^^^^^^ it should be the 2nd entry of .got.plt, 0x8049ff4->0x804a00c at 0x00000ff4: .got.plt ALLOC LOAD DATA HAS_CONTENTS 程序然后跳转到全局偏移表的第三个表项中(got_plus_8 or 8(%ebx))的地址,其将控制转换给动态链接器。 ^^^^^^^^^ .got.plt, isn't? (gdb) x/x 0x8049ffc 0x8049ffc <_GLOBAL_OFFSET_TABLE_+8>: 0x00123270 (gdb) disassemble 0x00123270,0x00123280 Dump of assembler code from 0x123270 to 0x123280: 0x00123270 <_dl_runtime_resolve+0>: push %eax 0x00123271 <_dl_runtime_resolve+1>: push %ecx 0x00123272 <_dl_runtime_resolve+2>: push %edx 0x00123273 <_dl_runtime_resolve+3>: mov 0x10(%esp),%edx 0x00123277 <_dl_runtime_resolve+7>: mov 0xc(%esp),%eax 0x0012327b <_dl_runtime_resolve+11>: call 0x11d5a0 <_dl_fixup> As we can see, `jmp *0x8049ffc' jumps to _dl_runtime_resolve, the entry of dynamic linker, 7 . When the dynamic linker receives control, it unwinds the stack, looks at the designated relocat... dynamic resolver will modify the content on address 0x804a008, 0x804a008 <_GLOBAL_OFFSET_TABLE_+20>: 0x0804831e 让我们看看dynamic linker如何修改这个,我们在0x804a008上设置一个硬件watchpoint (gdb) watch *0x804a008 Hardware watchpoint 2: *0x804a008 (gdb) c Continuing. Hardware watchpoint 2: *0x804a008 Old value = 134513438 New value = 1616016 _dl_fixup (l=<value optimized out>, reloc_arg=<value optimized out>) at dl-runtime.c:155 155 dl-runtime.c: No such file or directory. in dl-runtime.c 我们可以看到,地址0x804a008上的内容,从134513438 变化到了1616016, (gdb) p/x 134513438 $2 = 0x804831e (gdb) p/x 1616016 $3 = 0x18a890 我们看看 这个新地址 (1616016 0x18a890) 是什么 (gdb) disassemble 0x18a890,0x18a8a0 Dump of assembler code from 0x18a890 to 0x18a8a0: 0x0018a890 <_IO_puts+0>: push %ebp 0x0018a891 <_IO_puts+1>: mov %esp,%ebp 0x0018a893 <_IO_puts+3>: sub $0x20,%esp 0x0018a896 <_IO_puts+6>: mov %ebx,-0xc(%ebp) 0x0018a899 <_IO_puts+9>: mov 0x8(%ebp),%eax 0x0018a89c <_IO_puts+12>: call 0x143a0f <__i686.get_pc_thunk.bx> Yay!, 我们能看到地址0x804a008上的内容已经变化成为了实际的glibc中的地址了。 (gdb) bt #0 _dl_fixup (l=<value optimized out>, reloc_arg=<value optimized out>) at dl-runtime.c:155 #1 0x00123280 in _dl_runtime_resolve () at ../sysdeps/i386/dl-trampoline.S:37 #2 0x080483f9 in main () at plt.c:6 8 . Subsequent executions of the procedure linkage table entry will transfer directly to name1, wit... On 05/09/2012 12:30 PM, Mingjie Xing wrote: > 原文摘自SYSTEM V APPLICATION BINARY INTERFACE。 > > Figure...