程序的加载和执行(五)——《x86汇编语言:从实模式到保护模式》读书笔记25
程序的加載和執行(五)——《x86匯編語言:從實模式到保護模式》讀書筆記25
前面幾篇博文終于把代碼分析完了。這篇就來說說代碼的編譯、運行和調試。
1.代碼的編譯及寫入鏡像文件
之前我們都是在命令行輸入命令進行編譯和寫入,源文件少的時候還不覺得麻煩,當源文件多了,就會覺得特別麻煩。有沒有簡單的方法呢?
當然有,就是用make工具。
1.1.什么是make工具
make是一個命令工具,它解釋Makefile中的指令。在Makefile文件中描述了整個工程所有文件的編譯順序、編譯規則。
注意:make命令不僅僅用于編譯程序。無論何時,當需要通過多個輸入文件來生成輸出文件時,我們都可以利用它來完成任務。
1.2.關于Makefile
Makefile有自己的書寫格式、關鍵字、函數。像C語言有自己的格式、關鍵字和函數一樣。而且在Makefile中可以使用系統shell所提供的任何命令來完成想要的工作。
以上只是非常簡略地對make和Makefile進行介紹。關于他們的使用,可以搜索相關關資料來學習。
1.3.針對第13章源文件的Makefile
1.3.1.我的Makefile文件
BIN = c13_mbr.bin c13_core.bin c13.bin empty A_DIR = /home/cjy/a.img C_DIR = /home/cjy/c.imgall:$(BIN).PHONY:all cleanc13_mbr.bin:c13_mbr.asmnasm $< -o $@dd if=$@ of=$(A_DIR)c13_core.bin:c13_core.asmnasm $< -o $@dd if=$@ of=$(C_DIR) bs=512 seek=1 conv=notruncc13.bin:c13.asmnasm $< -o $@dd if=$@ of=$(C_DIR) bs=512 seek=50 conv=notruncempty:diskdata.txtdd if=$< of=$(C_DIR) bs=512 seek=100 conv=notrunctouch $@clean:$(RM) $(BIN)這就是我自己的寫的Makefile,至于為什么這樣寫,還有Makefile的入門知識,我以后會寫博文來介紹。
1.3.2.使用說明
可以看到,我們需要的.bin文件都生成了,對A盤和C盤的寫入也完成了。
2.運行結果
終于可以看結果了,我們啟動Bochs,運行結果如圖:
3.在源碼的基礎上修修改改
僅僅得到書上的結果是不夠的,不愛折騰的程序員不是好程序員。
3.1.寫代碼就像寫作文
我覺得寫代碼和寫作文是一樣一樣的。想想我們大多數人學寫作文的過程:開始不會寫,怎么辦?抄唄。(這個就是學習人家的源代碼,跑出人家的結果。)再然后呢,我們不是全抄,而是在人家的基礎上修改成自己的。(這個就是我們現在要做的事情,在人家代碼的基礎上加上自己的想法,看看結果會怎么樣。)最后呢,我們不需要抄了,上了考場就可以自己寫出來,結果得分還挺高。(這就是我們的終極目標,博采眾長,自成一家。)
我針對第13章的代碼,制作了自己的補丁包。有需要的朋友可以去下載。下載地址是:
http://download.csdn.net/detail/u013490896/9486717
或者
https://github.com/LeslieChe/from-real-mode-to-protected-mode
接下來,我會針對補丁包,對修改的部分加以講解。
3.2.讓字符顯示出不同的顏色
看了上面的運行結果,你是否覺得顏色有點單調?好的,我們修改源碼,把字符的屬性作為參數傳給過程。
首先我們定義一些常量,表示不同的顏色。
除了要把字符串的首地址傳入DS:EBX之外,還要壓入屬性值。
在Beyond Compare軟件中比較修改后和修改前的差異,如下圖
另外,過程put_char有兩個地方需要修改。第二個地方是一個小BUG.
這樣修改后,我們調用put_string的時候,需要先壓棧字符屬性。如下圖:
修改后的運行效果如下圖:
3.3.對過程put_hex_dword的修改
3.3.1.配書源碼講解
之前的博文沒有講解這個過程,所以先說一下這個過程。
源碼是:
這段代碼的原理很簡單,EDX寄存器是32位的,從右到左,4位一組,一共分成8組。每組的值都在0x0~0xF之間,我們把它的值轉換成對應的字符0~F;
第218行用了查表指令xlat,該指令要求事先在DS:EBX(32位模式)或者DS:BX(16位模式)處存放一張表格,指令執行時,用AL的值作為偏移量,從表格對應位置取回一個字節,傳送到AL;舉例來說,如果在DS:EBX處存放了第374行定義的表格,那么當AL=0的時候,執行xlat后,AL中的值就是字符0的ASCII碼。
第215行用了循環左移指令rol,第一次循環將EDX的高4位移到最右邊,和0x0000_000F相與,于是AL中就得到高四位對應的值,然后查表,就得到對應的字符。
第221~222,把這個字符打印到屏幕上(打印位置是當前光標所在處,并推進光標)。
3.3.2.我的修改
修改前,假設在用戶程序中,我們要輸出寄存器EAX的值,那么我們需要
mov edx,eaxcall far [fs:put_hex_dword]現在我希望可以這么用:
push 'eax'push eaxcall far [fs:put_hex_dword]也就是通過棧傳遞參數,第一個參數是字符串'eax',第二個參數是寄存器EAX的值。
執行效果如下(淺藍色第一行):
也許有的朋友會奇怪,push 'eax'這種寫法可以嗎?
對于NASM編譯器,這種寫法是允許的。'eax'屬于字符常數。
一個字符常數最多由包含在雙引號或單引號中的四個字符組成。一個具有多個字符的字符常數會被序列化成小端序。
相當于
mov eax,0x64636261所以,我們可以把'eax'這種字符常數壓入棧中(因為在32位模式下,所以默認按4個字節壓入,最高位會補零),作為參數傳遞給過程。在過程中把這個參數的每個字符提取出來,顯示在屏幕上。
下圖顯示這個過程的第一處改動:
從標號.p_char到.ok之間的代碼,就是從棧中依次取出我們要顯示的字符(遇到0值為止),輸出到屏幕。
.ok后面的2行,是為了打印等號=;
這個過程的第二處改動如下圖:
3.3.3.本地Label
在源碼中,會發現作者在很多地方都使用了以.開頭的標號,這樣的標號屬于本地標號。
以下摘自NASM的官方手冊
http://www.nasm.us/doc/nasmdoc3.html#section-3.9
NASM gives special treatment to symbols beginning with a period. A label beginning with a single period is treated as a local label, which means that it is associated with the previous non-local label. So, for example:
label1 ; some code .loop ; some more code jne .loop ret label2 ; some code .loop ; some more code jne .loop retIn the above code fragment, each JNE instruction jumps to the line immediately before it, because the two definitions of .loop are kept separate by virtue of each being associated with the previous non-local label.
我覺得這樣做可以方便用戶,不用為給label起名字而傷腦筋。
3.4.符號表的重定位
我的博文
程序的加載和執行(三)——《x86匯編語言:從實模式到保護模式》讀書筆記23
已經指出在重定位符號表的時候,有一個小BUG.
我準備加入調試打印信息,證明這確實是一個BUG,同時也證明我的修改是對的。
第575~583行,我加入了一些代碼,用于打印將要比較的用戶符號和內核符號。
執行完573行時候,DS:ESI指向了內核符號表的某個條目,ES:EDI指向了用戶符號表的某個條目。紅色代碼就是把這兩個條目打印到屏幕上,左邊是用戶符號,右邊是內核符號。
過程put_usr_salt的代碼如下:
67:從棧中取得屬性值
68~74:用于打印以0結尾的字符串。
76~80:用于打印4個空格。
過程put_core_salt的代碼類似,這里不再贅述。
看一下執行效果吧:
左邊黃色的是用戶符號,右邊紅色的是內核符號。我們可以清晰地看到符號的比較過程:
@TerminateProgram比較了2次后匹配上了;
@ReadDiskData比較了2次后匹配上了;
@PrintDwordAsHexString比較了3次才匹配上。
這篇博文就到這里。下篇博文,會講NASM的條件編譯,Makefile的一些改動,另外還有13章的習題。敬請期待…
總結
以上是生活随笔為你收集整理的程序的加载和执行(五)——《x86汇编语言:从实模式到保护模式》读书笔记25的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用指针编写程序将输入的字符串倒序输出
- 下一篇: 策略产品经理那些事