Chinaunix首页 | 论坛 | 博客
  • 博客访问: 363128
  • 博文数量: 64
  • 博客积分: 2975
  • 博客等级: 少校
  • 技术积分: 831
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-14 10:59
文章存档

2014年(2)

2012年(7)

2010年(40)

2009年(5)

2008年(8)

2007年(2)

分类: LINUX

2010-05-04 17:39:06

ld.so分析4 PIC,GOT和PLT

1.PIC

PIC就是Position Independent Code(位置无关代码).那么何谓位置无关代码?

如果代码不需要被重定位,那么这种代码就是位置无关的。

我们要区分位置无关代码和可重入代码(Reentry Code)的不同,两者是无关的概念,不能混淆。

例如
int f()
{
return 1;
}

[zws@mail ~]$gcc -S x.c
[zws@mail ~]$cat x.s
        .file   "x.c"
        .text
.globl f
        .type   f,@function
f:
        pushl   %ebp
        movl    %esp, %ebp
        movl    $1, %eax
        leave
        ret
.Lfe1:
        .size   f,.Lfe1-f
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"

这是PIC也是RC

char * f()
{
return "a";
}

[zws@mail ~]$gcc -S x.c
[zws@mail ~]$cat x.s
        .file   "x.c"
        .section        .rodata
.LC0:
        .string "a"
        .text
.globl f
        .type   f,@function
f:
        pushl   %ebp
        movl    %esp, %ebp
        movl    $.LC0, %eax
        leave
        ret
.Lfe1:
        .size   f,.Lfe1-f
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"

这不是PIC但是是RC

int f()
{
static int a=0;
a++;
return a;
}
[zws@mail ~]$gcc -S x.c
[zws@mail ~]$cat x.s
        .file   "x.c"
        .data
        .align 4
        .type   a.0,@object
        .size   a.0,4
a.0:
        .long   0
        .text
.globl f
        .type   f,@function
f:
        pushl   %ebp
        movl    %esp, %ebp
        incl    a.0
        movl    a.0, %eax
        leave
        ret
.Lfe1:
        .size   f,.Lfe1-f
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"

两者都不是

将上面的汇编语言改成PIC

[zws@mail ~]$cat x.s
        .file   "x.c"
        .data
        .align 4
        .type   a.0,@object
        .size   a.0,4
a.0:
        .long   0
        .text
.globl f
        .type   f,@function
f:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ebx
        call    .L2
.L2:
        popl    %ebx// %ebx中为当前指令的地址
        subl    $.L2-a.0, %ebx//该指令地址-相对a.0的偏移,即为a.0的地址,在%ebx中
        incl    (%ebx)
        movl    (%ebx), %eax
        popl    %ebx
        leave
        ret
.Lfe1:
        .size   f,.Lfe1-f
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"

现在这个代码是PIC而不是RC

综上,PIC代码中不能引用绝对地址,否则需要重定位.(上面的 subl    .L2-a.0, %ebx不算引用,因为gas会计算偏移,最终形成的指令中存放的是数)
RC代码不能使用共享变量,否则需要锁。

位置无关代码有什么优点?多个可执行程序运行这样的代码(例如动态链接库)时,虽然加载地址可能不一样,但是该代码不需要重定位,也就是不

需要修改代码,那么多个可执行程序就能共享一个代码副本,从而节省内存。缺点是占用一个寄存器计算地址,代码长度增加一点,执行时间增加

一点。

2.GOT

GOT是GLOBAL OFFSET TABLE(全局偏移表).

我们称一个可执行文件或动态链接库为一个模块.

一个模块中的数据或函数只允许被自己访问,称为本地局部数据或本地局部函数.例如static 类型的变量或函数就是这种类型.
一个模块中的数据或函数不但允许被自己访问,也允许外部访问,称为本地全局数据或本地全局函数.例如没有static修饰的变量或函数就是这种类

型.

相应地一个模块引用另一个模块中的数据或函数,则称为外部全局数据或外部全局函数.例如使用extern修饰的类型的变量或函数就是这种类型
.

局部肯定是本地的,外部一定是全局的。

