###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.
代码比较简单, 在1
lib/printfmt.c:void vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
case 'o':
    // Replace this with your code.
    num = getuint(&ap, lflag);
    base = 8;
    goto number;Be able to answer the following questions:
Explain the interface between printf.c and console.c. Specifically, what function does console.c export? How is this function used by printf.c?
系统在console打印出字符的流程如下:
- 用户调用1 kern:printf.c int cprintf(const char *fmt, ...) 
- 1 - cprintf - 1 - kern:printf.c int vcprintf(const char *fmt, va_list ap) 
- 1 - vcprintf - 1 - lib/printfmt.c void vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap) 
- 1 - vprintfmt - 1 - putch - 1 - vprintfmt 
- 1 - vprintfmt - 1 - putdat - 1 - vcprintf - 1 - vcprintf - 1 - cprintf 
- 1 - putch 
Explain the following from console.c:
if (crt_pos >= CRT_SIZE) {
        int i;
        memcpy(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
        for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
                crt_buf[i] = 0x0700 | ' ';
        crt_pos -= CRT_COLS;
}有宏 1
#define CRT_SIZE (CRT_ROWS * CRT_COLS)
1
CRT_SIZE
- 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\n", x, y, z);- 
    In the call to cprintf(), to what does fmt point? To what does ap point? That’s Too Easy. 
- 
    List (in order of execution) each call to cons_putc, va_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. 上文已经有说. 
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? 输出为 1 He110 World - e110好理解, 是57616的 hex 表示. 那么 rld 是从哪儿来的呢?
- x86小端(little-endian)模式下, 高位在低地址存放. 假设局部变量i在栈中的地址为[addr, addr + 3]
- 0x00(最高位)的地址为addr + 3, 0x72(最低位)的地址则是 addr + 0
- 所以1 printf("%s", &i);
 
- 
    In the following code, what is going to be printed after ‘y=’? (note: the answer is not a specific value.) Why does this happen? 1 cprintf("x=%d y=%d", 3);Too Easy. 
- 
    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? 暂时没想法:( 欢迎讨论 
##The Stack In the final exercise of this lab, we will explore in more detail the way the C language uses the stack on the x86, and in the process write a useful new kernel monitor function that prints a backtrace of the stack: a list of the saved Instruction Pointer (IP) values from the nested call instructions that led to the current point of execution.
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?
阅读1
kern/entry.S
.data
###################################################################
# boot stack
###################################################################
	.p2align	PGSHIFT		# force page alignment
	.globl		bootstack
bootstack:
	.space		KSTKSIZE
	.globl		bootstacktop   
bootstacktop:注意到1
.data .space .globl
- 1 - .data 
- 1 - .space 
- 1 - .globl 
- .p2align[wl] abs-expr, abs-expr, abs-expr
    - 
        Pad the location counter (in the current subsection) to a particular storage boundary. The first expression (which must be absolute) is the number of low-order zero bits the location counter must have after advancement. For example `.p2align 3’ advances the location counter until it a multiple of 8. If the location counter is already a multiple of 8, no change is needed. 
- 
        The second expression (also absolute) gives the fill value to be stored in the padding bytes. It (and the comma) may be omitted. If it is omitted, the padding bytes are normally zero. However, on some systems, if the section is marked as containing code and the fill value is omitted, the space is filled with no-op instructions. 
- 
        The third expression is also absolute, and is also optional. If it is present, it is the maximum number of bytes that should be skipped by this alignment directive. If doing the alignment would require skipping more bytes than the specified maximum, then the alignment is not done at all. You can omit the fill value (the second argument) entirely by simply using two commas after the required alignment; this can be useful if you want the alignment to be filled with no-op instructions when appropriate. 
 
- 
        
- GNU Assembler(GAS) 完整的指令列表可以在这查看Assembler Directives
- 有了这些指令, 那这段汇报代码就比较好理解了.
    - 在当前的.data段, 开一新的页(PGSHIFT = 12) 也就是在2^12 = 4k处对齐.
- 1 - bootstack 
- 栈顶(1 bootstacktop 1 bootstack + KSTKSIZE 1 .space 
- 1 - KSTKSIZE - 1 - 8 * PGSIZE = 32kb 
- 栈顶的地址是1 0xf0110000 
 
- 验证, 将1 .space KSTKSIZE 1 .space KSTKSIZE 0x1 1 kern:entry.S 
(gdb) p/x bootstacktop
$4 = 0x111021
(gdb) x/10x (0x110000-4096*8-3)
    0x107ffd:   0x01000000  0x01010101  0x01010101  0x01010101
    0x10800d:   0x01010101  0x01010101  0x01010101  0x01010101
    0x10801d:   0x01010101  0x01010101- 具体有几字节对不上, 可能是因为编译器增加了一些guard之类的举措. 但是结果基本是符合我们结论的.
###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.
实现1
kern/monitor.c:mon_backtrace
Stack backtrace:
ebp f0109e58  eip f0100a62  args 00000001 f0109e80 f0109e98 f0100ed2 00000031
ebp f0109ed8  eip f01000d6  args 00000000 00000000 f0100058 f0109f28 00000061栈调用的结构应该都非常熟悉了. 并且在1
inc/x86.h
1
read_ebp()
获得寄存器1
ebp
- 当前frame的返回指位于地址1 ebp+4 1 eip 
- 当前函数scope内, 第n个参数的地址为1 ebp+4(n+1) 
- 内存1 ebp 1 调用者(caller)的ebp 
具体编码需要注意的还是c的老问题
uint32_t ui = 0x00000000;
uint32_t *uip = 0x00000000;
ui += 1;  //ui = 0x00000001
uip += 1; //uip = 0x00000004这个清楚之后, 看代码1
kern/monitor.c
int
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
	// Your code here.
	volatile uint32_t ebp;
	volatile uint32_t *p;
	struct Eipdebuginfo info;
	ebp = read_ebp();
	while (ebp > KSTACKTOP) {
		p = (uint32_t *)ebp;
		cprintf("ebp %08x eip %08x args %08x %08x %08x %08x\n",
                p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6));
		ebp = *p;
	}
	return 0;
}