6.828: Operating System Engineering (17)
Exercise 3
cprintf 関数の中の
va_start(ap, fmt);
した時点で
- fmt は "x %d, y %x, z %d\n" な文字列の先頭アドレスを指していて
- ap は二番目の引数なスタックの番地を指してます
次。cons_putc, va_arg, and vcprintf の順で云々という記述があるな。とりあえず stdarg.h なマクロはおおよそ理解できたのでスルー。
Exercise 4
はニガ手な endian に関する問題らしい
unsigned int i = 0x00646c72; printf("H%x Wo%s", 57616, &i);
別でプログラム作って実行してみたら以下。
$ ./test He110 World$
0xe110 って確かに 57616 ですね。i も gdb で確認。
(gdb) b main Breakpoint 1 at 0x4004fc: file main.c, line 5. (gdb) r Starting program: /home/rms/Documents/debian/boutC/3.printf/test Breakpoint 1, main () at main.c:5 5 unsigned int i = 0x00646c72; (gdb) n 6 printf("H%x Wo%s", 57616, &i); (gdb) p &i $1 = (unsigned int *) 0x7fffffffdbcc (gdb) x/x 0x7fffffffdbcc 0x7fffffffdbcc: 0x00646c72 (gdb) x/c 0x7fffffffdbcc 0x7fffffffdbcc: 114 'r' (gdb) x/c 0x7fffffffdbcd 0x7fffffffdbcd: 108 'l' (gdb) x/c 0x7fffffffdbce 0x7fffffffdbce: 100 'd' (gdb) x/c 0x7fffffffdbcf 0x7fffffffdbcf: 0 '\000' (gdb)
メモリの中では
726c6400
って形で入ってる、って意味でしたっけorz
Exercise 5
ここは cprintf 呼び出し元のスタックフレームというかローカル変数または戻り番地を指す形になるのかどうか。
Exercise 6
引数格納する順が変わるのであれば、stdarg.h なマクロの記述が変わってきますね。可変長引数の直前の引数の位置が分からない、ということは結構キビシい気がしますがどうなのでしょうか。
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.
なんか frame buffer が云々って見えるんですがorz
これは置いといて先に進もう。
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?
このあたりのソレを確認してみれば良いのかな。
relocated: # 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
bootstacktop も entry.S で定義されてますね。
################################################################### # boot stack ################################################################### .p2align PGSHIFT # force page alignment .globl bootstack bootstack: .space KSTKSIZE .globl bootstacktop bootstacktop:
どっかで見たと思っていたのですがメモリマップなマンガが memlayout.h にあったので以下に引用しておきます。
/* * Virtual memory map: Permissions * kernel/user * * 4 Gig --------> +------------------------------+ * | | RW/-- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * : . : * : . : * : . : * |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| RW/-- * | | RW/-- * | Remapped Physical Memory | RW/-- * | | RW/-- * KERNBASE -----> +------------------------------+ 0xf0000000 * | Cur. Page Table (Kern. RW) | RW/-- PTSIZE * VPT,KSTACKTOP--> +------------------------------+ 0xefc00000 --+ * | Kernel Stack | RW/-- KSTKSIZE | * | - - - - - - - - - - - - - - -| PTSIZE * | Invalid Memory (*) | --/-- | * ULIM ------> +------------------------------+ 0xef800000 --+ * | Cur. Page Table (User R-) | R-/R- PTSIZE * UVPT ----> +------------------------------+ 0xef400000 * | RO PAGES | R-/R- PTSIZE * UPAGES ----> +------------------------------+ 0xef000000 * | RO ENVS | R-/R- PTSIZE * UTOP,UENVS ------> +------------------------------+ 0xeec00000 * UXSTACKTOP -/ | User Exception Stack | RW/RW PGSIZE * +------------------------------+ 0xeebff000 * | Empty Memory (*) | --/-- PGSIZE * USTACKTOP ---> +------------------------------+ 0xeebfe000 * | Normal User Stack | RW/RW PGSIZE * +------------------------------+ 0xeebfd000 * | | * | | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * . . * . . * . . * |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| * | Program Data & Heap | * UTEXT --------> +------------------------------+ 0x00800000 * PFTEMP -------> | Empty Memory (*) | PTSIZE * | | * UTEMP --------> +------------------------------+ 0x00400000 --+ * | Empty Memory (*) | | * | - - - - - - - - - - - - - - -| | * | User STAB Data (optional) | PTSIZE * USTABDATA ----> +------------------------------+ 0x00200000 | * | Empty Memory (*) | | * 0 ------------> +------------------------------+ --+ * * (*) Note: The kernel ensures that "Invalid Memory" (ULIM) is *never* * mapped. "Empty Memory" is normally unmapped, but user programs may * map pages there if desired. JOS user programs map pages temporarily * at UTEMP. */
上記にも記述がある、KSTACKTOP とか KSTKSIZE の定義が inc/memlayout.h で以下。
#define VPT (KERNBASE - PTSIZE) #define KSTACKTOP VPT #define KSTKSIZE (8*PGSIZE) // size of a kernel stack
KERNBASE は 0xF0000000 で
// All physical memory mapped at this address #define KERNBASE 0xF0000000
ええと PTSIZE は inc/mmu.h で以下な定義。
// Page directory and page table constants. #define NPDENTRIES 1024 // page directory entries per page directory #define NPTENTRIES 1024 // page table entries per page table #define PGSIZE 4096 // bytes mapped by a page #define PGSHIFT 12 // log2(PGSIZE) #define PTSIZE (PGSIZE*NPTENTRIES) // bytes mapped by a page directory entry #define PTSHIFT 22 // log2(PTSIZE)
4096 かける 1024 ということでよいのかな。と思ったらマンガに以下な記述あり。
* KERNBASE -----> +------------------------------+ 0xf0000000 * | Cur. Page Table (Kern. RW) | RW/-- PTSIZE * VPT,KSTACKTOP--> +------------------------------+ 0xefc00000 --+ * | Kernel Stack | RW/-- KSTKSIZE | * | - - - - - - - - - - - - - - -| PTSIZE * | Invalid Memory (*) | --/-- | * ULIM ------> +------------------------------+ 0xef800000 --+
うう、計算できんorz
とりあえず ebp は 0 で esp が 0xefc00000 なことが確認できれば良いのかな。
動作確認
なんとなく忘れている。
とりあえず、ある screen な端末で
$ make qemu-gdb *** *** Now run 'gdb'. *** /opt/bin/qemu -hda obj/kern/kernel.img -serial mon:stdio -S -gdb tcp::26000
して、違う screen な端末で
$ gdb
しておいて、bootloader 相手にしないのであれば kernel に jmp する直前で止めて si すりゃ良いのかな。
(gdb) b *0x7d84 Breakpoint 1 at 0x7d84 (gdb) c Continuing. The target architecture is assumed to be i386 => 0x7d84: call *%eax Breakpoint 1, 0x00007d84 in ?? () (gdb) si => 0x10000c: movw $0x1234,0x472 0x0010000c in ?? () (gdb)
これ、最初から *0x10000c に breakpoint 貼れば良いのかどうか。
とりあえず relocated ラベルまで進めます。
(gdb) si => 0x10000c: movw $0x1234,0x472 0x0010000c in ?? () (gdb) si => 0x100015: lgdtl 0x110018 0x00100015 in ?? () (gdb) si => 0x10001c: mov $0x10,%eax 0x0010001c in ?? () (gdb) si => 0x100021: mov %eax,%ds 0x00100021 in ?? () (gdb) si => 0x100023: add %al,(%eax) 0x00100023 in ?? () (gdb) si => 0x100025: add %al,(%eax) 0x00100025 in ?? () (gdb) si => 0x100027: add %al,(%eax) 0x00100027 in ?? () (gdb) si => 0xf010002e <relocated>: mov $0x0,%ebp relocated () at kern/entry.S:61 61 movl $0x0,%ebp # nuke frame pointer (gdb)
で、
(gdb) si => 0xf0100033 <relocated+5>: mov $0xf0110000,%esp relocated () at kern/entry.S:64 64 movl $(bootstacktop),%esp (gdb) si => 0xf0100038 <relocated+10>: call 0xf010013a <i386_init> 67 call i386_init (gdb)
ここで ebp および esp を確認してみれば良いのか。
(gdb) info register eax 0x10 16 ecx 0x0 0 edx 0x8d 141 ebx 0x10094 65684 esp 0xf0110000 0xf0110000 ebp 0x0 0x0 esi 0x10094 65684 edi 0x0 0 eip 0xf0100038 0xf0100038 <relocated+10> eflags 0x6 [ PF ] cs 0x8 8 ss 0x10 16 ds 0x10 16 es 0x10 16 fs 0x10 16 gs 0x10 16 (gdb)
あら? って思ったら、ここでも load と link の違いなソレなのかなぁ。
Exercise 10
なんか微妙な気がしますが構わず続けます。
To become familiar with the C calling conventions on the x86, find the address of the test_backtrace function in obj/kern/kernel.asm, set a breakpoint there, and examine what happens each time it gets called after the kernel starts. How many 32-bit words does each recursive nesting level of test_backtrace push on the stack, and what are those words?
Note that, for this exercise to work properly, you should be using the patched version of QEMU available on the tools page or on Athena. Otherwise, you'll have to manually translate all breakpoint and memory addresses to linear addresses.
これはまた何とゆーか面白そうなナニが仕込んであるのかどうなのか。って、init.c に以下が仕込んであるのを発見。
void i386_init(void) { extern char edata[], end[]; // Before doing anything else, complete the ELF loading process. // Clear the uninitialized global data (BSS) section of our program. // This ensures that all static/global variables start out zero. memset(edata, 0, end - edata); // Initialize the console. // Can't call cprintf until after we do this! cons_init(); cprintf("6828 decimal is %o octal!\n", 6828); // Test the stack backtrace function (lab 1 only) test_backtrace(5);
まぢですか。とりあえず最初から。というか breakpoint 貼る番地が分からん。
(gdb) b test_backtrace Breakpoint 2 at 0xf01000e7: file kern/init.c, line 14. (gdb)
できた?
(gdb) c Continuing. => 0xf01000e7 <test_backtrace+10>: mov %ebx,0x4(%esp) Breakpoint 2, test_backtrace (x=5) at kern/init.c:14 14 cprintf("entering test_backtrace %d\n", x); (gdb)
できたみたい。ええと、kern/init.c とのことなのか。手続き定義てきには以下?
// Test the stack backtrace function (lab 1 only) void test_backtrace(int x) { cprintf("entering test_backtrace %d\n", x); if (x > 0) test_backtrace(x-1); else mon_backtrace(0, 0, 0); cprintf("leaving test_backtrace %d\n", x); }
x は 5 らしい。stack の状態見てみれば良いの?
(gdb) info registers eax 0x0 0 ecx 0x3d5 981 edx 0x3d5 981 ebx 0x5 5 esp 0xf010ffc0 0xf010ffc0 ebp 0xf010ffd8 0xf010ffd8 esi 0x10094 65684 edi 0x0 0 eip 0xf01000e7 0xf01000e7 <test_backtrace+10> eflags 0x86 [ PF SF ] cs 0x8 8 ss 0x10 16 ds 0x10 16 es 0x10 16 fs 0x10 16 gs 0x10 16 (gdb)
む。以下に着目して確認。
esp 0xf010ffc0 0xf010ffc0 ebp 0xf010ffd8 0xf010ffd8
ええと stack のてっぺんは 0xf0110000 だったですね。とりあえずベースポインタについて確認してみました。
(gdb) x/x 0xf010ffd8 0xf010ffd8 <bootstack+32728>: 0xf010fff8 (gdb) x/x 0xf010fff8 0xf010fff8 <bootstack+32760>: 0x00000000 (gdb)
i386_init の stack frame な base pointer が 0xf020fff8 な模様。
kernel.asm の test_backtrace な記述が以下。
// Test the stack backtrace function (lab 1 only) void test_backtrace(int x) { f01000dd: 55 push %ebp f01000de: 89 e5 mov %esp,%ebp f01000e0: 53 push %ebx f01000e1: 83 ec 14 sub $0x14,%esp f01000e4: 8b 5d 08 mov 0x8(%ebp),%ebx cprintf("entering test_backtrace %d\n", x); f01000e7: 89 5c 24 04 mov %ebx,0x4(%esp) f01000eb: c7 04 24 32 1a 10 f0 movl $0xf0101a32,(%esp) f01000f2: e8 34 08 00 00 call f010092b <cprintf>
今停止しているのは f01000e7 番地。スタック操作なあたりは当たり前ですがスルーなんですね。上記コードによれば
- 現状 ebp には直前フレームの ebp の値が格納されている
- 直前フレームを push した時点の esp の値?
- esp は ebx を push して 0x14 を引いてるので値の差分としては正しい
- esp はスタックのてっぺん (というか底) なので加算の形でローカル変数にアクセスできる
ここで止める
今日、へろへろながらも好調気味に進めてたのですが、そろそろ限界。