前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ELF 64 格式详解

ELF 64 格式详解

作者头像
一只小虾米
发布2022-11-23 15:25:26
7510
发布2022-11-23 15:25:26
举报
文章被收录于专栏:Android点滴分享Android点滴分享

本篇介绍

本篇详细介绍下ELF 64的文件格式。

ELF文件分类

  • 可重定位文件(.o),包含代码和数据,但是代码和数据都没有指定绝对地址,需要链接其他目标文件来生成可执行文件或共享目标文件
  • 共享目标文件(.so),包含代码和数据,以供链接器使用
  • 可执行文件, 包含代码和数据,是可以执行运行的程序,代码和数据都有固定的地址

ELF文件内容

一个ELF文件需要包含以下部分:

  • elf文件头,必须出现在elf文件的开头
  • 节头表(Section header table), 重定位的文件(可重定位文件)必须包含,可加载的文件可选(共享目标文件,可执行文件)
  • 段头表(Program header table),可加载的文件必选,重定位文件可选
  • 段和节的实际内容,包括可加载的数据,符号表等

节头表和段头表其实分别是链接和加载的视图,结构大致如下:

image.png

ELF 64的数据类型定义如下:

image.png

ELF文件头格式

文件头格式如下:

image.png

可以找一个so,用 readelf -h 看看输出,结果可以完全对上:

image.png

对于MacBook M1 的设备可能没有readelf,objdump等命令,一个简单的方法是可以直接使用ndk中的命令,llvm-readelf, llvm-objdump等

接下来分别看下各个字段的含义:

e_ident

elf文件的标识,一共16个字节,各个字段的含义如下:

image.png

  • e_ident[EI_MAG0] ~ e_ident[EI_MAG3] 是用来标识ELF文件的魔数,0x7f, 'E','L','F'
  • e_ident[EI_CLASS] 用来标识对应的ELF文件类别,可取的值如下:

image.png

  • e_ident[EI_DATA] 用来区分字节序,可取的值如下

image.png

  • e_ident[EI_VERSION] 目标文件格式的版本,目前就是EV_CURRENT,也就是1
  • e_ident[EI_OSABI] 该文件的目标操作系统和ABI,可取的值如下

image.png

  • e_ident[EI_ABIVERSION] 该文件的目标ABI版本,如果兼容System V ABI 第三版,该字段应该是0
  • e_type 该文件的类型,可取的值如下

image.png

  • e_machine 标识目标架构
  • e_version 文件格式的版本
  • e_entry 程序入口的虚拟地址
  • e_phoff 程序段头表在该文件内的偏移,单位是字节
  • e_shoff 节头表在该文件内的偏移,单位是字节
  • e_flags 包含处理器特定的标记
  • e_ehsize ELF头的大小,单位是字节
  • e_phentsize 程序段头表项的大小,单位是字节
  • e_phnum 程序段头表项的数量
  • e_shentsize 节头表项的大小,单位是字节
  • e_shnum 节头表项的数量
  • e_shstrndx 节头表中包含节名字的字符串表索引。

ELF节

节包含了ELF文件中除了文件头,程序段头表,节头表之外的所有内容。 节的索引中有几项是特殊的,比如如下几个:

image.png

可以实际看一下节的内容,通过readelf -S 命令就可以看到:

image.png

再看下节头表中项结构的定义,可以和输出的格式对上:

image.png

  • sh_name 节头名字在字符串表中的偏移,单位是字节。举一个例子,上图中第一项的名字是".note.android.ident",看看是如何计算的。 节头表的位置是1393984, 转成16进制 0xD4EBC0, 每项64字节,加上第0个保留项,那偏移就是0xD4EC00,具体偏移是0x0b

image.png

字符串表的索引是24,那偏移就是0xD4F1C0,内容如下:

image.png

按照上述的结构定义,可以看到sh_offset 的偏移是0xD4EADC, 再加上字符串偏移 0x0B,位置就是0xD4EAE7

image.png

这样就字符串位置计算出来了。

  • sh_type 节的类型,可选的值如下:

image.png

  • sh_flags 当前节的属性,可选的值如下:

image.png

  • sh_addr 该节在内存中的虚拟地址,如果不加载到内存中,地址是0
  • sh_offset 该节在文件中的偏移,单位是字节
  • sh_size 当前节在文件中占用的空间,唯一的例外是SHT_NOBITS,不占用文件空间
  • sh_link 当前节关联的节索引,用途如下所示

image.png

  • sh_info 当前节的其他信息,用途如下

image.png

  • sh_addralign 对齐参考,需要是2的幂
  • sh_entsize 如果节中包含表,该字段表中每项的大小,单位是字节

用于程序代码和数据的节如下:

image.png

用于文件信息的节如下:

image.png