GOT有四种功能:
>>为本地访问本地局部数据(静态变量或常量)访问提供PIC支持。
>>为本地访问本地全局数据访问提供PIC支持(配合.got节)
>>为本地访问外地全局数据访问提供PIC支持(配合.got节)
>>为本地访问本地全局函数访问提供PIC支持(配合.plt节和.got.plt节)
>>为本地访问外地全局函数调用提供PIC支持(配合.plt节和.got.plt节)
>>为动态链接提供支持(配合.rel.dyn节,rel.plt节,.got节,.got.plt节)

由于函数调用使用的都是相对寻址,且本地局部函数地址已知,因此本地访问本地局部函数调用不需要GOT支持.

(1)为本地访问本地局部数据(静态变量或常量)访问提供PIC支持。
[zws@mail ~]$cat x.c
static int a=0;

int f()
{
a++;
return a;
}


[zws@mail ~]$gcc -fPIC -S x.c
[zws@mail ~]$cat x.s
        .file   "x.c"
        .data
        .align 4
        .type   a,@object
        .size   a,4
a:
        .long   0
        .text
.globl f
        .type   f,@function
f:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ebx
        call    .L2
.L2:
        popl    %ebx
        addl    $_GLOBAL_OFFSET_TABLE_+[.-.L2], %ebx
        incl    a@GOTOFF(%ebx)
        movl    a@GOTOFF(%ebx), %eax
        popl    %ebx
        leave
        ret
.Lfe1:
        .size   f,.Lfe1-f
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"

[zws@mail ~]$readelf -a x.o
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          196 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         9
  Section header string table index: 6

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 00001f 00  AX  0   0  4
  [ 2] .rel.text         REL             00000000 0002dc 000018 08      7   1  4
  [ 3] .data             PROGBITS        00000000 000054 000004 00  WA  0   0  4
  [ 4] .bss              NOBITS          00000000 000058 000000 00  WA  0   0  4
  [ 5] .comment          PROGBITS        00000000 000058 000033 00      0   0  1
  [ 6] .shstrtab         STRTAB          00000000 00008b 000039 00      0   0  1
  [ 7] .symtab           SYMTAB          00000000 00022c 000090 10      8   7  4
  [ 8] .strtab           STRTAB          00000000 0002bc 00001f 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

There are no program headers in this file.

Relocation section '.rel.text' at offset 0x2dc contains 3 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0000000c  0000080a R_386_GOTPC       00000000   _GLOBAL_OFFSET_TABLE_
00000012  00000309 R_386_GOTOFF      00000000   .data
00000018  00000309 R_386_GOTOFF      00000000   .data

There are no unwind sections in this file.

Symbol table '.symtab' contains 9 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000000     0 FILE    LOCAL  DEFAULT  ABS x.c
     2: 00000000     0 SECTION LOCAL  DEFAULT    1
     3: 00000000     0 SECTION LOCAL  DEFAULT    3
     4: 00000000     0 SECTION LOCAL  DEFAULT    4
     5: 00000000     4 OBJECT  LOCAL  DEFAULT    3 a
     6: 00000000     0 SECTION LOCAL  DEFAULT    5
     7: 00000000    31 FUNC    GLOBAL DEFAULT    1 f
     8: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_

No version information found in this file.

[zws@mail ~]$ objdump -d x.o

x.o:     file format elf32-i386

Disassembly of section .text:

00000000 :
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   53                      push   %ebx
   4:   e8 00 00 00 00          call   9
   9:   5b                      pop    %ebx
   a:   81 c3 03 00 00 00       add    $0x3,%ebx
  10:   ff 83 00 00 00 00       incl   0x0(%ebx)
  16:   8b 83 00 00 00 00       mov    0x0(%ebx),%eax
  1c:   5b                      pop    %ebx
  1d:   c9                      leave  
  1e:   c3                      ret  
分析

call .L2,将下一条指令地址压栈
popl %ebx,将本指令地址弹出到%ebx寄存器
addl    $_GLOBAL_OFFSET_TABLE_+[.-.L2], %ebx
$说明这个操作数是立即数,_GLOBAL_OFFSET_TABLE_,特殊符号,gas能够识别,并为改该操作数生成R_386_GOTPC重定位类型.例如上面的
0000000c  0000080a R_386_GOTPC       00000000   _GLOBAL_OFFSET_TABLE_
地址0000000c指向指令   a:   81 c3 03 00 00 00       add    $0x3,%ebx的源操作数部分[03 00 00 00]
ld链接时,检查重定位表,发现包含R_386_GOTPC重定位项,创建.got和.got.plt节,.got节存放全局数据地址,.got.plt存放全局函数地址,GOT地

