我試圖在切換到保護模式後從我的啓動引導程序跳到我加載的內核。彙編調用映射到錯誤的地址
內核加載正常並且處於正確位置,但是當加載程序的第二階段調用內核的主函數時,它會調用錯誤的地址。
這裏是裝載器的第二級():
global load_kern
extern main
[bits 32]
load_kern:
call main
cli
hlt
我然後組裝並與交流對象文件鏈接此創建一個ELF可執行:
nasm -f elf32 loader.asm -o loader.o
ld -melf_i386 -n -Tos.lds loader.o os.o -o kernel.elf
我使用這個文件來鏈接它們:
ENTRY(load_kern);
PHDRS
{
headers PT_PHDR FILEHDR PHDRS;
code PT_LOAD;
}
SECTIONS
{
.text 0x500: ALIGN(0x100) { *(.text) } :code
.data : { *(.data) }
.bss : { *(.bss) }
/DISCARD/ : { *(.eh_frame) }
}
然後我把這個kernel.elf
放在2n d扇區的軟盤映像(引導扇區後)。
當我objdump的kernel.elf
輸出是正確的:
os/kernel.elf: file format elf32-i386
Disassembly of section .text:
00000500 <load_kern>:
500: e8 43 00 00 00 call 548 <main>
505: fa cli
506: f4 hlt
...
00000548 <main>:
548: 55 push %ebp
549: 89 e5 mov %esp,%ebp
54b: 68 5c 05 00 00 push $0x55c
550: 6a 05 push $0x5
552: e8 b0 ff ff ff call 507 <write_string>
557: 83 c4 08 add $0x8,%esp
55a: eb fe jmp 55a <main+0x12>
的主要地址似乎映射罰款,但是當我打開我的內核部門,並跳轉到它這是GDB顯示:
┌─────────────────────────────────────────────────────────────────┐
>│0x600 call 0x646 │
│0x603 add BYTE PTR [bx+si],al │
│0x605 cli │
│0x606 hlt
...
│0x646 leave │
│0x647 ret │
│0x648 push bp │
│0x649 mov bp,sp │
│0x64b push 0x55c |
內核在0x500被加載,但文件的文本部分的對齊方式爲0x100,所以代碼從0x600開始(在elf標頭之後)而不是0x500。代碼加載正常,但中的調用使用0x646,而不是主啓動時的0x648。
請注意,在0x603有一個額外的彙編行,不應該在那裏。它看起來像一個垃圾指令,我認爲這可能是拋棄它。它看起來像呼叫指令
500: e8 43 00 00 00 call 548 <main>
正在被正確解釋,但零將攜帶到下一個指令創建額外的意外指令。不知道這是否可能。
我弄不明白爲什麼它使用2的地址,這也發生在內核代碼的其他部分。
隨意指出我犯過的其他錯誤,我正在學習。我不確定這是否與我如何鏈接它們,我使用的格式或不同的東西有關。
編輯:
所以,看來這其實是我的引導程序的一個問題:
global start
[bits 16]
[org 0x7c00]
start: jmp boot
boot:
cli ; clear interrupts
cld ; clear direction flag
mov ax, 0
mov es, ax
mov bx, 0x500 ; set bx to where we load the kernel
mov al, 0x12 ; set lower byte of ax to read 18 sectors
mov ch, 0 ; set higher byte of cx to read track 0 (first track)
mov cl, 2 ; set lower byte of cx to read sector 2 (bootloader is sec1)
mov dh, 0 ; set higher byte of dx to read head 0 (top side of disk)
mov dl, 0 ; set lower byte of dx to read drive 0 (floppy drive)
mov ah, 0x02 ; read
int 0x13 ; call BIOS interrupt 13 to read drive
int 0x10 ; clear screen
jmp pm_switch
hlt ; this instruction should not execute
pm_switch:
xor ax, ax ; clear ds (used by lgdt)
mov ds, ax
cli
lgdt [gdt_desc]
mov eax, cr0
or eax, 1 ; switch to protected mode
mov cr0, eax
jmp 08h:select_jump
select_jump:
xor eax, eax
mov ax, 0x10 ; set data segments to data selector (0x10)
mov ds, ax
mov ss, ax
mov esp, 09000h
mov ax, 0
mov es, ax
; do a far jump to set cs and go to kernel code
jmp 08h:0x600
gdt: ; Address for the GDT
gdt_null: ; Null Segment
dd 0
dd 0
;KERNEL_CODE equ $-gdt
gdt_kernel_code:
dw 0FFFFh ; Limit 0xFFFF
dw 0 ; Base 0:15
db 0 ; Base 16:23
db 09Ah ; Present, Ring 0, Code, Non-conforming, Readable
db 00Fh ; Page-granular
db 0 ; Base 24:31
;KERNEL_DATA equ $-gdt
gdt_kernel_data:
dw 0FFFFh ; Limit 0xFFFF
dw 0 ; Base 0:15
db 0 ; Base 16:23
db 092h ; Present, Ring 0, Data, Expand-up, Writable
db 00Fh ; Page-granular
db 0 ; Base 24:32
gdt_end: ; Used to calculate the size of the GDT
gdt_desc: ; The GDT descriptor
dw gdt_end - gdt - 1 ; Limit (size)
dd gdt ; Address of the GDT
; pad the file to file the rest of the sector (512 bytes) and add boot sig
times 510 - ($-$$) db 0
dw 0xAA55
SOLUTION:
原來我GDT代碼段是s等到16位。我將它改爲32,並在開關保護後立即添加了[bits 32]
指令(就在select jump
之前)。
這就是我所懷疑的。這是否意味着我沒有在自舉程序中正確切換到保護模式? – Serial
可能不是。你在哪裏試圖啓用保護模式?你如何設置頁面表? – duskwuff
在我的引導程序中。我正在使用非常基本的GDT,通過設置cr0來切換到保護狀態,設置段寄存器然後跳轉到內核代碼。引導程序代碼:http://pastebin.com/RLWC7KkA – Serial