2017-02-26 48 views
2

我試圖在切換到保護模式後從我的啓動引導程序跳到我加載的內核。彙編調用映射到錯誤的地址

內核加載正常並且處於正確位置,但是當加載程序的第二階段調用內核的主函數時,它會調用錯誤的地址。

這裏是裝載器的第二級(​​):

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之前)。

回答

2

您正在指導彙編程序爲[bits 32]生成保護模式(32位)的代碼,但您的處理器正在實模式(16位)下運行。

您最終運行的代碼是無稽之談。許多指令在實際模式和保護模式下的解釋不同 - 例如,jmp在實模式下只需要兩個即時字節。 (例如,這是0x603的意外add來自哪裏,例如 - 這是錯誤的32位立即值的後半部分。)

+0

這就是我所懷疑的。這是否意味着我沒有在自舉程序中正確切換到保護模式? – Serial

+0

可能不是。你在哪裏試圖啓用保護模式?你如何設置頁面表? – duskwuff

+0

在我的引導程序中。我正在使用非常基本的GDT,通過設置cr0來切換到保護狀態,設置段寄存器然後跳轉到內核代碼。引導程序代碼:http://pastebin.com/RLWC7KkA – Serial