址是.got.plt的地址(ld也可以合并这两个节成一个.got节),并计算地址GOT和地址0000000c的差值,加入0000000c处的值并写入,这就是R_386_GO

TPC重定位的内容。

上面的[.-.L2]意思是计算当前指令地址和.L2地址之差,即popl %ebx指令长度,应该是1.但是为何最终的指令却是add $0x3,%ebx呢?操作数3是如

何计算出来的呢?

这是因为重定位R_386_GOTPC项时计算的是该操作数与GOT的差值,而不是该条指令与GOT的差值.因此需要计算该操作数的偏移,即指令
   a:   81 c3 03 00 00 00       add    $0x3,%ebx
地址00000009加上81 c3这两字节操作码长度,形成最终地址0000000c.
相应的%ebx存放的应该是该操作数的加载地址,即(popl %ebx指令地址)+(popl %ebx指令长度)+(add    

$0x3,%ebx指令操作码长度)=%ebx+1+2=add $0x3,%ebx

然而指令        
addl    $_GLOBAL_OFFSET_TABLE_+[.-.L2], %ebx
并没有明确指出再加上addl的操作码长度,其实这是gas替我们隐含计算了.gas分析该指令的操作数,碰到是立即数,且含有符号_GLOBAL_OFFS

ET_TABLE_,会在形成最终的操作时,自动加上操作码长度,得到我们想要的结果。

a++生成的指令是 incl    a@GOTOFF(%ebx)生成的机器指令是
  10:   ff 83 00 00 00 00       incl   0x0(%ebx)
重定位项是
00000012  00000309 R_386_GOTOFF      00000000   .data
在连接时重定位类型R_386_GOTOFF执行的操作时计算计算该符号与GOT的偏移,并加入重定位处(GOTOFF即GOT OFFSET).
可见a@GOTOFF会指示gas生成R_386_GOTOFF重定位项,比较适合只被自己使用的变量。

在符号表中
     5: 00000000     4 OBJECT  LOCAL  DEFAULT    3 a
a的bind类型是local.

(2)为本地访问本地全局数据访问提供PIC支持(配合.got节)
[zws@mail ~]$cat x.c
int a=0;

int f()
{
a++;
return a;
}
[zws@mail ~]$cat x.s
        .file   "x.c"
.globl a
        .data
        .align 4
        .type   a,@object
        .size   a,4
a:
        .long   0
        .text
.globl f
        .type   f,@function
f:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ebx
        call    .L2
.L2:
        popl    %ebx
        addl    $_GLOBAL_OFFSET_TABLE_+[.-.L2], %ebx
        movl    a@GOT(%ebx), %eax
        incl    (%eax)
        movl    a@GOT(%ebx), %eax
        movl    (%eax), %eax
        popl    %ebx
        leave
        ret
.Lfe1:
        .size   f,.Lfe1-f
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
[zws@mail ~]$readelf -a x.o
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          200 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         9
  Section header string table index: 6

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 000023 00  AX  0   0  4
  [ 2] .rel.text         REL             00000000 0002e0 000018 08      7   1  4
  [ 3] .data             PROGBITS        00000000 000058 000004 00  WA  0   0  4
  [ 4] .bss              NOBITS          00000000 00005c 000000 00  WA  0   0  4
  [ 5] .comment          PROGBITS        00000000 00005c 000033 00      0   0  1
  [ 6] .shstrtab         STRTAB          00000000 00008f 000039 00      0   0  1
  [ 7] .symtab           SYMTAB          00000000 000230 000090 10      8   6  4
  [ 8] .strtab           STRTAB          00000000 0002c0 00001f 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

There are no section groups in this file.

There are no program headers in this file.

Relocation section '.rel.text' at offset 0x2e0 contains 3 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0000000c  0000080a R_386_GOTPC       00000000   _GLOBAL_OFFSET_TABLE_
00000012  00000603 R_386_GOT32       00000000   a
0000001a  00000603 R_386_GOT32       00000000   a

There are no unwind sections in this file.

