Lab_1:練習2——使用qemu執行並調試lab1中的軟件

 

1、實驗內容bootstrap

爲了熟悉使用qemu和gdb進行的調試工做,咱們進行以下的小練習:vim

(一)從CPU加電後執行的第一條指令開始,單步跟蹤BIOS的執行。sass

(二)在初始化位置0x7c00設置實地址斷點,測試斷點正常。網絡

(三)從0x7c00開始跟蹤代碼運行,將單步跟蹤反彙編獲得的代碼與bootasm.S和 bootblock.asm進行比較。ide

(四)本身找一個bootloader或內核中的代碼位置,設置斷點並進行測試。oop

2、實驗步驟 測試

補充材料:ui

咱們主要經過硬件模擬器qemu來進行各類實驗。在實驗的過程當中咱們可能會趕上各類各樣的問題,調試是必要的。qemu支持使用gdb進行的強大而方便的調試。因此用好qemu和gdb是完成各類實驗的基本要素this

默認的gdb須要進行一些額外的配置才進行qemu的調試任務。qemu和gdb之間使用網絡端口1234進行通信。在打開qemu進行模擬以後,執行gdb並輸spa

target remote :1234

便可鏈接qemu,此時qemu會進入中止狀態,遵從gdb的命令

另外,咱們可能須要qemu在一開始便進入等待模式,則咱們再也不使用make qemu開始系統的運行,而使用make debug來完成這項工做。這樣qemu便不會在gdb還沒有鏈接的時候擅自運行了。

gdb的地址斷點

在gdb命令行中,使用b *[地址]即可以在指定內存地址設置斷點,當qemu中的cpu執行到指定地址時,便會將控制權交給gdb。

 

(一)從CPU加電後執行的第一條指令開始,單步跟蹤BIOS的執行

1.修改gdbinit文件

首先,在 /moocos/ucore_lab/labcodes_answer/lab1_result/tools  目錄下,修改gdbinit文件

進入目錄:

cd ./moocos/ucore_lab/labcodes_answer/lab1_result/tools

修改方法爲:

輸入vim gdbinit

用D刪除gdbinit中原有的內容(D爲刪除整行,x或X爲刪除單個字符)

 

將如下內容粘貼入gdbinit中

 

set architecture i8086 target remote :1234 

 

 

2.make debug

輸入cd ..,退回到./moocos/ucore_lab/labcodes_answer/lab1_result

輸入make debug

隨後執行make debug,將彈出gdb窗口,如圖所示:

在gdb窗口中使用si命令便可單步追蹤

(注意:你沒必要每次輸入si,輸入一次si後,只要按回車便可執行上次的指令)

在gdb界面下,可經過以下命令來看BIOS的代碼

x /2i $pc(顯示當前eip處的彙編指令)

 

 (二)在初始化位置0x7c00設置實地址斷點,測試斷點正常。

1.修改gdbinit文件

進入目錄:

cd ./moocos/ucore_lab/labcodes_answer/lab1_result/tools

修改方法與(一)相同,

修改的內容以下:

target remote :1234 //鏈接qemu,此時qemu會進入中止狀態,遵從gdb的命令 set architecture i8086 //設置當前調試的CPU是8086 b *0x7c00 //在0x7c00處設置斷點。此地址是bootloader入口點地址,可看boot/bootasm.S的start地址處 c //continue簡稱,表示繼續執行 x/10i $pc //顯示當前eip處的彙編指令 

 

 

2.make debug 

輸入cd ..,退回到./moocos/ucore_lab/labcodes_answer/lab1_result

輸入make debug

 

