技术分析:从字节码的粒度来探索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  下一页>