可重定位目标文件的解析

从源代码到可执行目标文件要经过以下几个步骤:

main.c-----预处理(cpp)-------->main.i------编译(ccl)----->main.s-----汇编(as)----->main.o-----链接(ld)----->main(可执行文件)

      可重定位目标文件是.o文件,包含二进制的代码和数据,其形式可以在编译时和其它的可重定位目标文件合并为可执行目标文件,每个.o文件由对应.c文件生成,每个.o文件代码和数据的地址都是从0开始。各个系统的目标文件格式各不相同,现在x86-64 Liinux和Unix系统使用可执行可链接格式(Executable and Linlable Format,ELF)。

        符号是指全局变量或用static修饰的变量(注意局部变量不是符号),在链接时链接器会做两件事情:符号解析和重定位。符号解析时编译器将程序中定义的符号放在符号表中(symbol table),链接器将每个符号引用和都与一个确定的符号定义建立关联。重定位是将多个代码段和数据段合并为一个单独的代码段和数据段,计算每个符号在虚拟空间中的绝对地址,将可执行文件中符号引用处的地址修改为重定位后的地址信息。

ELF中各节存储的信息如下:

ELF头:包括十六字节标识信息、文件类型(.o,exec,.so)、机器类型、节头表的偏移、节头表的表项大小以及表项个数。

.text:已编译程序的机器代码。

.rodate:只读数据,比如printf语句中的格式串和switch的跳转表。

.data:已经初始化的全局变量和静态C变量。

.bss:未初始化的全局变量和静态C变量,以及所有被初始化为0的全局或静态变量,仅是占位符,不占据任何实际磁盘空间。区分初始化和非初始化是为了空间效率。

.symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量静态变量的信息,不包括局部变量。

.rel.text:一个.text节中位置的列表,当链接器把这个目标文件和其他文件组合时,需修改这些位置。

.rel.data:被模块引用或定义的所有全局变量的重定位信息。

.debug:一个调试符号(gcc -g)。

.line:原始C源程序中的行号和.text节中机器指令之间的映射。

.strtab:一个字符串表包含symtab和debug节中符号及节名。

Section header table(节头表):每个节的节名、偏移和大小。

 

下面着重解析main.o和sum.o可重定位目标文件的ELF格式(main函数里调用sum函数):

main.c源代码

/* main.c */
/* $begin main */
int sum(int *a, int n);

int array[2] = {1, 2};

int main() 
{
    int val = sum(array, 2);
    return val;
}
/* $end main */

sum.c源代码

/* sum.c */
/* $begin sum */
int sum(int *a, int n)
{
    int i, s = 0;
    
    for (i = 0; i < n; i++) { 
        s += a[i];
    }
    return s;
}        
/* $end sum */

首先将main.c和sum.c文件转为main.o和sum.o文件在Linux中用下列命令:

$gcc -c main mian.c

$gcc -c sum sum.c

于是在源文件的目录下就能看到main.o和sum.o文件,接下来在命令行中输入readelf -h 文件名就能看到ELF的头部信息啦!

此处插入一个小知识:readelf一般用于查看ELF格式的文件信息,想要看某个命令的参数只要man一下即可,下面就是我用man查看的readelf的参数呀!

NAME
       readelf - Displays information about ELF files.

SYNOPSIS
       readelf [-a|--all]
               [-h|--file-header]                               //头部信息
               [-l|--program-headers|--segments]
               [-S|--section-headers|--sections]     //节头信息
               [-g|--section-groups]
               [-t|--section-details]
               [-e|--headers]
               [-s|--syms|--symbols]                       //符号信息
               [--dyn-syms]
               [-n|--notes]
               [-r|--relocs]
               [-u|--unwind]
               [-d|--dynamic]
               [-V|--version-info]
               [-A|--arch-specific]
               [-D|--use-dynamic]
               [-x <number or name>|--hex-dump=<number or name>]
               [-p <number or name>|--string-dump=<number or name>]
               [-R <number or name>|--relocated-dump=<number or name>]
               [-z|--decompress]
               [-c|--archive-index]

上面注释的都是要重点讲解的,有兴趣的小伙伴可以去看看其它的命令的作用呦(一定要有好奇心!)