(三)從0x7c00開始跟蹤代碼運行,將單步跟蹤反彙編獲得的代碼與bootasm.S和 bootblock.asm進行比較

 bootasm.S的完整代碼爲:

 1 #include <asm.h>  2  3 # Start the CPU: switch to 32-bit protected mode, jump into C.  4 # The BIOS loads this code from the first sector of the hard disk into  5 # memory at physical address 0x7c00 and starts executing in real mode  6 # with %cs=0 %ip=7c00.  7  8 .set PROT_MODE_CSEG, 0x8 # kernel code segment selector  9 .set PROT_MODE_DSEG, 0x10 # kernel data segment selector 10 .set CR0_PE_ON, 0x1 # protected mode enable flag 11 12 # start address should be 0:7c00, in real mode, the beginning address of the running bootloader 13 .globl start 14 start: 15 .code16 # Assemble for 16-bit mode 16  cli # Disable interrupts 17  cld # String operations increment 18 19  # Set up the important data segment registers (DS, ES, SS). 20 xorw %ax, %ax # Segment number zero 21 movw %ax, %ds # -> Data Segment 22 movw %ax, %es # -> Extra Segment 23 movw %ax, %ss # -> Stack Segment 24 25  # Enable A20: 26  # For backwards compatibility with the earliest PCs, physical 27 # address line 20 is tied low, so that addresses higher than 28 # 1MB wrap around to zero by default. This code undoes this. 29 seta20.1: 30 inb $0x64, %al # Wait for not busy(8042 input buffer empty). 31 testb $0x2, %al 32 jnz seta20.1 33 34 movb $0xd1, %al # 0xd1 -> port 0x64 35 outb %al, $0x64 # 0xd1 means: write data to 8042's P2 port 36 37 seta20.2: 38 inb $0x64, %al # Wait for not busy(8042 input buffer empty). 39 testb $0x2, %al 40 jnz seta20.2 41 42 movb $0xdf, %al # 0xdf -> port 0x60 43 outb %al, $0x60 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 1 44 45 # Switch from real to protected mode, using a bootstrap GDT 46 # and segment translation that makes virtual addresses 47  # identical to physical addresses, so that the 48 # effective memory map does not change during the switch. 49  lgdt gdtdesc 50 movl %cr0, %eax 51 orl $CR0_PE_ON, %eax 52 movl %eax, %cr0 53 54 # Jump to next instruction, but in 32-bit code segment. 55 # Switches processor into 32-bit mode. 56  ljmp $PROT_MODE_CSEG, $protcseg 57 58 .code32 # Assemble for 32-bit mode 59 protcseg: 60 # Set up the protected-mode data segment registers 61 movw $PROT_MODE_DSEG, %ax # Our data segment selector 62 movw %ax, %ds # -> DS: Data Segment 63 movw %ax, %es # -> ES: Extra Segment 64 movw %ax, %fs # -> FS 65 movw %ax, %gs # -> GS 66 movw %ax, %ss # -> SS: Stack Segment 67 68 # Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00) 69 movl $0x0, %ebp 70 movl $start, %esp 71  call bootmain 72 73 # If bootmain returns (it shouldn't), loop. 74 spin: 75  jmp spin 76 77 # Bootstrap GDT 78 .p2align 2 # force 4 byte alignment 79 gdt: 80 SEG_NULLASM # null seg 81 SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg for bootloader and kernel 82 SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg for bootloader and kernel 83 84 gdtdesc: 85 .word 0x17 # sizeof(gdt) - 1 86 .long gdt # address gdt
