Chinaunix首页 | 论坛 | 博客
  • 博客访问: 56862
  • 博文数量: 27
  • 博客积分: 2000
  • 博客等级: 大尉
  • 技术积分: 300
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-24 17:31
文章分类
文章存档

2011年(1)

2010年(8)

2009年(18)

我的朋友

分类: LINUX

2009-06-28 12:56:19

 

3.5 数据段重定位

 

在数据段中的重定位是指对指针类型的静态变量、全局变量进行初始化。它与代码段中的重定位比较起来至少有以下明显不同:

一、在用户程序获得控制权(即main函数开始执行)之前就要全部完成;

二、不经过GOT表间接寻址,这是因为此时%ebx中还没有正确的GOT表首地址;(# main()还没有开始执行,估计得执行到第一个被调用函数时,才计算本模块GOT表地址。而且不同的函数可能属于不同的.so文件,不同的.so文件的GOT表位置也不相同,所以%ebx值也是随调用的函数而变化的。参考3.4节(a))

三、直接修改数据段,而代码段重定位时不能修改代码段。(# 数据段可以直接修改,而浮动代码段在动态重定位时不能再做地址修改,应该在连接时候完成)。

 

a) 如引用静态变量函数字符串常量编译器会在目标文件(*.o)中设上R_386_32重定位标记,并计算被引用变量、函数相对于所在段(section)首地址的偏移量。连接器把它改成R_386_RELATIVE重定位标记,计算它相对于动态连接库(*.so)首地址(通常首地址为零)的偏移量。加载器会把运行模块(*.so)在内存中的首地址与该偏移量相加,得到的结果用来初始化修改指针变量。

 

代码片断如下:

.section .rodata

.LC0: .string "aaaa1234\n"

.data

p:     .long .LC0        # .LC0 是引用的一个串常量 "Ok\n"

.o:  R_386_32 w/section

.so: R_386_RELATIVE

 

b) 引用全局变量函数编译器同样设上R_386_32重定位标记并且记录引用的符号名字。连接器不必动作。最后加载器查找被引用符号,得到的结果用来初始化指针变量。对于全局函数,查找的结果仍然是函数在PLT表中的代码片断,而不是实际入口。这与前面引用全局函数的讨论相同。

 

代码片断如下:

.data

p:       .long printf    #printf 是引用的一个全局函数

.o:  R_386_32 w/symbol

.so: R_386_32 w/symbol

 

 