Symbol table '.symtab' contains 9 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000000     0 FILE    LOCAL  DEFAULT  ABS x.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    5
     6: 00000000     4 OBJECT  GLOBAL DEFAULT    3 a
     7: 00000000    35 FUNC    GLOBAL DEFAULT    1 f
     8: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_

No version information found in this file.
[zws@mail ~]$

和前面的唯一差别就是对变量的访问方式.由a@GOTOFF变成a@GOT,重定位方式也从R_386_GOTOFF变成R_386_GOT32.
a@GOT的访问方式是,将变量a的地址值存入.got节,访问a时,先根据GOT计算存放变量a的地址值在.got中的地址,然后取该地址值,即为变量a的

地址,用一条指令就能实现
        movl    a@GOT(%ebx), %eax
然后就可以对该变量执行操作了
例如a++生成的指令时        incl    (%eax),对该地址处的值增一。

在符号表中
     6: 00000000     4 OBJECT  GLOBAL DEFAULT    3 a
a的bind类型是GLOBAL.

再写一个y.c
[zws@mail ~]$cat y.c
void f();

int main()
{
f();
return 0;
}
[zws@mail ~]$gcc y.c x.o

分析生成的可执行文件a.out可发现

ld在处理R_386_GOT32时,将该符号的地址x存入.got节,并记录其在.got

节中的地址y,然后计算y相对于GOT偏移,存入该符号所有的R_386_GOT32类型重定位地址处。最后在目标文件中为该符号生成R_386_GLOB_D

AT类型重定位项例如
readelf -a a.out

  [20] .got              PROGBITS        080494e0 0004e0 000008 04  WA  0   0  4
  [21] .got.plt          PROGBITS        080494e8 0004e8 000010 04  WA  0   0  4

080494e4  00000406 R_386_GLOB_DAT    08049504   a(显然地址080494e4在.got中)

R_386_GLOB_DAT类型执行的操作是,将模块加载地址加入该重定位处.这样变量的地址就确定了,可以功过y来访问,而且不需要对代码重定位。

如果该变量被其他模块访问(例如动态链接库中的变量被可执行文件访问或动态链接库中的变量被其他动态链接库库访问),则执行动态链接时,只

需要将该变量所在的地址x存入引用模块的.got节y处,就能实现共享且PIC.

其实本地访问本地全局数据访问也可以使用GOTOFF方式(例如本例的x.c).想一想为什么不这样做?从指导ld的方面去想。

(3)为本地访问外部全局数据访问提供PIC支持(配合.got节)

[zws@mail ~]$cat x.c
extern int a;

int f()
{
a++;
return a;
}
[zws@mail ~]$gcc -fPIC -S x.c
[zws@mail ~]$cat x.s
        .file   "x.c"
        .text
.globl f
        .type   f,@function
f:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ebx
        call    .L2
.L2:
        popl    %ebx
        addl    $_GLOBAL_OFFSET_TABLE_+[.-.L2], %ebx
        movl    a@GOT(%ebx), %eax
        incl    (%eax)
        movl    a@GOT(%ebx), %eax
        movl    (%eax), %eax
        popl    %ebx
        leave
        ret
.Lfe1:
        .size   f,.Lfe1-f
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"

[zws@mail ~]$gcc -shared x.s -o libx.so

readelf 查看一下是否和上面分析的一致

(4)为本地访问本地全局函数调用提供PIC支持(配合.plt节和.got.plt节)
[zws@mail ~]$cat x.c
void f()
{
}

void g()
{
f();
}
[zws@mail ~]$gcc -fPIC -S x.c
[zws@mail ~]$cat x.s
        .file   "x.c"
        .text
.globl f
        .type   f,@function
f:
        pushl   %ebp
        movl    %esp, %ebp
        leave
        ret
.Lfe1:
        .size   f,.Lfe1-f
.globl g
        .type   g,@function
g:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ebx
        subl    $4, %esp
        call    .L3
.L3:
        popl    %ebx
        addl    $_GLOBAL_OFFSET_TABLE_+[.-.L3], %ebx
        call    f@PLT
        addl    $4, %esp
        popl    %ebx
        leave
        ret
.Lfe2:
        .size   g,.Lfe2-g
        .ident  "GCC: (GNU) 3.2.2 20030222 (Red Hat Linux 3.2.2-5)"
