《程序员的自我修养》学习笔记(三)————目标文件(2):ELF相关结构体

        我们通过《程序员的自我修养》学习笔记(二)————目标文件(1):格式大致了解了ELF文件的轮廓,接着就来看看ELF文件的结构格式。

图1 ELF文件结构

         ELF目标文件格式的最前部是ELF文件头(ELF Header),它包含了描述整个文件的基本信息,比如ELF的文件版本、目标机器型号、程序入口地址等。紧接着是ELF文件各个段。其中ELF文件中与段有关的重要结构就是段表(Section Header Table),该表描述了ELF文件包含的所有段的信息,比如每个段的段名、长度、在文件中的偏移、读写权限等。

1. 文件头

        使用readelf查看ELF文件,如图2所示:

图2 ELF文件头

        从上面输出的结构可以看到:ELF文件头定义了ELF魔数、文件机器字节长度、数据存储方式、版本、运行平台等。
        ELF文件头结构及相关常数被定义在“/usr/include/elf.h”,因为ELF文件在各种平台下都通用,ELF文件有32位版本和64位版本的ELF文件的文件头内容是一样的,只不过有些成员的大小不一样。它的文件图也有两种版本:分别叫 “Elf32_Ehdr” 和 “Elf64_Ehdr”。

typedef struct
{
  unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */
  Elf64_Half	e_type;			/* Object file type */
  Elf64_Half	e_machine;		/* Architecture */
  Elf64_Word	e_version;		/* Object file version */
  Elf64_Addr	e_entry;		/* Entry point virtual address */
  Elf64_Off	e_phoff;		/* Program header table file offset */
  Elf64_Off	e_shoff;		/* Section header table file offset */
  Elf64_Word	e_flags;		/* Processor-specific flags */
  Elf64_Half	e_ehsize;		/* ELF header size in bytes */
  Elf64_Half	e_phentsize;		/* Program header table entry size */
  Elf64_Half	e_phnum;		/* Program header table entry count */
  Elf64_Half	e_shentsize;		/* Section header table entry size */
  Elf64_Half	e_shnum;		/* Section header table entry count */
  Elf64_Half	e_shstrndx;		/* Section header string table index */
} Elf64_Ehdr;

        查看ELF文件头结构和前面图2的信息,可以看出基本一致。但,“Elf64_Ehdr”中的e_ident对应了readelf输出结果中的“Class”、“Data”、“Version”、“OS/ABI”和“ABI Version”这5个参数。其他的,可以一一对应。

成员

readelf 输出结果与含义

e_ident

Magic:7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00

Class: ELF64

Data:  2's complement, little endian

Version: 1 (current)

OS/ABI:  UNIX-System V

ABI Version: 0

e_type

Type: REL (Relocatable file)

ELF文件的类型

e_machine

Machine: Advanced Micro Devices X86-64

ELF文件的CPU平台属性,相关常量以EM_开头

e_version

Version:0x1

ELF版本号

e_entry

Entry point address: 0x0

入口地址,规定ELF程序的入口虚拟地址,操作系统在加载完程序后从这个地址开始执行进程的指令。可重定位文件一般没有入口地址,则这个值为0

e_phoff

Start of program headers: 0 (bytes into file)

e_shoff

Start of section headers: 392 (bytes into file)

段表在文件中的偏移。

e_word

Flags: 0x0

标志位

e_ehsize

Size of this header: 64 (bytes)

即ELF文件头本身的大小

e_phentsize

Size of program headers: 0 (bytes)

e_phnum

Number of program headers: 0

e_shentsize

Size of section headers: 64 (bytes)

段表描述符的大小,一般等于sizeof(Elf64_Ehdr)

e_shnum

Number of section headers:13

段表描述符数量

e_shstrndx

Section header string table index: 10

段表字符串表所在的段在段表中的下标。

    ELF魔数

图3

    文件类型

    e_type表示ELF的文件类型

图4

    机器类型

    e_machine表示该ELF平台的属性

图5

2 段表

         ELF文件有很多段,这个段表(Section Header Table)就是保存这些段的基本属性的结构。ELF文件的段结构就是由段表来决定的,编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。段表在ELF文件中的位置由ELF头文件的“e_shoff”成员决定。

图6

      段表的结构比较简单,是以“Elf64_Shdr”结构体的元素的数组。ELF段表的这个数组的第一个元素是无效的,其类型为“NULL”。