(# 全局变量 有可能在其他模块中;静态变量一定在本模块中)

(# 定义是指确定变量的名字类型并分配空间;初始化是指 赋予变量一个初始值。自动变量/局部变量:直接化为.text代码段里的堆栈地址;静态变量和全局变量:已初始化的存放在.data,未初始化的存放在.bss;字符串常量:存放在 .rodata )

 

(# 指针类型的变量存放的是一个地址,看来重定位都是对不确定的地址 重新定位。比如static char *p3 = abcde,对于.so文件,abcde实际存放在.rodata,而指针型静态变量*p3存放在.data区。

*p3变量本身有个地址,在.so文件中,*p3变量的位置实际上是一个偏移量,加载进内存时要重定位出*p3具体的内存地址,计算方法是以GOT表为基准来计算*p3在文件内的偏移,详见 3.4 b)。

3.5节所讲的是对*p3里的内容进行重定位。*p3指针里存放的内容是一个地址,该地址是abcde在.rodata里的位置,也是一个偏移量. *.so文件内,abcde相对于文件开头的偏移量是固定的。*.so文件被加载进内存后,加载器把*.so在内存的起始地址+abcde在文件的偏移量,得到abcde在内存中的实际地址,也就完成了地址重定位;

)

 

(# 实际例子

[elf@redhat elf]# vi bbb.c

int a111 = 15;                # 全局变量

char *g111;                   # 全局指针变量 未初始化。

char *g222 = "456";               # 全局指针变量 已经初始化 放在.data

static char *p111;                # 静态全局指针变量 未初始化 放在.bss

static char *p222 = "aaaaaaaaaa";   # 静态全局指针变量 已经初始化 放在.data

static char *p333;            # 静态全局指针变量 未初始化 放在.bss

static char *p444 = "bbbb";   # 静态全局指针变量 已经初始化 放在.data

 

int foo2()

{

    p333 = "cc";

    char *p555 = "dd";

    static char *p666= "123";  # 静态局部指针变量 已经初始化 放在.data

 

    p111 = (char *)malloc(10);

 

    strcpy(p111, "13579");

}

 

 

[elf@redhat elf]# gcc -fPIC -c bbb.c

[elf@redhat elf]# gcc -shared -o bbb.so ./bbb.o

[elf@redhat elf]# objdump -s bbb.o

Contents of section .data:

 0000 0f000000                             ....      # 全局变量a111=15,.data

Contents of section .rodata:

 0000 34353600 61616161 61616161 61610062  456.aaaaaaaaaa.b

 0010 62626200 31323300 63630064 64003133  bbb.123.cc.dd.13

 0020 35373900                             579.           

Contents of section .data.rel.local:

 0000 00000000 04000000 0f000000 14000000  ................

 

[elf@redhat elf]# readelf -a ./bbb.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:          348 (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:         13

  Section header string table index: 10

 

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

  [ 2] .rel.text         REL             00000000 000510 000048 08     11   1  4

  [ 3] .data             PROGBITS        00000000 00008c 000004 00  WA  0   0  4

  [ 4] .bss              NOBITS          00000000 000090 000008 00  WA  0   0  4

  [ 5] .rodata           PROGBITS        00000000 000090 000024 00   A  0   0  1

  [ 6] .data.rel.local   PROGBITS        00000000 0000b4 000010 00  WA  0   0  4

  [ 7] .rel.data.rel.loc REL             00000000 000558 000020 08     11   6  4

  [ 8] .note.GNU-stack   NOTE            00000000 0000c4 000000 00      0   0  1

  [ 9] .comment          PROGBITS        00000000 0000c4 000031 00      0   0  1

  [10] .shstrtab         STRTAB          00000000 0000f5 000065 00      0   0  1

  [11] .symtab           SYMTAB          00000000 000364 000150 10     12   e  4

  [12] .strtab           STRTAB          00000000 0004b4 00005a 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 program headers in this file.

 

Relocation section '.rel.text' at offset 0x510 contains 9 entries:

 Offset     Info    Type            Sym.Value  Sym. Name

0000000f  0000110a R_386_GOTPC       00000000   _GLOBAL_OFFSET_TABLE_

00000015  00000509 R_386_GOTOFF      00000000   .rodata

0000001b  00000409 R_386_GOTOFF      00000000   .bss

00000021  00000509 R_386_GOTOFF      00000000   .rodata

0000002e  00001204 R_386_PLT32       00000000   malloc

00000037  00000409 R_386_GOTOFF      00000000   .bss

00000040  00000509 R_386_GOTOFF      00000000   .rodata

00000047  00000409 R_386_GOTOFF      00000000   .bss

0000004c  00001304 R_386_PLT32       00000000   strcpy

 

Relocation section '.rel.data.rel.local' at offset 0x558 contains 4 entries:

 Offset     Info    Type            Sym.Value  Sym. Name

00000000  00000501 R_386_32          00000000   .rodata

00000004  00000501 R_386_32          00000000   .rodata

00000008  00000501 R_386_32          00000000   .rodata

0000000c  00000501 R_386_32          00000000   .rodata

 

There are no unwind sections in this file.

 

Symbol table '.symtab' contains 21 entries:

   Num:    Value  Size Type    Bind   Vis      Ndx Name

     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND

     1: 00000000     0 FILE    LOCAL  DEFAULT  ABS bbb.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     0 SECTION LOCAL  DEFAULT    6

     7: 00000004     4 OBJECT  LOCAL  DEFAULT    6 p222

     8: 00000008     4 OBJECT  LOCAL  DEFAULT    6 p444

     9: 0000000c     4 OBJECT  LOCAL  DEFAULT    6 p666.0

    10: 00000004     4 OBJECT  LOCAL  DEFAULT    4 p333

    11: 00000000     4 OBJECT  LOCAL  DEFAULT    4 p111

    12: 00000000     0 SECTION LOCAL  DEFAULT    8

    13: 00000000     0 SECTION LOCAL  DEFAULT    9

    14: 00000000     4 OBJECT  GLOBAL DEFAULT    3 a111

    15: 00000000     4 OBJECT  GLOBAL DEFAULT    6 g222

    16: 00000000    88 FUNC    GLOBAL DEFAULT    1 foo2

    17: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_

    18: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND malloc

    19: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND strcpy

    20: 00000004     4 OBJECT  GLOBAL DEFAULT  COM g111

 

No version information found in this file.

[elf@redhat elf]#

 

1

objdump s ./bbb.o 可看到全部字符串常量存放在.rodata

 

2

char *g222 = "456";               # 全局指针变量 已经初始化 放在.data

static char *p222 = "aaaaaaaaaa";   # 静态全局指针变量 已经初始化 放在.data

static char *p444 = "bbbb";   # 静态全局指针变量 已经初始化 放在.data

static char *p666= "123";  # 静态局部指针变量 已经初始化 放在.data

 

上面代码里,4个全局/静态指针变量是初始化的,将来会被ld放在.so文件的.data里。编译器在.o里把非指针类型的全局变量int a111=15 直接放进 .data, 其他已初始化的全局/静态指针类型变量放进半成品:.data.rel.local(将来供连接器ld进一步加工后放进.data)。

.rel.data.rel.local里有4个重定位项目:记录4个变量:*g222/*p222/*p444/*p666 .rodata里对应的字符串相对于.rodata节头部的偏移量。

 

3

.rel.data.rel.local的重定位是先于.rel.text重定位的。.rel.data.rel.local表示让ld.data.rel.local里的内容进行重定位。注意地址排序方式:0xd8840408 其实是:0x080484d8

 

[elf@redhat elf]# objdump s ./bbb.so

Contents of section .rodata:

 07cf 34353600 61616161 61616161 61610062  456.aaaaaaaaaa.b

 07df 62626200 31323300 63630064 64003133  bbb.123.cc.dd.13

 07ef 35373900                             579.           

Contents of section .eh_frame:

 07f4 00000000                             ....           

Contents of section .data:

 17f8 f8170000 e0180000 0f000000 cf070000  ................

 1808 d3070000 de070000 e3070000           ............   

Contents of section .dynamic:

 

连接器ld .so里把4个变量*g222/*p222/*p444/*p666放进.data取值=4个变量在.rodata里每个字符串相对于bbb.so文件头部的偏移量。加载器把bbb.so加载进内存,再次计算4个变量对应的字符串在内存中的地址。

)

 

3.6 总结

 

下表给出了前面讨论得到的全部结果:(看起来,函数符号的引用和调用的处理是有些区别Di)

 

                                .o             .so

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

      |装载GOT表首地址         R_386_GOTPC      NULL

代码段|-----------------------------------------------------

重定位|引用变量函数地址   静态  R_386_GOTOFF     NULL

      |                 全局  R_386_GOT32      R_386_GLOB_DAT

      |-----------------------------------------------------

      |直接调用函数      静态  R_386_PLT32      NULL

      |                 全局  R_386_PLT32      R_386_JMP_SLOT

------|-----------------------------------------------------

数据段|引用变量函数地址  静态   R_386_32 w/sec   R_386_RELATIVE

重定位|                 全局   R_386_32 w/sym   R_386_32 w/sym

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

 

4 结束语

 

Windows使用PE文件格式Linux使用ELF文件格式这是两种动态连接库不同的根源。本文从ELF规范出发,深入讨论了Linux动态连接库的具体实现,目的在于进一步推广Linux的研究与应用。

 

5 附录:Linux汇编程序语法

 

x86体系结构上的Linux汇编器兼容于AT&T System V/386汇编器的语法,与常见的Intel语法颇有不同,如下表:

 

                      AT&T                   Intel

常数                  前缀$:pushl $4        push 4

寄存器                前缀%:%ebx            ebx

跳转指令(绝对地址)   前缀*:jmp *fun

跳转指令(相对偏移)   无标记:jmp fun

目的、源操作数的顺序   源在前:movl $4,%eax   目的在前:mov eax,4

操作数尺寸            后缀b、w、l:movl       修饰符byte ptr等等

变址寻址              [base+disp]            disp(base)

 

参考文献

 

[1] Executable and Linking Format Spec v1.2, TIS Committee, 1995

   

[2] GNU Project (gcc, libc, binutils), Free Software Foundation, Inc., 1999

   

[3] Solaris 2.5 Linker and Libraries Guide, Sun Microsystems Inc., 1999

   

    ftp://192.18.99.138/802-1955/802-1955.pdf

[4] SVR4 ABI x86 Supplement, The Santa Cruz Operation, Inc., 1999

    http://www.sco.com/developer/devspecs/abx86-4.pdf

[5] ELF: From The Programmer's Perspective, H J Lu, 1995

   

[6] Using ld: The GNU linker, S Chamberlain, Cygnus Support, 1994

    2.9.1/ps/ld.ps.gz

 

 

 

补充:

1. objdump 用法

Objdump dx  反汇编文件里.text section

Objdump D   反汇编文件里所有section

Objdump s   察看所有节的内容

Readelf a   察看文件头里的连接信息

 

2. 查看elf文件里某个偏移处的内容:

$ readelf -a test2

 先根据readelf的结果,找到.interp section 的文件内偏移为0x00f4

$ hexdump -s 0x00f4 -n 40 -c test2

 

3. 察看内存里某个偏移处的内容

$ gdb -q test2

(gdb) disass 0x8049518

(gdb) x/20x 0x8049518    # 按16进制查看

(gdb) x/20i 0x8049518    # 反汇编查看

 

4创建静态库:

$ more haha.c

# include

void haha(char* argv)

{

printf(haha:--- %s\n,argv);

}

 

$ more huhu.c

# include

void huhu(char* argv)

{

printf(huhu:--- %s\n,argv);

}

 

$ gcc c haha.c huhu.c

$ ar -crv libmy.a haha.o huhu.o

 

应用静态库:

$ more mylib.h

void haha(char *);

void huhu(char *);

 

$ more m.c

# include mylib.h

int main()

{

haha(Hello World);

    exit(0);

}

 

 

$ gcc c m.c

$ gcc o test m.o haha.o

 

$ rm rf test

$ gcc o test m.c libmy.a

 

$ gcc static o test m.c mylib.a   # 生成不需要共享函数库的程序

 

很多方面,linux共享库都很象Windows的动态链接库,为了更好理解我们可以把.so库想象成.DLL文件,而.sa库想象成.LIB文件。

 

 

5.创建动态库:

 

实际例子1

$ more foo1.c

int foo1()

{

        printf("[+] foo1 address:%p\n", foo1);

}

 

$ more test2.c

# include

 

int main(int argc, char *argv[])

{

        foo1();

        return 0;

}

 

$ gcc -fPIC -c foo1.c

$ gcc -shared -o foo1.so foo1.o

$ gcc -c test2.c

$ gcc -o test2 test2.o foo1.so

$ export LD_LIBRARY_PATH=.

$ ldd ./test2

 

 

实际例子2

$ more libprint.c

/* file libprint.c */

#include "stdio.h"

void printstring(char* str)

{

printf("String:---------------> %s\n", str);

}

 

$ gcc -c libprint.c

$ readelf -a libprint.o > 11

 

$ gcc -fPIC -c libprint.c

$ readelf -a libprint.o > 22

$ diff 11 22

51c52

< Symbol table '.symtab' contains 11 entries:

---

> Symbol table '.symtab' contains 12 entries:

62,63c63,65

<      9: 00000000    27 FUNC    GLOBAL DEFAULT    1 printstring

<     10: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

---

>      9: 00000000    45 FUNC    GLOBAL DEFAULT    1 printstring

>     10: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_

>     11: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

 

结论:普通目标文件.o 和 位置无关目标文件.o elf头相比少了个_GLOBAL_OFFSET_TABLE_符号。

 
阅读(1018) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~