[zws@mail ~]$as x.s -o x.o
[zws@mail ~]$readelf -r x.o

Relocation section '.rel.text' at offset 0x2dc contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000014  0000080a R_386_GOTPC       00000000   _GLOBAL_OFFSET_TABLE_
00000019  00000604 R_386_PLT32       00000000   f

本地调用本地全局函数生成的代码是        call    f@PLT
gas为call f@PLT生成的重定位项是R_386_PLT32       ,指导ld生成.plt节。
[zws@mail ~]$gcc -shared x.o -o libx.so
[zws@mail ~]$readelf -r libx.so

Relocation section '.rel.dyn' at offset 0x22c contains 5 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00001500  00000008 R_386_RELATIVE   
00001504  00000008 R_386_RELATIVE   
000014dc  00000106 R_386_GLOB_DAT    00000000   __gmon_start__
000014e0  00000206 R_386_GLOB_DAT    00000000   _Jv_RegisterClasses
000014e4  00000806 R_386_GLOB_DAT    00000000   __cxa_finalize

Relocation section '.rel.plt' at offset 0x254 contains 3 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
000014f4  00000207 R_386_JUMP_SLOT   00000000   _Jv_RegisterClasses
000014f8  00000607 R_386_JUMP_SLOT   00000390   f
000014fc  00000807 R_386_JUMP_SLOT   00000000   __cxa_finalize

[zws@mail ~]$objdump -d libx.so
Disassembly of section .plt:

00000284 <_Jv_RegisterClasses@plt-0x10>:
 284:   ff b3 04 00 00 00       pushl  0x4(%ebx)
 28a:   ff a3 08 00 00 00       jmp    *0x8(%ebx)
 290:   00 00                   add    %al,(%eax)
        ...

00000294 <_Jv_RegisterClasses@plt>:
 294:   ff a3 0c 00 00 00       jmp    *0xc(%ebx)
 29a:   68 00 00 00 00          push   $0x0
 29f:   e9 e0 ff ff ff          jmp    284 <_init+0x18>

000002a4 :
 2a4:   ff a3 10 00 00 00       jmp    *0x10(%ebx)
 2aa:   68 08 00 00 00          push   $0x8
 2af:   e9 d0 ff ff ff          jmp    284 <_init+0x18>

000002b4 <__cxa_finalize@plt>:
 2b4:   ff a3 14 00 00 00       jmp    *0x14(%ebx)
 2ba:   68 10 00 00 00          push   $0x10
 2bf:   e9 c0 ff ff ff          jmp    284 <_init+0x18>

。。。。。。。。。。。。。
00000390 :
 390:   55                      push   %ebp
 391:   89 e5                   mov    %esp,%ebp
 393:   c9                      leave  
 394:   c3                      ret    

00000395 :
 395:   55                      push   %ebp
 396:   89 e5                   mov    %esp,%ebp
 398:   53                      push   %ebx
 399:   83 ec 04                sub    $0x4,%esp
 39c:   e8 00 00 00 00          call   3a1
 3a1:   5b                      pop    %ebx
 3a2:   81 c3 47 11 00 00       add    $0x1147,%ebx
 3a8:   e8 f7 fe ff ff          call   2a4
 3ad:   83 c4 04                add    $0x4,%esp
 3b0:   5b                      pop    %ebx
 3b1:   c9                      leave  
 3b2:   c3                      ret    
 3b3:   90                      nop

至于这里的涉及到的原理看下面,这里的%ebx存放的是本模块的GOT地址

(5)为本地访问外部全局函数调用提供PIC支持(配合.plt节和.got.plt节)
[zws@mail ~]$cat x.c
int a=0;

int f()
{
a++;
return a;
}
[zws@mail ~]$gcc -fPIC -shared x.c -o x.o
[zws@mail ~]$cat y.c
void f();

int main()
{
f();
return 0;
}
[zws@mail ~]$gcc y.c libx.so
[zws@mail ~]objdump -d a.out
看看外部全局函数调用使用什么方式

