6.828: Operating System Engineering (23)

とりあえず struct Command 型の配列にコマンドを追加。

static struct Command commands[] = {
	{ "help", "Display this list of commands", mon_help },
	{ "kerninfo", "Display information about the kernel", mon_kerninfo },
	{ "backtrace", "Display information about the kernel", mon_backtrace },
};

で、mon_backtrace 手続きを以下に修正。

int
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
    // Your code here.
    int i;
    unsigned int ebp_ptr = read_ebp();

    while(ebp_ptr) {
        cprintf("ebp %x  eip %x  args ", ebp_ptr, *((int *)ebp_ptr + 1));

        for(i = 0; i < 5; i++) {
            cprintf("%08x ", *((int *)ebp_ptr + i + 2));
        }

        cprintf("\n");

        struct Eipdebuginfo info;
        debuginfo_eip(*((int *)ebp_ptr + 1), &info);

        {
            int i, offset;

            offset = *((int *)ebp_ptr + 1) - info.eip_fn_addr;

            cprintf("       %s:%d: %.*s+%d\n", 
                    info.eip_file, 
                    info.eip_line, 
                    info.eip_fn_namelen, 
                    info.eip_fn_name, offset);
        }

        ebp_ptr = *(int *)ebp_ptr;
    }

    return 0;
}

これでもまだテスツは 30 点だったりします。

Printf: WRONG (1.5s)
Backtrace:
   Count OK (1.6s)
   Args OK (1.7s)
   Symbols OK (1.8s)
Score: 30/50

あら? Printf が WRONG ってなってるな。jos.out 見てみたら

6828 decimal is 015254 octal!

なんで %o で 6828 出力したらアタマに 0 が、って自分でヤッてるのかorz

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

コメントアウトして make grade

Printf: OK (1.3s)
Backtrace:
   Count OK (1.4s)
   Args OK (1.5s)
   Symbols OK (1.6s)
Score: 50/50

やれやれようやく満点もらえました。

LEC 2

下記な形で Data Segment に初期化されてないナニがある場合、filesz と memsz が異なるケイスがあるらしい。bss はファイルには持たないのですね。

$ objdump  -p obj/kern/kernel

obj/kern/kernel:     file format elf32-i386

Program Header:
    LOAD off    0x00001000 vaddr 0xf0100000 paddr 0xf0100000 align 2**12
         filesz 0x00007524 memsz 0x00007524 flags r-x
    LOAD off    0x00009000 vaddr 0xf0108000 paddr 0xf0108000 align 2**12
         filesz 0x00008320 memsz 0x00008980 flags rw-
   STACK off    0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2
         filesz 0x00000000 memsz 0x00000000 flags rwx

$

で、以下な記述があるんですが、

Notice that the second section, the data section, has a memsz larger than its filesz: the first 0x79e bytes are loaded from the kernel binary and the remaining 0x6046 bytes are zeroed.
Bootmain uses the addresses in the proghdr to direct the loading of the kernel.
It reads each section’s content starting from the disk location offset bytes after the start of the ELF header, and writes to memory starting at address va. Bootmain calls readseg to load data from disk (1237) and calls stosb to zero the remainder of the segment (1239). Stosb (0442) uses the x86 instruction rep stosb to initialize every byte of a block of memory.

んな事ヤッてないじゃん、って思ったら kickoff される側でヤッてました。

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);

edata とか end はリンカ・スクリプトにて定義されてますね。

	PROVIDE(edata = .);

	.bss : {
		*(.bss)
	}

	PROVIDE(end = .);

これはこれで無理矢理すぎる気がしますが、そーゆーものなのでしょうね。
あと、セクタ毎で読みこみで端数な部分ってどうなってるのだろ、と思ってたのですが、objdump -p な出力見たら align が 2048 とかなサイズになっててびっくり。

bootmain

boot/main.c のソレ、なかなかに面白いのですが、KOZOS なナニ含めで再度確認必要かな、と思っております。確認入れれたら追記するかもです。

追記

load している部分は bootmain 手続きの以下なのですが、

	// load each program segment (ignores ph flags)
	ph = (struct Proghdr *) ((uint8_t *) ELFHDR + ELFHDR->e_phoff);
	eph = ph + ELFHDR->e_phnum;
	for (; ph < eph; ph++)
		readseg(ph->p_va, ph->p_memsz, ph->p_offset);

KOZOS では PA 使えとあるがこっちは VA になってます。これは readseg の中にそれで OK な仕掛けがあるはず。
なんとなく

  • PA ではなくて VA で OK なあたり
  • 0xFFFFF と & してるとか
  • 最初に読み込む ELF ヘッダ
    • SECTSIZE*8 で読みこんでて、2^12 を計算してみたら 4096 だった件
    • これ、リンカスクリプトにそれ風の情報があるのかなぁ

な宿題がありますねorz