typedef struct
{
  Elf64_Word	sh_name;		/* Section name (string tbl index) */
  Elf64_Word	sh_type;		/* Section type */
  Elf64_Xword	sh_flags;		/* Section flags */
  Elf64_Addr	sh_addr;		/* Section virtual addr at execution */
  Elf64_Off	sh_offset;		/* Section file offset */
  Elf64_Xword	sh_size;		/* Section size in bytes */
  Elf64_Word	sh_link;		/* Link to another section */
  Elf64_Word	sh_info;		/* Additional section information */
  Elf64_Xword	sh_addralign;		/* Section alignment */
  Elf64_Xword	sh_entsize;		/* Entry size if section holds table */
} Elf64_Shdr;
图7

    段的类型
    对编译器和链接器来说,主要决定段的属性的是段的类型(sh_type)和段的标志位(sh_flag)。

图8

    段的标志位
    段的标志位(sh_flag)表示该段在进程虚拟地址空间中的属性。

图9

    段的链接信息
    如果段的类型是与链接有关的,比如重定位表、符号表等,那么sh_link、sh_info的含义如下表所示:

图10

3 重定位表
          从图6中可以看到,有一个段叫做“.rel.text”,它的类型为“SHT_RELA”,也就是重定位表。“.rel.text”针对的是“.text”段的重定位表。它的“sh_link”表示符号表的下标,它的“sh_info”表示它作用于哪个段。比如“.rel.text”作用于“.text”段,而“.text”段的下标为“1”,所以“sh_info”为“1”。

4 字符串表
         ELF文件中用到很多字符串,比如段名、变量名等。因为字符串的长度往往不固定,所以用固定的结构来表示比较困难。一种常见的做法是把字符串集中起来存放到一个表,然后使用字符串在表中的偏移来引用字符串。

图11 字符串表
图12 偏移对应的字符串

         通过这种方法,在ELF文件中引用字符串只需要给出一个数字下标即可,而不考虑字符串长度的问题。这也是段表结构“Elf64_Shdr”成员sh_name类型为Elf64_Word(uint32_t)的原因。一般字符串表在ELF文件中也已段的形式保存,常见段名为“.strtab”或“.shstrtab”,即字符串表和段表字符串表。字符串表用来保存普通的字符串,段表字符串表用来保存段表中用到的字符串,最常见的就是段名(sh_name)。
         ELF头文件结构Elf64_Ehdr中的“e_shstrndx”表示“.shstrtab”在段表中的下标,即段表字符串表在段表中的下标。

5 符号

         在链接中,我们将函数和变量统称为符号,函数名和变量名就是符号名。链接过程中很关键的一部分就是符号的管理,每一个目标文件都会有一个相应的符号表(Symbol Table),这个表里面记录了目标文件中所用到的所有符号。每个定义的符号有一个对应值,叫做符号值(Symbol Value),对于变量和函数来说,符号值就是它们的地址。我们可以使用readelf、nm等命令查看符号。

图13

        ELF文件中符号表也是一个段,段名叫“.symtab”。表的结构是“Elf64_Sym”的数组。

typedef struct
{
  Elf64_Word	st_name;		/* Symbol name (string tbl index) */
  unsigned char	st_info;		/* Symbol type and binding */
  unsigned char st_other;		/* Symbol visibility */
  Elf64_Section	st_shndx;		/* Section index */
  Elf64_Addr	st_value;		/* Symbol value */
  Elf64_Xword	st_size;		/* Symbol size */
} Elf64_Sym;
图14

    符号类型和绑定信息(st_info)

        该成员低4位表示符号的类型(Symbol Type),高28位表示符号绑定信息(Symbol Binding),如图15、图16所示。

图15
图16

   符号所在段(sh_shndx)

     如果符号定义在本目标文件中,那么这个成员表示符号所在的段在段表中的下标;但是如果符号不是定义在本目标文件中,或者对于有些特殊符号,sh_shndx的值有些特殊,如图17所示。

图17


    符号值(st_value)

       如果这个符号是一个函数或者变量的定义,那么符号的值就是这个函数或者变量的地址。

 

参考文件:

https://www.jianshu.com/p/10b2e410aed0

https://www.cnblogs.com/linhaostudy/p/8855238.html

https://download.csdn.net/download/mikeiii/747643

《程序员的自我修养——链接、装载与库》


 

 

 

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值