您好,登錄后才能下訂單哦!
Q.從上節課可以引出一個小問題,下面的語句是否有Bug的存在
在進入保護模式后,在第一處使用了棧段選擇址對ss賦值,然后使用打印,覺得會出現Bug的存在的是因為使用了調用函數,而使用調用函數需要棧空間,還需要棧頂指針-esp寄存器,而在這里的代碼中沒有對esp寄存器進行賦值。在這里需要對esp寄存器進行查找。
A.對上節課的代碼進行實驗
在這里進行的步驟是首先反編譯找到跳轉處的地址值,然后在bochs上設置斷點通過一步一步的單步調試使其運行到圖5中即將進行call命令處,然后實驗reg命令對寄存器地址進行查看,發現esp寄存器的地址是0x00007c00,與上章的棧頂寄存器地址一致,程序的運行結果符合預期。這里會出現一個疑問,沒有對esp寄存器地址進行賦值,為什么會在32保護模式下,esp寄存器的值卻指向棧頂,可以再一次的進行實驗
實驗-對esp進行驗證
![]
這里運行的情況是,同樣首先通過反編譯對圖1處的地址進行查找,然后設置斷點并查該斷點處的esp寄存器值,然后單步調試值跳轉處在對,esp寄存器值進行查看,最后進入到32位保護模式下對esp寄存器地址進行查看,發現所有的esp寄存器的值都是相同的,所以在32位保護模式下剋有不對esp寄存器進行賦值.
a.保護模式下的棧段
1.指定一段空間,并為其定義段描述符
2.根據段描述符表中的位置定義選擇子
3.初始化段寄存器
4.初始化棧頂指針
B.棧段的一般性定義(保護模式下)
主要對上章的段描述符與基址進行了修改,同時重新定義了一個新的代碼節,還有定義保護模式下專用的棧段
%include "inc.asm"
org 0x9000
jmp CODE16_SEGMENT
[section .gdt]
; GDT definition
;
GDT_ENTRY : Descriptor 0, 0, 0
CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32
VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32
DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_DR + DA_32
STACK32_DESC : Descriptor 0, TopOfStack32, DA_DRW + DA_32
; GDT end
GdtLen equ $ - GDT_ENTRY
GdtPtr:
dw GdtLen - 1
dd 0
; GDT Selector
Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL0
Data32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0
Stack32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL0
; end of [section .gdt]
TopOfStack16 equ 0x7c00
[section .dat]
[bits 32]
DATA32_SEGMENT:
DTOS db "D.T.OS!", 0
DTOS_OFFSET equ DTOS - $$
HELLO_WORLD db "Hello World!", 0
HELLO_WORLD_OFFSET equ HELLO_WORLD - $$
Data32SegLen equ $ - DATA32_SEGMENT
[section .s16]
[bits 16]
CODE16_SEGMENT:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, TopOfStack16
; initialize GDT for 32 bits code segment
mov esi, CODE32_SEGMENT
mov edi, CODE32_DESC
call InitDescItem
mov esi, DATA32_SEGMENT
mov edi, DATA32_DESC
call InitDescItem
mov esi, STACK32_SEGMENT
mov edi, STACK32_DESC
call InitDescItem
; initialize GDT pointer struct
mov eax, 0
mov ax, ds
shl eax, 4
add eax, GDT_ENTRY
mov dword [GdtPtr + 2], eax
; 1. load GDT
lgdt [GdtPtr]
; 2. close interrupt
cli
; 3. open A20
in al, 0x92
or al, 00000010b
out 0x92, al
; 4. enter protect mode
mov eax, cr0
or eax, 0x01
mov cr0, eax
; 5. jump to 32 bits code
jmp dword Code32Selector : 0
; esi --> code segment label
; edi --> descriptor label
InitDescItem:
push eax
mov eax, 0
mov ax, cs
shl eax, 4
add eax, esi
mov word [edi + 2], ax
shr eax, 16
mov byte [edi + 4], al
mov byte [edi + 7], ah
pop eax
ret
[section .s32]
[bits 32]
CODE32_SEGMENT:
mov ax, VideoSelector
mov gs, ax
mov ax, Stack32Selector
mov ss, ax
mov eax, TopOfStack32
mov esp, eax
mov ax, Data32Selector
mov ds, ax
mov ebp, DTOS_OFFSET
mov bx, 0x0C
mov dh, 12
mov dl, 33
call PrintString
mov ebp, HELLO_WORLD_OFFSET
mov bx, 0x0C
mov dh, 13
mov dl, 31
call PrintString
jmp $
; ds:ebp --> string address
; bx --> attribute
; dx --> dh : row, dl : col
PrintString:
push ebp
push eax
push edi
push cx
push dx
print:
mov cl, [ds:ebp]
cmp cl, 0
je end
mov eax, 80
mul dh
add al, dl
shl eax, 1
mov edi, eax
mov ah, bl
mov al, cl
mov [gs:edi], ax
inc ebp
inc dl
jmp print
end:
pop dx
pop cx
pop edi
pop eax
pop ebp
ret
Code32SegLen equ $ - CODE32_SEGMENT
[section .gs]
[bits 32]
STACK32_SEGMENT:
times 1024 * 4 db 0
Stack32SegLen equ $ - STACK32_SEGMENT
TopOfStack32 equ Stack32SegLen - 1
make之后在bochs下的運行結果
現在通過實驗對esp再次進行查看,首先再次通過反編譯對跳轉指令查看,然后設置斷點并單步查看,并單步查看的寄存器地址的變化-ss段寄存器指向了定義的棧段,最后esp指向棧空間的限制處,最后打印處字符串,說明自定義的棧段空間被使用了
C.是否能從保護模式返回實模式?
a.80x86中的一個神秘限制
1.無法直接從32位代碼段回到實模式
2.只能從16位代碼段間接返回實模式
3.在返回前必須用合適的選擇子對寄存器賦值
b.處理器中的設計簡介
1.80286之后的處理器都提供兼容8086的實模式
2.然而,絕大多數處理器都運行于保護模式
3.因此,保護模式的運行效率至關重要
4.那么,處理器如何高效的訪問內存中的段描述符?
解決方案-高速緩存存儲器
a.當使用選擇子設置段寄存器時
1.根據選擇子訪問內存中的段描述符
2.將段描述符加載到段寄存器的高速緩沖存儲器
3.需要段描述符信息時,直接從高速緩沖存儲器中獲得
但是會出現一個問題,當處理器運行于實模式時,段寄存器的高速緩沖存儲器是否會用到?
在這里需要注意的是-在實模式下,高速緩沖存儲器仍然發揮著作用,段基址是32位,其值是相應段寄存器的值乘以16,實模式下段基址有效位為20位,段界限固定為0xFFFF(64K),段屬性的值不可設置,只能繼續沿用保護方式下所設置的值
因此,當從保護模式返回實模式時-提供加載一個合適的描述符選擇子到有關段寄存器,以使得對應段描述符高速緩沖寄存器中含有合適的段界限和屬性
返回實模式的流程
代碼
%include "inc.asm"
org 0x9000
jmp ENTRY_SEGMENT
[section .gdt]
; GDT definition
; 段基址, 段界限, 段屬性
GDT_ENTRY : Descriptor 0, 0, 0
CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32
VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32
DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_DR + DA_32
STACK32_DESC : Descriptor 0, TopOfStack32, DA_DRW + DA_32
CODE16_DESC : Descriptor 0, 0xFFFF, DA_C
UPDATE_DESC : Descriptor 0, 0xFFFF, DA_DRW
; GDT end
GdtLen equ $ - GDT_ENTRY
GdtPtr:
dw GdtLen - 1
dd 0
; GDT Selector
Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0
VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL0
Data32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0
Stack32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL0
Code16Selector equ (0x0005 << 3) + SA_TIG + SA_RPL0
UpdateSelector equ (0x0006 << 3) + SA_TIG + SA_RPL0
; end of [section .gdt]
TopOfStack16 equ 0x7c00
[section .dat]
[bits 32]
DATA32_SEGMENT:
DTOS db "D.T.OS!", 0
DTOS_OFFSET equ DTOS - $$
HELLO_WORLD db "Hello World!", 0
HELLO_WORLD_OFFSET equ HELLO_WORLD - $$
Data32SegLen equ $ - DATA32_SEGMENT
[section .s16]
[bits 16]
ENTRY_SEGMENT:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, TopOfStack16
mov [BACK_TO_REAL_MODE + 3], ax
; initialize GDT for 32 bits code segment
mov esi, CODE32_SEGMENT
mov edi, CODE32_DESC
call InitDescItem
mov esi, DATA32_SEGMENT
mov edi, DATA32_DESC
call InitDescItem
mov esi, STACK32_SEGMENT
mov edi, STACK32_DESC
call InitDescItem
mov esi, CODE16_SEGMENT
mov edi, CODE16_DESC
call InitDescItem
; initialize GDT pointer struct
mov eax, 0
mov ax, ds
shl eax, 4
add eax, GDT_ENTRY
mov dword [GdtPtr + 2], eax
; 1. load GDT
lgdt [GdtPtr]
; 2. close interrupt
cli
; 3. open A20
in al, 0x92
or al, 00000010b
out 0x92, al
; 4. enter protect mode
mov eax, cr0
or eax, 0x01
mov cr0, eax
; 5. jump to 32 bits code
jmp dword Code32Selector : 0
BACK_ENTRY_SEGMENT:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, TopOfStack16
in al, 0x92
and al, 11111101b
out 0x92, al
sti
mov bp, HELLO_WORLD
mov cx, 12
mov dx, 0
mov ax, 0x1301
mov bx, 0x0007
int 0x10
jmp $
; esi --> code segment label
; edi --> descriptor label
InitDescItem:
push eax
mov eax, 0
mov ax, cs
shl eax, 4
add eax, esi
mov word [edi + 2], ax
shr eax, 16
mov byte [edi + 4], al
mov byte [edi + 7], ah
pop eax
ret
[section .s16]
[bits 16]
CODE16_SEGMENT:
mov ax, UpdateSelector
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov eax, cr0
and al, 11111110b
mov cr0, eax
BACK_TO_REAL_MODE:
jmp 0 : BACK_ENTRY_SEGMENT
Code16SegLen equ $ - CODE16_SEGMENT
[section .s32]
[bits 32]
CODE32_SEGMENT:
mov ax, VideoSelector
mov gs, ax
mov ax, Stack32Selector
mov ss, ax
mov eax, TopOfStack32
mov esp, eax
mov ax, Data32Selector
mov ds, ax
mov ebp, DTOS_OFFSET
mov bx, 0x0C
mov dh, 12
mov dl, 33
call PrintString
mov ebp, HELLO_WORLD_OFFSET
mov bx, 0x0C
mov dh, 13
mov dl, 31
call PrintString
jmp Code16Selector : 0
; ds:ebp --> string address
; bx --> attribute
; dx --> dh : row, dl : col
PrintString:
push ebp
push eax
push edi
push cx
push dx
print:
mov cl, [ds:ebp]
cmp cl, 0
je end
mov eax, 80
mul dh
add al, dl
shl eax, 1
mov edi, eax
mov ah, bl
mov al, cl
mov [gs:edi], ax
inc ebp
inc dl
jmp print
end:
pop dx
pop cx
pop edi
pop eax
pop ebp
ret
Code32SegLen equ $ - CODE32_SEGMENT
[section .gs]
[bits 32]
STACK32_SEGMENT:
times 1024 * 4 db 0
Stack32SegLen equ $ - STACK32_SEGMENT
TopOfStack32 equ Stack32SegLen - 1
運行結果
小結
1.定義保護模式的棧段時,必須設置段選擇子和棧頂指針
2.從保護模式能夠間接跳轉返回實模式
3.在實模式下,依然使用高速緩沖存儲器中數據做有效性判斷
4.通過運行時修改指令中的數據能夠動態決定代碼的行為
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。