三.編寫簡單的匯編啟動程序


1 匯編啟動程序

這部分主要是用來學習linux-0.11,做預前匯編準備工作。同時也是一個啟動程序,用來引導操作系統代碼。

1.1 工具

1.bin86,所以我們使用的是as86匯編語言。可以使用:

$ sudo apt-get intall bin86

安裝軟件工具。然后可以使用:

$ man 1 as86

查詢相應的匯編代碼。

這只是一個boot部分的代碼,使用的是as86匯編器,ld86鏈接器,而如果我們要加載head的代碼,則需要使用GNU的gas匯編器,用GNU ld鏈接。

需要知道,目前這部分的boot代碼,對于整個bios來說,只有512B,也就是說,啟動整個linux系統,還需要第二部分的代碼,也就是loader代碼,之后才是Linux代碼。

對于loader代碼,比較有名的有Lilo,Grub和spfdisk等。

注意事項

as86匯編器對于boot.s程序編譯時生成boot.o目標文件,再用ld86進行鏈接操作,生成MINIX結構的可執行文件,但在Linux中用作引導時需要去除其MINIX文件頭結構。

2.GNU as工具

as匯編語言語法和intel的匯編語言有很大不同,被稱為AT&T語法,最主要的區別是以下幾點:

a. AT&T語法立即操作數前面加”$”,而寄存器操作數前面要加%與程序計數器相關的語句操作數前面要加*,intel沒有限制。
b. AT&T語法的源操作數和目的操作數與intel的位置正好相反,它是源在前,目的在后。
c. AT&T語法中內存操作數的長度由操作碼的最后一個字符決定比如b for byte, w for word, l for long,
Intel則通過前綴區分比如Intel的是mov al,byte ptr foo,而AT&T中是movb foo,%al 。
d. AT&T不提供對多代碼段的支持,UNIX類操作系統要求所有代碼在一個段中。

1.2 基礎知識

默認的引導代碼會放在軟盤的引導扇區,也就是軟盤的頭512個字節位置。而bios會將這部分代碼讀取出來, 放到內存的0x7c00:0x0處。因此,我們的代碼運行地址都是基于0x7c00的。

intel架構下的寄存器

只列出關鍵的一些寄存器:

8個數據寄存器:EAX、EBX、ECX、EDX
2個變址寄存器:ESI、EDI  
2個指針寄存器:ESP、EBP
1個標志寄存器:EFLAGS
6個段寄存器:CS、DS、ES、FS、GS、SS

1.數據寄存器
32位CPU有4個32位的通用寄存器eax、ebx、ecx和edx。對低16位數據的存取,不會影響高16位的數據。這些低16位寄存器分別命名為:ax、bx、cx和dx,它和先前的CPU中的寄存器相一致。
4個16位寄存器又可分割成8個獨立的8位寄存器(ax:ah-al、bx:bh-bl、cx:ch-cl、dx:dh-dl),每個寄存器都有自己的名稱,可獨立存取。程序員可利用數據寄存器的這種“可分可合”的特性,靈活地處理字/字節的信息。

寄存器ax和al通常稱為累加器(accumulator),用累加器進行的操作需要更少的時間。累加器可用于乘、除、輸入/輸出等操作。

寄存器bx稱為基地址寄存器(base register),可作為存儲器指針使用。

寄存器cx稱為計數寄存器(count register),在循環和字符串操作時,可用它來控制循環次數,在位操作中,當移多位時,要用cl來指明移位的位數。

寄存器dx稱為數據寄存器(data register),在進行乘、除運算時,它可作為默認的操作數參與運算,也可用于存放I/O的端口地址。

在16位cpu中,ax、bx、cx、dx不能作為基址和變址寄存器來存放存儲單元的地址,但在32位cpu中,其32位寄存器eax、ebx、ecx、edx不僅可以傳送數據、暫存數據保存算術邏輯運算結果,而且也可作為指針寄存器,所以,這些32位寄存器更具通用性。

2.變址寄存器
32位cpu有2個32位通用的寄存器esi和dsi。其低16位對應先前cpu中的si和di,對低16位數據的存取,不影響高16位的數據。

寄存器esi、edi、si和di稱為變址寄存器(index register),它們主要用于存放存儲單元在段內的偏移量,用它們可以實現多種存儲器操作數的尋址方式,為以不同的地址形式訪問存儲單元提供方便。

變址寄存器不可分割為8位寄存器。作為通用寄存器,也可存儲算術邏輯運算的操作數和運算結果。

3.指針寄存器
32位cpu有2個32位通用寄存器ebp和esp。其低16位對應先前cpu中的sbp和sp,對低16位數據的存取,不影響高16位的數據。

寄存器ebp、esp、bp和sp稱為指針寄存器(pointer register),主要用來存放堆棧內存儲單元的偏移量,用它們可實現多種寄存器操作數的尋址方式,可以位不同的地址形式訪問存儲單元提供方便。