/*sum.c 的ELF头部信息         在Linux中输入命令readelf -h sum.o*/ 
ELF 头:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00  //魔数:文件开头的几个字节通常用来确定文件的类型和格式
  类别:                              ELF64
  数据:                              2 补码,小端序 (little endian)           //数据以小端模式存储
  版本:                              1 (current)                                        
  OS/ABI:                            UNIX - System V                           //操作系统平台
  ABI 版本:                          0
  类型:                              REL (可重定位文件)                         //目标文件的类型
  系统架构:                          Advanced Micro Devices X86-64 //机器结构类型
  版本:                              0x1
  入口点地址:               0x0                                                    //程序执行的入口地址
  程序头起点:          0 (bytes into file)                                    //程序头表的起始位置
  Start of section headers:          576 (bytes into file)               //节头表的起始位置
  标志:             0x0                                                                 //程序执行时的第一条指令
  本头的大小:       64 (字节)                                                    //程序头表的长度
  程序头大小:       0 (字节)
  Number of program headers:         0
  节头大小:         64 (字节)                                                      //节头表的长度
  节头数量:         11
  字符串表索引节头: 10

ps:加载文件时可用魔数确认文件类型是否正确。

/*sum.c 的节头信息          Linux中输入的命令是readelf-S sum.c*/
There are 11 section headers, starting at offset 0x240:               
节头:
  [号] 名称                               类型                              地址                           偏移量
       大小                                全体大小                         旗标            链接   信息   对齐
  [ 0]                                       NULL                              0000000000000000  00000000
       0000000000000000       0000000000000000                                0     0     0
  [ 1] .text                               PROGBITS                     0000000000000000  00000040
       0000000000000045       0000000000000000         AX                  0     0     1
  [ 2] .data                              PROGBITS                     0000000000000000  00000085
       0000000000000000       0000000000000000         WA                 0     0     1
  [ 3] .bss                               NOBITS                         0000000000000000  00000085
       0000000000000000       0000000000000000         WA                 0     0     1
  [ 4] .comment                      PROGBITS                   0000000000000000  00000085
       000000000000002b       0000000000000001         MS                 0     0     1
  [ 5] .note.GNU-stack           PROGBITS                   0000000000000000  000000b0
       0000000000000000      0000000000000000                                0     0     1
  [ 6] .eh_frame                     PROGBITS                   0000000000000000  000000b0
       0000000000000038      0000000000000000            A                  0     0     8
  [ 7] .rela.eh_frame              RELA                            0000000000000000  000001d0
       0000000000000018      0000000000000018            I                   8     6     8
  [ 8] .symtab                        SYMTAB                       0000000000000000  000000e8
       00000000000000d8      0000000000000018                                9     8     8
  [ 9] .strtab                           STRTAB                       0000000000000000  000001c0
       000000000000000b     0000000000000000                                 0     0     1
  [10] .shstrtab                      STRTAB                       0000000000000000  000001e8
       0000000000000054     0000000000000000                                 0     0     1

节头表描述每个节的节名,在文件中的偏移量、大小、访问属性、对齐方式等。

 

/*main.o的符号表信息   在Linux命令中输入readelf -s main.o*/

Symbol table '.symtab' contains 12 entries:
   Num:    Value                   Size       Type         Bind        Vis         Ndx           Name
     0: 0000000000000000     0       NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0            FILE    LOCAL  DEFAULT  ABS           main.c
     2: 0000000000000000     0      SECTION LOCAL  DEFAULT    1 
     3: 0000000000000000     0      SECTION LOCAL  DEFAULT    3 
     4: 0000000000000000     0      SECTION LOCAL  DEFAULT    4 
     5: 0000000000000000     0      SECTION LOCAL  DEFAULT    6 
     6: 0000000000000000     0      SECTION LOCAL  DEFAULT    7 
     7: 0000000000000000     0      SECTION LOCAL  DEFAULT    5 
     8: 0000000000000000     8      OBJECT  GLOBAL DEFAULT    3               array
     9: 0000000000000000    33       FUNC    GLOBAL DEFAULT    1               main
    10: 0000000000000000     0     NOTYPE  GLOBAL DEFAULT  UND     _GLOBAL_OFFSET_TABLE_
    11: 0000000000000000     0     NOTYPE  GLOBAL DEFAULT  UND           sum

8:array是main.o中.data偏移量为0的符号,是全局变量数组中有两个元素占8B。

9:main是main.o中.text偏移量为0的符号,是全局函数占33B.

11:sum是未定义的符号,是在其它模块定义的全局符号。

最后,贴图为证:

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值