0%

## Exercise 7

Here is some GDB record concerning the first question.

=> 0x100025:    mov    %eax,%cr0Breakpoint 2, 0x00100025 in ?? ()
(gdb) x/10x 0x100000
0x100010:  0x34000004  0x2000b812  0x220f0011  0xc0200fd8
0x100020:  0x0100010d  0xc0220f80
(gdb) x/10x 0xf0100000
0xf0100000 <_start-268435468>:  0x00000000  0x00000000  0x00000000  0x00000000
0xf0100010 <entry+4>:   0x00000000  0x00000000  0x00000000  0x00000000
0xf0100020 <entry+20>:  0x00000000  0x00000000
(gdb) si
=> 0x100028:    mov    $0xf010002f,%eax 0x00100028 in ?? () (gdb) x/10x 0x100000 0x100000: 0x1badb002 0x00000000 0xe4524ffe 0x7205c766 0x100010: 0x34000004 0x2000b812 0x220f0011 0xc0200fd8 0x100020: 0x0100010d 0xc0220f80 (gdb) x/10x 0xf0100000 0xf0100000 <_start-268435468>: 0x1badb002 0x00000000 0xe4524ffe 0x7205c766 0xf0100010 <entry+4>: 0x34000004 0x2000b812 0x220f0011 0xc0200fd8 0xf0100020 <entry+20>: 0x0100010d 0xc0220f80  It's obvious that after instruction movl %eax, %cr0, address 0xf0100000 is mapped to 0x00100000. Thus we can make a reasonable assumption that the instruction enables paging mechanism. Looking deep in function of reg CR0 and comparing it with the value of CR0 10000000000000010000000000000001, we find OS now enables paging and write protect. Following is a brief introduction about Paging bit. Enables paging when set; disables paging when clear. When paging is disabled, all linear addresses are treated as physical addresses. The PG flag has no effect if the PE flag (bit 0 of register CR0) is not also set; setting the PG flag when the PE flag is clear causes a general-protection exception (#GP). Comment out the instruction, recompile the kernel and trace into it, we find it exits here: => 0x100028: jmp *%eax 0x00100028 in ?? () (gdb) => 0xf010002a <relocated>: add %al,(%eax) relocated () at kern/entry.S:74 74 movl$0x0,%ebp           # nuke frame pointer
(gdb) si
Remote connection closed


with qemu error information:

## Exercise 9

(gdb) x/20i 0xf010002f
=> 0xf010002f <relocated>:   mov    $0x0,%ebp 0xf0100034 <relocated+5>: mov$0xf0110000,%esp
0xf0100039 <relocated+10>:   call   0xf01000aa <i386_init>


The kernel initializes its stack at 0xf0100034, and the stack is located at 0xf0110000.

In lab1/kern/entry.S, there is a more clear way describing the initialization of the kernel stack and the size of it. KSTKSIZE(4*PAGESIZE defined in lab1/lib/memlayout.h and PAGESIZE=4096 defined in lab1/lib/mmu.h) space has been reserved for the stack. The stack pointer, of course, points the top of the stack, as shown in bootstack section.

## Exercise 10

Here's corresponding output during calling the test_backtrace function.

Each time it gets called after the kernel starts, a corresponding stack frame is created. Here is a record when test_backtrace is called for the first time.

=> 0xf0100048 <test_backtrace+8>:   push   %ebx
0xf0100048 in test_backtrace (x=-267321352) at kern/init.c:13
13  {
Stack level 0, frame at 0xf010ffd8:
eip = 0xf0100048 in test_backtrace (kern/init.c:13); saved eip = 0x10094
called by frame at 0xf010ffe0
source language c.
Arglist at 0xf010ffd0, args: x=-267321352
Locals at 0xf010ffd0, Previous frame's sp is 0xf010ffd8
Saved registers:
eip at 0xf010ffd4
eax            0x0                 0
ecx            0x3d4               980
edx            0x3d5               981
ebx            0xf0111308          -267316472
esp            0xf010ffd4          0xf010ffd4
ebp            0xf010ffd8          0xf010ffd8
esi            0x10094             65684
edi            0x0                 0
eip            0xf0100048          0xf0100048 <test_backtrace+8>
eflags         0x46                [ PF ZF ]
cs             0x8                 8
ss             0x10                16
ds             0x10                16
es             0x10                16
fs             0x10                16
gs             0x10                16


The address stack pointer(%esp) pointing changes during the function call, and here is the structure of the frame.

In lab1/obj/kern/kern.asm we observe that add \$0x10,%esp appears after each function call inside test_backtrace, which indicates that the space of the (local vars. + other data) = 0x10 bytes. Actually, in test_backtrace there is no local vars., and other data = arg. list+return addr. of a calling function (cprintf for example).

Back to the question, test_backtrace pushes many 32-bit words each recursive nesting level of on the stack. I say many because counting is meaningless for me after understanding the structure of the stack frame.

## Exercise 12

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.

In debuginfo_eip, where do __STAB_* come from? This question has a long answer; to help you to discover the answer, here are some things you might want to do:

• look in the file kern/kernel.ld for __STAB_*
• run objdump -h obj/kern/kernel
• run objdump -G obj/kern/kernel
• run gcc -pipe -nostdinc -O2 -fno-builtin -I. -MD -Wall -Wno-format -DJOS_KERNEL -gstabs -c -S kern/init.c, and look at init.s.

Complete the implementation of debuginfo_eip by inserting the call to stab_binsearch to find the line number for an address.

Add a backtrace command to the kernel monitor, and extend your implementation of mon_backtrace to call debuginfo_eip and print a line for each stack frame of the form:

Each line gives the file name and line within that file of the stack frame's eip, followed by the name of the function and the offset of the eip from the first instruction of the function (e.g., monitor+106 means the return eip is 106 bytes past the beginning of monitor).

Be sure to print the file and function names on a separate line, to avoid confusing the grading script.

Tip: printf format strings provide an easy, albeit obscure, way to print non-null-terminated strings like those in STABS tables. printf("%.*s", length, string) prints at most length characters of string. Take a look at the printf man page to find out why this works.

You may find that some functions are missing from the backtrace. For example, you will probably see a call to monitor() but not to runcmd(). This is because the compiler in-lines some function calls. Other optimizations may cause you to see unexpected line numbers. If you get rid of the -O2 from GNUMakefile, the backtraces may make more sense (but your kernel will run more slowly).

There are three tasks in summary:

• Complete the implementation of debuginfo_eip
• Modify test_backtrace to meet requirements
• Add command backtrace to call test_backtrace

As for the first one, only a small piece of code with simple logic needs to be added, which is shown as follow.

The completed debuginfo_eip is now used to modify function test_traceback.

Note that a tip mentioned above should be used to control the length of eip_fn_name.