它們主要用于訪問堆棧內的存儲單元,并做了規定:
bp為基指針(base pointer)寄存器,用來可直接存取堆棧中的數據。

sp為堆棧指針(stack pointer)寄存器,用它只可以訪問棧頂。

4.段寄存器
段寄存器時根據內存分段的管理模式而設置的。內存單元的物理地址由段寄存器的值和一個偏移量組合而成的,這樣可用兩個較少的值合成一個可訪問較大物理空間的內存地址。

cpu內部的段寄存器:
cs:
代碼段寄存器(code segment register),值為代碼段的段值。
ds:
數據段寄存器(data segment register),值為數據段的段值。
es:
附加段寄存器(extra segment register),值為附加數據段的段值。
ss:
堆棧段寄存器(stack segment register),值為堆棧段段值。
fs:
附加段寄存器(extra segment register),值為附加數據段的段值。
gs:
附加段寄存器(Extra Segment Register),其值為附加數據段的段值。

在16位CPU系統中,它只有4個段寄存器,所以,程序在任何時刻至多有4個正在使用的段可直接訪問;在32位微機系統中,它有6個段寄存器,所以,在此環境下開發的程序最多可同時訪問6個段。

32位CPU有兩個不同的工作方式:實模式和保護模式。在每種方式下,段寄存器的作用是不同的。有關規定簡單描述如下:

實模式: 前4個段寄存器CS、DS、ES和SS與先前CPU中的所對應的段寄存器的含義完全一致,內存單元的邏輯
地址仍為“段值:偏移量”的形式。為訪問某內存段內的數據,必須使用該段寄存器和存儲單元的偏移量。

保護模式: 在此方式下,情況要復雜得多,裝入段寄存器的不再是段值,而是稱為“選擇子”(Selector)的某個值。

5.指令寄存器

32位CPU把指令指針擴展到32位,并記作EIP,EIP的低16位與先前CPU中的IP作用相同。

指令指針EIP、IP(Instruction Pointer)是存放下次將要執行的指令在代碼段的偏移量。在具有預取指令功能的系統中,下次要執行的指令通常已被預取到指令隊列中,除非發生轉移情況。所以,在理解它們的功能時,不考慮存在指令隊列的情況。

在實模式下,由于每個段的最大范圍為64K,所以,EIP中的高16位肯定都為0,此時,相當于只用其低16位的IP來反映程序中指令的執行次序。

6.標志寄存器
包括運算結果標志位、狀態控制標志位、32位標志寄存器增加的標志位等等。

1.3 系統架構

系統架構
地址映射

2 編寫代碼與分析代碼

我們可以參照linux-0.11的bootsect.s中的源代碼。

我們可用編寫的boot.s源碼如下:

.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 ax,#0x0600
        mov cx,#0x0000
        mov dx,#0x0fff
        int 0x10

        mov cx,#20
        mov dx,#0x0000
        mov bx,#0x000c
       mov bp,#msg1
        mov ax,#0x1301
        int 0x10

loop0:  jmp loop0

msg1:   .ascii "loading system..."
        .byte 13,10

.org 510
        .word 0xaa55

.text
endtext:
.data
enddata:
.bss
endbss:

3 編譯與運行

a.編譯

$ as86 -0 -a -o boot.o boot.s
$ ld86 -0 -s -o boot boot.o

b.制作軟盤文件

$ dd bs=32 if=boot of=boot.img skip=1

c.編輯bochsrc文件:

megs:32

romimage:file=/usr/share/bochs/BIOS-bochs-latest
vgaromimage:file=/usr/share/bochs/VGABIOS-lgpl-latest

floppya:1_44=boot.img,status=inserted

boot:floppy

mouse:enabled=0

keyboard:keymap=/usr/share/bochs/keymaps/x11-pc-us.map

d.執行

$ bochs -f bochsrc

4 調試

4.1 基本調試技巧

我們可以利用vim本身自帶的查看二進制方法:

$ vim -b boot

然后使用:

:%!xxd -g 1

我們對上面的文件進行分析,可以看到如下圖所示:


可執行文件二進制分析

當然,我們也要注意一個小細節,如下圖所示:


生成的文件內容

我們要計算一下,0x210+16 = 544。也就是說,生成的文件是544字節的。比我們需要生成的512字節多了32字節。整個呢,就是minix可執行文件的頭結構。

5 補充知識

5.1 軟盤

若使用3.5寸的軟盤,則分為上下兩面,每面分為80個磁道,每個磁道分為18個扇區,而一個扇區就是512字節。
因此,計算其大小為:

2*80*18*512/1024.0 = 1440.0KB = 1.44MB

軟盤需要軟驅對其進行讀寫操作。軟驅的主要組成有:控制電路板、馬達、磁頭定位器和磁頭。磁頭其實是很小的,上下各有一個,我們看到的是它的滑軌。

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

推薦閱讀更多精彩內容