c09_mbr — MBR 加载器主流程框图
c09_mbr.asm · SECTION mbr · vstart=0x7C00 · 实模式
《x86汇编语言:从实模式到保护模式》· 李忠
第9章 · MBR加载器 (第1页 / 共2页)
主流程
重定位流程
子程序
循环体
判断分支
段间跳转
主流程 BIOS 跳转入口 CS=0x0000, IP=0x7C00 · MBR被加载到物理地址0x7C00 初始化堆栈 SS=0x0000, SP=0x0000 · 利用内存高端作临时栈 计算加载段地址 dx:ax ← [cs:phy_base] = 0x00010000 dx:ax ÷ 16 → ax=0x1000 · DS=ES=0x1000 读取第一个扇区 DI:SI = 0:100(LBA扇区号) DS:BX = 0x1000:0x0000 → 写入物理地址0x10000 call read_hard_disk_0 dx == 0? 程序长度是512整数倍? ax = ax − 1(已读1扇区) ax == 0? 程序长度≤512字节? 循环读取剩余扇区(CX次) DS+=0x20(下一512字节段), SI++ call read_hard_disk_0 → loop call direct: 单独回填入口点段地址 dx:ax ← header[0x08:0x06] call calc_segment_base → [0x06] ← ax call 段重定位表处理(realloc循环) CX=[0x0a]=5项,BX=0x0c起 dx:ax←[BX+2:BX] → calc → [BX]←ax → BX+=4 loop realloc(共5次) call ×5 jmp far [0x04] CS←[0x06](code_1段值),IP←[0x04](start偏移0x00A2) 子程序 read_hard_disk_0 输入:DI:SI=32位LBA扇区号 DS:BX=目标内存地址 ① 0x1F2←1(读1扇区) ② 0x1F3←SI低8,0x1F4←SI高8(LBA bit15~0) ③ 0x1F5←DI低8(LBA bit23~16) ④ 0x1F6←0xE0|DI高4位(LBA28主盘模式) ⑤ 0x1F7←0x20(读命令) ⑥ 等待状态(0x1F7 & 0x88)==0x08 ⑦ 循环256次从0x1F0读字→[DS:BX],ret calc_segment_base 输入:DX:AX = 32位文件内偏移 返回:AX = 16位段值 ① dx:ax += phy_base(0x10000) add ax,[cs:phy_base] adc dx,[cs:phy_base+2] ② 右移4位得段值 shr ax,4 ror dx,4 / and dx,0xF000 or ax,dx → AX=段值,ret header 内存布局(DS:0x00起) [0x00~0x03]program_length(程序总字节数) [0x04~0x05]start偏移(IP)= 0x00A2 [0x06~0x09]code_1段地址 ← 单独回填 [0x0a~0x0b]重定位表项数 = 5 [0x0c~0x0f]code_1_segment ← 重定位表[0] [0x10~0x13]code_2_segment ← 重定位表[1] [0x14~0x17]data_1_segment ← 重定位表[2] [0x18~0x1b]data_2_segment ← 重定位表[3] [0x1c~0x1f]stack_segment ← 重定位表[4]
c09_mbr — 段重定位详细框图
direct: 单独回填入口点 · realloc: 循环处理重定位表 · calc_segment_base 算法
《x86汇编语言:从实模式到保护模式》· 李忠
第9章 · MBR加载器 (第2页 / 共2页)
重定位主流程
calc_segment_base 内部
循环体
判断分支
数值说明
重定位主流程 (pop ds 恢复DS后,直接进入 direct:) direct: 单独处理入口点段地址 ax ← header[0x06](code_1文件偏移低16位) dx ← header[0x08](code_1文件偏移高16位) call calc_segment_base 输入 dx:ax=文件偏移,返回 ax=16位段值 call 回填入口点段值 header[0x06] ← ax(高2字节[0x08~0x09]废弃) ↓ 进入重定位表循环 初始化重定位循环 CX ← header[0x0a] = 5(重定位项数) BX ← 0x0c(重定位表首项偏移) realloc 循环体 ① ax ← [DS:BX] 低16位文件偏移 ② dx ← [DS:BX+2] 高16位文件偏移 ③ call calc_segment_base → ax=段值 ④ [DS:BX] ← ax 回填低2字节 ⑤ BX += 4 指向下一项 ⑥ loop realloc CX-- 不为0继续 call ×5 CX-- ≠ 0? 还有重定位项? 是(继续) 否(5项全部完成) 重定位完成 → jmp far [0x04] CS:IP = code_1段值:0x00A2(start) 重定位表5项处理示意(BX递增) BX 字段 编译时(文件偏移) 回填后 0x0c code_1_segment 0x00000020 0x1002 0x10 code_2_segment 0x000000e0 0x100e 0x14 data_1_segment 0x000000f0 0x100f 0x18 data_2_segment 0x00000290 0x1029 0x1c stack_segment 0x000002a0 0x102a (偏移值为示意,实际取决于各段大小) 回填后高2字节[BX+2~BX+3]不再使用 段值 = (phy_base + 文件偏移) ÷ 16 = (0x10000 + offset) >> 4 calc_segment_base 内部流程 输入:DX:AX = 32位文件内偏移 返回:AX = 16位段值 push dx(保存DX,计算结束后恢复) 步骤①:加上 phy_base(0x10000) add ax, [cs:phy_base] ax += 0x0000 adc dx, [cs:phy_base+2] dx += 0x0001(含进位) → dx:ax = 文件偏移 + 0x10000 = 物理地址 例:code_1偏移=0x0020 dx:ax = 0x0001:0x0020 → 物理地址 0x00010020 步骤②:右移4位得16位段值 shr ax,4 ax高4位补0,低4位丢弃 ror dx,4 dx循环右移4位,低4位→高4位 and dx,0xF000 只保留dx的最高4位 or ax,dx 合并dx高4位到ax,组成段值 → AX = 完整16位段值 例:物理地址 0x00010020 shr ax(0x0020),4 → ax=0x0002 ror dx(0x0001),4 → dx=0x1000 and dx,0xF000 → dx=0x1000 or ax,dx → ax=0x1002 pop dx(恢复DX)· ret → AX=0x1002(段值) 右移4位位运算示意 物理地址 dx:ax = 0x0001 : 0x0020 二进制:0000 0000 0000 0001 : 0000 0000 0010 0000 shr ax,4: ax=0x0020 → 0000 0000 0010 0000 ax=0x0002 → 0000 0000 0000 0010(低4位0000丢弃) ror dx,4: dx=0x0001 → 0000 0000 0000 0001 dx=0x1000 → 0001 0000 0000 0000(低4位0001→最高4位) and dx,0xF000: dx=0x1000(最高4位保留,其余清零) or ax,dx: ax=0x0002 → 0000 0000 0000 0010 dx=0x1000 → 0001 0000 0000 0000 ax=0x1002 → 0001 0000 0000 0010 ← 最终16位段值 物理地址验证:0x1002 × 16 = 0x10020 ✓ 原理:(dx:ax) >> 4 等效于 物理地址 ÷ 16 = 段值 cs: 前缀:phy_base在代码段内,DS未知,必须用CS寻址