ELF节头

前面是程序头相关内容,接下来了解下节头(section header)相关内容

段(segment)和节(section)是有区别的,节不是段,通俗点理解即一个段里可以包含多个节

段(segment)是程序执行的必要组成部分,在每个段中会有代码或者数据被划分为不同的节

节头表是对这些节的位置和大小的描述,主要用于链接和调试

节头对于程序的执行不是必需的,没有节头表程序仍可以执行,因为节头没有对内存布局进行描述,对内存布局的描述是程序头表的任务,节头是对程序头的补充

节头对于像 objcopy、objdump、gdb等工具特别重要,如果去掉了节头,那么就不能定位到符号信息,那么这些工具就废了一大半

当然了,通过对于程序头的分析,可以重构部分节头表

通过 readelf -S file 可以查看具体的节头表,通过节头表的偏移和大小就可以定位到具体的节

节头c结构体定义

typedef struct elf_internal_shdr {
  unsigned int  sh_name;    /* 节头名偏移量(要到符号表去查) */
  unsigned int  sh_type;    /* 节类型 */
  unsigned long sh_flags;   /* Miscellaneous section attributes */
  unsigned long sh_addr;    /* Section virtual addr at execution */
  unsigned long  sh_offset;    /* 文件偏移量 */
  unsigned long  sh_size;     /* 节大小 */
  unsigned int  sh_link;    /* 对应到其他节的索引*/
  unsigned int  sh_info;    /* Additional section information */
  unsigned long sh_addralign;   /* Section alignment */
  unsigned long sh_entsize;   /* 如果是一个表,那么为单个表条目的大小(即数组中一个元素的大小) */
} Elf_Internal_Shdr;

下面是常见的节描述:

.text 节

保存了代码指令的节

.rodata 节

保存了只读数据,比如字符串常量 "hello world"

.plt 节

过程链接表,包含了动态链接器从共享库导入函数所必需的代码

.data 节

保存了初始化的全局变量等数据,是 data段 的一部分(注意区分 .data节 和 data段)

.bss 节

保存了未初始化的全局数据,是 data段 的一部分,在 x86-64 中只占用8个字节,仅表示本身的空间。因为未初始化的全局数据会在程序运行时置0

.got.plt 节

.got 节保存了全局偏移表,.got 和 .plt 一起提供了对导入的共享库函数的访问入口,由动态链接器在运行时进行修改

.dynsym 节

保存了从动态库导入的动态符号信息

.dynstr 节

保存了动态符号的字符串信息

.rela.plt 节

保存了重定位相关信息,这些信息描述了如何在链接或者运行时对elf目标文件的某部分内容或者进程镜像进行补充或者修改

.gnu.hash 节

保存了用于查找符号的散列表

.symtab 节

保存了所有符号信息(包括.dynsym的动态符号)

.strtab 节

保存了符号字符串表

.shstrtab 节

保存了节头字符串表


至于这些节分别属于哪个段,可以参考上一节


下面命令可以打印出节头表,通过里面的偏移和大小可以定位到具体的节

readelf -S main

There are 20 section headers, starting at offset 0x1458:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  ...
       0000000000000078  0000000000000018   A       5     1     8
  [ 5] .dynstr           STRTAB           0000000000400320  00000320
       000000000000002a  0000000000000000   A       0     0     1
  [ 6] .rela.plt         RELA             0000000000400350  00000350
       0000000000000018  0000000000000018  AI       4     7     8
  [ 7] .plt              PROGBITS         0000000000400370  00000370
       0000000000000020  0000000000000010  AX       0     0     16
  [ 8] .text             PROGBITS         0000000000400390  00000390
       0000000000000025  0000000000000000  AX       0     0     1
  ...
  [14] .data             PROGBITS         0000000000601020  00001020
       0000000000000010  0000000000000000  WA       0     0     8
  [15] .bss              NOBITS           0000000000601030  00001030
       0000000000000008  0000000000000000  WA       0     0     4
  [16] .comment          PROGBITS         0000000000000000  00001030
       000000000000002d  0000000000000001  MS       0     0     1
  [17] .shstrtab         STRTAB           0000000000000000  0000105d
       00000000000000a6  0000000000000000           0     0     1
  [18] .symtab           SYMTAB           0000000000000000  00001108
       00000000000002e8  0000000000000018          19    22     8
  [19] .strtab           STRTAB           0000000000000000  000013f0
       0000000000000063  0000000000000000           0     0     1


ELF符号

.dynsym节保存了引用来自外部文件的全局符号

.symtab节保存了所有符号包括本地的符号如全局变量、本地函数

即.dynsym里有的,.symtab节里也有


.symtab节的符号是用来调试和链接的,在最终产生的二进制文件如果不需要调试,那么可以删除掉.symtab节

而.dynsym节里的符号是在运行时被解析,是运行时动态链接器所需要的唯一符号,所以对于动态链接的可执行程序来说是必需的,不能删除

下面是符号结构体

struct elf_internal_sym {
  unsigned int  st_name;    /* Symbol name, index in string tbl */
  unsigned char st_info;    /* Type and binding attributes */
  unsigned char st_other;   /* Visibilty, and target specific */
  unsigned short  st_shndx;   /* Associated section index */
  unsigned long st_value;   /* Value of the symbol */
  unsigned long st_size;    /* Associated symbol size */
};

st_name

节头名字符串偏移

.symtab节里的符号的符号名存放在 .strtab节里

.dynsym节里的符号的符号名存放在 .dynstr节里  

st_value

存放符号的值,可能是地址,也可能是位置偏移量

st_size

存放一个符号的大小,如全局函数的指针大小,64位通常为8字节

st_other

定义了符号的可见性

st_shndx

如果该符号跟某些节有关联,那么为该节在所有节中的索引

st_info

符号类型及绑定属性,可以查阅 man 5 elf

符号类型有

STT_NOTYPE:未定义

STT_FUNC:表示该符号与函数或者其他可执行代码关联

STT_OBJECT:表示该符号与数据目标文件关联

符号绑定有:

STB_LOCAL:本地符号,比如声明为 static 的变量或者函数

STB_GLOBAL:全局符号

STB_WEAK:与全局符号类似,只是优先级比全局符号低,被标记为 STB_WEAK 的符号可能被同名的未标记为 STB_WEAK 符号替换


上一篇: ELF文件类型
下一篇: ELF重定位
作者邮箱: 203328517@qq.com