描述 Program header table 的结构体:
描述 Section header table 的结构体:
ELF header(ELF 头)
头部内容,就相当于是一个总管,它决定了这个完整的 ELF 文件内部的所有信息,比如:
这是一个 ELF 文件;
一些基本信息:版本,文件类型,机器类型;
Program header table(程序头表)的开始地址,在整个文件的什么地方;
Section header table(节头表)的开始地址,在整个文件的什么地方;
你是不是有点纳闷,好像没有说 Sections(从链接器角度看) 或者 Segments(从加载器角度看) 在 ELF 文件的什么地方。
为了方便描述,我就把 Sections 和 Segments 全部统一称为 Sections 啦!
其实是这样的,在一个 ELF 文件中,存在很多个 Sections,这些 Sections 的具体信息,是在 Program header table 或者 Section head table 中进行描述的。
就拿 Section head table 来举例吧:
假如一个 ELF 文件中一共存在 4 个 Section: .text、.rodata、.data、.bss,那么在 Section head table 中,将会有 4 个 Entry(条目)来分别描述这 4 个 Section 的具体信息(严格来说,不止 4 个 Entry,因为还存在一些其他辅助的 Sections),就像下面这样:
在开头我就说了,我要用字节码的粒度,扒开来给你看!
为了不耍流氓,我还是用一个具体的代码示例来描述,只有这样,你才能看到实实在在的字节码。
程序的功能比较简单:
// mymath.c
int my_add(int a, int b)
{
return a + b;
}
// main.c
#include <stdio.h>
extern int my_add(int a, int b);
int main()
{
int i = 1;
int j = 2;
int k = my_add(i, j);
printf("k = %d ", k);
}
从刚才的描述中可以知道:动态库文件 libmymath.so, 目标文件 main.o 和 可执行文件 main,它们都是 ELF 文件,只不过属于不同的类型。
这里就以可执行文件 main 来拆解它!
我们首先用指令 readelf -h main 来看一下 main 文件中,ELF header 的信息。
readelf 这个工具,可是一个好东西啊!一定要好好的利用它。
这张图中显示的信息,就是 ELF header 中描述的所有内容了。这个内容与结构体 Elf32_Ehdr 中的成员变量是一一对应的!
有没有发现图中第 15 行显示的内容:Size of this header: 52 (bytes)。
也就是说:ELF header 部分的内容,一共是 52 个字节。那么我就把开头的这 52 个字节码给你看一下。
这回,我用 od -Ax -t x1 -N 52 main 这个指令来读取 main 中的字节码,简单解释一下其中的几个选项:
-Ax: 显示地址的时候,用十六进制来表示。如果使用 -Ad,意思就是用十进制来显示地址;
-t -x1: 显示字节码内容的时候,使用十六进制(x),每次显示一个字节(1);
-N 52:只需要读取 52 个字节;
这 52 个字节的内容,你可以对照上面的结构体中每个字段来解释了。
首先看一下前 16 个字节。
在结构体中的第一个成员是 unsigned char e_ident[EI_NIDENT];,EI_NIDENT 的长度是 16,代表了 EL header 中的开始 16 个字节,具体含义如下:
0 - 15 个字节
怎样样?我以这样的方式彻底暴露自己,向你表白,足以表现出我的诚心了吧?!
如果被感动了,别忘记在文章的最底部,点击一下在看和收藏,也非常感谢您转发给身边的小伙伴。赠人玫瑰,手留余香!
为了权威性,我把官方文档对于这部分的解释也贴给大家看一下:
关于大端、小端格式,这个 main 文件中显示的是 1,代表小端格式。啥意思呢,看下面这张图就明白了:
那么再来看一下大端格式: