fix a bug, should be: Virtual Address=Physical Address+0xC0000000
This commit is contained in:
parent
5346cc9003
commit
89d28c7ba1
|
@ -58,7 +58,7 @@
|
|||
输出日志到 qemu.log 文件。
|
||||
|
||||
其他参数说明可以参考:http://bellard.org/qemu/qemu-doc.html#SEC15 。其他qemu的安装和使用的说明可以参考http://bellard.org/qemu/user-doc.html。
|
||||
|
||||
|
||||
或者在命令行收入 qemu (没有参数) 显示帮助。
|
||||
|
||||
在实验中,例如 lab1,可能用到的命令如:
|
||||
|
@ -68,4 +68,4 @@
|
|||
或
|
||||
|
||||
qemu -S -s -hda ucore.img -monitor stdio # 用于与gdb配合进行源码调试
|
||||
|
||||
|
||||
|
|
|
@ -31,13 +31,13 @@ qemu中monitor的常用命令:
|
|||
0x000fe05b: xor %ax, %ax
|
||||
|
||||
step命令为单步命令,即qemu执行一步,能够跳过 breakpoint 断点执行。如果此时使用cont命令,则qemu 运行改为连续执行。
|
||||
|
||||
|
||||
log命令能够保存qemu模拟过程产生的信息(与qemu运行参数 `-d' 相同),具体参数可以参考命令帮助。产生的日志信息保存在 “/tmp/qemu.log” 中,例如使用 'log in_asm'命令以后,运行过程产生的的qemu.log 文件为:
|
||||
|
||||
1 ----------------
|
||||
2 IN:
|
||||
3 0xfffffff0: ljmp $0xf000,$0xe05b
|
||||
4
|
||||
4
|
||||
5 ----------------
|
||||
6 IN:
|
||||
7 0x000fe05b: xor %ax,%ax
|
||||
|
|
|
@ -12,4 +12,4 @@
|
|||
使用 diff 命令对修改后的 ucore 代码和 ucore 源码进行比较,比较之前建议使用 make clean 命令清除不必要文件。(如果有ctags 文件,需要手工清除。)
|
||||
|
||||
(3)应用修改:参见 patch 命令说明。
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
(gdb) file obj/kernel/kernel.elf
|
||||
|
||||
之后gdb就会载入这个文件中的符号信息了。
|
||||
|
||||
|
||||
通过gdb可以对ucore代码进行调试,以lab1中调试memset函数为例:
|
||||
|
||||
(1) 运行 qemu -S -s -hda ucore.img -monitor stdio
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
target remote 127.0.0.1:1234
|
||||
file obj/kernel/kernel.elf
|
||||
|
||||
|
||||
为了让gdb在启动时执行这些命令,使用下面的命令启动gdb:
|
||||
|
||||
$ gdb -x gdbinit
|
||||
|
||||
|
||||
如果觉得这个命令太长,可以将这个命令存入一个文件中,当作脚本来执行。
|
||||
|
||||
另外,如果直接使用上面的命令,那么得到的界面是一个纯命令行的界面,不够直观,就像下图这样:
|
||||
|
@ -21,4 +21,4 @@
|
|||
如果想获得上面右图那样的效果,只需要再加上参数-tui就行了,比如:
|
||||
|
||||
gdb -tui -x gdbinit
|
||||
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
下面,我们要求gdb将linker加载到0x6fee6180这个地址上:
|
||||
|
||||
(gdb) add-symbol-file android_test/system/bin/linker 0x6fee6180
|
||||
|
||||
|
||||
这样的命令默认是将代码段(.data)段的调试信息加载到0x6fee6180上,当然,你也可以通过“-s”这个参数来指定,比如:
|
||||
(gdb) add-symbol-file android_test/system/bin/linker –s .text 0x6fee6180
|
||||
|
||||
|
||||
这样,在执行到linker中代码时gdb就能够显示出正确的代码和调试信息出来。
|
||||
|
||||
这个方法在操作系统中调试动态链接器时特别有用。
|
||||
|
|
|
@ -6,4 +6,4 @@
|
|||
(gdb) set arch i8086
|
||||
|
||||
这个方法在调试不同架构或者说不同模式的代码时还是有点用处的。
|
||||
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
|
||||
#### 2.4.4 结合gdb和qemu源码级调试ucore
|
||||
#### 2.4.4 结合gdb和qemu源码级调试ucore
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
#### 3.2.4 ELF文件格式概述
|
||||
|
||||
ELF(Executable and linking format)文件格式是Linux系统下的一种常用目标文件(object file)格式,有三种主要类型:
|
||||
ELF(Executable and linking format)文件格式是Linux系统下的一种常用目标文件(object file)格式,有三种主要类型:
|
||||
|
||||
- 用于执行的可执行文件(executable file),用于提供程序的进程映像,加载的内存执行。 这也是本实验的OS文件类型。
|
||||
- 用于连接的可重定位文件(relocatable file),可与其它目标文件一起创建可执行文件和共享目标文件。
|
||||
- 共享目标文件(shared object file),连接器可将它与其它可重定位文件和共享目标文件连接成其它的目标文件,动态连接器又可将它与可执行文件和其它共享目标文件结合起来创建一个进程映像。
|
||||
- 用于连接的可重定位文件(relocatable file),可与其它目标文件一起创建可执行文件和共享目标文件。
|
||||
- 共享目标文件(shared object file),连接器可将它与其它可重定位文件和共享目标文件连接成其它的目标文件,动态连接器又可将它与可执行文件和其它共享目标文件结合起来创建一个进程映像。
|
||||
|
||||
这里只分析与本实验相关的ELF可执行文件类型。ELF header在文件开始处描述了整个文件的组织。ELF的文件头包含整个执行文件的控制结构,其定义在elf.h中:
|
||||
|
||||
|
@ -36,8 +36,8 @@ struct proghdr {
|
|||
uint type; // 段类型
|
||||
uint offset; // 段相对文件头的偏移值
|
||||
uint va; // 段的第一个字节将被放到内存中的虚拟地址
|
||||
uint pa;
|
||||
uint filesz;
|
||||
uint pa;
|
||||
uint filesz;
|
||||
uint memsz; // 段在内存映像中占用的字节数
|
||||
uint flags;
|
||||
uint align;
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
栈是一个很重要的编程概念(编译课和程序设计课都讲过相关内容),与编译器和编程语言有紧密的联系。理解调用栈最重要的两点是:栈的结构,EBP寄存器的作用。一个函数调用动作可分解为:零到多个PUSH指令(用于参数入栈),一个CALL指令。CALL指令内部其实还暗含了一个将返回地址(即CALL指令下一条指令的地址)压栈的动作(由硬件完成)。几乎所有本地编译器都会在每个函数体之前插入类似如下的汇编指令:
|
||||
|
||||
```
|
||||
pushl %ebp
|
||||
movl %esp , %ebp
|
||||
pushl %ebp
|
||||
movl %esp , %ebp
|
||||
```
|
||||
这样在程序执行到一个函数的实际指令前,已经有以下数据顺序入栈:参数、返回地址、ebp寄存器。由此得到类似如下的栈结构(参数入栈顺序跟调用方式有关,这里以C语言默认的CDECL为例):
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ IDT和IDTR寄存器的结构和关系如下图所示:
|
|||
![IDT和IDTR寄存器的结构和关系图](../lab1_figs/image007.png "IDT和IDTR寄存器的结构和关系图")
|
||||
图8 IDT和IDTR寄存器的结构和关系图
|
||||
|
||||
在保护模式下,最多会存在256个Interrupt/Exception Vectors。范围[0,31]内的32个向量被异常Exception和NMI使用,但当前并非所有这32个向量都已经被使用,有几个当前没有被使用的,请不要擅自使用它们,它们被保留,以备将来可能增加新的Exception。范围[32,255]内的向量被保留给用户定义的Interrupts。Intel没有定义,也没有保留这些Interrupts。用户可以将它们用作外部I/O设备中断(8259A IRQ),或者系统调用(System Call 、Software Interrupts)等。
|
||||
在保护模式下,最多会存在256个Interrupt/Exception Vectors。范围[0,31]内的32个向量被异常Exception和NMI使用,但当前并非所有这32个向量都已经被使用,有几个当前没有被使用的,请不要擅自使用它们,它们被保留,以备将来可能增加新的Exception。范围[32,255]内的向量被保留给用户定义的Interrupts。Intel没有定义,也没有保留这些Interrupts。用户可以将它们用作外部I/O设备中断(8259A IRQ),或者系统调用(System Call 、Software Interrupts)等。
|
||||
|
||||
(2) IDT gate descriptors
|
||||
|
||||
|
@ -51,7 +51,7 @@ Interrupts/Exceptions应该使用Interrupt Gate和Trap Gate,它们之间的唯
|
|||
- 如果存在特权级转换(从内核态转换到用户态),则还需要从内核栈中弹出用户态栈的ss和esp,这样也意味着栈也被切换回原先使用的用户态的栈了;
|
||||
- 如果此次处理的是带有错误码(errorCode)的异常,CPU在恢复先前程序的现场时,并不会弹出errorCode。这一步需要通过软件完成,即要求相关的中断服务例程在调用iret返回之前添加出栈代码主动弹出errorCode。
|
||||
|
||||
下图显示了从中断向量到GDT中相应中断服务程序起始位置的定位方式:
|
||||
下图显示了从中断向量到GDT中相应中断服务程序起始位置的定位方式:
|
||||
|
||||
![中断向量与中断服务例程起始地址的关系](../lab1_figs/image009.png "中断向量与中断服务例程起始地址的关系")
|
||||
图10 中断向量与中断服务例程起始地址的关系
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
当bootloader通过读取硬盘扇区把ucore在系统加载到内存后,就转跳到ucore操作系统在内存中的入口位置(kern/init.c中的kern_init函数的起始地址),这样ucore就接管了整个控制权。当前的ucore功能很简单,只完成基本的内存管理和外设中断管理。ucore主要完成的工作包括:
|
||||
|
||||
- 初始化终端;
|
||||
- 初始化终端;
|
||||
- 显示字符串;
|
||||
- 显示堆栈中的多层函数调用关系;
|
||||
- 切换到保护模式,启用分段机制;
|
||||
|
|
|
@ -41,7 +41,7 @@ Virtual Address= Linear Address
|
|||
|
||||
改为
|
||||
|
||||
Virtual Address=Linear Address-0xC0000000
|
||||
Virtual Address=Linear Address + 0xC0000000
|
||||
|
||||
由于gcc编译出的虚拟起始地址从0xC0100000开始,ucore被bootloader放置在从物理地址0x100000处开始的物理内存中。所以当kern\_entry函数完成新的段映射关系后,且ucore在没有建立好页映射机制前,CPU按照ucore中的虚拟地址执行,能够被分段机制映射到正确的物理地址上,确保ucore运行正确。
|
||||
|
||||
|
|
Loading…
Reference in New Issue