引言 #
本来自己查了很多资料,想自己写出来,结果下笔的时候发现别人已经把我想写的部分全部写出来了,而且比我想的还要具体,所以我就不写了,把链接放出了,顺便我补充一些
http://leenjewel.github.io/blog/2014/07/29/%5B%28xue-xi-xv6%29%5D-cong-shi-mo-shi-dao-bao-hu-mo-shi/
http://leenjewel.github.io/blog/2015/05/26/%5B%28xue-xi-xv6%29%5D-jia-zai-bing-yun-xing-nei-he/
ELF文件 #
首先你要知道什么是ELF,可以看一下这篇博客,简单来说就是编译完C后的机器码,前面加了一些数据表记录程序的分布情况
为了帮助我们逆向分析这些代码有什么,我们必须要借助两个工具
- objdump
- readelf
我们接下来就用实际例子解读mit6.828里面的引导和核心
引导 #
首先我们查看一下引导文件,在boot/
目录下有两个文件
- boot.S
- main.c
两者是通过gcc编译器将汇编和C编译成为一个elf
文件,具体在Makefile
文件中(boot/Makefrag
),我将它简单翻译一下
gcc -N -e start -Ttext 0x7C00 -o boot.out boot.o main.o
boot.o
和main.o
就是boot.S
和main.c
编译后的文件,-e start
意思程序从boot.S
的start
中开始运行(这样就能从汇编开始执行),-Ttext
中text
代表代码段,也就是说直接指定代码入口地址为0x7C00
,我们可以用readelf
验证一下
mit-6.828-2014 ➤ readelf -h obj/boot/boot.out git:lab1*
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x7c00
Start of program headers: 52 (bytes into file)
Start of section headers: 4868 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 2
Size of section headers: 40 (bytes)
Number of section headers: 9
Section header string table index: 6
我们读取了一下生成的elf
文件头部,我们可以看到Entry point address
这个字段,就是我们设定的0x7c00
,要了解这个地址的含义我们必须知道虚拟地址和物理地址的区别,可以看一下这篇博客,接着我们看一下反汇编的汇编代码obj/boot/boot.asm
中,我们知道现在0x7c00
代表程序把自己当做在内存上的真实内存上面,但是不一定会真的存在这块上,所以我们称它为虚拟的
首先我们了解一个系统知识,因为电脑启动后会按照启动盘顺序,把每个盘第一个扇形区512B取出来,如果最后两个字节为0xAA55
的话就把它放到内存上面的0x7c00
上去,我们看一下我们生成的obj/boot/boot
使用hexdump obj/boot/boo
可以看到(我把最后一行复制出来)
00001f0 0000 0000 0000 0000 0000 0000 0000 aa55
最后连个字节为0xaa55
,且文件大小刚刚好512B,这时候你可以有一个疑惑了,我们知道gcc
我们生成的boot.out
的elf文件,但是这个文件还有头部存贮数据,我们如果把整个文件放到磁盘上,当程序在内存0x7c00
处执行时,那么文件头部碰到的就是elf头了,所以
为了把机器码提取出来并生成合适文件(512B尾部为0xaa55
),程序干了两件事
- 用objcopy将elf文件中执行代码提取出来(相当于去掉elf头部)
- 用脚本修改尾部两字节(在
boot/sign.pl
用了perl程序来将生成512B且尾部为0xaa55
的boot
文件)
总结 #
后面将核心加载到内存,上面的给出资料写的很详细,我就不多说,只不过由于当前2014
的版本同资料有点不同,这里我提一下
当前版本是将内核头加载在0x10000
上,然后在把内核代码加载到0x100000
上(前面4个0,后面5个0,我当初看错了,百思不得其解),并将内核地址映射到f0100000
上。
由于资料给的操作系统与2016的操作系统实现细节有点不同,其中最主要的就是一个很重要的KERNBASE
常量值由0x80000000
变成0xf0000000
,这个变量牵扯到了在给出的book-rev8.pdf
资料中第一章的最后一个问题,我就把我对这个问题的思考放到这篇文章中。
引用 #
http://leenjewel.github.io/blog/2014/07/29/%5B%28xue-xi-xv6%29%5D-cong-shi-mo-shi-dao-bao-hu-mo-shi/