zoukankan      html  css  js  c++  java
  • MIT OS lab1

    Lab 1: Booting a PC

    1、Introduction

    Software setup

    Get jos code:

    athena% mkdir ~/6.828

    athena% cd ~/6.828

    athena% add git

    athena% git clone http://pdos.csail.mit.edu/6.828/2014-jos.git lab

    Cloning into lab...

    athena% cd lab

    Part 1: PC Bootstrap

    1.1   getting started with X86 assembly.

    1 Exercise 1. Familiarize yourself with the assembly language materials available on the 6.828 reference page. You don't have to read them now, but you'll almost certainly want to refer to some of this material when reading and writing x86 assembly.
    2 
    3 We do recommend reading the section "The Syntax" in Brennan's Guide to Inline Assembly. It gives a good (and quite brief) description of the AT&T assembly syntax we'll be using with the GNU assembler in JOS.

     熟悉Intel格式的汇编和AT&T格式汇编即可。

    1.2   simulating the X86

    查看主目录下的Makefile,可知make/ make qemu 等编译命令操作内容。

    使用make编译代码,make qemu启动qemu虚拟机。启动以后可以看到如下命令

    K> help
    help - display this list of commands
    kerninfo - display information about the kernel
    K> kerninfo
    Special kernel symbols:
      entry  f010000c (virt)  0010000c (phys)
      etext  f0101a75 (virt)  00101a75 (phys)
      edata  f0112300 (virt)  00112300 (phys)
      end    f0112960 (virt)  00112960 (phys)
    Kernel executable memory footprint: 75KB
    K>
    PC机的物理地址空间如下:
    +------------------+  <- 0xFFFFFFFF (4GB)
    |      32-bit      |
    |  memory mapped   |
    |     devices      |
    |                  |
    //////////
    
    //////////
    |                  |
    |      Unused      |
    |                  |
    +------------------+  <- depends on amount of RAM
    |                  |
    |                  |
    | Extended Memory  |
    |                  |
    |                  |
    +------------------+  <- 0x00100000 (1MB)
    |     BIOS ROM     |
    +------------------+  <- 0x000F0000 (960KB)
    |  16-bit devices, |
    |  expansion ROMs  |
    +------------------+  <- 0x000C0000 (768KB)
    |   VGA Display    |
    +------------------+  <- 0x000A0000 (640KB)
    |                  |
    |    Low Memory    |
    |                  |
    +------------------+  <- 0x00000000

    1.3 the ROM BIOS

    Exercise 2. Use GDB's si (Step Instruction) command to trace into the ROM BIOS for a few more instructions, and try to guess what it might be doing. You might want to look at Phil Storrs I/O Ports Description, as well as other materials on the 6.828 reference materials page. No need to figure out all the details - just the general idea of what the BIOS is doing first.

     在一个窗口输入make qemu-gdb, 之后再另一个窗口输入make gdb:

    make gdb
    gdb -x .gdbinit
    ...
    + target remote localhost:25000
    The target architecture is assumed to be i8086
    [f000:fff0] 0xffff0: ljmp $0xf000,$0xe05b
    0x0000fff0 in ?? ()
    + symbol-file obj/kern/kernel

     1 (gdb) i registers 
     2 eax            0x0    0
     3 ecx            0x0    0
     4 edx            0x663    1635
     5 ebx            0x0    0
     6 esp            0x0    0x0
     7 ebp            0x0    0x0
     8 esi            0x0    0
     9 edi            0x0    0
    10 eip            0xfff0    0xfff0
    11 eflags         0x2    [ ]
    12 cs             0xf000    61440
    13 ss             0x0    0
    14 ds             0x0    0
    15 es             0x0    0
    16 fs             0x0    0
    17 gs             0x0    0

    可以看到CS:IP 为 0xf000:0xfff0, 即BIOS第一条指令地址为0xf000 * 0x10 + 0xfff0 = 0xffff0, 此处指令内容为:

      ljump 0xf000, 0xe05b

    即跳转到0xfe05b. 如下:

    (gdb) si
    [f000:e05b]    0xfe05b:    cmpl   $0x0,%cs:0x65a4
    0x0000e05b in ?? ()
    (gdb) x/10i 0xfe05b
       0xfe05b:    cmpl   $0x0,%cs:0x65a4
       0xfe062:    jne    0xfd2b9
       0xfe066:    xor    %ax,%ax
       0xfe068:    mov    %ax,%ss
       0xfe06a:    mov    $0x7000,%esp
       0xfe070:    mov    $0xf3c2f,%edx
       0xfe076:    jmp    0xfd12a
       0xfe079:    push   %ebp
       0xfe07b:    push   %edi
       0xfe07d:    push   %esi

    Part 2: The Boot Loader

    BIOS会把floppy或hard disk中的512字节的boot sector 加载到物理地址0x7C00, 并通过jmp指令将CS:IP设置为0x0000:0x7C00

    Exercise 3. Take a look at the lab tools guide, especially the section on GDB commands. Even if you're familiar with GDB, this includes some esoteric GDB commands that are useful for OS work.
    
    Set a breakpoint at address 0x7c00, which is where the boot sector will be loaded. Continue execution until that breakpoint. Trace through the code in boot/boot.S, using the source code and the disassembly file obj/boot/boot.asm to keep track of where you are. Also use the x/i command in GDB to disassemble sequences of instructions in the boot loader, and compare the original boot loader source code with both the disassembly in obj/boot/boot.asm and GDB.
    
    Trace into bootmain() in boot/main.c, and then into readsect(). Identify the exact assembly instructions that correspond to each of the statements in readsect(). Trace through the rest of readsect() and back out into bootmain(), and identify the begin and end of the for loop that reads the remaining sectors of the kernel from the disk. Find out what code will run when the loop is finished, set a breakpoint there, and continue to that breakpoint. Then step through the remainder of the boot loader.
    
    Be able to answer the following questions:
    1、At what point does the processor start executing 32-bit code? What exactly causes the switch from 16- to 32-bit mode?
    2、What is the last instruction of the boot loader executed, and what is the first instruction of the kernel it just loaded?
    3、Where is the first instruction of the kernel?
    4、How does the boot loader decide how many sectors it must read in order to fetch the entire kernel from disk? Where does it find this information?

    gdb显示0x7C00处的代码如下:

    (gdb) x/36i 0x7c00
       0x7c00:    cli    
    => 0x7c01:    cld    
       0x7c02:    xor    %ax,%ax
       0x7c04:    mov    %ax,%ds
       0x7c06:    mov    %ax,%es
       0x7c08:    mov    %ax,%ss
       0x7c0a:    in     $0x64,%al
       0x7c0c:    test   $0x2,%al
       0x7c0e:    jne    0x7c0a
       0x7c10:    mov    $0xd1,%al
       0x7c12:    out    %al,$0x64
       0x7c14:    in     $0x64,%al
       0x7c16:    test   $0x2,%al
       0x7c18:    jne    0x7c14
       0x7c1a:    mov    $0xdf,%al
       0x7c1c:    out    %al,$0x60
       0x7c1e:    lgdtw  0x7c64
       0x7c23:    mov    %cr0,%eax
       0x7c26:    or     $0x1,%eax
       0x7c2a:    mov    %eax,%cr0
       0x7c2d:    ljmp   $0x8,$0x7c32
       0x7c32:    mov    $0xd88e0010,%eax
       0x7c38:    mov    %ax,%es
       0x7c3a:    mov    %ax,%fs
       0x7c3c:    mov    %ax,%gs
       0x7c3e:    mov    %ax,%ss
    ---Type <return> to continue, or q <return> to quit---
       0x7c40:    mov    $0x7c00,%sp
       0x7c43:    add    %al,(%bx,%si)
       0x7c45:    call   0x7d1a
       0x7c48:    add    %al,(%bx,%si)
       0x7c4a:    jmp    0x7c4a
    (gdb) x/36i 0x7c00
       0x7c00:    cli    
    => 0x7c01:    cld    
       0x7c02:    xor    %ax,%ax
       0x7c04:    mov    %ax,%ds
       0x7c06:    mov    %ax,%es
       0x7c08:    mov    %ax,%ss
       0x7c0a:    in     $0x64,%al
       0x7c0c:    test   $0x2,%al
       0x7c0e:    jne    0x7c0a
       0x7c10:    mov    $0xd1,%al
       0x7c12:    out    %al,$0x64
       0x7c14:    in     $0x64,%al
       0x7c16:    test   $0x2,%al
       0x7c18:    jne    0x7c14
       0x7c1a:    mov    $0xdf,%al
       0x7c1c:    out    %al,$0x60
       0x7c1e:    lgdtw  0x7c64
       0x7c23:    mov    %cr0,%eax
       0x7c26:    or     $0x1,%eax
       0x7c2a:    mov    %eax,%cr0
       0x7c2d:    ljmp   $0x8,$0x7c32
       0x7c32:    mov    $0xd88e0010,%eax
       0x7c38:    mov    %ax,%es
       0x7c3a:    mov    %ax,%fs
       0x7c3c:    mov    %ax,%gs
       0x7c3e:    mov    %ax,%ss
       0x7c40:    mov    $0x7c00,%sp
       0x7c43:    add    %al,(%bx,%si)
       0x7c45:    call   0x7d1a
       0x7c48:    add    %al,(%bx,%si)
       0x7c4a:    jmp    0x7c4a

    1、处理器进入boot loader后,0x7c2a mov %eax, cr0 将cr0低位置1,处理器从实模式跳入保护模式, 通过ljmp $0x8, $0x7c32跳入地址0x7c32,开始执行32位实模式代码.

    2、在boot/main.c的第58行进入kernel:

        // call the entry point from the ELF header
        // note: does not return!
        ((void (*)(void)) (ELFHDR->e_entry))();

    3、用gdb调试代码:

    (gdb) x/36i 0x7d1c
       0x7d1c:    push   %ebp
       0x7d1d:    mov    %esp,%ebp
     ...
       0x7d6d:    add    $0xc,%esp
       0x7d70:    cmp    %esi,%ebx
       0x7d72:    jb     0x7d5c
       0x7d74:    call   *0x10018
       0x7d7a:    mov    $0x8a00,%edx
       0x7d7f:    mov    $0xffff8a00,%eax
       0x7d84:    out    %ax,(%dx)
       0x7d86:    mov    $0xffff8e00,%eax
       0x7d8b:    out    %ax,(%dx)

    结合source code, 可知通过call *0x10018执行上述 e_entry,

    (gdb) b *0x7d74
    Breakpoint 2 at 0x7d74
    (gdb) c
    Continuing.
    => 0x7d74:    call   *0x10018
    
    Breakpoint 2, 0x00007d74 in ?? ()
    (gdb) si
    => 0x10000c:    movw   $0x1234,0x472
    0x0010000c in ?? ()

    可知地址0x10000c为kernel中的首地址.

    4、通过objdump即可

    root@ubuntu:2014-jos# objdump -x obj/kern/kernel
    
    obj/kern/kernel:     file format elf32-i386
    obj/kern/kernel
    architecture: i386, flags 0x00000112:
    EXEC_P, HAS_SYMS, D_PAGED
    start address 0x0010000c
    
    Program Header:
        LOAD off    0x00001000 vaddr 0xf0100000 paddr 0x00100000 align 2**12
             filesz 0x000073d9 memsz 0x000073d9 flags r-x
        LOAD off    0x00009000 vaddr 0xf0108000 paddr 0x00108000 align 2**12
             filesz 0x0000a300 memsz 0x0000a944 flags rw-
       STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2
             filesz 0x00000000 memsz 0x00000000 flags rwx
    
    Sections:
    Idx Name          Size      VMA       LMA       File off  Algn
      0 .text         00001915  f0100000  00100000  00001000  2**4
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
      1 .rodata       00000710  f0101920  00101920  00002920  2**5
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      2 .stab         00003a21  f0102030  00102030  00003030  2**2
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      3 .stabstr      00001988  f0105a51  00105a51  00006a51  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
      4 .data         0000a300  f0108000  00108000  00009000  2**12
                      CONTENTS, ALLOC, LOAD, DATA
      5 .bss          00000644  f0112300  00112300  00013300  2**5
                      ALLOC
      6 .comment      0000002d  00000000  00000000  00013300  2**0
                      CONTENTS, READONLY

    2.1 Loading the kernel

    在loading kernel阶段,主要由C语言完成。

    Exercise 4. Read about programming with pointers in C. The best reference for the C language is The C Programming Language by Brian Kernighan and Dennis Ritchie (known as 'K&R'). We recommend that students purchase this book (here is an Amazon Link) or find one of MIT's 7 copies.
    
    Read 5.1 (Pointers and Addresses) through 5.5 (Character Pointers and Functions) in K&R. Then download the code for pointers.c, run it, and make sure you understand where all of the printed values come from. In particular, make sure you understand where the pointer addresses in lines 1 and 6 come from, how all the values in lines 2 through 4 get there, and why the values printed in line 5 are seemingly corrupted.
    
    There are other references on pointers in C (e.g., A tutorial by Ted Jensen that cites K&R heavily), though not as strongly recommended.
    
    Warning: Unless you are already thoroughly versed in C, do not skip or even skim this reading exercise. If you do not really understand pointers in C, you will suffer untold pain and misery in subsequent labs, and then eventually come to understand them the hard way. Trust us; you don't want to find out what "the hard way" is.

    只需自己熟悉C语言即可。

    Exercise 5. 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 make clean, recompile the lab with make, and trace into the boot loader again to see what happens. Don't forget to change the link address back and make clean again afterward!

    正常情况下:

    (gdb) x/30i 0x7c00
    => 0x7c00:    cli    
       0x7c01:    cld    
    。。。
       0x7c0a:    in     $0x64,%al
       0x7c0c:    test   $0x2,%al
       0x7c0e:    jne    0x7c0a
       0x7c10:    mov    $0xd1,%al
       0x7c12:    out    %al,$0x64
       0x7c14:    in     $0x64,%al
       0x7c16:    test   $0x2,%al
       0x7c18:    jne    0x7c14
       0x7c1a:    mov    $0xdf,%al
       0x7c1c:    out    %al,$0x60
       0x7c1e:    lgdtw  0x7c64
       0x7c23:    mov    %cr0,%eax
       0x7c26:    or     $0x1,%eax
       0x7c2a:    mov    %eax,%cr0
       0x7c2d:    ljmp   $0x8,$0x7c32
       0x7c32:    mov    $0xd88e0010,%eax
    。。。
       0x7c45:    call   0x7d1a
       0x7c48:    add    %al,(%bx,%si)

    将-Ttext 0x7c00修改为 -Ttext 0x7c04后,

    (gdb) x/30i 0x7c00
    ...
       0x7c2d:    ljmp   $0x8,$0x7c36
       0x7c32:    mov    $0xd88e0010,%eax
       0x7c38:    mov    %ax,%es
       0x7c3a:    mov    %ax,%fs
       0x7c3c:    mov    %ax,%gs
       0x7c3e:    mov    %ax,%ss
       0x7c40:    mov    $0x7c04,%sp
       0x7c43:    add    %al,(%bx,%si)
       0x7c45:    call   0x7d1a
       0x7c48:    add    %al,(%bx,%si)

    跳转到了0x7c36, 并非一个正常指令地址,导致错误。

    Exercise 6. We can examine memory using GDB's x command. The GDB manual has full details, but for now, it is enough to know that the command x/Nx ADDR prints N words of memory at ADDR. (Note that both 'x's in the command are lowercase.) Warning: The size of a word is not a universal standard. In GNU assembly, a word is two bytes (the 'w' in xorw, which stands for word, means 2 bytes).
    
    Reset the machine (exit QEMU/GDB and start them again). Examine the 8 words of memory at 0x00100000 at the point the BIOS enters the boot loader, and then again at the point the boot loader enters the kernel. Why are they different? What is there at the second breakpoint? (You do not really need to use QEMU to answer this question. Just think.)

    调试gdb

    (gdb) b *0x7c00
    Breakpoint 1 at 0x7c00
    (gdb) c
    Continuing.
    [   0:7c00] => 0x7c00:    cli    
    
    Breakpoint 1, 0x00007c00 in ?? ()
    (gdb) x/8x 0x100000
    0x100000:    0x00000000    0x00000000    0x00000000    0x00000000
    0x100010:    0x00000000    0x00000000    0x00000000    0x00000000
    (gdb) b *0x10000c
    Breakpoint 2 at 0x10000c
    (gdb) c
    Continuing.
    The target architecture is assumed to be i386
    => 0x10000c:    movw   $0x1234,0x472
    
    Breakpoint 2, 0x0010000c in ?? ()

    (gdb) x/8i 0x10000c
    => 0x10000c: movw $0x1234,0x472
    0x100015: mov $0x110000,%eax
    0x10001a: mov %eax,%cr3
    0x10001d: mov %cr0,%eax
    0x100020: or $0x80010001,%eax
    0x100025: mov %eax,%cr0
    0x100028: mov $0xf010002f,%eax
    0x10002d: jmp *%eax

    在刚进入boot loader时,kernel没有被载入内存,因此0x100000地址为空.

    boot loader 执行完毕,kernel已经载入。

    Part 3: The Kernel

    3.1 Use Virtual Memory

    Exercise 7. Use QEMU and GDB to trace into the JOS kernel and stop at the movl %eax, %cr0. Examine memory at 0x00100000 and at 0xf0100000. Now, single step over that instruction using the stepi GDB command. Again, examine memory at 0x00100000 and at 0xf0100000. Make sure you understand what just happened.
    
    What is the first instruction after the new mapping is established that would fail to work properly if the mapping weren't in place? Comment out the movl %eax, %cr0 in kern/entry.S, trace into it, and see if you were right.

    gdb跟踪如下:

    (gdb) b *0x10000c
    Breakpoint 1 at 0x10000c
    (gdb) c
    Continuing.
    The target architecture is assumed to be i386
    => 0x10000c:    movw   $0x1234,0x472
    
    Breakpoint 1, 0x0010000c in ?? ()
    (gdb) si
    => 0x100015:    mov    $0x110000,%eax
    0x00100015 in ?? ()
    (gdb) 
    => 0x10001a:    mov    %eax,%cr3
    0x0010001a in ?? ()
    (gdb) x 0x00100000
       0x100000:    add    0x1bad(%eax),%dh
    (gdb) x 0xf0100000
       0xf0100000:    add    %al,(%eax)
    (gdb) si
    => 0x10001d:    mov    %cr0,%eax
    0x0010001d in ?? ()
    (gdb) 
    => 0x100020:    or     $0x80010001,%eax
    0x00100020 in ?? ()
    (gdb) 
    => 0x100025:    mov    %eax,%cr0
    0x00100025 in ?? ()
    (gdb) 
    => 0x100028:    mov    $0xf010002f,%eax
    0x00100028 in ?? ()
    (gdb) x 0x00100000
       0x100000:    add    0x1bad(%eax),%dh
    (gdb) x 0xf0100000
       0xf0100000:    add    0x1bad(%eax),%dh
    (gdb) 

    在执行 mov %eax, %cr0之前,0xf0100000和0x00100000的内容不一致,当CR0_PG标志位改变之后,(高位的)虚拟地址被(页表)转换为物理地址。

    然后我们执行stepi指令,此时Paging已经启用,entry_pgdir将0xf0000000到0xf0400000范围内虚拟地址转换成了0x00000000到0x00400000范围的物理地址。所以再次检查地址内容时候发现地址0xf0100000的内容和0x00100000的内容相同。

    3.2 Formatted Printing 

    Exercise 8. We have omitted a small fragment of code - the code necessary to print octal numbers using patterns of the form "%o". Find and fill in this code fragment.

    Be able to answer the following questions:

    1. Explain the interface between printf.c and console.c. Specifically, what function does console.c export? How is this function used by printf.c?
    2. Explain the following from console.c:
      1      if (crt_pos >= CRT_SIZE) {
      2              int i;
      3              memcpy(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
      4              for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
      5                      crt_buf[i] = 0x0700 | ' ';
      6              crt_pos -= CRT_COLS;
      7      }
      
    3. For the following questions you might wish to consult the notes for Lecture 2. These notes cover GCC's calling convention on the x86.

      Trace the execution of the following code step-by-step:

      int x = 1, y = 3, z = 4;
      cprintf("x %d, y %x, z %d
      ", x, y, z);
      
      • In the call to cprintf(), to what does fmt point? To what does ap point?
      • List (in order of execution) each call to cons_putcva_arg, and vcprintf. For cons_putc, list its argument as well. For va_arg, list what ap points to before and after the call. For vcprintf list the values of its two arguments.
    4. Run the following code.
          unsigned int i = 0x00646c72;
          cprintf("H%x Wo%s", 57616, &i);
      
      What is the output? Explain how this output is arrived at in the step-by-step manner of the previous exercise. Here's an ASCII table that maps bytes to characters.

      The output depends on that fact that the x86 is little-endian. If the x86 were instead big-endian what would you set i to in order to yield the same output? Would you need to change 57616 to a different value?

      Here's a description of little- and big-endian and a more whimsical description.

    5. In the following code, what is going to be printed after 'y='? (note: the answer is not a specific value.) Why does this happen?
          cprintf("x=%d y=%d", 3);
      
    6. Let's say that GCC changed its calling convention so that it pushed arguments on the stack in declaration order, so that the last argument is pushed last. How would you have to change cprintf or its interface so that it would still be possible to pass it a variable number of arguments?
    Challenge Enhance the console to allow text to be printed in different colors. The traditional way to do this is to make it interpret ANSI escape sequences embedded in the text strings printed to the console, but you may use any mechanism you like. There is plenty of information on the 6.828 reference page and elsewhere on the web on programming the VGA display hardware. If you're feeling really adventurous, you could try switching the VGA hardware into a graphics mode and making the console draw text onto the graphical frame buffer.

    将vprintfmt函数中的case 'o'修改如下:

                // (unsigned) octal
            case 'o':
                // Replace this with your code.
                num = getuint(&ap, lflag);
                base = 8;
                goto number;

    1、console.c中到处函数cputchar, 打印一个字符, 供printf.c中的打印函数。

    2、打印满屏时的处理,如果满屏,整体向上移动一行。

    3、fmt指向格式化字符串,即 "x %d, y %x, z %d ", ap指向不定长参数第一个参数x的地址。

    4、He110, World 57616即0xe110, 0x00646c72的ascii码字符为{r, l, d, ''}

    5、打印未知, 因为第二个参数没有指定,ap向后找一个位置,内容未知。

    6、修改宏va_start和va_arg

    3.3 The Stack

    Exercise 9. Determine where the kernel initializes its stack, and exactly where in memory its stack is located. How does the kernel reserve space for its stack? And at which "end" of this reserved area is the stack pointer initialized to point to?

    stack初始化在调用i386_init之前:

        # Clear the frame pointer register (EBP)
        # so that once we get into debugging C code,
        # stack backtraces will be terminated properly.
        movl    $0x0,%ebp            # nuke frame pointer
    
        # Set the stack pointer
        movl    $(bootstacktop),%esp
    .data
    ###################################################################
    # boot stack
    ###################################################################
        .p2align    PGSHIFT        # force page alignment
        .globl        bootstack
    bootstack:
        .space        KSTKSIZE
        .globl        bootstacktop   
    bootstacktop:

    stack放在.data段,共分配了KSTKSIZE大小的堆栈,bootstacktop指向栈顶.

    // Test the stack backtrace function (lab 1 only)
    void
    test_backtrace(int x)
    {
    f01000e4:    55                       push   %ebp
    f01000e5:    89 e5                    mov    %esp,%ebp
    f01000e7:    53                       push   %ebx
    f01000e8:    83 ec 14                 sub    $0x14,%esp
    f01000eb:    8b 5d 08                 mov    0x8(%ebp),%ebx
        cprintf("entering test_backtrace %d
    ", x);
    f01000ee:    89 5c 24 04              mov    %ebx,0x4(%esp)
    f01000f2:    c7 04 24 72 19 10 f0     movl   $0xf0101972,(%esp)
    f01000f9:    e8 39 08 00 00           call   f0100937 <cprintf>
        if (x > 0)
    f01000fe:    85 db                    test   %ebx,%ebx
    f0100100:    7e 0d                    jle    f010010f <test_backtrace+0x2b>
            test_backtrace(x-1);
    f0100102:    8d 43 ff                 lea    -0x1(%ebx),%eax
    f0100105:    89 04 24                 mov    %eax,(%esp)
    f0100108:    e8 d7 ff ff ff           call   f01000e4 <test_backtrace>
    f010010d:    eb 1c                    jmp    f010012b <test_backtrace+0x47>
        else
            mon_backtrace(0, 0, 0);
    f010010f:    c7 44 24 08 00 00 00     movl   $0x0,0x8(%esp)
    f0100116:    00 
    f0100117:    c7 44 24 04 00 00 00     movl   $0x0,0x4(%esp)
    f010011e:    00 
    f010011f:    c7 04 24 00 00 00 00     movl   $0x0,(%esp)
    f0100126:    e8 6f 05 00 00           call   f010069a <mon_backtrace>
        cprintf("leaving test_backtrace %d
    ", x);
    f010012b:    89 5c 24 04              mov    %ebx,0x4(%esp)
    f010012f:    c7 04 24 8e 19 10 f0     movl   $0xf010198e,(%esp)
    f0100136:    e8 fc 07 00 00           call   f0100937 <cprintf>
    }
    f010013b:    83 c4 14                 add    $0x14,%esp
    f010013e:    5b                       pop    %ebx
    f010013f:    5d                       pop    %ebp
    f0100140:    c3                       ret    

    两次push和一次sub 0x14, 共有4 + 4 + 20 = 28Bytes, 再加上

    call f01000e4 <test_backtrace>

    会自动压栈eip,共有32Bytes。

    Exercise 11. Implement the backtrace function as specified above. Use the same format as in the example, since otherwise the grading script will be confused. When you think you have it working right, run make grade to see if its output conforms to what our grading script expects, and fix it if it doesn't. After you have handed in your Lab 1 code, you are welcome to change the output format of the backtrace function any way you like.
    
    If you use read_ebp(), note that GCC may generate "optimized" code that calls read_ebp() before mon_backtrace()'s function prologue, which results in an incomplete stack trace (the stack frame of the most recent function call is missing). While we have tried to disable optimizations that cause this reordering, you may want to examine the assembly of mon_backtrace() and make sure the call to read_ebp() is happening after the function prologue.
     1 int
     2 mon_backtrace(int argc, char **argv, struct Trapframe *tf)
     3 {
     4     // Your code here.
     5     uint32_t *ebp = (uint32_t *)read_ebp();
     6     uint32_t *eip = (uint32_t *)ebp[1];
     7     uint32_t args[5], i;
     8     for (i = 0; i < 5; i++)
     9         args[i] = ebp[i + 2];
    10     
    11     cprintf("Stack_backtrace:
    ");
    12     while (ebp != NULL)
    13     {
    14         cprintf(" ebp:0x%08x eip:0x%08x args:0x%08x 0x%08x 0x%08x 0x%08x 0x%08x
    ",
    15             ebp, eip, args[0], args[1], args[2], args[3], args[4]);
    16         ebp = (uint32_t *)ebp[0];
    17         eip = (uint32_t *)ebp[1];
    18         for (i = 0; i < 5; i++)
    19             args[i] = ebp[i + 2];
    20     }
    21     return 0;
    22 }

    运行结果:

    entering test_backtrace 5
    entering test_backtrace 4
    entering test_backtrace 3
    entering test_backtrace 2
    entering test_backtrace 1
    entering test_backtrace 0
    Stack_backtrace:
     ebp:0xf010ff18 eip:0xf010012b args:0x00000000 0x00000000 0x00000000 0x00000000 0xf01009c1
     ebp:0xf010ff38 eip:0xf010010d args:0x00000000 0x00000001 0xf010ff78 0x00000000 0xf01009c1
     ebp:0xf010ff58 eip:0xf010010d args:0x00000001 0x00000002 0xf010ff98 0x00000000 0xf01009c1
     ebp:0xf010ff78 eip:0xf010010d args:0x00000002 0x00000003 0xf010ffb8 0x00000000 0xf01009c1
     ebp:0xf010ff98 eip:0xf010010d args:0x00000003 0x00000004 0x00000000 0x00000000 0x00000000
     ebp:0xf010ffb8 eip:0xf010010d args:0x00000004 0x00000005 0x00000000 0x00010094 0x00010094
     ebp:0xf010ffd8 eip:0xf010018e args:0x00000005 0x00001aac 0x00000644 0x00000000 0x00000000
     ebp:0xf010fff8 eip:0xf010003e args:0x00111021 0x00000000 0x00000000 0x00000000 0x00000000
    leaving test_backtrace 0
    leaving test_backtrace 1
    leaving test_backtrace 2
    leaving test_backtrace 3
    leaving test_backtrace 4
    leaving test_backtrace 5
    Welcome to the JOS kernel monitor!
    Exercise 12. Modify your stack backtrace function to display, for each eip, the function name, source file name, and line number corresponding to that eip.

    在kern/kdebug.c中添加

        stab_binsearch(stabs, &lline, &rline, N_SLINE, addr);
        if (lline > rline)
            return -1;
        info->eip_line = stabs[lline].n_desc;

    修改kern/monitor.c修改

     1 int
     2 mon_backtrace(int argc, char **argv, struct Trapframe *tf)
     3 {
     4     // Your code here.
     5     struct Eipdebuginfo dbg_info;
     6     uint32_t *ebp = (uint32_t *)read_ebp();
     7     uint32_t *eip = (uint32_t *)ebp[1];
     8     uint32_t args[5], i;
     9     for (i = 0; i < 5; i++)
    10         args[i] = ebp[i + 2];
    11     
    12     cprintf("Stack_backtrace:
    ");
    13     while (ebp != NULL)
    14     {
    15         cprintf(" ebp:0x%08x eip:0x%08x args:0x%08x 0x%08x 0x%08x 0x%08x 0x%08x
    ",
    16             ebp, eip, args[0], args[1], args[2], args[3], args[4]);
    17         debuginfo_eip((uintptr_t)eip, &dbg_info);
    18         cprintf("	%s:%d %.*s+%d
    ", dbg_info.eip_file, dbg_info.eip_line, 
    19             dbg_info.eip_fn_namelen, dbg_info.eip_fn_name, ebp[1] - dbg_info.eip_fn_addr);
    20         ebp = (uint32_t *)ebp[0];
    21         eip = (uint32_t *)ebp[1];
    22         for (i = 0; i < 5; i++)
    23             args[i] = ebp[i + 2];
    24     }
    25     return 0;
    26 }

    17,18行为添加内容。

  • 相关阅读:
    【原创】绝对居中
    MSSQL数据库各角色权限
    如何升级至 XHTML?
    XHTML 语法
    5.4 删除一个元素节点或者文本节点
    Ajax实现无刷新的获取数据并绑定到GridView,以及无刷新更新数据[转]
    鼠标滑过时显示图片内容隐藏和鼠标滑过图片隐藏内容显示的两种小方法
    纯CSS3魔方的制作
    201920201学期 20192415 《网络空间安全专业导论》XOR加密
    201920201学期 20192415 《网络空间安全专业导论》第四周学习总结
  • 原文地址:https://www.cnblogs.com/ym65536/p/4268951.html
Copyright © 2011-2022 走看看