BootLoader内存布局
低----->高
0x7c00之前是栈空间,0x9000之后是Loader程序,之间是主引导程序。最终会将控制权从主引导程序(boot程序)跳转到0x9000处向后执行,也就是将控制权从boot程序转交给loader程序。fat表在0x7e00到0x9000之间,4kB。
通过fat边加载文件内容:
bx寄存器指向fat表的起始地址,之后EntryItem(目标文件的文件目录项)+0x1A,bp指向了DIR_FstClus目标文件所在的第一簇,因为fat12里边1簇就是一个扇区,所以DIR_FstClus指向了目标文件的第一个扇区,si指向了0x9000,(目标地址),然后循环,读扇区内容,指定逻辑扇区号dx+31(对应33+j-2),备份dx,bx值,pop cx,将dx中的值放到cx中,然后调用FatVec获取下一个扇区。si向后偏移512字节,准备读取下一个扇区。
试验步骤:
1.在虚拟软盘中创建体积较大的文本文件(Loader)
2.将Loader的内容加载到BaseOfLoader(0x9000)地址处
3、打印Loader中的文本(判断加载是否完全)
cx必须对应的是表项下标、
dx是读取到的值,与0xFF7比较,如果小于,接着循环。
代码量超过了512字节,调整
读取文件长度,在0x1c的地方 mov cx, [EntryItem+ 0x1C]
到虚拟软盘查看:将虚拟软盘挂载到linux中去:
sudo mount -o loop data.img /mnt/hgfs/
打开loader这个文件:
sudo gedit /mnt/hgfs/loader
将这个虚拟软盘从当前linux中移除掉:
sudo umount /mnt/hgfs/
将代码复制到d.t.中,大小是4kb,一个扇区放不下,结果打印一下,是代码,这样就把文件内容加载到内存中了。
移除,重新运行查看。make bochs 6 c
mov dx, [EntryItem + 0x1A]
mov si, BaseOfLoader
loading:
mov ax,dx
add ax,31
mov cx,1
push dx
push bx
mov bx,si
call ReadSector
pop bx
pop cx
; pop dx
; mov cx, dx
call FatVec
cmp dx,0xFF7
jnb BaseOfLoader
add si, 512
jmp loading
接下来创建一个loader程序,
第一个Loader程序:
起始地址0x9000(org 0x9000)
通过int 0x10在屏幕上打印字符串
汇编小贴士:标志寄存器
如果ZF为1,就执行跳转。
汇编小贴士:
jxx代表了一个指令族,功能是根据标志位进行跳转。
jo:当OF为1则跳转
jc:当CF为1则跳转
jns:当SF不为1则跳转
jz:当ZF为1则跳转
je:比较结果为相等则跳转(即:jz)
试验:
org 0x9000 当前程序放到这个地址处进行执行
begin:
mov si, msg si指向要打印的代码
print:
mov al, [si]
add si, 1
cmp al, 0x00
je end 相等结束
mov ah, 0x0E
mov bx, 0x0F
int 0x10
jmp print
//mov ax, 0x1301 打印不是这个吗?
//mov bx, 0x0007
//int 0x10
end:db 0x00
保存之后编译, nasm loader.asm -o loader
为了说明问题:反编译
ndisasm -o 0x9000 loader > loader.txt 从地址0x9000处向后反编译
反编译结果:
00009000 BE1B90 mov si,0x901b
00009003 8A04 mov al,[si]
00009005 81C60100 add si,0x1
00009009 3C00 cmp al,0x0对应 cmp al, 0x00
0000900B 740A jz 0x9017 ====je编译器将je作为jz
0000900D B40E mov ah,0xe
然后将loader拷贝到软盘中,然后 从boot跳转到loader来进行执行,,
将当前虚拟软盘挂载到linux中:
sudo mount -o loop data.img /mnt/hgfs/
然后将loader拷贝过去:
sudo cp loader /mnt/hgfs/loader
之后将虚拟软盘卸载下来:
sudo umount /mnt/hgfs
接下来运行:bochs 6 c
然后将data虚拟软盘文件拷贝到E盘,然后在虚拟机运行
改写makefile:
加上虚拟软盘在linux中的挂载路径IMG_PATH := /mnt/hgfs
编译:nasm $^ -o $@
编译完boot程序后,将boot程序烧写到虚拟软盘中。
dd if=@ of=$(IMG) bs=512 count=1 conv=notrunc
接下来定义规则编写loader程序,
编译完之后将结果拷贝到虚拟软盘中,所以说先进行挂载:
sudo mount -o loop $(IMG) $(IMG_PATH)
挂载好之后进行拷贝:
sudo cp $@ $(IMG_PATH)/$@
拷贝完之后卸载:sudo umount $(IMG_PATH)
clean也要变化:
$(RM) $(IMG) $(BOOT_OUT) $(LOADER_OUT)
.PHONY : all clean rebuild
BOOT_SRC := boot.asm
BOOT_OUT := boot
LOADER_SRC :=loader.asm
LOADER_OUT :=loader
IMG := data.img
IMG_PATH := /mnt/hgfs
RM := rm -fr
all : $(IMG) $(BOOT_OUT) $(LOADER_OUT)
@echo "build Success==>w.s.os"
$(IMG) :
bximage $@ -q -fd -size=1.44
$(BOOT_OUT) : $(BOOT_SRC)
nasm $^ -o $@
dd if=$@ of=$(IMG) bs=512 count=1 conv=notrunc
$(LOADER_OUT) : $(LOADER_SRC)
nasm $^ -o $@
sudo mount -o loop $(IMG) $(IMG_PATH)
sudo cp $@ $(IMG_PATH)/$@
sudo umount $(IMG_PATH)
clean :
$(RM) $(IMG) $(BOOT_OUT) $(LOADER_OUT)
rebuild :
@$(MAKE) clean
@$(MAKE) all
运行结果:只有当Target db "LOADER "是这个的时候在会打印w.s.os(或者是6个空格),当内容改变成别的时候就不会打印这个,会打印no leader(控制权没有转移)
小结:
Boot需要进行重构保证在512字节内完成功能。
在汇编程序中尽量确保函数调用前后通用寄存器的状态不变、
Boot成功加载Loader后将控制权转移。
Loader程序没有代码体积上的限制。