字符串表

字符串节表包含用于节名字和符号名字的字符串,内部的字符串表是包含C格式的字符串,对外的索引就是对应字符串的起始位置偏移,单位是字节。

符号表

符号表结构如下:

image.png

  • st_name 符号名字在符号字符串表中的偏移
  • st_info 符号的绑定属性和类型,高4比特是绑定属性,低4比特是符号类型, 绑定属性定义如下:

image.png

符号类型定义如下:

image.png

image.png

  • st_other 保留字段,保持是0就行
  • st_shndx 定义当前符号的节索引,如果是未定义的,字段值是SHN_UNDEF,对于绝对符号的,值是SHN_ABS,common符号的,值是SHN_COMMON
  • st_value 符号的地址,可能是绝对或相对的地址,对于可重定位的文件,值是定义该符号的节的相对偏移,对于可执行或可供行的文件,值是定义该符号的虚拟地址
  • st_size 该符号对应的值的存储空间大小,如果未知,字段值是0

可重定位表

ELF 文件有2种重定位格式,"Rel"和"Rela", 前者较短,记录相对于符号原始值的偏移,后者是记录相对于特定字段的偏移。结构定义如下:

image.png

  • r_offset 标识需要重定位的位置,对于可重定位文件,是从节开头到需要被重定位的存储位置的偏移量,对于可执行或共享库,是需要被重定位的存储位置的虚拟地址,单位都是字节
  • r_info 包含符号表索引和重定向类型,符号表索引用于标识当前项在对应符号表中的符号,重定向类型是处理机指定的。
代码语言:javascript
复制
#define ELF64_R_SYM(i)((i) >> 32)
#define ELF64_R_TYPE(i)((i) & 0xf f f f f f f f L)
#define ELF64_R_INFO(s, t)(((s) << 32) + ((t) & 0xf f f f f f f f L))
  • r_addend 计算重定向位置时候需要额外加的常数项

程序段头表

对于可执行和共享库,为了加载方便,用的视图是段,也就是内容一样,只是分类方式变化了。 可以先实际看下共享库的段表信息,readelf -l libtxffmpeg.so

image.png

可以看到,段是由节组成的,这是对于加载器,权限一样的,就可以合并到一块,方便内存的管理。

再看下段结构,可以和上图对得上:

image.png

  • p_type 段的类型,参考如下:

image.png

image.png

  • p_flags 段属性,高8比特是处理器专用,接下来8比特是环境变量专用

image.png

  • p_offset 当前段相对于文件的偏移,单位是字节
  • p_vaddr 当前段在内存中的虚拟地址
  • p_paddr 保留项,用于物理寻址的系统
  • p_filesz 当前段在文件中的大小,单位是字节
  • p_memsz 当前段在内存中的大小,单位是字节
  • p_align 段的对齐约束,需要是2的幂

Note 节

SHT_NOTE的节或PT_NOTE的段,被编译器或其他用于用于存放一些特殊的信息,而这些信息可以被特定的工具所用,格式如下:

image.png

  • namesz and name 记录用于该项所有者的名字长度和名字,name 包含一个C格式的字符串,并且按8字节对齐
  • descsz and desc 该项的描述符长度和描述符信息,描述符信息需要 8字节对齐
  • type 和该项所有者,信息解析者相关的一个值

动态段表

动态段表的实际内容如下:

image.png

结构定义如下:

image.png

  • d_tag 该项的类型,也会决定d_un的解析,具体取值如下:

image.png

image.png

image.png

  • d_val 按整数值解析
  • d_ptr 按虚拟地址解析

哈希表

使用哈希表可以加快动态符号表的查找速度。哈希表就是DT_HASH的节,看下实际例子的输出,命令是readelf --gnu-hash-table

image.png

内容比较多,忽略了一部分,接下来看下结构:

image.png

对应的哈希函数如下:

image.png

一个哈希表需要解决如何快速查找,如何解决冲突的问题。 看看hash 表如何快速查找,这儿用到了一个Bloom Filter, 本质上就是在查找前先用Bloom Filter判断下,如果结果是不在,那么就没必要查找了,如果是在,实际上也不一定在,就需要实际去查一下。 解决冲突时利用了chain数组,在查找符号时,如果Bloom Filter判断出在,然后就在bueket中对应索引位置看看,如果等于期望的字符串,那么直接返回,然后在chain数组同样索引位置拿到下一个需要查找的位置,然后递归查找下去,直到 chain中的值是STN_UNDEF。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本篇介绍
  • ELF文件分类
  • ELF文件内容
    • ELF文件头格式
      • e_ident
    • ELF节
      • 字符串表
        • 符号表
          • 可重定位表
            • 程序段头表
              • Note 节
                • 动态段表
                  • 哈希表
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档