1 Star 0 Fork 1

louislin19/course-xv6

forked from 殊蕤/xv6-course 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
12-from-asm-to-c.org 13.20 KB
一键复制 编辑 原始数据 按行查看 历史
殊蕤 提交于 2023-11-03 10:01 . update: 添加 gdt 流程图

从汇编到 C 语言

编译流程分析

  1. 构建 MBR, bootsect.s
  2. 构建内核 c 语言入口 kernel
    • entry.s
    • bootmain.c
    • 磁盘 fda.img
      • MBR bootsect.bin
      • entry.s
      • bootmain.c -> bootmain.bin

读取磁盘,加载 kernel 文件

设置断点观察读取磁盘 | link

代码文件 lab/10-real-to-protected/bootsect.s

(gdb)
[   0:7c34] => 0x7c34:  int    $0x13
0x00007c34 in ?? ()
(gdb) x/4i $eip
=> 0x7c34:      int    $0x13
   0x7c36:      popa
   0x7c37:      ret
   0x7c38:      mov    $0x10,%ax
(gdb) b *0x7c36
Breakpoint 2 at 0x7c36
(gdb) c
Continuing.
[   0:7c36] => 0x7c36:  popa

Breakpoint 2, 0x00007c36 in ?? ()
(gdb) x/40h 0x1000
0x1000: 0x37b9  0x0010  0xb800  0x8000  0x000b  0x48ba  0x0000  0x8800
0x1010: 0xc610  0x0140  0x830a  0x02c0  0xc183  0x0f01  0x11b6  0xfa80
0x1020: 0x750a  0xebec  0x66fe  0x6690  0x6690  0x6690  0x6690  0x9090
0x1030: 0xcbe8  0xffff  0xebff  0x48fe  0x6c65  0x6f6c  0x5720  0x726f
0x1040: 0x646c  0x000a  0x0014  0x0000  0x0000  0x0000  0x7a01  0x0052
(gdb)

查看磁盘镜像的数据

hexdump -s 512 ./lab/10-real-to-protected/fda.img
0000200 37b9 0010 b800 8000 000b 48ba 0000 8800
0000210 c610 0140 830a 02c0 c183 0f01 11b6 fa80
0000220 750a ebec 66fe 6690 6690 6690 6690 9090
0000230 cbe8 ffff ebff 48fe 6c65 6f6c 5720 726f
0000240 646c 000a 0014 0000 0000 0000 7a01 0052
0000250 7c01 0108 0c1b 0404 0188 0000 0010 0000
0000260 001c 0000 ff9c ffff 0025 0000 0000 0000
0000270

GDT 全局描述符表

  1. GDT 主要保护以下信息, 总共 64 位, link
    • base = low(16) + middle(8) + high(8)
    • limit = low(16) + high(4)
    • flags = (12)
  2. GDT 的第一项必须全是 0x00
  3. GDT 加载通过 lgdt 指令

下面是一个调试 gdt 内存的示例,使用 qemu 模拟器调试

(qemu) info registers
...
GS =0000 00000000 0000ffff 00009300
LDT=0000 00000000 0000ffff 00008200
TR =0000 00000000 0000ffff 00008b00
GDT=     00007c2b 00000017
IDT=     00000000 000003ff
CR0=00000010 CR2=00000000 CR3=00000000 CR4=00000000
...
XMM06=0000000000000000 0000000000000000 XMM07=0000000000000000 0000000000000000
(qemu) x/8x 0x7c2b
00007c2b: 0x00000000 0x00000000 0x0000ffff 0x00cf9a00
00007c3b: 0x0000ffff 0x00cf9200 0x7c2b0017 0x00000000
(qemu)

查询到 gdtr 后也可以使用 gdb 打印内存地址

(gdb) x/8x 0x7c2b
0x7c2b: 0x00000000      0x00000000      0x0000ffff      0x00cf9a00
0x7c3b: 0x0000ffff      0x00cf9200      0x7c2b0017      0x00000000

