概述
? ?? ? 最近在拜讀《inux內(nèi)核源碼剖析》一書,以下內(nèi)容皆為此書引出,為后續(xù)的閱讀做個鋪墊悬襟。
? ?? ? 本篇講述如何搭建與使用qemu對內(nèi)核啟動程序進(jìn)行匯編級別調(diào)試端仰,通過本篇可以了解如下內(nèi)容:
- 使用qemu遠(yuǎn)程調(diào)試方式跟蹤系統(tǒng)啟動過程
- gdb調(diào)試8086程序的方法
環(huán)境準(zhǔn)備
- ubuntu16
- 安裝qemu與編譯鏈接工具
apt get install qemu bin86
啟動鏡像制作
我們要實現(xiàn)系統(tǒng)啟動后,在啟動界面上打印hello world!
as86 匯編代碼如下:
;
; display "hello world" in the boot screen
;
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
BOOTSEG = 0x07c0
entry start
start:
jmpi go, BOOTSEG
go:
mov ax, cs
mov ds, ax
mov es, ax
mov cx, #16 ;顯示16個字節(jié)
mov dx, #1804 ;
mov bx, #0x000c ;字符為紅色
mov bp, #msg1 ;字符串地址
mov ax, #0x1301 ;BIOS中斷調(diào)用, 功能0x13, 子功能01
int 0x10
loop0: jmp loop0
msg1: .byte 13, 10
.ascii "Hello World!"
.byte 13, 10
.org 510
.word 0xAA55
.text
endtext:
.data
enddata:
.bss
endbss:
makefile如下
all:
as86 -0 -a -o boot.o boot.s
ld86 -0 -s -o boot boot.o
dd bs=32 if=boot of=./boot_image skip=1
執(zhí)行make即可生成鏡像文件boot_image.
開始調(diào)試
啟動qemu
用qemu啟動鏡像文件,命令如下:
qemu-system-i386 -s -S -vnc :2 boot_image
其中-s是設(shè)置gdbserver的監(jiān)聽端口,-S則是讓cpu加電后被掛起作郭。
-s
? ? ? ? Shorthand for -gdb tcp::1234, i.e. open a gdbserver on TCP port 1234 (see gdb_usage).
-S
? ? ? ? Do not start CPU at startup (you must type ’c’ in the monitor).
啟動gdb
- 輸入
gdb -q
- 連接gdb server
target remote :1234
- 設(shè)置被調(diào)試環(huán)境架構(gòu)
set architecture i8086
- 顯示將要執(zhí)行的匯編指令
display /5i $cs * 0x10 + $pc
- 打斷點調(diào)試
? ?? ? qemu的 -S選項相當(dāng)于將斷點打在CPU加電后要執(zhí)行的第一條指令處,也就是BIOS程序的第一條指令弦疮。
? ?? ?通過info reg命令看到夹攒,cpu加電后的寄存器值,cs值為0xF000, pc值為0xFFF0胁塞。
? ?? ?不要直接單步程序咏尝,加電后bios代碼先運行,運行完后將系統(tǒng)引導(dǎo)程序加載到內(nèi)存0x7c00開始的位置啸罢,之后跳轉(zhuǎn)到0x7c00處编检。
所以斷點應(yīng)打在0x7c00處。輸入continue扰才,程序停在斷點處≡识現(xiàn)在, 是不是看到了boot.s文件里的匯編指令衩匣?
注意事項
- 匯編代碼每行必須要以回車結(jié)尾
以回車結(jié)尾包括最后一行蕾总,否則as86會報錯(這個錯誤提示真讓人摸不著頭腦)
as: error reading input
- 關(guān)于加電后第一條指令地址
Intel手冊上寫了,上電后cpu加載的第一條指令的地址其實是0xFFFFFFF0琅捏,原文如下:
The address FFFFFFF0H is beyond the 1-MByte addressable range of the processor while in real-address mode. The
processor is initialized to this starting address as follows. The CS register has two parts: the visible segment
selector part and the hidden base address part. In real-address mode, the base address is normally formed by shifting the 16-bit segment selector value 4 bits to the left to produce a 20-bit base address. However, during a hardware reset, the segment selector in the CS register is loaded with F000H and the base address is loaded with FFFF0000H. The starting address is thus formed by adding the base address to the value in the EIP register (that
is, FFFF0000 + FFF0H = FFFFFFF0H).
只有在修改cs值后才會變成真正的實模式尋址
The first time the CS register is loaded with a new value after a hardware reset, the processor will follow the normal rule for address translation in real-address mode (that is, [CS base address = CS segment selector * 16]). To insure that the base address in the CS register remains unchanged until the EPROM based software-initialization code is completed, the code must not contain a far jump or far call or allow an interrupt to occur (which would cause the CS selector value to be changed).
總感覺還是沒說明白生百,應(yīng)該還有很多故事,有興趣的可以挖一下柄延。
參考文檔
- https://qemu.weilnetz.de/doc/qemu-doc.html
- 《linux內(nèi)核完全剖析》
- Intel? 64 and IA-32 Architectures Software Developer’s Manual