我们之前在加载mbr和loader的时候,是直接用了它们的地址,比如mbr是0x7c00,loader是0x900,这样的缺点是我们必须每次都用这个地址,不能改,需要提前与程序约定调用地址,这样很不方便,那么怎么改呢?
我们只需要在程序文件的某个特定的地方(比如开始处)写入程序开始的地方,然后从这个特定的地方读出程序开始的地址就可以了,这就是文件头的由来。文件头中还有一些其他的信息,比如程序的大小。
这样纯二进制程序就变成了文件头+文件体的形式。
好处是操作系统加载程序的时候,方法可以是通用的。
不好的地方是这个文件头不是代码,不能执行,程序不再是纯粹的二进制可执行文件。
加载程序的步骤:
1、把程序头加载到内存,得到相关信息
2、把程序体加载到内存,跳到程序开始的地址执行。
linux下可执行文件采用elf(Executable and Linkable Format)的格式
ELF header
1、e_ident[16]占用16个字节。
开头的4个字节是elf的魔数,分别是0x7F和字符串ELF的ASCII码0x45 0x4C 0x46;
e_ident[4]用来标识elf文件的类型,值为0表示不可识别,值为1表示是32位,值为2表示是64位;
e_ident[5],用来指定是大端序还是小端序,值为0表示非法编码,为1表示小端序(LSB),为2表示大端序(MSB)。
e_ident[6],ELF的版本信息,值为0表示非法版本,为1表示当前版本
e_ident[7~15],暂时不用,均为0。
2、e_type占用两个字节
用来指定elf目标文件的类型。2表示可执行。
3、e_machine占2字节,用来描述该文件在哪种硬件平台运行
4、e_version占4字节,用来表示版本信息
5、e_entry占4字节,用来指明操作系统运行该程序时,将控制权转交到的虚拟地址。
6、e_phoff占4字节,用来指明程序头表(program header table)在文件内的字节偏移量,若没有程序头表,则为0。
7、e_shoff占4字节,用来指明节头表(section header table)在文件内的字节偏移量,若没有节头表,则为0。
8、e_flags占4字节,用来指明与处理器相关的标志。
9、e_ehsize占2字节,用来指明elf header的字节大小
10、e_phentsize占2字节,指明程序头表中每个条目的字节大小。
11、e_phnum占2字节,用来指明程序头表中的条目数量,即段的个数。
12、e_phnum占2字节,用来指明节头表中每个条目(entry)的大小。
13、eshnum占2字节,用来指明节头表中条目的数量,即节的个数。
14、e_shstrndxz占2字节,用来指明string name table在节头表中的索引index。
程序头表中的条目(entry)的数据结构
用来描述各个段的信息,其结构名为struct_Elf32_Phdr。
1、p_type占用4字节
2、p_offset占4字节,指明本段在文件内的起始偏移字节。
3、p_vaddr占4字节,指明本段在内存中的起始虚拟地址。
4、p_paddr占4字节,仅用于与物理地址相关的系统中。
5、p_filesz占4字节,指明本段在文件中的大小。
6、p_memsz占4字节,指明本段在内存中的大小。
7、p_flags占4字节,指明与本段相关的标志。
8、p_align占4字节,指明本段在文件和内存中的对齐方式。如果为0或1,表示不对齐,否则p_align是2的幂次数。