學習筆記
《x86匯編語言:從實模式到保護模式》
http://www.lxweimin.com/p/d481cb547e9f
答案參考
https://blog.csdn.net/longintchar/article/details/51167487?locationNum=5&fps=1
本章習題
- 在本章中,用戶程序只給出建議的棧大小,但并不提供棧空間。現在,修改內核程序和用戶程序,改由用戶程序自行提供棧空間。要求:棧段必須定義在用戶程序之后。
習題解答
修改內核程序 c13_core.asm
;建立程序堆棧段描述符
mov ecx,[edi+0x0c] ;4KB的倍率
mov ebx,0x000fffff
sub ebx,ecx ;得到段界限
mov eax,4096
mul dword [edi+0x0c]
mov edx,edi
add edx,[edi+0x08]
add eax,edx ;得到棧的高端物理地址
mov ecx,0x00c09600 ;4KB粒度的堆棧段描述符
call sys_routine_seg_sel:make_seg_descriptor
call sys_routine_seg_sel:set_up_gdt_descriptor
mov [edi+0x08],cx
- 計算段界限
計算段界限
《x86匯編語言:從實模式到保護模式》 第237頁
修改用戶程序 c13.asm
usr_stack_len equ 1
usr_stack_cap equ usr_stack_len*0x1000 ; 0x1000 = 4096
;===============================================================================
SECTION header vstart=0
program_length dd program_end ;程序總長度#0x00
head_len dd header_end ;程序頭部的長度#0x04
stack_seg dd section.stack.start ;用于接收堆棧段選擇子#0x08
stack_len dd usr_stack_len ;程序建議的堆棧大小#0x0c
;以4KB為單位
prgentry dd start ;程序入口#0x10
code_seg dd section.code.start ;代碼段位置#0x14
code_len dd code_end ;代碼段長度#0x18
data_seg dd section.data.start ;數據段位置#0x1c
data_len dd data_end ;數據段長度#0x20
;-------------------------------------------------------------------------------
;符號地址檢索表
salt_items dd (header_end-salt)/256 ;#0x24
salt: ;#0x28
PrintString db '@PrintString'
times 256-($-PrintString) db 0
TerminateProgram db '@TerminateProgram'
times 256-($-TerminateProgram) db 0
ReadDiskData db '@ReadDiskData'
times 256-($-ReadDiskData) db 0
header_end:
;===============================================================================
SECTION stack vstart=0
stack_start:
times usr_stack_cap db 0 ;4KB的棧空間
stack_end:
;===============================================================================
- 增加兩個常量 用戶程序棧的大小(以4KB為單位)
usr_stack_len
以及 用戶程序棧的容量usr_stack_cap
; - 修改標號
stack_seg
以及stack_len
后面的初始值; - 增加段
SECTION stack
,這樣就由用戶程序自己給自己提供棧空間;
Bochs 調試
- 從加載程序跳轉到內核程序
由加載程序的
行數 匯編地址 機器碼 匯編指令
136 00000138 FF6F10 jmp far [edi+0x10]
在 bochs使用
b 0x7d38
c
就可以跳轉到 加載程序 轉移控制權給 內核程序的這一條指令;
0x7d38 等于 0x7c00+0x138
- 執行內核程序的指令
來到內核程序之后,遇到的第一條指令是:
行數 匯編地址 機器碼 匯編指令
561 00000159 B930000000 mov ecx,core_data_seg_sel
而這條指令的真實物理地址是 0x410a9
我希望直接跳到,加載完全部描述符的
這條指令:
503 000000EB 66894F08 mov [edi+0x08],cx
經過換算,可以得出,目標的真實物理地址
應該是 0x4103b
因此,在bochs里面使用
b 0x4103b
c
就可以跳轉到這條指令了。
- 使用
info gdt
查看GDT的內容,看到Expand-down
字樣說明加載棧段成功
Expand-dwon字樣說明這是向下生長的數據段即棧段.png
棧段都是Expand-down.png
Debug
1、調試用戶程序的時候,將某個SECTION的標號當做是從整個程序開頭的偏移量了
標記了vstart=0 說明標號的值是從段開始的偏移量
2、實際測試 push pop
- 這里一開始忘記給字符串 message_test 最后加上 0 作為結尾,結果每次運行都死機,死機還死在不同的位置(就是顯示的字符串數量完全不一樣)
;===============================================================================
SECTION data vstart=0
buffer times 1024 db 0 ;緩沖區
message_1 db 0x0d,0x0a,0x0d,0x0a
db '**********User program is runing**********'
db 0x0d,0x0a,0
message_2 db ' Disk data:',0x0d,0x0a,0
message_test db 0x0d,0x0a,0x0d,0x0a
db '>>>>>> test stack pass <<<<<<',0x0d,0x0a,0
data_end:
;===============================================================================
;>>>>>>>>>>>> TEST STACK <<<<<<<<<<<<<<<<<<<<<<
push ecx
mov ecx,1000
testpush:
push ebx
loop testpush
mov ecx,1000
testpop:
pop ebx
loop testpop
pop ecx
mov ebx,message_test
call far [fs:PrintString]
;>>>>>>>>>>>> TEST STACK <<<<<<<<<<<<<<<<<<<<<<
實際測試用戶程序的棧 測試結果
- 心得:以后如果要寫有關測試的輸出語句,就應該在代碼可以正常運行的時候,先保證這條輸出語句也可以正常運行,再去功能化新的代碼,不然根本分不清是新代碼的功能有缺陷還是輸出語句本身錯了,誰會想到一條簡單的輸出函數調用都會錯,而且錯是錯在輸出用的字符串這里,沒錯,這就是匯編。
完整源碼
用戶程序 c13.asm (第十三章 習題答案)
;代碼清單13-3
;文件名:c13.asm
;文件說明:用戶程序
;創建日期:21:58 2018/6/4
usr_stack_len equ 1
usr_stack_cap equ usr_stack_len*0x1000 ; 0x1000 = 4096
;===============================================================================
SECTION header vstart=0
program_length dd program_end ;程序總長度#0x00
head_len dd header_end ;程序頭部的長度#0x04
stack_seg dd section.stack.start ;用于接收堆棧段選擇子#0x08
stack_len dd usr_stack_len ;程序建議的堆棧大小#0x0c
;以4KB為單位
prgentry dd start ;程序入口#0x10
code_seg dd section.code.start ;代碼段位置#0x14
code_len dd code_end ;代碼段長度#0x18
data_seg dd section.data.start ;數據段位置#0x1c
data_len dd data_end ;數據段長度#0x20
;-------------------------------------------------------------------------------
;符號地址檢索表
salt_items dd (header_end-salt)/256 ;#0x24
salt: ;#0x28
PrintString db '@PrintString'
times 256-($-PrintString) db 0
TerminateProgram db '@TerminateProgram'
times 256-($-TerminateProgram) db 0
ReadDiskData db '@ReadDiskData'
times 256-($-ReadDiskData) db 0
header_end:
;===============================================================================
SECTION stack vstart=0
stack_start:
times usr_stack_cap db 0 ;4KB的棧空間
stack_end:
;===============================================================================
SECTION data vstart=0
buffer times 1024 db 0 ;緩沖區
message_1 db 0x0d,0x0a,0x0d,0x0a
db '**********User program is runing**********'
db 0x0d,0x0a,0
message_2 db ' Disk data:',0x0d,0x0a,0
message_test db 0x0d,0x0a,0x0d,0x0a
db '>>>>>> test stack pass <<<<<<',0x0d,0x0a,0
data_end:
;===============================================================================
[bits 32]
;===============================================================================
SECTION code vstart=0
start:
mov eax,ds
mov fs,eax
mov eax,[stack_seg]
mov ss,eax
mov esp,0
mov eax,[data_seg]
mov ds,eax
mov ebx,message_1
call far [fs:PrintString]
mov eax,100 ;邏輯扇區號100
mov ebx,buffer ;緩沖區偏移地址
call far [fs:ReadDiskData] ;段間調用
mov ebx,message_2
call far [fs:PrintString]
mov ebx,buffer
call far [fs:PrintString] ;too.
;>>>>>>>>>>>> TEST STACK <<<<<<<<<<<<<<<<<<<<<<
push ecx
mov ecx,1000
testpush:
push ebx
loop testpush
mov ecx,1000
testpop:
pop ebx
loop testpop
pop ecx
mov ebx,message_test
call far [fs:PrintString]
;>>>>>>>>>>>> TEST STACK <<<<<<<<<<<<<<<<<<<<<<
jmp far [fs:TerminateProgram] ;將控制權返回到系統
code_end:
;===============================================================================
SECTION trail
;-------------------------------------------------------------------------------
program_end: