elf之relocatable文件学习总结

elf有三种文件类型:可执行文件,共享目标文件(xx.so),可重定位文件(xx.o/xx.ko),本篇分析可重定位文件类型,也就是relocatable file.

代码:

ubuntu@ubuntu:~/work/hello$cat hello.c

 

#include

 

intfunc(void)

{

        return 0;

}

 

staticint local_func(void)

{

        return 0;

}

 

 

intglobal_func(void)

{

        return 0;

}

 

intmain(void)

{

        local_func();

        global_func();

        printf("hello world\n");

        return 0;

}

编译:

ubuntu@ubuntu:~/work/hello$gcc -c hello.c -o hello.o

ubuntu@ubuntu:~/work/hello$file hello.o

hello.o:ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped

 

反汇编:

objdump-S hello.o:

0000001e

:

  1e:  55                      push   %ebp

  1f:  89 e5                   mov    %esp,%ebp

  21:  83 e4 f0                and   $0xfffffff0,%esp

  24:  83 ec 10                sub    $0x10,%esp

  27:   e8 de ff ff ff          call  a

  2c:   e8 fc ff ff ff          call  2d

  31:  c7 04 24 00 00 00 00    movl   $0x0,(%esp)

  38:   e8 fc ff ff ff          call  39

  3d:  b8 00 00 00 00          mov    $0x0,%eax

  42:  c9                      leave

  43:  c3                      ret

 

上面的2d 39  都是当前指令的下一个指令地址,此时都是无意义的地址,需要在链接时重定位(relocatable),那么链接时是如何重定位的呢

 

查看section:

ubuntu@ubuntu:~/work/hello$readelf -S hello.o

Thereare 13 section headers, starting at offset 0x1a8:

 

SectionHeaders:

  [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 000044 00  AX 0   0  4

  [ 2] .rel.text         REL             00000000 0004c0 000018 08     11  1  4

  [ 3] .data             PROGBITS        00000000 000078 000000 00  WA 0   0  4

  [ 4] .bss              NOBITS          00000000 000078 000000 00  WA 0   0  4

  [ 5] .rodata           PROGBITS        00000000 000078 00000c 00   A 0   0  1

  [ 6] .comment          PROGBITS        00000000 000084 00002b 01  MS 0   0  1

  [ 7] .note.GNU-stack   PROGBITS        00000000 0000af 000000 00      0  0  1

  [ 8] .eh_frame         PROGBITS        00000000 0000b0 000098 00   A 0   0  4

  [ 9] .rel.eh_frame     REL             00000000 0004d8 000020 08     11  8  4

  [10] .shstrtab         STRTAB          00000000 000148 00005f 00      0  0  1

  [11] .symtab           SYMTAB          00000000 0003b0 0000e0 10     12 10  4

  [12] .strtab           STRTAB          00000000 000490 00002f 00      0  0  1

 

我们需要关注第2,11,12这3个section

首先是重定位section:

ubuntu@ubuntu:~/work/hello$readelf -x 2  hello.o

 

Hex dumpof section '.rel.text':

  0x00000000 2d000000 020b000034000000 01060000 -.......4.......

  0x00000010 39000000 020d0000                   9.......

这里可以发现上面所说的3个需要重定位的3个地址中的两个:2d  39都在这里(注意X86是小端),为什么a不在这里,因为它是local_func的跳转,local_func是用static定义的本地函数,a就是local_func的代码地址,它不需要重定位,因为它只在本地使用:

0000000a:

   a:  55                      push   %ebp

   b:  89 e5                   mov    %esp,%ebp

   d:  b8 00 00 00 00          mov    $0x0,%eax

  12:  5d                      pop    %ebp

  13:  c3  

 

还回到刚才的.rel.text段,2d000000 020b0000和39000000 020d0000分别是个组合,对应elf的Elf32_Rel结构体:

typedefstruct

{

  Elf32_Addr r_offset;         /* Address */

  Elf32_Word r_info;                   /* Relocation type and symbolindex */

}Elf32_Rel;

#defineELF32_R_TYPE(val)         ((val) &0xff)

r_info右移8位,就是一个索引值(再次强调注意这是小端):

2d000000 020b0000: 0b

39000000 020d0000:   0d

这个索引是用来索引另外一个段:.symtab

ubuntu@ubuntu:~/work/hello$readelf -x 11  hello.o

 

Hex dumpof section '.symtab':

  0x00000000 00000000 00000000 00000000 00000000................

  0x00000010 01000000 00000000 000000000400f1ff ................

  0x00000020 00000000 00000000 0000000003000100 ................

  0x00000030 00000000 00000000 0000000003000300 ................

  0x00000040 00000000 00000000 0000000003000400 ................

  0x00000050 09000000 0a000000 0a00000002000100 ................local_func

  0x00000060 00000000 00000000 0000000003000500 ................

  0x00000070 00000000 00000000 0000000003000700 ................

  0x00000080 00000000 00000000 0000000003000800 ................

  0x00000090 00000000 00000000 0000000003000600 ................

  0x000000a0 14000000 00000000 0a00000012000100 ................

  0x000000b0 1900000014000000 0a000000 12000100 ................ global_func

  0x000000c0 25000000 1e000000 2600000012000100 %.......&.......

  0x000000d0 2a00000000000000 00000000 10000000 *............... printf(put)

 

简单的说就是第b行和第d行。

这又是一个结构体:

typedefstruct

{

  Elf32_Word st_name;       /* Symbol name (string tbl index) */

  Elf32_Addr st_value;        /* Symbol value */

  Elf32_Word st_size;          /* Symbol size */

  unsigned char   st_info;          /* Symbol type and binding */

  unsigned char   st_other;        /* Symbol visibility */

  Elf32_Section    st_shndx;       /* Section index */

}Elf32_Sym;

以global_func举例,其中 st_value 对应代码段中的位置:

00000014:

  14:  55                      push   %ebp

  15:  89 e5                   mov    %esp,%ebp

  17:  b8 00 00 00 00          mov    $0x0,%eax

  1c:  5d                      pop    %ebp

  1d:  c3       

st_size为代码长度,这不用多说。

st_info    是BIND类型,表明它是全局符号还是本地符号,还有其他类型我也搞不清楚

st_other表明符号类型,2表明是函数

st_shndx表明它属于哪个段

st_name 又是一个新section .strtab 的索引:

ubuntu@ubuntu:~/work/hello$readelf -x 12 hello.o

 

Hex dumpof section '.strtab':

  0x00000000 0068656c 6c6f2e63 006c6f63616c5f66 .hello.c.local_f

  0x00000010 756e6300 66756e63 00676c6f62616c5f unc.func.global_

  0x00000020 66756e63 006d6169 6e007075 747300   func.main.puts.

这里索引了函数的符号。

 

这样global_func的函数代码起点,名字,类型等等就和最开始的引用点绑定起来了。在链接时,会将这些section和其他.o的section合并到一起,因此里面的值也会变化(加偏移),再通过这个绑定关系,修改掉引用处的值

 

 

另外个printf的    st_value为0,这是因为相对于hello.o来说它是个未定义符号,无法绑定其代码地址,不过链接时会为其设置一个plt地址,这又是elf另外两种格式的知识了(以前的相关总结在前公司内网里取不出来!)

 

可能有些写得不对,以后提高后再修改。

 

这里的st_value和st_shndx 最为关键,如果对其进行篡改,可以原本的符号篡改到elf的任意地址,详细见这个链接:

http://phrack.org/issues/68/11.html#article

:)

你可能感兴趣的:(elf之relocatable文件学习总结)