实模式(16)到保护模式(32)切换

  1. 实模式 Real Mode
    • x86 处理器加点后都是先进入实模式
    • 实模式时直接访问所有地址空间
      • pc = cs:eip
      • pc = eip
    • 实模式只有 1 MB 的寻址空间,并且没有内存保护
  2. 保护模式 Protected Mode
    • 保护模式提供了一些高级特性
      • memory protection
      • multitasking
      • enhanced memory addressing
    • 它支持更大的地址空间,并且多任务有不同的运行级别 (rings)

实模式到保护模式切换流程

  1. 关闭中断
    • cli
  2. 加载 gdt
    • 段寄存器变成段选择子 (selector)
  3. 设置控制寄存器 cr0
    • 设置 PE 位
  4. 通过远程跳转来 flush 流水线
    • 通过长跳转来设置 cs 寄存器, cs 变化成 0x8
    • 同时跳转到 .code32 位置
  5. 更新所有的段寄存器
    • 更新数据段寄存器 ds, es, ss, fs, gs
    • 通用设置成 0x10
  6. 更新系统栈
    • 初始化系统栈 ebp, esp
  7. 跳转到 32 位指令出执行
    • 跳转到正式的启动代码
    • 启动代码也是 32 为 C 语言代码入口

实模式和保护模式寻址方式的变化

实模式

addr=seg:offset

保护模式

linear_addr = logic_addr + gdt[selector].base
  1. 通过 gdtr 的地址找到 gdt 的地址
  2. 通过选择子(selector=ds/es/ss/fs/gs), 找到 gdt 的偏移
  3. 找到 gdt 项,选取 base 作为基地址
  4. base + logic_addr 则为线性地址

寻址流程

                        Logic Address (input)

