2017-02-26 4 views
2

Ich versuche, von meinem Bootloader zu meinem geladenen Kernel nach dem Umschalten in den geschützten Modus zu springen.Assembly Anruf auf falsche Adresse

Der Kernel lädt gut und ist an der richtigen Stelle, aber wenn die zweite Stufe des Loader ruft die Hauptfunktion des Kernels ruft es die falsche Adresse. Hier

ist die zweite Stufe des Laders (loader.asm):

global load_kern 
extern main 

[bits 32] 

load_kern: 
    call main 
    cli 
    hlt 

ich dann montieren und diese Verbindung mit ac Objektdatei eine Elf ausführbare Datei zu erstellen:

nasm -f elf32 loader.asm -o loader.o 
ld -melf_i386 -n -Tos.lds loader.o os.o -o kernel.elf 

Ich benutze diese Datei sie verlinken:

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) } 

} 

ich stelle dann diese kernel.elf im 2n d Sektor meines Disketten-Images (nach dem Bootsektor).

Wenn ich kernel.elf objdump die Ausgabe korrekt ist:

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> 

Die Adresse der Haupt fein abgebildet zu werden scheint, aber wenn ich meinen Kernel Sektor laden und es springen das ist, was gdb zeigt:

┌─────────────────────────────────────────────────────────────────┐ 
    >│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            | 

Der Kernel wird um 0x500 geladen, aber der Textabschnitt der Datei hat eine Ausrichtung von 0x100, so dass der Code bei 0x600 (nach dem Elf-Header) statt 0x500 beginnt. Der Code wird geladen, aber der Aufruf in loader.asm verwendet 0x646 anstelle von 0x648, wo main startet.

Beachten Sie, dass es bei 0x603 eine zusätzliche Zeile der Baugruppe gibt, die nicht dort sein soll. Es sieht wie eine Junk-Instruktion aus und ich denke, es könnte sein, was es ablegt. Es sieht aus wie die der Aufrufbefehl

500: e8 43 00 00 00   call 548 <main> 

richtig interpretiert wird, sondern die Nullen auf die nächste Anweisung tragen irgendwie den zusätzlichen unerwarteten Befehl zu erstellen. Nicht sicher, ob das eine Möglichkeit ist.

Ich kann nicht herausfinden, warum es eine Adresse verwendet, die 2 off ist, geschieht dies auch in anderen Teilen des Kernel-Codes.

Fühlen Sie sich frei, um irgendwelche anderen Fehler, die ich gemacht habe, aufzuzeigen, ich lerne, wie ich gehe. Ich bin mir nicht sicher, ob das damit zu tun hat, wie ich sie verbinde, welches Format ich verwende oder etwas anderes.


EDIT:

Also, es scheint dies tatsächlich ein Problem mit meinem Bootloader ist:

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 

LÖSUNG:

stellt sich heraus, meine GDT Codesegment war s et zu 16-Bit. Ich änderte es zu 32 und fügte eine [bits 32] Anweisung direkt hinzu, nachdem ich den Schalter zu geschütztem mache (direkt vor select jump).

Antwort

2

Sie weisen den Assembler an, Code für den geschützten Modus (32 Bit) mit [bits 32] zu generieren, aber Ihr Prozessor läuft im Realmodus (16 Bit).

Der Code, den Sie am Laufen haben, ist Unsinn. Viele Anweisungen werden im Real- und im Protected-Modus unterschiedlich interpretiert - zum Beispiel nimmt jmp im Real-Modus nur zwei unmittelbare Bytes. (Hier kommt zum Beispiel das unerwartete add bei 0x603 her - es ist die zweite Hälfte des fehlerhaften 32-Bit-Sofortwerts.)

+0

Das habe ich vermutet. Bedeutet das, dass ich nicht korrekt in den geschützten Modus meines Bootloaders umschalte? – Serial

+0

Wahrscheinlich nicht. Wo versuchen Sie den geschützten Modus zu aktivieren? Wie richten Sie die Seitentabellen ein? – duskwuff

+0

In meinem Bootloader. Ich benutze einen sehr einfachen GDT, indem ich auf protected umstelle, indem ich cr0 setze, setze die Segmentregister und springe dann weit zum Kernel-Code. Bootloader-Code: http://pastebin.com/RLWC7KkA – Serial

Verwandte Themen