Assembly 如何在保护模式下设置堆栈段?
这个问题是我在x86保护模式下使用GDT下的选择器定义了一个数据和堆栈段。当jmp进入保护模式时,我似乎可以访问数据部分,但在推送eax时崩溃。请参阅以下代码:Assembly 如何在保护模式下设置堆栈段?,assembly,x86,operating-system,nasm,protected-mode,Assembly,X86,Operating System,Nasm,Protected Mode,这个问题是我在x86保护模式下使用GDT下的选择器定义了一个数据和堆栈段。当jmp进入保护模式时,我似乎可以访问数据部分,但在推送eax时崩溃。请参阅以下代码: %include "../inc/descriptor.asm" %include "../inc/define.asm" org 7c00h jmp begin ; -----------------------------------------------------------------------
%include "../inc/descriptor.asm"
%include "../inc/define.asm"
org 7c00h
jmp begin
; -----------------------------------------------------------------------
; Const variable
STACK_BASE EQU 1000000h ; 16M
DATA_BASE EQU 2000000h ; 32M
STACK_SIZE EQU 8000h ; 32K
STACK_LIMIT EQU 1008000h ; 16M + 32K
DATA_SIZE EQU 100000h ; 1M
; GDT and LDT
; Descriptor base limit property
[SECTION .gdt]
GDT: Descriptor 0, 0, 0
LDT_CODE32: Descriptor 0, SEG_CODE32_LEN - 1, DA_C + DA_32
LDT_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW
LDT_STACK: Descriptor STACK_BASE, STACK_SIZE - 1, DA_DRWA + DA_B
LDT_DATA: Descriptor DATA_BASE, DATA_SIZE - 1, DA_DRW
GDTLEN EQU $ - GDT
GDTPTR DW GDTLEN - 1
DD 0
; Selectors
SLT_CODE32 EQU LDT_CODE32 - GDT
SLT_VIDEO EQU LDT_VIDEO - GDT
SLT_STACK EQU LDT_STACK - GDT
SLT_DATA EQU LDT_DATA - GDT
; -----------------------------------------------------------------------
; Real mode code
[SECTION .s16]
[BITS 16]
begin:
mov ax, cs
mov ds, ax
; init 32 bits code section descriptor
xor eax, eax
mov ax, cs
shl eax, 4
add eax, code32
mov word [LDT_CODE32 + 2], ax
shr eax, 16
mov byte [LDT_CODE32 + 4], al
mov byte [LDT_CODE32 + 7], ah
; prepare for loading gdtr
xor eax, eax
mov ax, ds
shl eax, 4
add eax, GDT
mov dword [GDTPTR + 2], eax
lgdt [GDTPTR]
cli
in al, 92h
or al, 10b
out 92h, al
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword SLT_CODE32:0
; protected mode code
[SECTION .s32]
[BITS 32]
code32:
mov ax, SLT_VIDEO
mov gs, ax
mov ax, SLT_STACK
mov ss, ax
mov esp, STACK_LIMIT - 16
mov ax, SLT_DATA
mov ds, ax
mov eax, 012345678h
xor edx, edx
mov [edx], eax
mov edx, [edx]
push eax ; **<= crashed here.**
; ---------------------------------
; PREPARE DEBUG CHAR
mov ax, SLT_VIDEO
mov gs, ax
mov bh, 0ch
mov bl, 'B'
mov esi, (80 * 1 + 1) * 2
mov [gs:esi], bx
jmp $
; ; END OF PREPARE DEBUG CHAR
; ---------------------------------
push eax
pop ebx
mov eax, DATA_BASE
mov dword [eax], ebx
mov edi, 0
mov esi, (80 * 1 + 1) * 2
call PRINT_DWORD
jmp $
; ---------------------------------
; ; PREPARE DEBUG CHAR
; mov ax, SLT_VIDEO
; mov gs, ax
; mov bh, 0ch
; mov bl, 'B'
; mov esi, (80 * 1 + 1) * 2
; mov [gs:esi], bx
; jmp $
; ; END OF PREPARE DEBUG CHAR
; ---------------------------------
SEG_CODE32_LEN EQU $ - code32
times 290 - ($ - $$) db 0
dw 0xaa55
; command reference:
; nasm protected_mode.asm -o pm.bin
; dd if=pm.bin of=pm.img bs=512 count=1
Define.asm:
;
DA_32 EQU 4000h
DA_DRW EQU 92h
DA_DRWA EQU 93h
DA_C EQU 98h
DA_B EQU DA_32
DA_ELEMENT_4K EQU 8000h
;
; Paging Entry Attribute
PG_P EQU 1
PG_RW_W EQU 2
PG_US_U EQU 4
由于将段基设置为
STACK\u base
,因此不能将其添加到堆栈指针中。因此,mov-esp,STACK\u LIMIT-16
应该是mov-esp,STACK\u SIZE-16
PS:您从未设置过cs,因此您的代码可能会在非零的系统上中断。最后,我解释了推送指令将导致崩溃的原因。谢谢杰斯特的帮助。我在这里写这封信是为了其他人可能会漏掉的评论 当定义一个堆栈段时,GDT的属性应始终设置为向下增长,类型为6/7。段基堆栈_基定义了最高的 具有堆栈大小的该段的地址。所以范围应该是从STACK_BASE-STACK_SIZE到STACK_BASE 然后将esp设置为STACK_BASE,现在就可以使用STACK了。
谢谢大家的帮助。:) 与定义单独的数据段、堆栈段和视频段不同,您可以像代码段那样只创建一个引用所有内存的数据段来简化操作。是的,我只想尝试一下独立的堆栈段选择器。按照惯例,应该按照你说的做。谢谢,但是堆栈esp应该是指向段的最高地址的指针。关于cs的另一个问题是,如何在保护模式下获得cs?是的,
esp
将是相对于段基的最高偏移量,段基是GDT中已经设置的堆栈基。我不明白你关于cs
的问题。你在那段代码中说我从来没有设置过cs寄存器。看来我对此一无所知,你能帮我解释一下吗?提前谢谢!使用跳远到您的入口点,而不是jmp begin
dojmp 0:begin
。我仍然对设置此段的esp有疑问。该段从16M开始,到16M+32K结束。如果将esp设置为STACK_BASE,则它是一个完整的堆栈?或者你的意思是STACK_BASE是堆栈段的最高地址,范围应该在16M-32K到16M之间?提前谢谢!
;
DA_32 EQU 4000h
DA_DRW EQU 92h
DA_DRWA EQU 93h
DA_C EQU 98h
DA_B EQU DA_32
DA_ELEMENT_4K EQU 8000h
;
; Paging Entry Attribute
PG_P EQU 1
PG_RW_W EQU 2
PG_US_U EQU 4