080483e8
:
 80483e8:       55                      push   %ebp
 80483e9:       89 e5                   mov    %esp,%ebp
 80483eb:       83 ec 08                sub    $0x8,%esp
 80483ee:       83 e4 f0                and    $0xfffffff0,%esp
 80483f1:       b8 00 00 00 00          mov    $0x0,%eax
 80483f6:       29 c4                   sub    %eax,%esp
 80483f8:       e8 2b ff ff ff          call   8048328
 80483fd:       b8 00 00 00 00          mov    $0x0,%eax
 8048402:       c9                      leave  
 8048403:       c3                      ret

call 8048328,这个地址在.plt节中
Disassembly of section .plt:

08048308 <__libc_start_main@plt-0x10>:
 8048308:       ff 35 a0 95 04 08       pushl  0x80495a0
 804830e:       ff 25 a4 95 04 08       jmp    *0x80495a4
 8048314:       00 00                   add    %al,(%eax)
        ...

08048318 <__libc_start_main@plt>:
 8048318:       ff 25 a8 95 04 08       jmp    *0x80495a8
 804831e:       68 00 00 00 00          push   $0x0
 8048323:       e9 e0 ff ff ff          jmp    8048308 <_init+0x18>

08048328 :
 8048328:       ff 25 ac 95 04 08       jmp    *0x80495ac
 804832e:       68 08 00 00 00          push   $0x8
 8048333:       e9 d0 ff ff ff          jmp    8048308 <_init+0x18>

jmp *0x80495ac,这个地址在.got.plt节中
[zws@mail ~]$objdump -sj .got.plt a.out

a.out:     file format elf32-i386

Contents of section .got.plt:
 804959c c8940408 00000000 00000000 1e830408  ................
 80495ac 2e830408                             ....            

该地址处的值是0804832e,就是前面jmp *0x80495ac的下一条指令地址
push $0x8,压入立即数8,其实是f的重定位项的在.rel.plt节中偏移(一个重定位项占8字节)
Relocation section '.rel.plt' at offset 0x2e0 contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
080495a8  00000407 R_386_JUMP_SLOT   00000000   __libc_start_main
080495ac  00000807 R_386_JUMP_SLOT   00000000   f
该f符号的重定位偏移是080495ac(就是在前面的.got.plt节中),类型是R_386_JUMP_SLOT.这样动态连接时,查找到f的地址后,写入080495ac处.
这样下次调用f时,就会直接跳到f的真实地址。

push $0x8的下一条指令时jmp    8048308,8048308处的指令时
 8048308:       ff 35 a0 95 04 08       pushl  0x80495a0
 804830e:       ff 25 a4 95 04 08       jmp    *0x80495a4

第一条pushl 0x80495a0,将0x80495a0地址处的值压栈。0x80495a0在.got.plt中
[zws@mail ~]$objdump -sj .got.plt a.out

a.out:     file format elf32-i386

Contents of section .got.plt:
 804959c c8940408 00000000 00000000 1e830408  ................
 80495ac 2e830408                             ....           

.got.plt的前三项是有特殊意义的,他们都是地址,在执行动态连接时要用到.第0项080494c8是.dynamic节地址.第1项是本模块的link_map地址,这

里是0,动态连接时会存入真实地址,第2项是_dl_runtime_resolve的地址,动态链接时存入.

将本模块的link_map地址压栈后,jmp    *0x80495a4, 显然是跳到_dl_runtime_resolve中,执行链接f任务,_dl_runtime_resolve解析到f地址后,

会存入80495ac处,并将该地址替换栈上的返回地址,这样,_dl_runtime_resolve返回时,直接返回到f中,并执行f.而下次再执行f时就不需要这么

麻烦了。

这种在需要执行时才进行符号链接是所谓的lazy方式动态链接,还有一种就是模块加载时一次性为所有的符号进行链接,无论用不用得到,所谓的

now方式动态链接。

综上.got节存放的都是被本地引用的本地全局数据(没有被本地引用的不会出现)和外部全局数据,.got.plt前三项特殊,后面都是被本地引用的本地全

局函数(没有被本地引用的不会出现)和外部全局函数地址..plt存放过程链接信息(procedure link

table)..rel.dyn重定位.got(类型为R_386_GLOB_DAT的项),.rel.plt重定位.got.plt.





阅读(1998) | 评论(0) | 转发(0) |
0

上一篇:ld.so分析3

下一篇:ld.so分析5 _dl_start

给主人留下些什么吧!~~