技术分析:从字节码的粒度来探索ELF文件
道哥分享初次见面
大家好,我是 ELF 文件,大名叫 Executable and Linkable Format。
经常在 Linux 系统中开发的小伙伴们,对于我肯定是再熟悉不过了,特别是那些需要了解编译、链接的家伙们,估计已经把我研究的透透的。
为了结识更多的小伙伴,今天呢,就是我的开放日,我会像洋葱一样,一层一层地拨开我的心,让更多的小伙伴来了解我,欢迎大家前来围观。
以前啊,我看到有些小伙伴在研究我的时候,看一下头部的汇总信息,然后再瞅几眼 Section 的布局,就当做熟悉我了。
从科学的态度上来说,这是远远不够的,未达究竟。
当你面对编译、链接的详细过程时,还是会一脸懵逼。
今天,我会从字节码的颗粒度,毫无保留、开诚布公、知无不言、言无不尽、赤胆忠心、一片丹心、鞠躬尽瘁、死而后已的把自己剖析一遍,让各位看官大开眼界、大饱眼福。
您了解这些知识之后呢,在今后继续学习编译、链接的底层过程,以及一个可执行程序在从硬盘加载到内存、一直到 main 函数的执行,心中就会非常的敞亮。
也就是说,掌握了 ELF 文件的结构和内容,是理解编译、链接和程序执行的基础。
你们不是有一句俗话嘛:磨刀不误砍柴工!
好了,下面我们就开始吧!
文件很单纯,复杂的是人
作为一种文件,那么肯定就需要遵守一定的格式,我也不例外。
从宏观上看,可以把我拆卸成四个部分:
图中的这几个概念,如果不明白的话也没关系,下面我会逐个说明的。
在 Linux 系统中,一个 ELF 文件主要用来表示 3 种类型的文件:
既然可以用来表示 3 种类型的文件,那么在文件中,肯定有一个地方用来区分这 3 种情况。
也许你已经猜到了,在我的头部内容中,就存在一个字段,用来表示:当前这个 ELF 文件,它到底是一个可执行文件?是一个目标文件?还是一个共享库文件?
另外,既然我可以用来表示 3 种类型的文件,那么就肯定是在 3 种不同的场合下被使用,或者说被不同的家伙来操作我:
可执行文件:被操作系统中的加载器从硬盘上读取,载入到内存中去执行;
目标文件:被链接器读取,用来产生一个可执行文件或者共享库文件;
共享库文件:在动态链接的时候,由 ld-linux.so 来读取;
就拿链接器和加载器来说吧,这两个家伙的性格是不一样的,它们看我的眼光也是不一样的。
链接器在看我的时候,它的眼睛里只有 3 部分内容:
也就是说,链接器只关心 ELF header, Sections 以及 Section header table 这 3 部分内容。
加载器在看我的时候,它的眼睛里是另外的 3 部分内容:
加载器只关心 ELF header, Program header table 和 Segment 这 3 部分内容。
对了,从加载器的角度看,对于中间部分的 Sections, 它改了个名字,叫做 Segments(段)。换汤不换药,本质上都是一样一样的。
可以理解为:一个 Segment 可能包含一个或者多个 Sections,就像下面这样:
这就好比超市里的货架上摆放的商品:有矿泉水、可乐、啤酒,巧克力,牛肉干,薯片。
从理货员的角度看:它们属于 6 种不同的商品;但是从超市经理的角度看,它们只属于 2 类商品:饮料和零食。
怎么样?现在对我已经有一个总体的印象了吧?
其实只要掌握到 2 点内容就可以了:
一个 ELF 文件一共由 4 个部分组成;
链接器和加载器,它们在使用我的时候,只会使用它们感兴趣的部分;
还有一点差点忘记给你提个醒了:在 Linux 系统中,会有不同的数据结构来描述上面所说的每部分内容。
我知道有些小伙伴比较性急,我先把这几个结构体告诉你。
初次见面,先认识一下即可,千万不要深究哦。
描述 ELF header 的结构体:
1 2 3 下一页>