[音乐] 下面我们继续来看可执行目标文件的一些细节
问题,我们通过一些例子来,说明可执行目标文件当中, 重要的程序头表的含义。
我们知道可执行文件描述的 执行视图,这个里面当然包含了一些代码和数据,
这些数据节包括data节和bss节 当中的这些变量,还有代码节当中的这些函数,
它们都是已经有确定地址了,这些地址实际上是虚拟地址空间里面的虚拟地址。
然后符号引用处也已经被重定位了,因为它生成可执行文件
是通过链接来生成的,链接的一个重要的工作是进行重定位,所以
已经进行了重定位,重定位的这个值,也就是重定位那个地方
的这些地址指向的是所引用的定义处的符号, 一个可执行文件在linux
里面它是没有扩展名的, 如果不指出这个文件名的话,默认的就是a.out
在windows里面,它这个可执行文件实际上就是exe文件。
这种可执行的目标文件是可以直接被CPU执行的,
里面指令的地址,或者指令指出的操作数的地址
都是虚拟地址,在可执行目标文件当中, 它把所有的这个代码段都合并成
text节, 把所有的数据包括
有初始化值的数据和无初始化值的数据都进行了合并。
显然这个里面还有其它的节,重要的节就是这些,这些节
它们有的是具有相同的访问属性的, 比如说,这两个节它们的访问属性,都是可读可写,rodata
这个只读数据节和text节,它们都只能够读不能写,
所以它们是具有相同属性,它们这些节是具有相同属性,这样具有
相同属性的这些节会进行合并映射到存储空间里面的一个段,
这个段就是连续的一块空间,那么这样的话,管理的时候就比较好管理,这个节里面是
可读可写的,这些节合并在一起的这个段是只读的,这些节合并到一起的这个段是可读可写的,
这样的话,相同属性的这些节合并起来以后,再进行存储管理的时候,就
很好进行管理了,要说明这些节如何映射到不同的这个段里面去,
需要有相应的说明的信息,这个说明的信息就是我们讲的程序头表,
程序头表是用来说明段信息的, 因此也称为段头表,这是说明这个
segment 的, 每个段它有属性,包括在可执行文件当中的位移,就是
这个段对应在可执行文件当中,从什么地方开始的多长的地方?
然后再虚拟空间当中,这个段是从什么地方开始?这个段开始
地方的对齐方式,比如说是4字节对齐还是怎么对齐?
这个访问属性是可读可写,还是只读,还是可以执行等等,这样
的一些信息都是在程序头表里面进行说明的。
这个就是程序头表的数据结构 ,它是一个struct,
就是由很多字段够构成的, 所以这个程序头表就描述可执行文件当中的节,
和虚拟空间当中的这个segment存储段之间的一种映射关系,
这个里面实际上有32个字节,有1、 2、 3、
4 5、 6、 7、 8就是每个字段是32位的4个字节,
四八三十二,所以每个表项就有32个字节, 这一个struct就是定义的一个表项,
对一个段进行的说明,一个表项 说明了一个段,或者是一个特殊的节,
一下给出来的是一个可执行文件的程序头表,这信息在这个里面。
这个程序头表里面给出了这个是属于什么节?
然后这个节在文件当中从什么位置开始?在文件当中的起始位置,
这个对应的映射到虚拟地址空间里面, 映射到存储空间里面,在存储空间里面的起始地址是什么?
真正的物理地址是什么,其实这两个地址是完全一样的,
因为现在物理地址实际上不考虑,所以跟虚拟地址是一样的,
然后对应的是在 文件当中有多长?然后在存储空间里面有多长?
然后这一个访问属性是什么?对齐方式是什么? 这个是一个表项,也就是我们这的这么一个表项,
它的信息,这个信息就是刚才我们讲的 它的Type,它的offset是指在文件当中从什么地方开始?
对应到虚拟地址空间当中的什么地方开始? 然后文件里面长度是多少?这么长的一串信息
把它映射到存储空间里面,这个存储空间的长度是多少?
这个是flags就是这边的这个存储属性也叫存储标志。
这是对齐方式,所以我们下面看到 这一个可执行文件实际上就是main这个可执行
文件,它用readelf这个命令 在—l这个选项下面表示要
把main这个文件当中的程序头表 通过相应的方式把它解析出来,变成
人可读的信息,本来在main这个文件当中, 它这个程序头表都是0/1序列,都是
这样的一些信息,直接显示是不可读的,通过这个
命令以后,或者是这个程序,这个软件, 把它进行解析以后,就显示成
这样的一个一个的有含义的可读的信息,比如说这儿我们可以看到
有一个程序头就是这个程序头表, 一个项,就是这个表项里面的第一项,就是程序头表,
然后还有两个LOAD类型,这两个我们称为可装入段,Type是等于LOAD的,
称为可装入段,它是需要装到存储 器去的,需要映射到存储空间里面去的,
程序头表里面的起始地址34,长度是16进制的100,
那么这两个值,大家应该能够回想出来,是什么意思? 34我们前面讲过在可重定位
目标文件当中,ELF头以后, 就是ELF头,紧接
着的那个位置就是0x34那个位置, 因为它的16进制是等于52,
我们前面讲过ELF头,它一共占了52个字节, 它有36个字节加上一开始的16个字节,
所以它占了52个字节,ELF头后面紧接着的那个位置
就是第52个字节开始的那个地方,也就是16进制34开始的地方。
所以我们可以猜出这个程序头表是紧接在ELF
头后面存放在文件里面的,这个程序头表长度是100
而程序头表的长度是程序头表里面 8个表项,每个表项占32个字节,这边我们看
出它是占32个字节,所以它总共的这个长度是8的表项,
每个表项是32个字节,所以我们可以看出它的长度
就是2的3次方乘上2的5次方,就是3加5
就是2的8次方的字节,2的8次方的字节就是1后面有8个0,
二进制对应的二进制就是1后面有8个0,
这一个长度实际上就是16进制的0x
100这16进制的0,这16进制的0,16进制的1,就是16进制的100,
因此就是所以我们可以看出,程序头表在文件当中
从这个位置开始,并且占了16进制的100个字节。
这是它的长度,所以我们后面来看它是这样子的。
这就是程序头表,这个是第一个可装入段, 是从文件的最开始的位置,
然后有4第4 这么长,在文件当中所占的长度是这么长。
然后映射到存储空间是8048000开始的这个地方。
8048000是一个特殊的值,我们等一会会讲。
这个在存储空间里面也是占相同的长度。
然后呢,是可读可执行的,不可写。
那么大家也能够猜出,这个应该是只读代码段。
就是只读数据和text节这种代码,都是在这个可装入段
里面,它的对其方式是按0x1000对齐的。
这个实际上就是2的12次方,2的12次方
就是等于4KB,就是按4KB
对齐的,所以,这个装入段的起始地址一定是
2的12次方的倍数,4K倍,实际上也就是16进制的最后3个是0。
对应的2进制的话,最后12个应该是0,也就是它的
起始地址最后12位应该是全0。
对应的16进制最后3位是全0。
然后第二个可装入段是从文件的
F0C的那个位置开始,从那个位置开始108个字节的信息
把它映射到从8049F0C那个位置开始,
开始的100,长度是1000的那个位置。
对齐方式也是按2的12次方字节对齐。
也就是说,这个装入段的起始地址是2的12次方的倍数。
也就是说它最后的12个都是0,也就是16进制的那3位是0。
我们可以详细的来解释一下。
第一个可装入段就是它,它的对应 的在文件当中的位置,就是00000到004d3,
一共是4d4个字节。
就是这边4d4个字节,这个范围 内的信息实际上是包含了ELF头、 程序头表,
然后初始节、 text节,和rodata节,它映射到
这个位置开始的长度为4d4字节的这个区域里面。
所以它的起始地址是8048000,8048000。
它的长度,和mem里面的长度也是4d4,也是4d4。
对齐方式呢是4KB对齐,就是 最后的16进制的这个地址,其3位都是0。
然后权限是可读可执行的,所以应该是只读代码段,我们来看一下。
就是这样子的,这个位置 是从000开始,
到4d3为止,一共有
4d4个字节,实际上也就是从
这个位置开始,一直到这个结束,也就是 ELF头,加上程序头表,加上这3个节。
一共有4d4个字节。
映射到存储空间的8048000开始的那个地方。
这个实际上是只读的,
为只读数据,只读数据,代码那么这是可执行的,是代码段,我们称为只读代码段。
这一块的长度,从这开始,也是4d4个字节的长度。
其中在这个范围内有一块区域是程序头表。
它的起始地址是00034,长度是 100,16进制的100。
然后我们来看第二个 装入段,第二个装入段就是蓝颜色这个表项所指出的。
我们可以看到这个是从文件的f0c这个地方开始的。
长度为108,那么这个地方开始的实际上是一个data节。
也就是在这个里面有0x108个字节是一些
已经初始化的那些变量的初始值放在这个节里面,它映射到
这个位置开始,就是8049f0c这个位置开始。
长度是100,长度是100,那么 我们可以看出data节里面实际上是108个字节。
但是我们在存储空间里面有了更多的 更长的空间,长多少呢?长了8个字节。
这8个字节实际上是对应的bss节。
在 data节里面的这些内容,实际上都是一些初始化的值。
它直接装入到前面的这个108字节里面去。
后面的8个字节,空出来的这个地方,实际上对应的是bss节里面的8个字节。
这8个字节对应的初始化的值是0,在磁盘
里面是不保留的,而在存储空间里面就给它填上0, 8个字节的0。
另外呢,它的对齐方式也是按4KB对齐,是可读可写的。
所以称为可读写数据段,我们可以看一下这个可读写数据段。
这个我们刚才讲过了,是从这开始,这个是前面的只读代码段,是在这。
后面紧接着的,我们讲是从文件里面的 00f0c那个地方开始的,我们看看
是000f0c开始的108个字节的data节。
我们可以看出是从f0c开始的这个
里面有108个字节是初始化
的一些数据,然后呢实际上这里面紧接着是bss节。
但是它在磁盘里面是没有信息的,它只是在节头表里面
指出它占8个字节,所以紧接着的这个位置, 这个位置实际上是
f0c加上108,就是data这个节是108。
加出来的这个位置就是4进1, 进1,因为这个c加8
就是20,20减16就是4。
所以这个地方4,然后进16,这就是1。
然后f就是15,加1是16,所以是1014,1014这个位置实际上是
这个节的起始位置,然后呢data节是108个字节,bss节
呢在磁盘里面实际上是不占空间,但是它的这个长度,是可以取到,在节头表里面会给出来。
这样的话,这个108加上008加出来的就是
110,8加8就是16,16的话就是0,这是1,这是1。
所以这个长度,2个加起来是100。
因此在存储空间里面 我们给可读写数据段分配空间的时候,除了这个data节,
还要加上bss节,所以一共是给它分配了110个字节。
当然这是16进制的,这个长度是110,因此我们可以看到它虽然data
节里面是108字节,但是我们在存储空间里面实际上是110字节。
这个位置 就是从F0C这个地方的这些信息从哪里开始放呢?是从
8049f0c这个位置开始放,这个位置是在
804900后面的这个位置,实际上 804900是可读写数据段的起始位置。
为什么它是它的起始位置呢?我们再回到前面去看。
它的对齐方式,我们可以看到它的对齐方式
是按4KB对齐的,也就是说它最后3位16进制地址 最后3位一定是000,这边的对齐方式。
所以我们可以看到,它这里面的起始地址应该是900这个地方。
最后3位是000。
然后这个地方开始的是这个 段的起始地址,这个段里面从这个位置
开始,映射的是data节加bss节的110
个字节的位置,这个8049f0c
是在刚才我们看到的那个程序头表里面给出来的这个数就是,
8049f0c,是在这给出来的。
所以我们可以看出,程序头表里面,实际上 详细地记录了目标文件当中
某一个位置和相应的,就是这个目标文件,
可执行目标文件当中相应这些位置和
存储器,实际上是个虚拟地址空间,虚拟地址空间之间的这个映射关系。
这个里面我们要注意的就是bss节是不占磁盘空间,这个地方实际上是不占磁盘空间。
紧接着应该是这个节,但是呢,它在存储空间分配的时候这个空间还要给它留着。
这个是可执行文件的存储映像。
[音乐] [音乐]