汇编调用映射到错误的地址
我试图在切换到保护模式后从我的启动引导程序跳到我加载的内核。汇编调用映射到错误的地址
内核加载正常并且处于正确位置,但是当加载程序的第二阶段调用内核的主函数时,它会调用错误的地址。
这里是装载器的第二级():
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
之前)。
您正在指导汇编程序为[bits 32]
生成保护模式(32位)的代码,但您的处理器正在实模式(16位)下运行。
您最终运行的代码是无稽之谈。许多指令在实际模式和保护模式下的解释不同 - 例如,jmp
在实模式下只需要两个即时字节。 (例如,这是0x603的意外add
来自哪里,例如 - 这是错误的32位立即值的后半部分。)
这就是我所怀疑的。这是否意味着我没有在自举程序中正确切换到保护模式? – Serial
可能不是。你在哪里试图启用保护模式?你如何设置页面表? – duskwuff
在我的引导程序中。我正在使用非常基本的GDT,通过设置cr0来切换到保护状态,设置段寄存器然后跳转到内核代码。引导程序代码:http://pastebin.com/RLWC7KkA – Serial