mit6.828 Lab1

    xiaoxiao2025-08-26  1

    Part 1

    Exercise 1

    熟悉x86汇编语言 参考资料:https://pdos.csail.mit.edu/6.828/2018/readings/pcasm-book.pdf 这本书中介绍的是使用nasm汇编器所支持的汇编(Intel Syntax),但在这个lab中实际使用的是GNU汇编器(AT&T Syntax) http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html

    Exercise 2

    使用gdb对JOS进行调试,调试步骤先在lab的终端中输入make qemu-gdb,再开另外一个终端输入make gdb 用GDB的si指令去追踪BIOS中所用到的指令

    Part 2

    Exercise 3

    查看lab tools guide,这里面包括一些调试OS特殊的GDB技巧

    See the GDB manual for a full guide to GDB commands. Here are some particularly useful commands for 6.828, some of which don't typically come up outside of OS development. Ctrl-c Halt the machine and break in to GDB at the current instruction. If QEMU has multiple virtual CPUs, this halts all of them. c (or continue) Continue execution until the next breakpoint or Ctrl-c. si (or stepi) Execute one machine instruction. b function or b file:line (or breakpoint) Set a breakpoint at the given function or line. b *addr (or breakpoint) Set a breakpoint at the EIP addr. set print pretty Enable pretty-printing of arrays and structs. info registers Print the general purpose registers, eip, eflags, and the segment selectors. For a much more thorough dump of the machine register state, see QEMU's own info registers command. x/Nx addr Display a hex dump of N words starting at virtual address addr. If N is omitted, it defaults to 1. addr can be any expression. x/Ni addr Display the N assembly instructions starting at addr. Using $eip as addr will display the instructions at the current instruction pointer. symbol-file file (Lab 3+) Switch to symbol file file. When GDB attaches to QEMU, it has no notion of the process boundaries within the virtual machine, so we have to tell it which symbols to use. By default, we configure GDB to use the kernel symbol file, obj/kern/kernel. If the machine is running user code, say hello.c, you can switch to the hello symbol file using symbol-file obj/user/hello. QEMU represents each virtual CPU as a thread in GDB, so you can use all of GDB's thread-related commands to view or manipulate QEMU's virtual CPUs. thread n GDB focuses on one thread (i.e., CPU) at a time. This command switches that focus to thread n, numbered from zero. info threads List all threads (i.e., CPUs), including their state (active or halted) and what function they're in. 通过指令b *0x7c00在地址0x7c00中打个断点通过指令c来时程序运行到断点处使用si进行单步执行,与boot.S中的指令进行比较使用x/8i $eip指令来查看从当前执行到的地址开始的8条指令 对boot/main.c中的bootmain()打断点(尚未完成,打不到该函数的断点)

    回答以下问题:

    在哪里开始处理器开始执行32位代码?是什么导致从16位到32位的转换? 从下面这段开始处理器开始执行32位代码 # Jump to next instruction, but in 32-bit code segment. # Switches processor into 32-bit mode. ljmp $PROT_MODE_CSEG, $protcseg boot loader的最后一条指令是什么?加载kernel后的第一条指令是什么?kernel的第一条指令的地址是什么?为了将整个kernel加载从磁盘加载到内存boot loader如何决定要加载多少个扇区?boot loader从哪里找到要加载的扇区的信息
    Exercise 4
    推荐弄懂其中的每一个细节,保证对C语言的掌握足以应对接下来的实验 pointer.c #include <stdio.h> #include <stdlib.h> void f(void) { int a[4]; int *b = malloc(16); int *c; int i; printf("1: a = %p, b = %p, c = %p\n", a, b, c); c = a; for (i = 0; i < 4; i++) a[i] = 100 + i; c[0] = 200; printf("2: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n", a[0], a[1], a[2], a[3]); c[1] = 300; *(c + 2) = 301; 3[c] = 302; //C语言中,数组和下标可以互换,这是由数组下标的指针定义决定的,由于存在加法交换律,只要一个是指针,另一个是整型就 //行,而无关顺序,a[3]等价于3[a],等价于*(a+3),等价于*(3+a)。 printf("3: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n", a[0], a[1], a[2], 3[a]); c = c + 1; *c = 400; printf("4: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n", a[0], a[1], a[2], a[3]); c = (int *) ((char *) c + 1); *c = 500; printf("5: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n", a[0], a[1], a[2], a[3]); b = (int *) a + 1; // addr = a.addr + sizeof(int) * 1,指针的算术运算 c = (int *) ((char *) a + 1); // addr = a.addr + sizeof(char) * 1 printf("6: a = %p, b = %p, c = %p\n", a, b, c); } int main(int ac, char **av) { f(); return 0; }

    输出结果

    1: a = 0x7fffb9dbf680, b = 0x117f010, c = 0x1 2: a[0] = 200, a[1] = 101, a[2] = 102, a[3] = 103 3: a[0] = 200, a[1] = 300, a[2] = 301, a[3] = 302 4: a[0] = 200, a[1] = 400, a[2] = 301, a[3] = 302 5: a[0] = 200, a[1] = 128144, a[2] = 256, a[3] = 302 6: a = 0x7fffb9dbf680, b = 0x7fffb9dbf684, c = 0x7fffb9dbf681 C语言中,数组和下标可以互换,这是由数组下标的指针定义决定的,由于存在加法交换律,只要一个是指针,另一个是整型就行,而无关顺序,a[3]等价于3[a],等价于*(a+3),等价于*(3+a)。

    当对一个C程序进行编译链接时,编译器将C源代码转化为包含二进制格式的汇编指令的object file(.o),链接器将所有的obejct file链接成单个二进制镜像,该镜像以ELF(executable linklable format)为标准。

    ELF ELF文件由4部分组成,分别是ELF头(ELF header)、程序头表(Program header table)、节(Section)和节头表(Section header table) elf section: .text: 存放程序的可执行指令.rodata:存放只读数据,例如C语言中的字符串常量.data:存放程序中已初始化的数据,例如被初始化的全局变量

    列出obj/kern/kernel所有section的信息

    $objdump -h obj/kern/kernel obj/kern/kernel: 文件格式 elf32-i386 节: Idx Name Size VMA LMA File off Algn 0 .text 000019e9 f0100000 00100000 00001000 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .rodata 000006c0 f0101a00 00101a00 00002a00 2**5 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .stab 00003b95 f01020c0 001020c0 000030c0 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .stabstr 00001948 f0105c55 00105c55 00006c55 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .data 00009300 f0108000 00108000 00009000 2**12 CONTENTS, ALLOC, LOAD, DATA 5 .got 00000008 f0111300 00111300 00012300 2**2 CONTENTS, ALLOC, LOAD, DATA 6 .got.plt 0000000c f0111308 00111308 00012308 2**2 CONTENTS, ALLOC, LOAD, DATA 7 .data.rel.local 00001000 f0112000 00112000 00013000 2**12 CONTENTS, ALLOC, LOAD, DATA 8 .data.rel.ro.local 00000044 f0113000 00113000 00014000 2**2 CONTENTS, ALLOC, LOAD, DATA 9 .bss 00000648 f0113060 00113060 00014060 2**5 CONTENTS, ALLOC, LOAD, DATA 10 .comment 0000002b 00000000 00000000 000146a8 2**0 CONTENTS, READONLY

    加载地址为程序在外存中存储的位置,链接地址为加载到内存中的地址。

    Exercise 5

    将Makefrag中的链接地址从0x7c00改为0x7c10,重新构建项目,将断点打在0x7c2a

    (gdb) b *0x7c2a Breakpoint 1 at 0x7c2a (gdb) c Continuing. [ 0:7c2a] => 0x7c2a: mov
    转载请注明原文地址: https://yun.8miu.com/read-140468.html
    最新回复(0)