bootasm.S

 

 bootblock.asm的完整代碼爲:

 1 obj/bootblock.o: file format elf32-i386  2  3  4 Disassembly of section .text:  5  6 00007c00 <start>:  7  8 # start address should be 0:7c00, in real mode, the beginning address of the running bootloader  9 .globl start  10 start:  11 .code16 # Assemble for 16-bit mode  12  cli # Disable interrupts  13  7c00: fa cli  14  cld # String operations increment  15  7c01: fc cld  16  17  # Set up the important data segment registers (DS, ES, SS).  18 xorw %ax, %ax # Segment number zero  19 7c02: 31 c0 xor %eax,%eax  20 movw %ax, %ds # -> Data Segment  21 7c04: 8e d8 mov %eax,%ds  22 movw %ax, %es # -> Extra Segment  23 7c06: 8e c0 mov %eax,%es  24 movw %ax, %ss # -> Stack Segment  25 7c08: 8e d0 mov %eax,%ss  26  27 00007c0a <seta20.1>:  28  # Enable A20:  29  # For backwards compatibility with the earliest PCs, physical  30 # address line 20 is tied low, so that addresses higher than  31 # 1MB wrap around to zero by default. This code undoes this.  32 seta20.1:  33 inb $0x64, %al # Wait for not busy(8042 input buffer empty).  34 7c0a: e4 64 in $0x64,%al  35 testb $0x2, %al  36 7c0c: a8 02 test $0x2,%al  37 jnz seta20.1  38 7c0e: 75 fa jne 7c0a <seta20.1>  39  40 movb $0xd1, %al # 0xd1 -> port 0x64  41 7c10: b0 d1 mov $0xd1,%al  42 outb %al, $0x64 # 0xd1 means: write data to 8042's P2 port  43 7c12: e6 64 out %al,$0x64  44  45 00007c14 <seta20.2>:  46  47 seta20.2:  48 inb $0x64, %al # Wait for not busy(8042 input buffer empty).  49 7c14: e4 64 in $0x64,%al  50 testb $0x2, %al  51 7c16: a8 02 test $0x2,%al  52 jnz seta20.2  53 7c18: 75 fa jne 7c14 <seta20.2>  54  55 movb $0xdf, %al # 0xdf -> port 0x60  56 7c1a: b0 df mov $0xdf,%al  57 outb %al, $0x60 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 1  58 7c1c: e6 60 out %al,$0x60  59  60 # Switch from real to protected mode, using a bootstrap GDT  61 # and segment translation that makes virtual addresses  62  # identical to physical addresses, so that the  63 # effective memory map does not change during the switch.  64  lgdt gdtdesc  65 7c1e: 0f 01 16 lgdtl (%esi)  66 7c21: 6c insb (%dx),%es:(%edi)  67 7c22: 7c 0f jl 7c33 <protcseg+0x1>  68 movl %cr0, %eax  69 7c24: 20 c0 and %al,%al  70 orl $CR0_PE_ON, %eax  71 7c26: 66 83 c8 01 or $0x1,%ax  72 movl %eax, %cr0  73 7c2a: 0f 22 c0 mov %eax,%cr0  74  75 # Jump to next instruction, but in 32-bit code segment.  76 # Switches processor into 32-bit mode.  77  ljmp $PROT_MODE_CSEG, $protcseg  78 7c2d: ea 32 7c 08 00 66 b8 ljmp $0xb866,$0x87c32  79  80 00007c32 <protcseg>:  81  82 .code32 # Assemble for 32-bit mode  83 protcseg:  84 # Set up the protected-mode data segment registers  85 movw $PROT_MODE_DSEG, %ax # Our data segment selector  86 7c32: 66 b8 10 00 mov $0x10,%ax  87 movw %ax, %ds # -> DS: Data Segment  88 7c36: 8e d8 mov %eax,%ds  89 movw %ax, %es # -> ES: Extra Segment  90 7c38: 8e c0 mov %eax,%es  91 movw %ax, %fs # -> FS  92 7c3a: 8e e0 mov %eax,%fs  93 movw %ax, %gs # -> GS  94 7c3c: 8e e8 mov %eax,%gs  95 movw %ax, %ss # -> SS: Stack Segment  96 7c3e: 8e d0 mov %eax,%ss  97  98 # Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00)  99 movl $0x0, %ebp 100 7c40: bd 00 00 00 00 mov $0x0,%ebp 101 movl $start, %esp 102 7c45: bc 00 7c 00 00 mov $0x7c00,%esp 103  call bootmain 104 7c4a: e8 b1 00 00 00 call 7d00 <bootmain> 105 106 00007c4f <spin>: 107 108 # If bootmain returns (it shouldn't), loop. 109 spin: 110  jmp spin 111 7c4f: eb fe jmp 7c4f <spin> 112 7c51: 8d 76 00 lea 0x0(%esi),%esi 113 114 00007c54 <gdt>: 115  ... 116  7c5c: ff (bad) 117 7c5d: ff 00 incl (%eax) 118 7c5f: 00 00 add %al,(%eax) 119 7c61: 9a cf 00 ff ff 00 00 lcall $0x0,$0xffff00cf 120 7c68: 00 92 cf 00 17 00 add %dl,0x1700cf(%edx) 121 122 00007c6c <gdtdesc>: 123 7c6c: 17 pop %ss 124 7c6d: 00 54 7c 00 add %dl,0x0(%esp,%edi,2) 125  ... 126 127 00007c72 <readseg>: 128 /* * 129  * readseg - read @count bytes at @offset from kernel into virtual address @va, 130  * might copy more than asked. 131  * */ 132 static void 133 readseg(uintptr_t va, uint32_t count, uint32_t offset) { 134 7c72: 55 push %ebp 135 7c73: 89 e5 mov %esp,%ebp 136 7c75: 57 push %edi 137 7c76: 56 push %esi 138 7c77: 89 c6 mov %eax,%esi 139 7c79: 53 push %ebx 140 uintptr_t end_va = va + count; 141 7c7a: 8d 04 10 lea (%eax,%edx,1),%eax 142 143 // round down to sector boundary 144 va -= offset % SECTSIZE; 145 7c7d: 31 d2 xor %edx,%edx 146 /* * 147  * readseg - read @count bytes at @offset from kernel into virtual address @va, 148  * might copy more than asked. 149  * */ 150 static void 151 readseg(uintptr_t va, uint32_t count, uint32_t offset) { 152 7c7f: 53 push %ebx 153 uintptr_t end_va = va + count; 154 7c80: 89 45 f0 mov %eax,-0x10(%ebp) 155 156 // round down to sector boundary 157 va -= offset % SECTSIZE; 158 7c83: 89 c8 mov %ecx,%eax 159 7c85: f7 35 e4 7d 00 00 divl 0x7de4 160 7c8b: 29 d6 sub %edx,%esi 161 162 // translate from bytes to sectors; kernel starts at sector 1 163 uint32_t secno = (offset / SECTSIZE) + 1; 164 7c8d: 8d 58 01 lea 0x1(%eax),%ebx 165 166 // If this is too slow, we could read lots of sectors at a time. 167 // We'd write more to memory than asked, but it doesn't matter -- 168 // we load in increasing order. 169 for (; va < end_va; va += SECTSIZE, secno ++) { 170 7c90: 3b 75 f0 cmp -0x10(%ebp),%esi 171 7c93: 73 65 jae 7cfa <readseg+0x88> 172 static inline void ltr(uint16_t sel) __attribute__((always_inline)); 173 174 static inline uint8_t 175 inb(uint16_t port) { 176  uint8_t data; 177 asm volatile ("inb %1, %0" : "=a" (data) : "d" (port)); 178 7c95: ba f7 01 00 00 mov $0x1f7,%edx 179 7c9a: ec in (%dx),%al 180 struct elfhdr * ELFHDR = ((struct elfhdr *)0x10000) ; // scratch space 181 182 /* waitdisk - wait for disk ready */ 183 static void 184 waitdisk(void) { 185 while ((inb(0x1F7) & 0xC0) != 0x40) 186 7c9b: 83 e0 c0 and $0xffffffc0,%eax 187 7c9e: 3c 40 cmp $0x40,%al 188 7ca0: 75 f3 jne 7c95 <readseg+0x23> 189 : "memory", "cc"); 190 } 191 192 static inline void 193 outb(uint16_t port, uint8_t data) { 194 asm volatile ("outb %0, %1" :: "a" (data), "d" (port)); 195 7ca2: b2 f2 mov $0xf2,%dl 196 7ca4: b0 01 mov $0x1,%al 197 7ca6: ee out %al,(%dx) 198 7ca7: 0f b6 c3 movzbl %bl,%eax 199 7caa: b2 f3 mov $0xf3,%dl 200 7cac: ee out %al,(%dx) 201 7cad: 0f b6 c7 movzbl %bh,%eax 202 7cb0: b2 f4 mov $0xf4,%dl 203 7cb2: ee out %al,(%dx) 204  waitdisk(); 205 206 outb(0x1F2, 1); // count = 1 207 outb(0x1F3, secno & 0xFF); 208 outb(0x1F4, (secno >> 8) & 0xFF); 209 outb(0x1F5, (secno >> 16) & 0xFF); 210 7cb3: 89 d8 mov %ebx,%eax 211 7cb5: b2 f5 mov $0xf5,%dl 212 7cb7: c1 e8 10 shr $0x10,%eax 213 7cba: 0f b6 c0 movzbl %al,%eax 214 7cbd: ee out %al,(%dx) 215 outb(0x1F6, ((secno >> 24) & 0xF) | 0xE0); 216 7cbe: 89 d8 mov %ebx,%eax 217 7cc0: b2 f6 mov $0xf6,%dl 218 7cc2: c1 e8 18 shr $0x18,%eax 219 7cc5: 83 e0 0f and $0xf,%eax 220 7cc8: 83 c8 e0 or $0xffffffe0,%eax 221 7ccb: ee out %al,(%dx) 222 7ccc: b0 20 mov $0x20,%al 223 7cce: b2 f7 mov $0xf7,%dl 224 7cd0: ee out %al,(%dx) 225 static inline void ltr(uint16_t sel) __attribute__((always_inline)); 226 227 static inline uint8_t 228 inb(uint16_t port) { 229  uint8_t data; 230 asm volatile ("inb %1, %0" : "=a" (data) : "d" (port)); 231 7cd1: ba f7 01 00 00 mov $0x1f7,%edx 232 7cd6: ec in (%dx),%al 233 struct elfhdr * ELFHDR = ((struct elfhdr *)0x10000) ; // scratch space 234 235 /* waitdisk - wait for disk ready */ 236 static void 237 waitdisk(void) { 238 while ((inb(0x1F7) & 0xC0) != 0x40) 239 7cd7: 83 e0 c0 and $0xffffffc0,%eax 240 7cda: 3c 40 cmp $0x40,%al 241 7cdc: 75 f3 jne 7cd1 <readseg+0x5f> 242 243 // wait for disk to be ready 244  waitdisk(); 245 246 // read a sector 247 insl(0x1F0, dst, SECTSIZE / 4); 248 7cde: 8b 0d e4 7d 00 00 mov 0x7de4,%ecx 249 return data; 250 } 251 252 static inline void 253 insl(uint32_t port, void *addr, int cnt) { 254 asm volatile ( 255 7ce4: 89 f7 mov %esi,%edi 256 7ce6: ba f0 01 00 00 mov $0x1f0,%edx 257 7ceb: c1 e9 02 shr $0x2,%ecx 258  7cee: fc cld 259 7cef: f2 6d repnz insl (%dx),%es:(%edi) 260 uint32_t secno = (offset / SECTSIZE) + 1; 261 262 // If this is too slow, we could read lots of sectors at a time. 263 // We'd write more to memory than asked, but it doesn't matter -- 264 // we load in increasing order. 265 for (; va < end_va; va += SECTSIZE, secno ++) { 266 7cf1: 03 35 e4 7d 00 00 add 0x7de4,%esi 267 7cf7: 43 inc %ebx 268 7cf8: eb 96 jmp 7c90 <readseg+0x1e> 269 readsect((void *)va, secno); 270  } 271 } 272 7cfa: 58 pop %eax 273 7cfb: 5b pop %ebx 274 7cfc: 5e pop %esi 275 7cfd: 5f pop %edi 276 7cfe: 5d pop %ebp 277  7cff: c3 ret 278 279 00007d00 <bootmain>: 280 281 /* bootmain - the entry of bootloader */ 282 void 283 bootmain(void) { 284 // read the 1st page off disk 285 readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0); 286 7d00: a1 e4 7d 00 00 mov 0x7de4,%eax 287 7d05: 31 c9 xor %ecx,%ecx 288  } 289 } 290 291 /* bootmain - the entry of bootloader */ 292 void 293 bootmain(void) { 294 7d07: 55 push %ebp 295 7d08: 89 e5 mov %esp,%ebp 296 7d0a: 56 push %esi 297 // read the 1st page off disk 298 readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0); 299 7d0b: 8d 14 c5 00 00 00 00 lea 0x0(,%eax,8),%edx 300 7d12: a1 e0 7d 00 00 mov 0x7de0,%eax 301  } 302 } 303 304 /* bootmain - the entry of bootloader */ 305 void 306 bootmain(void) { 307 7d17: 53 push %ebx 308 // read the 1st page off disk 309 readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0); 310 7d18: e8 55 ff ff ff call 7c72 <readseg> 311 312 // is this a valid ELF? 313 if (ELFHDR->e_magic != ELF_MAGIC) { 314 7d1d: a1 e0 7d 00 00 mov 0x7de0,%eax 315 7d22: 81 38 7f 45 4c 46 cmpl $0x464c457f,(%eax) 316 7d28: 75 3a jne 7d64 <bootmain+0x64> 317  } 318 319 struct proghdr *ph, *eph; 320 321 // load each program segment (ignores ph flags) 322 ph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff); 323 7d2a: 8b 58 1c mov 0x1c(%eax),%ebx 324 7d2d: 01 c3 add %eax,%ebx 325 eph = ph + ELFHDR->e_phnum; 326 7d2f: 0f b7 40 2c movzwl 0x2c(%eax),%eax 327 7d33: c1 e0 05 shl $0x5,%eax 328 7d36: 8d 34 03 lea (%ebx,%eax,1),%esi 329 for (; ph < eph; ph ++) { 330 7d39: 39 f3 cmp %esi,%ebx 331 7d3b: 73 18 jae 7d55 <bootmain+0x55> 332 readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset); 333 7d3d: 8b 43 08 mov 0x8(%ebx),%eax 334 struct proghdr *ph, *eph; 335 336 // load each program segment (ignores ph flags) 337 ph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff); 338 eph = ph + ELFHDR->e_phnum; 339 for (; ph < eph; ph ++) { 340 7d40: 83 c3 20 add $0x20,%ebx 341 readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset); 342 7d43: 8b 4b e4 mov -0x1c(%ebx),%ecx 343 7d46: 8b 53 f4 mov -0xc(%ebx),%edx 344 7d49: 25 ff ff ff 00 and $0xffffff,%eax 345 7d4e: e8 1f ff ff ff call 7c72 <readseg> 346 7d53: eb e4 jmp 7d39 <bootmain+0x39> 347  } 348 349 // call the entry point from the ELF header 350 // note: does not return 351 ((void (*)(void))(ELFHDR->e_entry & 0xFFFFFF))(); 352 7d55: a1 e0 7d 00 00 mov 0x7de0,%eax 353 7d5a: 8b 40 18 mov 0x18(%eax),%eax 354 7d5d: 25 ff ff ff 00 and $0xffffff,%eax 355 7d62: ff d0 call *%eax 356 asm volatile ("outb %0, %1" :: "a" (data), "d" (port)); 357 } 358 359 static inline void 360 outw(uint16_t port, uint16_t data) { 361 asm volatile ("outw %0, %1" :: "a" (data), "d" (port)); 362 7d64: b8 00 8a ff ff mov $0xffff8a00,%eax 363 7d69: 89 c2 mov %eax,%edx 364 7d6b: 66 ef out %ax,(%dx) 365 7d6d: b8 00 8e ff ff mov $0xffff8e00,%eax 366 7d72: 66 ef out %ax,(%dx) 367 7d74: eb fe jmp 7d74 <bootmain+0x74>
bootblock.asm

 反彙編獲得的代碼是:

 

下圖是bootasm.S中14到28行的代碼:

下面是bootblock.asm中10到25行的代碼

 

比較可知,三者基本一致。

 

(四)本身找一個bootloader或內核中的代碼位置,設置斷點並進行測試

相關文章
相關標籤/搜索