我正在为学习目的编写一个玩具内核,我遇到了一些麻烦。我做了一个简单的引导加载程序,从软盘加载一个段(用32位代码写入),然后引导加载程序启用A20门并打开保护模式。如果我用汇编程序写它,我可以跳到32位代码,但是如果我用C语言编写它,我会得到一个三重错误。当我反汇编C代码时,我可以看到前两条指令涉及设置一个新的堆栈帧。这是工作ASM代码和失败的C代码之间的关键区别。 我使用NASM v2.10.05作为ASM代码,使用来自DJGPP 4.72集合的GCC作为C代码。
这是引导加载程序代码:
org 7c00h
BITS 16
entry:
mov [drive], dl ;Save the current drive
cli
mov ax,cs ; Setup segment registers
mov ds,ax ; Make DS correct
mov ss,ax ; Make SS correct
mov bp,0fffeh
mov sp,0fffeh ;Setup a temporary stack
sti
;Set video mode to text
;===================
mov ah, 0
mov al, 3
int 10h
;===================
;Set current page to 0
;==================
mov ah, 5
mov al, 0
int 10h
;==================
;Load the sector
;=============
call load_image
;=============
;Clear interrupts
;=============
cli
;=============
;Disable NMIs
;============
in ax, 70h
and ax, 80h ;Set the high bit to 1
out 70h, ax
;============
;Enable A20:
;===========
mov ax, 02401h
int 15h
;===========
;Load the GDT
;===========
lgdt [gdt_pointer]
;===========
;Clear interrupts
;=============
cli
;=============
;Enter protected mode
;==================
mov eax, cr0
or eax, 1 ;Set the low bit to 1
mov cr0, eax
;==================
jmp 08h:clear_pipe ;Far jump to clear the instruction queue
;======================================================
load_image:
reset_drive:
mov ah, 00h
; DL contains *this* drive, given to us by the BIOS
int 13h
jc reset_drive
read_sectors:
mov ah, 02h
mov al, 01h
mov ch, 00h
mov cl, 02h
mov dh, 00h
; DL contains *this* drive, given to us by the BIOS
mov bx, 7E0h
mov es, bx
mov bx, 0
int 13h
jc read_sectors
ret
;======================================================
BITS 32 ;Protected mode now!
clear_pipe:
mov ax, 10h ; Save data segment identifier
mov ds, ax ; Move a valid data segment into the data segment register
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax ; Move a valid data segment into the stack segment register
mov esp, 90000h ; Move the stack pointer to 90000h
mov ebp, esp
jmp 08h:7E00h ;Jump to the kernel proper
;===============================================
;========== GLOBAL DESCRIPTOR TABLE ==========
;===============================================
gdt: ; Address for the GDT
gdt_null: ; Null Segment
dd 0
dd 0
gdt_code: ; Code segment, read/execute, nonconforming
dw 0FFFFh ; LIMIT, low 16 bits
dw 0 ; BASE, low 16 bits
db 0 ; BASE, middle 8 bits
db 10011010b ; ACCESS byte
db 11001111b ; GRANULARITY byte
db 0 ; BASE, low 8 bits
gdt_data: ; Data segment, read/write, expand down
dw 0FFFFh
dw 0
db 0
db 10010010b
db 11001111b
db 0
gdt_end: ; Used to calculate the size of the GDT
gdt_pointer: ; The GDT descriptor
dw gdt_end - gdt - 1 ; Limit (size)
dd gdt ; Address of the GDT
;===============================================
;===============================================
drive: db 00 ;A byte to store the current drive in
times 510-($-$$) db 00
db 055h
db 0AAh
这是内核代码:
void main()
{
asm("mov byte ptr [0x8000], 'T'");
asm("mov byte ptr [0x8001], 'e'");
asm("mov byte ptr [0x8002], 's'");
asm("mov byte ptr [0x8003], 't'");
}
内核只是将这四个字节插入内存,我可以在VMPlayer虚拟机中运行代码时检查。如果出现字节,那么我知道代码正在运行。如果我在ASM中编写看起来像这样的代码,那么该程序可以工作:
org 7E00h
BITS 32
main:
mov byte [8000h], 'T'
mov byte [8001h], 'e'
mov byte [8002h], 's'
mov byte [8003h], 't'
hang:
jmp hang
唯一的区别是我在反汇编的C代码中找到的两个堆栈操作,它们是:
push ebp
mov ebp, esp
对此事的任何帮助将不胜感激。我想我错过了一些相对较小的东西,但至关重要,在这里,因为我知道这种事情是可行的。
答案 0 :(得分:2)
答案 1 :(得分:1)
我会尝试将保护模式堆栈的地址移动到其他位置 - 比如0x80000 - 其中(根据这个:http://wiki.osdev.org/Memory_Map_(x86))是RAM,保证免费使用(与您当前的地址相对)使用 - 0x90000 - 显然可能不存在 - 取决于您作为测试环境使用的内容。)
答案 2 :(得分:0)
我按照Michael的链接中的说明,将.text部分解压缩到二进制文件,现在我已经获得了一些可启动的C代码。所以看起来问题根本不是堆栈,而是C文件中的额外信息。我正在用堆栈吠叫错误的树!感谢大家的建议,我很感激。