selector(16)            [Offset(32) ]  {3}         [+]   --> Linear Address (output) {4}
    |
    |                                               ^
    |                                               . {3}
    |                                               .
    |                              @................@
    | {2}                          .
    |                   |----------+--------------------------|
    |                   | Base(32) .   | Limit(20) | Flag(12) |
    |                   |----------+---|-----------|----------|
    |                   |          .   |           |          |
    |                   | ...      .   | ...       | ...      |
    |                   |          .   |           |          |
    |              0x10 | 0        .   | 0xffff    | Data     |
    `------------>  0x8 | 0       @@@  | 0xffff    | Code     |
                    0x0 | 0            | 0         | Null     |
gdtr {1} -------->      |-------------------------------------|

Bochs 调试

编译安装手册 link

注意需要开启下面的选项:

  1. –enable-gdb-stub 支持 gdb 调试
  2. –enable-debugger 开启自带的调试器
  3. –enable-debugger-gui 开启图形调试界面

需要注意的是 –enable-debugger 和 –enable-gdb-stub 不能同时开启

bochs -q -f bochsrc.bxrc
========================================================================
                        Bochs x86 Emulator 2.7
              Built from SVN snapshot on August  1, 2021
                Timestamp: Sun Aug  1 10:07:00 CEST 2021
========================================================================
00000000000i[      ] BXSHARE not set. using compile time default '/opt/bochs-2.7-native/share/bochs'
00000000000i[      ] reading configuration from bochsrc.bxrc
00000000000e[      ] bochsrc.bxrc:968: wrong value for parameter 'mode'
00000000000e[PCSPK ] bochsrc.bxrc:968: unknown parameter for speaker ignored.
00000000000e[      ] bochsrc.bxrc:968: unknown parameter 'volume'
00000000000e[PCSPK ] bochsrc.bxrc:968: unknown parameter for speaker ignored.
00000000000i[      ] installing x module as the Bochs GUI
00000000000i[      ] using log file bochsout.txt
Next at t=0
(0) [0x0000fffffff0] f000:fff0 (unk. ctxt): jmpf 0xf000:e05b          ; ea5be000f0
<bochs:1> b 0x7c00
<bochs:2> c
(0) Breakpoint 1, 0x0000000000007c00 in ?? ()
Next at t=4435772
(0) [0x000000007c00] 0000:7c00 (unk. ctxt): xor ax, ax                ; 31c0
<bochs:3> u/10
0000000000007c00: (                    ): xor ax, ax                ; 31c0
0000000000007c02: (                    ): mov ds, ax                ; 8ed8
0000000000007c04: (                    ): mov ss, ax                ; 8ed0
0000000000007c06: (                    ): mov es, ax                ; 8ec0
0000000000007c08: (                    ): mov fs, ax                ; 8ee0
0000000000007c0a: (                    ): mov gs, ax                ; 8ee8
0000000000007c0c: (                    ): call .+21  (0x00007c24)   ; e81500
0000000000007c0f: (                    ): cli                       ; fa
0000000000007c10: (                    ): lgdt ds:0x7c6b            ; 0f01166b7c
0000000000007c15: (                    ): mov eax, cr0              ; 0f20c0
<bochs:4> s
Next at t=4435773
(0) [0x000000007c02] 0000:7c02 (unk. ctxt): mov ds, ax                ; 8ed8
<bochs:5>
Next at t=4435774
(0) [0x000000007c04] 0000:7c04 (unk. ctxt): mov ss, ax                ; 8ed0
<bochs:6>
Next at t=4435775
(0) [0x000000007c06] 0000:7c06 (unk. ctxt): mov es, ax                ; 8ec0
<bochs:7>
Next at t=4435776
(0) [0x000000007c08] 0000:7c08 (unk. ctxt): mov fs, ax                ; 8ee0
<bochs:8>
Next at t=4435777
(0) [0x000000007c0a] 0000:7c0a (unk. ctxt): mov gs, ax                ; 8ee8
<bochs:9>
Next at t=4435778
(0) [0x000000007c0c] 0000:7c0c (unk. ctxt): call .+21  (0x00007c24)   ; e81500
<bochs:10>
Next at t=4435779
(0) [0x000000007c24] 0000:7c24 (unk. ctxt): pusha                     ; 60
<bochs:11>
Next at t=4435780
(0) [0x000000007c25] 0000:7c25 (unk. ctxt): mov ah, 0x02              ; b402
<bochs:12>
Next at t=4435781
(0) [0x000000007c27] 0000:7c27 (unk. ctxt): mov al, 0x08              ; b008
<bochs:13>
Next at t=4435782
(0) [0x000000007c29] 0000:7c29 (unk. ctxt): mov ch, 0x00              ; b500
<bochs:14> u/10
0000000000007c29: (                    ): mov ch, 0x00              ; b500
0000000000007c2b: (                    ): mov cl, 0x02              ; b102
0000000000007c2d: (                    ): mov dh, 0x00              ; b600
0000000000007c2f: (                    ): mov dl, 0x00              ; b200
0000000000007c31: (                    ): mov bx, 0x1000            ; bb0010
0000000000007c34: (                    ): int 0x13                  ; cd13
0000000000007c36: (                    ): popa                      ; 61
0000000000007c37: (                    ): ret                       ; c3
0000000000007c38: (                    ): mov eax, 0xd88e0010       ; 66b810008ed8
0000000000007c3e: (                    ): mov ss, ax                ; 8ed0
<bochs:15> b 0x7c36
<bochs:16> x/32h 0x1000
[bochs]:
0x0000000000001000 <bogus+       0>:    0x0000  0x0000  0x0000  0x0000  0x0000  0x0000  0x0000  0x0000
0x0000000000001010 <bogus+      16>:    0x0000  0x0000  0x0000  0x0000  0x0000  0x0000  0x0000  0x0000
0x0000000000001020 <bogus+      32>:    0x0000  0x0000  0x0000  0x0000  0x0000  0x0000  0x0000  0x0000
0x0000000000001030 <bogus+      48>:    0x0000  0x0000  0x0000  0x0000  0x0000  0x0000  0x0000  0x0000
<bochs:17> c
(0) Breakpoint 2, 0x0000000000007c36 in ?? ()
Next at t=8881120
(0) [0x000000007c36] 0000:7c36 (unk. ctxt): popa                      ; 61
<bochs:18> x/32h 0x1000
[bochs]:
0x0000000000001000 <bogus+       0>:    0xe0e8  0x0000  0xeb00  0x55fe  0xe589  0x558b  0xec08  0xc35d
0x0000000000001010 <bogus+      16>:    0x8955  0x0fe5  0x55b7  0x0f08  0x45b6  0xee0c  0xc35d  0x8955
0x0000000000001020 <bogus+      32>:    0x53e5  0xd4bb  0x0003  0xb800  0x000e  0x0000  0xda89  0xbaee
0x0000000000001030 <bogus+      48>:    0x03d5  0x0000  0x89ec  0xb8c1  0x000f  0x0000  0xda89  0x0fee
<bochs:19>

查看寄存器, reg, sreg, creg

<bochs:19> sreg <= 段寄存器
es:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
cs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
ss:0x0000, dh=0x00009300, dl=0x0000ffff, valid=7
        Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
ds:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
fs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
gs:0x0000, dh=0x00009300, dl=0x0000ffff, valid=1
        Data segment, base=0x00000000, limit=0x0000ffff, Read/Write, Accessed
ldtr:0x0000, dh=0x00008200, dl=0x0000ffff, valid=1
tr:0x0000, dh=0x00008b00, dl=0x0000ffff, valid=1
gdtr:base=0x00000000000f9ad7, limit=0x30
idtr:base=0x0000000000000000, limit=0x3ff
<bochs:20> reg <= 通用寄存器
rax: 00000000_60000008
rbx: 00000000_00001000
rcx: 00000000_00090002
rdx: 00000000_00000000
rsp: 00000000_0000ffc4
rbp: 00000000_00000000
rsi: 00000000_000e0000
rdi: 00000000_0000070c
r8 : 00000000_00000000
r9 : 00000000_00000000
r10: 00000000_00000000
r11: 00000000_00000000
r12: 00000000_00000000
r13: 00000000_00000000
r14: 00000000_00000000
r15: 00000000_00000000
rip: 00000000_00007c36
eflags 0x00000046: id vip vif ac vm rf nt IOPL=0 of df if tf sf ZF af PF cf
<bochs:21> creg <= 控制寄存器
CR0=0x60000010: pg CD NW ac wp ne ET ts em mp pe
CR2=page fault laddr=0x0000000000000000
CR3=0x000000000000
    PCD=page-level cache disable=0
    PWT=page-level write-through=0
CR4=0x00000000: pks cet pke smap smep keylock osxsave pcid fsgsbase smx vmx la57 umip osxmmexcpt osfxsr pce pge mce pae pse de tsd pvi vme
CR8: 0x0
EFER=0x00000000: ffxsr nxe lma lme sce
XCR0=0x00000001: cet_s cet_u pkru hi_zmm zmm_hi256 opmask bndcfg bndregs ymm sse FPU
<bochs:22>

配置图形调试器

# gui_debug 开启图形调试器, 需要编译时添加 --enable-debugger-gui 选项
display_library: x, options="gui_debug"

总结

  1. 通过磁盘中断 int 13 加载 bootmain.bin
  2. GDT 加载
  3. 16 => 32 汇编
  4. 32 => start_kernel C 语言函数
  5. 0xb8000 显存
  6. in/out 指令获取光标位置
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/louislin19/course-xv6.git
git@gitee.com:louislin19/course-xv6.git
louislin19
course-xv6
course-xv6
master

搜索帮助