又有闲时间了,一直没有在x86的计算机上做过裸机编程,只要闲下来就手痒,去买了本《30天自制操作系统》,因为之前听说过这本书,而且是在x86的windows电脑上做的,所以就买来研读了一番,之前写的东西也只在群里共享了,这次正好想开个博客,剩下有一年的闲时间可以打理一下这个博客。所以说,这是第一篇文章,我不去照搬照抄书上的内容,我只是参考一下它搭建的过程,所有我操作的过程我会记下来放在这里,以后给自己和大家能有一个参考,毕竟GCC编写操作系统和AT&T汇编这方面的资料太少了。
作为一个忠实的linux用户,我的操作系统在linux上搭建,具体点说是ubuntu,而且有很多地方我写的比较粗,还是得有一定linux基础的小伙伴来看这些文章。有不太懂的或者我写错的地方,可以直接评论区回复,我会尽可能的回答。
创建一个文件夹,以后这就是操作系统的诞生地了,我起的名字是OranOS
一、罗列一下要用的工具
编译器:GCC,具体说是as、ld和gcc,这三个经常写硬件的小伙伴应该是耳熟能详了,第一个是AT&T格式的汇编器,第二个是连接器,第三个是c语言编译器
汇编语言的编写:gedit,默认的gedit不会高亮显示AT&T汇编语言,我一会儿传一个脚本上来,是从国外网站上搬进来的,特别好用,支持AT&T i386高亮显示而且做的挺好看的,当然Eclipse也是个不错的选择,但是前期就没有必要使用了,等系统复杂了我再使用Eclipse
虚拟机:qemu,书上就是用的这个虚拟机,我之前一直都是vmware,不过看起来还是很好用的,apt-get install装上,具体怎么用现学现卖吧
好像写一个系统也就这些东西,似乎很简单的样子,写文章总喜欢感谢一下GNU,在这里再感谢一下这帮伙计。
二、创建今天用的文件
开始了,第一步,在文件夹里创建好今天要用的文件,先说一嘴,操作系统的大致启动步骤,首先CPU上电,内部电路控制CPU去0xFFFFFFF0这个位置取指令,就是在这16个bytes中存着一个小程序,把BIOS加载到内存某个位置,然后jmp去启动BIOS,BIOS会将某种启动方式的启动程序加载到0x7c00处,比如说用U盘启动,它会把U盘最开始的512个字节加载到0x7c00处,然后这512个字节中就要我们自己写一个loader程序,把我们的系统加载到内存中并且启动起来,真复杂哈,不如arm处理器做的简洁。当然,如果作为一个Linux老司机,GRUB是必须的,GRUB会把操作系统加载到0x10000位置,然后启动。我们写的是基于bios启动的,所以,不关心GRUB了,我们的第一步当然就是把这个loader做出来。
创建今天用的文件:
loader.s:loader的汇编源程序
loader.lds:我习惯用lds指定link方式,当然不用也可以
Makefile:如果想每次都输入那么长的命令行的话,就把这个也省略了吧
1、先把次要工作做完,把Makefile的代码贴上来
run : loader.bin
dd if=loader.bin of=loader.img bs=512 count=1
dd if=/dev/zero of=zero.img bs=1474048 count=1 conv=sync
dd if=zero.img of=loader.img bs=512 seek=1
qemu-system-i386 -fda loader.img -boot a
loader.bin : loader.o Makefile loader.lds
ld -M --oformat binary -m elf_i386 -o loader.bin loader.o -T loader.lds
loader.o : loader.s Makefile
as --32 loader.s -o loader.o
clean :
rm *.bin *.o *.img
说一下这么写是什么意思,因为我们要用gemu虚拟机,所以必须要创建一个镜像文件.img,我们按照书上来,选择用软盘启动,所以我们要创建一个含有我们程序的软盘镜像,linux下可以用dd命令创建镜像文件,第一个dd把生成的纯二进制文件(raw binary file)打包成了一个.img,其实就是复制过程,没有做任何改变,第二个dd我创建了一个空文件,它的大小是1474560-512,是除去loader程序的一个空的镜像文件,因为一个软盘的容量是1474560字节,我们就要创建一个这么大的文件来模拟软盘。第三个dd是把两个文件合并了,最后qemu加载软盘跑起来。
在这里说一下ld命令,ld是链接命令,编译和链接是两个过程,-M是生成位图文件,便于我们查看文件具体是怎么链接的,-oformat是指定生成文件格式,选择纯二进制binary,-m是制定机器语言指令集,这里是i386指令集,-o输出文件,-T指定链接文件,一会儿简单讲一下lds文件怎么做。
as命令是gcc编译器的汇编器,默认支持的是AT&T格式汇编,但是似乎也支持NASM格式,NASM们的福音,可以在选项里边指定格式,我因为长期使用gcc做arm,所以可能还是AT&T格式更习惯一些吧,当然30天自制操作系统用的是NASM格式,各取所需。
最后加了个clean
2、这个是lds文件,后边有时间我写一个这方面的文章,网上资料太少了,这么好用的文件
这个lds最基本了,就是把.text段放在了0x7c00位置处,跟ORG命令异曲同工
SECTIONS
{
. = 0x7c00;
.text :
{
*(.text)
}
.data : { *(.data) }
.bss : { *(.bss) }
}
3、开始汇编
.code16
.global _start
.section .text
_start:
jmp entry
.byte 0x90
.ascii "HELLOIPL"
.word 512
.byte 1
.word 1
.byte 2
.word 224
.word 2880
.byte 0xf0
.word 9
.word 18
.word 2
.long 0
.long 2880
.byte 0,0,0x29
.long 0xffffffff
.ascii "HELLO-OS "
.ascii "FAT12 "
.fill 18
entry:
这一大堆是软盘的头文件,软盘格式规定必须要有,最开始给留了2 byte,可以放一个跳转指令,跳转到了程序入口entry,然后我们在程序入口把磁盘的一部分复制到内存中。以后上传代码我的两个代码段中间一定会有重叠,比如这个地方重叠entry:方便一下大家联系。
entry:
movl $0, %eax
movl %eax, %ds
movl %eax, %ss
movl $0x7c00,%esp
movl $0x0820,%eax
movl %eax, %es
movb $0, %ch #柱面0
movb $0, %dh #磁头0
movb $2, %cl #扇区2
read: movb $0x02, %ah #读磁盘
movb $1, %al #读一个扇区
movw $0, %bx
movb $0, %dl #A驱动器
int $0x13 #读磁盘
movl %es, %eax
add $0x0020,%eax
movl %eax, %es
inc %cl
cmp $18, %cl
jbe read
movb $1, %cl
inc %dh
cmp $1, %dh
jbe read
movb $0, %dh
inc %ch
cmp $9, %ch
jbe read
jmp 0x8200
marker1:
.fill 0x1fe-(marker1-_start)
.byte 0x55, 0xaa
不像书里写的那么复杂,我就把复制过程简化了,不考虑出现读盘错误的情况,毕竟在虚拟机里做是不可能出错的,以后完善的时候再考虑,复制过程调用了bios中断int13,可以操作各种存储设备,bios中断向量表网上一查一大堆,自己下载一个对照着指令就会用了,注意是bios!!!不是windows中断向量表,也不是linux。我把软盘前18*2*10*512字节的东西复制到了0x8200的位置处,之所以movl $0x0820, %eax的原因是前边有512字节的东西被bios加载到了0x7c00位置,就不读这一部分了,把前边512字节空出来,这样地址看起来比较完整,毕竟你也不缺那512字节,现在电脑动不动就是16G内存。可能还有小伙伴不懂为什么是0x0820而不是0x8200,因为我们赋值的是ES段,ESx16+BX才是最终地址,不要忘记这是段寄存器。
最后marker1位置处填充了一大堆0,留下最后的两个字节,写入了0x55,0xaa。其实55AA是一段神器的代码,几乎所有的通信校验位,启动标志位都是55AA,英文又把55AA翻译成magic code,确实很神奇哈,为什么人类达成了这样一个共识呢,我们把55 和 AA分别换算成二进制01010101,10101010,哇塞!就是通过这样的一个交替出现的设置,使得信息校验可以获得最大的准确度,具体的要涉及到硬件啦,当个笑话看看。所以呢,只有当bios校验了软盘最开始的512字节的最后两位是55AA,bios才能确定这里边装了一个loader,如果你不把这两位设置为55AA,那即使你的软盘里装了系统,bios也不会加载的,硬盘也是同样的道理。所以,我们现在把软盘的前2*18*10*512的东西装到了内存0x8000处,并且我们最后把指令指针交给了0x8200地址处,我们只需要在这个位置放上我们的系统初始程序就可以了
三、整理文件
整理一下文件夹,在os文件夹里创建一个system文件夹和一个loader文件夹,把loader有关的东西放到loader里边,在system文件夹里再创建三个文件,system.s,system.lds,Makefile
四、编写system.s
我们要开始写系统啦
system.lds链接文件同loader,把初始地址. = 0x7c00改为. = 0x8200
Makefile:
system.bin : system.o Makefile system.lds
ld -M --oformat binary -m elf_i386 -o system.bin system.o -T system.lds
system.o : system.s Makefile
as --32 system.s -o system.o
clean :
rm *.bin *.o *.img
等把makefile改成嵌套型,就好用多了,现将就一下
打开system.s,写一个最简单的程序,把命令行模式改成画图模式,屏幕就会变黑,我们就证明我们的“系统”启动了
.code16
.global _start
.section .text
_start:
movb $0x13, %al
movb $0, %ah
int $0x10
final:
hlt
jmp final
就这么简单,int 10中断可以操作屏幕,具体的自己搜咯
make一下,生成了system.bin,拷贝到loader文件夹下面,我们改一下makefile
run : loader.bin
dd if=loader.bin of=loader.img bs=512 count=1
dd if=system.bin of=system.img bs=1474048 count=1 conv=sync
dd if=system.img of=loader.img bs=512 seek=1
qemu-system-i386 -fda loader.img -boot a
loader.bin : loader.o Makefile loader.lds
ld -M --oformat binary -m elf_i386 -o loader.bin loader.o -T loader.lds
loader.o : loader.s Makefile
as --32 loader.s -o loader.o
clean :
rm *.bin *.o *.img
改成这个样子,意思是把loader.bin和system.bin接起来,生成了loader.img,这就是我们的虚拟磁盘,make一下,OH yeah!
什么都没有就是成功了,这里推荐几个软件
二进制编辑器,bless,直接apt-get就可以了
objdump,readelf,都是不可或缺的软件。
orandragon原创,转载请链接地址http://blog.csdn.net/cheng7606535/article/details/76038518谢谢咯