debug bootloader,最方便简单直观的办法就是print into console。所以在hacking kernel歇业一个月后,重拾老代码,准备重新改造一番,今天就从增加printf函数开始吧:
;function:在是模式下,打印一串字符串
;@1 input: 要打印的字符串的物理地址, 低位:ax, 高位 dx
;@2 input: 打印的字符长度: cx
;output: null
printf:
push ax
push bx
push cx
push dx
push es
push ds
;1. first of all, trans the phy addres into logic address
mov bx, 16
div bx
;做完除法后,ax保存了段地址, dx保存了段内偏移
mov ds, ax
mov ax, 0xb800
mov es, ax
;cx为字符串的长度,循环次数是字符串的1/2倍
mov ax, cx
mov bl, 2
div bl
cmp ah, 0x00 ;比较是否存在余数,如果存在则需要将商(al)+1
je no ;如果不存在余数
;如果存在余数
inc al
no:
xor cx, cx
mov cl, al
mov bx, dx
;当前的id从CUR_INDEX中获取
mov di, [CUR_INDEX]
lp:
;一次性取出2个字节,提高读取效率
mov ax, [bx] ;直接用dx来寻址把内存赋值到ax还不行~~ 得用bx,神囧啊
mov byte [es:di], al
inc di
mov byte [es:di], 10000100B
inc di
mov byte [es:di], ah
inc di
mov byte [es:di], 10000100B
inc di
add bx, 0x02
loop lp
;loop完成后,要把当前的di写回到CUR_INDEX中去,下次重新调用的时候,不会覆盖之前打印的信息
inc di
mov [CUR_INDEX], di
pop ds
pop es
pop dx
pop cx
pop bx
pop ax
ret
仔细一看,起始这里出现了一个很隐蔽、而且很严重的问题:混乱的ds布置。
看看数据段的定义:
SECTION data ;align=16
msg1: db "loading app at 0x07c00 under raw mode..." ;40bytes 0x7cf0
msg2: db "now, entering the protect mode! @devxxxx" ;40bytes 0x7d18
msg3: db "prepare to jump into protected mode....."
;6bytes用于ldgt
gdt_size: dw 0 ;2bytes
gdt_baseaddr: dd 0x08000 ;4bytes
;用于填充gdt内的段描述符数据 段基址 段界限 段属性
NULL_DES: descriptor 0x00000000, 0x00000000, 0x0000 ;8bytes
CS_DES: descriptor 0x07c00, code_length, 0x4000+0x98 ;8bytes
;DS_DES: descriptor 0x7c00+section.data.start, data_end, 0x92 ;8bytes
DS_DES: descriptor 0, data_end, 0x92 ;8bytes 0x7d56
;保存当前显存区间,当前的index
CUR_INDEX: db 0x00
times 510-($-$$)-code_length db "Q"
db 0x55,0xaa
;数据段的长度
data_end equ $ - $$
;END SECTION data
很明显,数据段的msg1,msg2,msg3,都是基于ds=0x0000的。
但是printf函数中,多了如下的一个ds的转换:
;1. first of all, trans the phy addres into logic address
mov bx, 16
div bx
;做完除法后,ax保存了段地址, dx保存了段内偏移
mov ds, ax
no:
xor cx, cx
mov cl, al
mov bx, dx
;当前的id从CUR_INDEX中获取
mov di, [CUR_INDEX]
;loop完成后,要把当前的di写回到CUR_INDEX中去,下次重新调用的时候,不会覆盖之前打印的信息
inc di
mov [CUR_INDEX], di
这2个代码片段分别涉及到了内才能的读操作和写操作。但是呢,[CURRENT]-->真长来说就是基于ds=0x0000,然后+offset current计算的。但是呢,由于print函数之前插一脚的ds重定位操作,导致ds!=0x0000,而是一个具体的段地址值。这样子,mov id, [CUR_INDEX]以及 mov [CUR_INDEX], di都存在重复计算的问题了!
问题现在看上去虽然很初级,也很低级。但是确的确存在,需要引起注意!