Exercise 7. Trace through the first few instructions of the boot loader again and identify the first instruction that would "break" or otherwise do the wrong thing if you were to get the boot loader's link address wrong. Then change the link address in boot/Makefrag to something wrong, run gmake clean, recompile lab1 with gmake, and trace into the boot loader again to see what happens. Don't forget to change the link address back afterwards!
以下摘于manual:
--section-start sectionname=org
Locate a section in the output file at the absolute address given by org. You may use this option as many times as necessary to locate multiple sections in the command line. org must be a single hexadecimal integer; for compatibility with other linkers, you may omit the leading 0x usually associated with hexadecimal values. Note: there should be no white space between sectionname, the equals sign ("="), and org.
|
当更改7c00为7c10之后,进入bochs,然后设置断点在0x7c00,trace on,step到某步时会出现以下错误:
- <bochs:28>
- (0).[239972216] [0x00007c2d] 0000:00007c2d (unk. ctxt): jmp far 0008:7c42 ; ea427c0800
- CPU 0: Exception 0x0d - (#GP) general protection fault occured
- (error_code=0x0008)
- CPU 0: Interrupt 0x0d occured (error_code=0x0008)
- CPU 0: Exception 0x0d - (#GP) general protection fault occured
- (error_code=0x006a)
- CPU 0: Exception 0x08 - (#DF) double fault occured (error_code=0x0000)
- CPU 0: Interrupt 0x08 occured (error_code=0x0000)
- CPU 0: Exception 0x0d - (#GP) general protection fault occured (error_code=0x0042)
- (0).[239972216] [0x00007c2d] 0000:00007c2d (unk. ctxt): jmp far 0008:7c42 ; ea427c0800
- Next at t=239972217
- (0) [0xfffffff0] f000:fff0 (unk. ctxt): jmp far f000:e05b ; ea5be000f0
原因就是在编译时指定了绝对地址,而JOS的BIOS总是会将boot loader载入到0x7c00的位置,一旦编译时指定的绝对地址与载入到内存时放置的地址不同,只要运行涉及到地址的命令,都会出错。注意在obj/boot/boot.asm中,几乎所有的地址都是根据编译时指定的那个绝对地址而设置的。比如当我们设置那个地址为0x7c10时,下面反汇编之后看到的地址都会比正确情况下多了10,当载入到内存时,其实会发生不一致,一些xor eax,eax的命令还好,ldgdt命令,jmp命令这些都是错的。
- #include <inc/mmu.h>
- # Start the CPU: switch to 32-bit protected mode, jump into C.
- # The BIOS loads this code from the first sector of the hard disk into
- # memory at physical address 0x7c00 and starts executing in real mode
- # with %cs=0 %ip=7c00.
- .set PROT_MODE_CSEG, 0x8 # kernel code segment selector
- .set PROT_MODE_DSEG, 0x10 # kernel data segment selector
- .set CR0_PE_ON, 0x1 # protected mode enable flag
- .globl start
- start:
- .code16 # Assemble for 16-bit mode
- cli # Disable interrupts
- cld # String operations increment
- # Set up the important data segment registers (DS, ES, SS).
- xorw %ax,%ax # Segment number zero
- movw %ax,%ds # -> Data Segment
- movw %ax,%es # -> Extra Segment
- movw %ax,%ss # -> Stack Segment
- # Enable A20:
- # For backwards compatibility with the earliest PCs, physical
- # address line 20 is tied low, so that addresses higher than
- # 1MB wrap around to zero by default. This code undoes this.
- seta20.1:
- inb $0x64,%al # Wait for not busy
- testb $0x2,%al
- jnz seta20.1
- movb $0xd1,%al # 0xd1 -> port 0x64
- outb %al,$0x64
- seta20.2:
- inb $0x64,%al # Wait for not busy
- testb $0x2,%al
- jnz seta20.2
- movb $0xdf,%al # 0xdf -> port 0x60
- outb %al,$0x60
- # Switch from real to protected mode, using a bootstrap GDT
- # and segment translation that makes virtual addresses
- # identical to their physical addresses, so that the
- # effective memory map does not change during the switch.
- lgdt gdtdesc
- movl %cr0, %eax
- orl $CR0_PE_ON, %eax
- movl %eax, %cr0
- # Jump to next instruction, but in 32-bit code segment.
- # Switches processor into 32-bit mode.
- ljmp $PROT_MODE_CSEG, $protcseg
- jnz seta20.1
- jnz seta20.2
- lgdt gdtdesc
- 00007c1a <seta20.1>:
- jnz seta20.1
- 7c1e: 75 fa jne 7c1a <seta20.1>
- 00007c24 <seta20.2>:
- jnz seta20.2
- 7c28: 75 fa jne 7c24 <seta20.2>
- 00007c74 <gdtdesc>:
- 7c74: 17 pop %ss
- 7c75: 00 5c 7c 00 add %bl,0x0(%esp,%edi,2)
- 7c79: 00 90 90 55 ba f7 add %dl,-0x845aa70(%eax)