前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ELF文件结构描述

ELF文件结构描述

作者头像
233333
发布2018-04-17 15:22:25
1.5K0
发布2018-04-17 15:22:25
举报
ELF目标文件格式最前部ELF文件头(ELF Header),它包含了描述了整个文件的基本属性,比如ELF文件版本、目标机器型号、程序入口地址等。其中ELF文件与段有关的重要结构就是段表(Section Header Table)

文件头

我们可以使用readelf命令来详细查看elf文件,代码如清单3-2所示:

Markdown
Markdown

从上面输出的结构可以看到:ELF文件头定义了ELF魔数、文件机器字节长度、数据存储方式、版本、运行平台等。

ELF文件头结构及相关常数被定义在“/usr/include/elf.h”,因为ELF文件在各种平台下都通用,ELF文件有32位版本和64位版本的ELF文件的文件头内容是一样的,只不过有些成员的大小不一样。它的文件图也有两种版本:分别叫“Elf32_Ehdr”“Elf64_Ehdr”

typedef struct {
    unsigned char e_ident[16];  
    Elf32_Half e_type;
    Elf32_Half e_machine;
    Elf32_Word e_version;
    Elf32_Addr e_entry;
    Elf32_Off e_phoff;
    Elf32_Off e_shoff;
    Elf32_Word e_flags;
    Elf32_Half e_ehsize;
    Elf32_Half e_phentsize;
    Elf32_Half e_phnum;
    Elf32_Half e_shentsize;
    Elf32_Half e_shnum;
    Elf32_Half e_shstrndx;
}Elf32_Ehdr;

段表

段表就是保存ELF文件中各种各样段的基本属性的结构。段表是ELF除了文件以外的最重要结构体,它描述了ELF的各个段的信息,ELF文件的段结构就是由段表决定的。编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。段表在ELF文件中的位置由ELF文件头的“e_shoff”成员决定的,比如SimpleSection.o中,段表位于偏移0x118。

重定位表

我们注意到:SimpleSection.o中有一个叫做".rel.text"的段,它的类型是(sh_type)为“SHT_REL”,也就是说它是一个重定位表(Relocation Table)。正如我们开始所说的,链接器在处理目标文件时,须要对目标文件中某些部位进行重定位,即代码段和数据段中哪些对绝对地址的引用的位置。这些重定位的信息都记录在ELF文件的重定位表里面,对于每个须要重定位代码段或数据段,都会有一个相应的重定位表。

字符串表

ELF文件中用到了许多的字符串,比如段名,变量名等。因为字符串的长度往往是不定的,所以用固定的结构来表示它比较困难。一种常见的做法是把字符串集中起来存放到一个表,然后使用字符串在表中的偏移来引用字符串。 通常用这种方式,在ELF文件中引用字符串只需给一个数字下标即可,不用考虑字符串的长度问题。一般字符串标在ELF文件中国也以段的方式保存,常见的段名为“.strtab”或“.shstrtab”。这两个字符串分别表示为字符串表和段表字符串表。 只有分析ELF文件头,就可以得到段表和段表字符串表的位置,从而解析整个ELF文件。

链接的接口-符号

链接的过程的本质就是要把多个不同目标文件之间相互“粘”到一起。或者说像玩具积木一样,可以拼装成一个整体。为了使不同目标文件之间能够相互黏合,这些目标文件之间必须有固定的规则才行,就像积木模块必须有凹凸部分才能相互黏合。在链接中,目标文件之间相互拼合实际上是目标文件之间对地址的引用,即对函数和变量的地址的引用。 比如目标文件B要用到目标文件中的函数“foo”,那么我们就成目标文件A定义(Define)了函数“foo”,称目标文件B引用(Reference)了目标文件A中的函数“foo”。这两个概念也同样适用于变量。每个函数或变量都有自己独特的名字,才能避免链接过程中不同变量和函数之间的混淆。在链接中,我们将函数和变量统称为符号(Symbol),函数名或变量名就是符号名(Symbol Name)。 我们可以将符号看作是链接中的粘合剂,整个链接过程正是基于符号才能够完成。链接过程中很关键的一部分是符号的管理,每一个目标文件都会有一个相应的符号表(Symbol Table),这个表里记录了目标文件所用到的所有符号。每个定义的符号有一个对应的值,叫做符号值(Symbol Value),对于变量和函数来说,符号值就是它们的地址,除了函数和变量之外,还存在着其他几种不常用的符号。我们将符号表中的所有符号进行分类,它们有可能是下面这些类型中的几种:

  • 定义在本目文件中的全局符号,可以被其他目标文件引用,比如SimpleSection.o里面的“func1”、“main”和“global_init_val”。
  • 在本目标文件中引用的全局符号,却没有定义在本目标文件,这一般叫做外部符号(External Symbol),也就是我们前面所讲的符号引用。比如SimpleSection.o里面的“printf”
  • 段名,这种符号往往是由编译器产生,它的值就是该段的起始地址。
  • 局部符号,这类符号只在编译单元内部可见。
  • 行号信息。 对于我们来说,最值得关注的是全局符号,即上面的第一类和第二类。

