首页 文章

汇编调用映射到错误的地址

提问于
浏览
2

在切换到保护模式后,我试图从我的引导程序跳转到我加载的内核 .

内核加载正常并且位于正确的位置,但是当加载器的第二阶段调用内核的主函数时,它会调用错误的地址 .

这是加载器的第二阶段( loader.asm ):

global load_kern
extern main

[bits 32]

load_kern: 
    call main
    cli 
    hlt

然后我组装并将其与c对象文件链接以创建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 放在我的软盘映像的第二个扇区中(在引导扇区之后) .

当我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>

main的地址似乎映射得很好,但是当我加载我的内核扇区并跳转到它时,这就是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 . 代码加载正常,但 loader.asm 中的调用使用0x646而不是0x648,其中main启动 .

请注意,在0x603处有一个额外的汇编线,不应该在那里 . 它看起来像一个垃圾指令,我认为它可能正在抛弃它 . 它看起来像是调用指令

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

正在被正确解释,但零以某种方式进行下一条指令以创建额外的意外指令 . 不确定是否有可能 .

我无法弄清楚为什么它使用2关闭的地址,这也发生在内核代码的其他部分 .

随意指出我所犯的任何其他错误,我正在学习 . 我不确定这是否与我如何链接它们,我使用的格式或不同的东西有关 .


EDIT:

所以,看起来这实际上是我的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:

结果我的GDT代码段被设置为16位 . 我将其更改为32并在切换到受保护(在 select jump 之前)后立即添加了 [bits 32] 指令 .

1 回答

  • 2

    您正在指示汇编程序使用 [bits 32] 为保护模式(32位)生成代码,但您的处理器正在以实模式(16位)运行 .

    The code you end up running is nonsense. 许多指令在实模式和保护模式下的解释方式不同 - 例如, jmp 在实模式下仅占用两个立即字节 . (这是0x603处的意外 add 来自 - 例如 - 它是错误的32位立即值的后半部分 . )

相关问题