2017-02-26 1 views
2

J'essaye de passer de mon chargeur de démarrage à mon noyau chargé après être passé en mode protégé. Le noyau se charge correctement et se trouve au bon endroit, mais lorsque le deuxième étage du chargeur appelle la fonction principale du noyau, il appelle la mauvaise adresse.L'assembly d'assemblage mappe la mauvaise adresse

Voici la deuxième étape du chargeur (loader.asm):

global load_kern 
extern main 

[bits 32] 

load_kern: 
    call main 
    cli 
    hlt 

Je puis assemblez et établir un lien avec le fichier objet ac pour créer un exécutable elfe:

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

J'utilise ce fichier pour les relier:

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

} 

je place alors ce kernel.elf dans le 2n d secteur de mon image de disquette (après le secteur de démarrage).

Quand j'objdump kernel.elf la sortie est correcte:

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> 

L'adresse de la principale semble être mis en correspondance bien mais quand je charge mon secteur du noyau et y sauter c'est ce que gdb montre:

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

Le noyau est chargé à 0x500 mais la section de texte du fichier a un alignement de 0x100, de sorte que le code commence à 0x600 (après l'en-tête elf) au lieu de 0x500. Le code se charge correctement, mais l'appel loader.asm utilise 0x646 au lieu de 0x648, où le démarrage principal. Notez qu'il existe une ligne d'assemblage supplémentaire à 0x603 qui n'est pas censée être là. Cela ressemble à une instruction indésirable et je pense que c'est peut-être ce qui le rejette. Il ressemble à l'instruction d'appel

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

est interprété correctement, mais les zéros mènent à l'instruction suivante en quelque sorte à créer l'instruction inattendue supplémentaire. Je ne sais pas si c'est une possibilité.

Je n'arrive pas à comprendre pourquoi il utilise une adresse qui est désactivée 2, cela se produit également dans d'autres parties du code du noyau. N'hésitez pas à signaler d'autres erreurs que j'ai faites, j'apprends comme je vais. Je ne suis pas sûr si cela a à voir avec la façon dont je les relie, le format que j'utilise ou quelque chose de différent.


EDIT:

Ainsi, il semble que c'est en fait un problème avec mon bootloader:

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:

tourne mon TDG segment de code était s et à 16 bits. Je l'ai changé à 32 et ajouté une instruction [bits 32] juste après que je mette le commutateur à protégé (juste avant select jump).

Répondre

2

Vous demandez à l'assembleur de générer du code pour le mode protégé (32 bits) avec [bits 32], mais votre processeur fonctionne en mode réel (16 bits).

Le code que vous finissez par exécuter n'a aucun sens. De nombreuses instructions sont interprétées différemment en mode réel et protégé - par exemple, jmp prend seulement deux octets immédiats en mode réel. (C'est de là que vient l'add inattendu à 0x603, par exemple - c'est la seconde moitié de la valeur immédiate erronée de 32 bits.)

+0

C'est ce que je soupçonnais. Cela signifie-t-il que je ne passe pas en mode protégé correctement dans mon bootloader? – Serial

+0

Probablement pas. Où essayez-vous d'activer le mode protégé? Comment configurez-vous les tables de pages? – duskwuff

+0

Dans mon bootloader. J'utilise un GDT très basique, en passant à cr0, en configurant les registres de segments puis en sautant très loin dans le code du noyau. code du bootloader: http://pastebin.com/RLWC7KkA – Serial