ELF符号表结构

ELF文件中的符号表往往是文件中的一个段,段名一般叫做“.symtab”。符号表的结构很简单,它是一个Elf32_Sym结构(32位ELF文件)的数组,每个Elf32_Sym结构对应一个符号。这个数组的第一个元素,也就是下标0的元素为无效的“未定义”符号。Elf32_Sym的结构对应如下:

typedef struct {
    Elf32_Word st_name;
    Elf32_Addr st_value;
    Elf32_Word st_size;
    unsigned char st_info;
    unsigned char st_other;
    Elf32_Half st_shndx;
} Elf32_Sym;

st_name

符号名。这个成员包含了该符号名在字符串表中的下标

st_value

符号对应的值。这个值跟符号有关,可能是一个绝对值

st_size

符号大小

st_info

符号类型和绑定信息

st_other

该成员目前为0,没用

st_shndx

符号所在段

特殊符号

当我们使用ld作为链接器产生可执行文件,它会为我们定义很多特殊符号。这些符号并没有在你的程序中定义,但是你可以直接声明并引用它,我们称之为特殊符号。其实这些符号是被定义在链接器脚本中的,我们无须定义它们,但可以声明它们并且使用它们。链接器在程序最终连接成可执行文件将其解析成正确的值,注意,只有使用ld链接生成最终可执行文件的时候这些符号才会存在。 几个很具有代表性的特殊符号如下:

  • __executable_start,该符号为程序的起始地址
  • __etext或_etext或etext,该符号为代码段结束地址,即代码段最末尾的地址
  • _edata或edata,该符号为数据段结束地址,即数据段的最末尾地址。
  • _end或end,该符号为程序的结束地址。
  • 以上地址都为程序被装载的虚拟地址。 我们可以在程序中直接使用这些符号。

符号修饰和函数签名

在早期,编译器编译源代码产生目标文件时,符号名与相应的变量和函数名字一样的。比如在一个汇编源代码中包含了一个函数foo,那么汇编器将它编译成目标文件后,foo在目标文件中所对应的符号名也是foo。后来的UNIX平台和C语言发明时,已经存在了相当多的使用汇编编写的库和目标文件。这样就产生了一个问题,那就是如果一个c程序要使用这些库的话,C语言不可以使用这些库中定义的函数和变量的名字作为符号名,否则将会跟现有的目标文件冲突。比如有个用汇编语言编写的库定义了一个函数叫做main,那么我们在C语言里面就不可以定义一个main函数或变量了。同样的道理,如果一个C语言的目标文件要用到一个使用Fortran语言编写的目标文件,我们也必须防止它们的名称冲突。 为了防止类似的符号名冲突,UNIX的C语言就规定,C语言源代码文件中的所有全局变量和函数经过编译后,相对应的符号名加上“”。而Fortran语言的源代码经过编译以后,所有符号名前加上“”,后面也加上“_” 这种方式虽然能够减少冲突的概率,但还是有可能造成冲突。于是C++开始考虑到这个问题,增加了namespace来解决多模块的符号冲突问题。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-04-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文件头
  • 段表
  • 重定位表
  • 字符串表
  • 链接的接口-符号
    • ELF符号表结构
      • 特殊符号
        • 符号修饰和函数签名
        相关产品与服务
        对象存储
        对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档