接上节,程序头可以自定义,只要我们按照自己定义的格式去解析就行。也许我光这么一说,很多同学还是不能彻底明白如何自定义文件头,因为大多数同学都是用高级语言来写程序,即使用了偏底层的c语言,不同平台的c编译器也会根据系统平台自动添加文件头,不给咱们亲手体验自定义程序头的机会。汇编语言非常 灵活,所以用它来构建任意文件格式则是非常方便的。书看到这里,我估计您已经发现我是个非常体贴的人,哈哈,所以给大家呈上以下代码来演示自定义文件头,请见以下的汇编程序代码:
[work@localhost book]$ cat -n header.S1 header:2 program_length dd program_end-program_start3 start_addr dd program_start4 ;;;;;;; 以上是文件头,以下是文件体 ;;;;;;;5 body:6 program_start:7 mov ax, 0x12348 jmp $9 program_end:10
[work@localhost book]$
以上测试代码header.S,经编译后生成的文件是header.bin,编译命令是nams –o header.bin header.S,所以header.bin依然是纯二进制文件。但此纯二进制文件的程序入口并不是文件开头,这和咱们的loader.bin很像,mbr是跳到loader.bin的中间某部分去执行。假设header.S是被调用的程序,调用方知道header.S的前8字节是程序头,在这8字节中,前4字节用来标明程序尺寸大小,后4字节用来指明程序的入口地址,也就是该程序的第一条指令地址。从8字节后到文件结束为文件体。在调用方程序已经了解此文件格式后,它可以这样做:
- 将header.bin前8字节的内容读到内存,前4字节是程序体长度,后4字节是程序的入口地址。
- 将header.bin开头偏移8字节的地方做为起始,将header.bin文件尾,即开头偏移(8+程序体长度)个字节的地方做为终止。
- 将起始至终止之间的程序体复制到入口地址
- 转到入口地址处执行
您看,被调用方header.bin被设计成这样文件格式,调用方就可以自由处理啦。为了验证文件格式,大家看下header.bin生成的二进制文件真实内容,前8字节用了两个下划线分别区分了两个程序头属性,即program_length是0x00000005,start_addr是0x00000008。顺序是反着的,小端字节序。B83412是mov ax,0x1234指令,EBFE则对应的是jmp $。
当然这仅仅是用来演示,目的是起到抛砖引玉的作用,说了这些是为了引出后面咱们要介绍的文件格式。
自己设计的文件头自己当然认识,但这毕竟不通用,我们需要选择一种流行的文件格式,咱们是在linux下用c语言编程,其编译器gcc生成的是elf文件格式,咱们在下一节展开elf可执行文件格式的内容。
本内容摘自《操作系统真象还原》