[音乐] 我们刚才讲了可重定位目标文件格式,这个可重定位 目标文件格式当中主要的是由若干个节构成的 除了这些节以外呢,还有两个非常重要的部分,一部分叫ELF头 还有一部分呢是节头表,这两部分的信息到底包含什么样的一些内容呢? 我们来看一下。 首先我们看一下ELF 头它是位于一个ELF文件的最开始 也就是从位移量0那个地方开始的一些信息 都属于ELF头信息,它这个里面包含了一些 文件结构的说明信息,给出的是 这个文件,就是ELF这个文件到底 是怎样组成的,它的组织结构是什么样的 这些组织结构的信息都在ELF头里面给出 它要分两种结构,一种是32位系统对应的结构,一种是 64位系统对应的结构,我们称为32位版本和64位版本 我们下面给出的是32位系统对应的一个结构 在这个里面我们可以看出它是一个结构类型的数据结构 由若干个字段我们叫field构成,在这些当中 最开始的是16个字节,这个常量定义的是16 那么也就是说这是一个数组,有16个 字节,这个16个字节里面定义了一些标识信息 然后呢紧接着是版本信息,还有呢 在这个里面定义了采用的数据的排列方式 是大端还是小端方式 因为在编译器对这些高级语言程序当中的这些变量 还有指令当中的一些立即数给它分配空间的时候 涉及到怎么排列的问题,这个先要约定好它 采用的大端方式排列还是小端方式排列,这个排列的方式 在ELF头里面会记录下来,另外呢还会给出 将来的程序所运行的机器的平台或者叫机器结构 是什么类型,是IA-32的这种32位架构呢,还是 x64这种64位架构,编译器以及汇编器在进行转换的时候 转换出来的目标代码是按照32位结构来转换的还是 按照64位架构来转换的等等,这些也要在这儿有约定 约定我们在解释这些指令的时候是按64位架构 来解释还是按32位架构的指令来解释 还有呢这个程序的入口地址 当然如果是可重定位目标文件的话,因为它 并不能够装载执行,而是要进行进一步链接的 所以它的入口地址应该是0后面的,例子里面可以看到 另外它还有程序头表或者叫段头表的起始位置长度 这是程序头表的起始位置,就是偏移量,程序头表是一个表 每个表项的大小以及有多少个表项,还有呢节头表的起始位置和长度 这个就是节头表的起始位置,还有节头表的每一个表项的长度 和节头表的个数等等,还有其他的一些信息 所以我们可以看出这个里面一开始 给出来了一个16字节的一些信息 在这16字节的信息当中最开头的那几个字节称为魔数 就是这16个字节的信息当中,16个字节实际上是一些标识信息 这标识信息最开始的是标识这个 文件的类型或者格式的这种标识信息 称为魔数,如果是a.out 这个文件的话,它的魔数,也就是它的标识信息 是01H 07H,如果是Windows里面用的这种 目标文件格式PE格式,它的魔数是4DH 和5AH,ELF这个目标文件的格式 它的魔数是什么呢?等一会我们后面的例子里面会 说明,这些魔数会用来标识 文件的类型,这样的话在加载和读取文件的时候,首先是把开头的几个字节读进来 读进来了以后和这些特定的值去比,通过比较就能够确定这个文件类型是什么 到底是PE格式的还是ELF格式的 是否是正确的格式等等,这个是魔数的概念 这个ELF头这样的 这个数据结构我们可以看出它一共占52个字节 因为这是4个字节,1 2,3,4,5,6,7 8,9,这是两个字节的,所以加起来4个 字节,这个是4个字节,这是4字节,4字节,4字节,4字节 这样的话一共有9个4字节,四九三十六,36再加16就是52 一共是52个字节 ELF头信息我们刚才讲过了一共有52个字节 这个头信息实际上这个.O文件,在这个.O文件的最开始的52个字节当中来 表示,最开始的52个字节当然都是一些0 1序列 我们是无法直接把它读出来去看它的信息的,都是 编码以后的0 1 信息,如果我们要把这个52个字节 的含义能够读出来的话,必须要用专门的 工具软件,比如说用readelf这种工具软件去读 如果我们要读的是ELF头信息的话,我们就可以这个readelf命令 按照-h这样的一个选项去读,这个 就是说明我们要读这个可重定位目标文件的 ELF的头,这个-h表示是读到这个头,这样的话就会显示 一串信息,就是把main.o里面的前面的52个字节ELF头 进行相应的解析,按照前面我们讲的structure的那个结构进行解析 解析成一些我们人可以读懂的信息,比如说 这个readelf -h读出来的这个信息就是下面这一块的信息 我们在屏幕上就可以看到它的魔数就是这个地方的 这四个,最开始呢是7f,7f就是表示一个0后面有 7个7,就是01111111 紧接着跟的是45h,4ch和46h 很显然这个大家能够猜出来,这个是大写的E这个字符的 ASCII码,编码,然后4c呢是L的ASCII码 46呢是F的ASCII码,所以我们可以看出这个头文件当中 最先的4个字符是ELF的魔数 这个就是ELF的魔数,紧接着 就是说明这个格式是32位版本的 然后呢紧接着说明这个数据采用的是补码表示,也就是说这个里面的 所有的带符号整数是采用的补码表示,而且按小端方式存放 就所有的数据是按小端方式存放的,版本是1,操作系统呢是 UNIX的System Ⅴ,然后呢 也是0版本的,ELF文件的类型是 可重定位的文件类型,就是REL,机器呢是在 32位机器上的编译的目标代码,然后程序的 入口地址是0,因为它是可重定位的,这边的这个类型是可重定位的 它是给出的是链接视图,它不是执行视图 所以它的装入的起始地址当然是0,也就是说它根本就不会 进行执行,然后呢它的这个 程序头表在这个里面是没有的,我们可以看到它只有ELF头和节头表 它没有程序头表,所以这个程序头表在这个里面的偏移量 是0,也就是说明它没有,并且呢程序头表的长度是0字节 而且程序头表当中没有任何表项,它的表项的个数 是0,因为这个类型是可重定位的目标文件,所以它这个里面不包含程序头表 然后给出这个节头表的起始位置是516字节 也就是说这个节头表的这个起始地址 是位于文件当中,从这个位移量为0的地方开始 往下数,数到这是516个字节的位置,开始是节头表 给它的起始地址是516字节,而且呢这个节头表里面 节头表的大小是40个字节 这个是说节头表的这个表项有15个表 然后每个表项是40个字节,节头表每个表项是40个字节 所以我们可以看出这个节头表整个的大小是15个 表项,每个表项是40个字节,所以它的大小一共有 15乘40字节。 这么多个字节 起始地址和大小就确定了节头表在文件当中的位置 在给出了ELF头,就是这个头,也就是ELF头 它的大小,刚才我们讲过了,ELF头是52个字节 是36加上16,一共是52个字节 52个字节,转换成16进制的话,那就是16进制的34 所以,它的字节数呢是0x34个字节 最后呢,它还会给出字符串表 在节头表当中的索引值,也就是在这个节头表里面,实际上是 一项一项指出上面的这些节,一共有15项,其中的第12项 是字符串表也就是这个 这个是ELF头给出来的信息,就是这个52 个字节,里面包含的这些信息,可以通过这样的一个命令 来把它解析出来。 最后我们来看一看节头表 刚才我们看到,在ELF文件格式当中,最后呢是一个节头表 这个节头表里面指出了,每一个节的节名 就是刚才我们看到它有个inmax,然后呢里面会给出一个节名 然后呢这个节在文件当中的偏移,这个节的大小 这个节的访问属性,对齐方式等等,会有很多的一些信息 对于32位系统对应的节头表当中,每一个表项的数据结构 是这样子的,这个里面一共有40个字节,我们可以看出 有1 2 3 4 5 6 7 8 9 10 10个4字节的,这个每一个都是4个字节长度 其中记录了节名,然后记录了节的类型 节的标志也就是访问属性,是可执行的还是可读可写的 然后呢是这个节如果加载到存储器的话,那么这个对应的虚拟地址是什么 如果是可重定位目标文件的话,那么这里面的这些 节的域里面,应该都是放的0,因为它不是被加载的 可重定位目标文件是用来链接的,而不是用来加载执行的,所以对应的应该都是0 然后呢,这个节在文件当中的偏移地址 以及这个节的大小,节在文件当中所占的长度 还有呢,其他的一些链接信息,这些链接信息只有相应的一些节当中 才会有,对于像text节、 data节等等就没有这两个信息 还有对齐方式,如果这个节要装入到存储器执行的时候 这个节在存储器里面的对应位置,起始位置有什么对齐要求 以及最后给出的是 节中每一个表项的这个长度,如果这个节是一个表 组成表的每一个表项的长度是什么。 如果这个节它不是一个 表,那么这个长度呢就是0,这个是节头表 的信息,我们后面会通过一个例子来说明 这个节头表里面的这些信息是什么含义 这就是一个例子,这个是说明 我们要把这一个.o文件,它的节头表信息 通过readelf这个命令把它读出来 如果要读的是节头表信息的话,用-S这个选项 在这个里面,它说明了,这个节头表里面 一共有11个节,描述了11个节的信息 节头表的起始位置是在16进制的120那个位置 我们可以看出在这个节头表里面描述了 0到10这11个节的情况,第0个节是 空节,实际上每一个这个.o文件都有 然后呢是有text节,有对text节的重定位信息的这个节,有data节、 bss节 rodata节等等等等,一共有11个表项 这每一个表项实际上就是一个40个字节的 一个刚才我们看到的,我们回过头来看 每一个表项就是下面的这样的一个数据结构,一共有 40个字节。 这个里面记录了相应的这些信息 在这些信息当中我们可以看到,虚拟地址这一个字段 每一个节都是0,因为它是.o文件,所以它只是 链接视图,它不是执行视图,所以它不会加载执行 所以对应的起始的虚拟地址都是0 [无声] 现在我们讲的是这个节头表里面的信息 在这个里面,我们看到text节 实际上它是从 0x34,16进制的34这个位置开始的 有多长呢?这个size是5b,也就是这个text节呢它的长度是占了 5b这样长,5b是个16进制的数 然后呢它是可装入的,装入到 内存去执行的,而且是只可执行的 它的对齐方式是按4字节对齐,同样的其它的 这些节都有起始位置和长度以及其它的一些信息 我们后面会详细地介绍这些信息 每一个节的位置在这都有说明。 对于刚才的 那样的一个例子,刚才的那个例子,我们把这个起始位置 和所在的长度把它串起来以后,我们可以看到 是有一定的顺序排列的,在这些所有的节里面 有4个节是会要分配存储空间的 也就是说系统运行的时候,需要给它们分配空间的 很显然这些代码是需要分配存储空间的。 text节在整个的虚拟地址空间里面 会占一定的空间,这些空间里面放的都是机器指令。 然后呢data节 肯定也要占空间,在这个里面放的都是初始值,就是我们在程序当中 也就是说.c文件源程序当中定义的那些带初始化的全局 变量,这些初始值要给它分配空间,并且把初始值赋到这个空间里面去 然后第三个呢,bss这个虽然没有初始值 但是它真正运行的时候,我们要对那个位置上面的值 去进行取或者是写结果的,因此它也要占空间 还有是只读数据段,它也要给它分配空间,并且把一些只读信息 事先存放在这个空间里面。 其它的这些节,在真正装入运行的时候它们是不占空间的 只有这4个节会在存储空间当中会分配给它们地址 我们前面看到那个虚拟地址空间当中的那个位置的时候,我们可以看到它是 占有一定的空间,后面我们也会举例子来说明 下面我们来看一看在这个里面的结构,就是text.o这个里面的这个结构 那么很显然ELF头呢是占52个字节,相当于16进制的34 所以在这个ELF头后面紧接着跟的是 text节,所以text节的起始位置就是这个offset 是0x34,所以我们可以看到ELF头 结束以后,紧接着就是跟的是text节 它的起始位置呢是34,而且呢在ELF头里面我们讲过 这个里面它有一个字段是指出节头表 的起始位置的,这个节头表的起始位置是 0x120这个位置。 所以这个实际上是一个指针,指出节头 头表的这个位置,在文件里面位于120开始的那块区域 然后在这个里面,放的就是这边这些信息,然后这里面的这个每个信息 给每一个节都指出了一个起始地址,实际上这个地方,节头表里面 每一个表项都指向了上面的一个节,我们来看一下 第一个是text节,它的起始地址是34 所以text节紧跟在elf头后面存放 而text节占5b的位置,所以我们这儿可以看到 34h加上5b 应该是等于,这边是等于f,因为是 b是相当于11,11加4是15,就是f,这是8f 因此它的最后一个地址就是8f,就是在这个地方 所以紧接着这个90,8f以后就是90这个位置,这个位置是 data节的起始位置,我们可以看到data节的起始位置在这个地方 而data节呢,它的长度是12个0c 所以data节就等于90加0c,是 下一个节的起始位置,90加0c就是9c 9c的话,就是rodata,我们可以看到rodata的起始位置就在这个地方,所以 只读数据节,紧接着跟在data节后面 然后9c加上04以后,正好是a0,9c加04 c就是12,加4就是16,就是0,这边 1,1加9就是10,10就是a,所以紧接着就是a0 a0这个地方是这一个节对应的起始地址 然后呢,a0加2e a0加2e,应该是e,然后这边是a a就是10,10加2就是12,12就是c 所以紧接着就是ce,因此我们可以看到,下面一个节 就是这个节,这个节的起始位置是ce 这ce呢,一共有51这么长,所以 ce加51,ce加51 e加1是等于f,就是14加1就是15f c就是12加5,12加5是17,17的话就是 11,所以是11f,然后节头表呢 是从120开始的,所以11f这个地方是空了一个 然后跟刚才那个8f这个地方也是一样的,空了一个 紧接着下面呢就是节头表,所以我们可以看出 在节头表当中,实际上在这个地方 把每一个节它的起始位置,长度 以及对齐方式,它的一些属性 等等都记录了下来,我们要对这个.o文件进行处理的时候 实际上我们只要先读这个ELF头 读到它的起始地址,然后从这个地方去读文件 把节头表里面的每一个表项,也就是每一个这边的信息 读进来,需要处理哪一个节的时候我们就可以 到这个节对应的起始位置开始读信息 这样子来进行处理的,这个就是节头表的一些信息,这个是下面的2d8 实际上就是120加上1b8,因为这个节头表的长度是1b8 为什么是1b8我们可以算一下就能算出来了,实际上每一个表项是40个字节 在这个地方,前面我们讲过了节头表当中的这个 每一个表项是40个字节,40个字节它这儿有11个表项 11个表项乘上40个字节 所以它是等于440个字节 440个字节就是相当于1b8,十六进制的1b8个字节 因此起始地址是120,加上1b8以后正好等于2d8 这个2d8正好是这一个符号表这个节的起始地址 2d8,然后2d8加上它的这个长度是 120,加上去以后正好等于3f8 那3f8正好是下面的字符串表这个节的起始位置 所以整个的是这样子的,最后呢是 text节的重定位信息,就是这个节的 这个信息,它的起始位置在这个地方,498这个位置 然后一共有28个字节,整个这个文件的结构就是 这样的,所以我们可以看出它通过ELF头链接了节头表 然后再通过节头表再把每一个节把它链接起来,这样的话 处理.o文件的时候就可以读到任何一个节的信息 [音乐] [音乐]