使用qemu調試內核啟動程序(匯編級)

概述

? ?? ? 最近在拜讀《inux內核源碼剖析》一書,以下內容皆為此書引出,為后續的閱讀做個鋪墊。

? ?? ? 本篇講述如何搭建與使用qemu對內核啟動程序進行匯編級別調試,通過本篇可以了解如下內容:

  • 使用qemu遠程調試方式跟蹤系統啟動過程
  • gdb調試8086程序的方法

環境準備

  1. ubuntu16
  2. 安裝qemu與編譯鏈接工具
apt get install qemu bin86

啟動鏡像制作

我們要實現系統啟動后,在啟動界面上打印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個字節
        mov dx, #1804   ;
        mov bx, #0x000c ;字符為紅色
        mov bp, #msg1   ;字符串地址
        mov ax, #0x1301 ;BIOS中斷調用, 功能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

執行make即可生成鏡像文件boot_image.

開始調試

啟動qemu

用qemu啟動鏡像文件,命令如下:

qemu-system-i386 -s -S -vnc :2 boot_image

其中-s是設置gdbserver的監聽端口,-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

  1. 輸入gdb -q
  2. 連接gdb server target remote :1234
  3. 設置被調試環境架構 set architecture i8086
  4. 顯示將要執行的匯編指令display /5i $cs * 0x10 + $pc
  5. 打斷點調試

? ?? ? qemu的 -S選項相當于將斷點打在CPU加電后要執行的第一條指令處,也就是BIOS程序的第一條指令。

first instruction

? ?? ?通過info reg命令看到,cpu加電后的寄存器值,cs值為0xF000, pc值為0xFFF0。

? ?? ?不要直接單步程序,加電后bios代碼先運行,運行完后將系統引導程序加載到內存0x7c00開始的位置,之后跳轉到0x7c00處。
所以斷點應打在0x7c00處。輸入continue,程序停在斷點處。現在, 是不是看到了boot.s文件里的匯編指令?

breakpoint in boot

注意事項

  1. 匯編代碼每行必須要以回車結尾
    以回車結尾包括最后一行,否則as86會報錯(這個錯誤提示真讓人摸不著頭腦
as: error reading input
  1. 關于加電后第一條指令地址
    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).

總感覺還是沒說明白,應該還有很多故事,有興趣的可以挖一下。

參考文檔

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。