使用链接器脚本在不同内存地址链接目标文件开发bootloader问题求助
问题根因定位
- 段地址与虚拟地址不匹配:链接脚本中.kernel段的虚拟地址设置为
0x1000,但你跳转用的jmp 0x1000:0x0000对应的物理地址是0x1000 <<4 + 0 = 0x10000,两者完全不匹配,内核代码执行时寻址会全部错位。 - 引导扇区格式错误:.boot段没有显式指定加载地址,默认和虚拟地址
0x7c00一致,生成的二进制文件开头会填充大量空字节到0x7c00位置,导致MBR魔数0xaa55不在第一个扇区的最后两个字节,BIOS识别不到这是合法引导扇区。 - 16位实模式寻址错误:kernel.s中用
offset flat:是32位平坦模式的寻址方式,在16位实模式下会出现地址计算错误。
修复方案
1. 修正链接脚本linker.ld
ENTRY(_start) OUTPUT_FORMAT("binary") SECTIONS { /* 引导扇区虚拟地址为0x7c00,加载地址为0,生成的二进制文件无前置空填充 */ .boot 0x7c00 : AT(0) { *(.boot) /* 固定.boot段占512字节,避免内核内容侵入引导扇区 */ . = 512; } /* 内核虚拟地址对应0x1000:0的物理地址0x10000,加载地址接在引导扇区之后的0x200位置 */ .kernel 0x10000 : AT(0x200) { *(.kernel) *(.text) *(.rodata) *(.data) . = ALIGN(4); __bss_start = .; *(.bss) *(COMMON) __bss_end = .; } }
2. 修正Boot.s的扇区加载逻辑
读扇区前先设置段寄存器es,保证内核写入的物理地址和链接脚本的虚拟地址匹配:
mov dh, 1 # 读取1个扇区 mov ax, 0x1000 mov es, ax # 段地址设为0x1000 mov bx, 0x0000 # 偏移为0,对应物理地址0x10000 call read_sectors jmp 0x1000:0x0000
3. 修正kernel.s代码
添加全局入口符号,调整寻址写法适配16位实模式:
.code16 .section .kernel .text .intel_syntax noprefix .globl kernel_entry kernel_entry: mov si, offset tesing_msg mov bl, 0xf0 call print16 call new_line16
逻辑校验
你的核心设计逻辑是合理的:BIOS默认将合法引导扇区加载到物理地址0x7c00,所以.boot段虚拟地址必须设置为0x7c00保证内部寻址正确;内核加载到0x10000位置用段地址0x1000访问,不会和低地址的BIOS中断向量、引导代码区域冲突。
内容的提问来源于stack exchange,提问作者Liwinux
相关产品推荐
相关产品推荐

