6.828: Operating System Engineering (19)

デバッグシンボルが盛り込まれているので、それを有効活用というか前の問題でダウト判定だった

	syms=`grep "kern/init.c:[0-9]*:  *test_backtrace[+]" jos.out`
	symcnt=`grep "kern/init.c:[0-9]*:  *test_backtrace[+]" jos.out | wc -l`

なナニを盛り込め、というもの。順に以下を確認せよ、とあるので見てみます。

  • look in the file kern/kernel.ld for __STAB_*
  • run i386-jos-elf-objdump -h obj/kern/kernel
  • run i386-jos-elf-objdump -G obj/kern/kernel
  • run i386-jos-elf-gcc -pipe -nostdinc -O2 -fno-builtin -I. -MD -Wall -Wno-format -DJOS_KERNEL -gstabs -c -S kern/init.c, and look at init.s.
  • see if the bootloader loads the symbol table in memory as part of loading the kernel binary

kernel.ld

以下の部分か。

	/* Include debugging information in kernel memory */
	.stab : {
		PROVIDE(__STAB_BEGIN__ = .);
		*(.stab);
		PROVIDE(__STAB_END__ = .);
		BYTE(0)		/* Force the linker to allocate space
				   for this section */
	}

	.stabstr : {
		PROVIDE(__STABSTR_BEGIN__ = .);
		*(.stabstr);
		PROVIDE(__STABSTR_END__ = .);
		BYTE(0)		/* Force the linker to allocate space
				   for this section */
	}

とりあえず上記のリンカスクリプトのソレを確認した方が良さげ。

  • PROVIDE シンボルの定義
  • BYTE(0) は値が 0 な 1byte なスペース確保するの?
    • force ってあるな
  • .stab はデバッグシンボルのテーブル
  • .stabstr はデバッグシンボルの文字列テーブル

開始と終了なアドレスが参照できるようになっているということで。

objdump -h

実行してみます。

$ objdump -h obj/kern/kernel

obj/kern/kernel:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00001a55  f0100000  f0100000  00001000  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .rodata       000006f4  f0101a60  f0101a60  00002a60  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .stab         00003979  f0102154  f0102154  00003154  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .stabstr      00001930  f0105acd  f0105acd  00006acd  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .data         00008320  f0108000  f0108000  00009000  2**12
                  CONTENTS, ALLOC, LOAD, DATA
  5 .bss          00000660  f0110320  f0110320  00011320  2**5
                  ALLOC
  6 .comment      0000002b  00000000  00000000  00011320  2**0
                  CONTENTS, READONLY
$

read only なソレと data セクションの間に配置されるんですね。.stab* も read only といえばそうですね。

objdump -G

こちらは情報が混在してるので

$ objdump -G obj/kern/kernel
obj/kern/kernel:     file format elf32-i386

Contents of .stab section:

Symnum n_type n_othr n_desc n_value  n_strx String

-1     HdrSym 0      1225   0000192f 1     
0      SO     0      0      f0100000 1      {standard input}
1      SOL    0      0      f010000c 18     kern/entry.S
2      SLINE  0      44     f010000c 0      
3      SLINE  0      47     f0100015 0      
4      SLINE  0      51     f010001c 0      
5      SLINE  0      52     f0100021 0      
6      SLINE  0      53     f0100023 0      
7      SLINE  0      54     f0100025 0      
8      SLINE  0      55     f0100027 0      
9      SLINE  0      61     f010002e 0      
10     SLINE  0      64     f0100033 0      
11     SLINE  0      67     f0100038 0      
12     SLINE  0      70     f010003d 0      
13     SO     0      2      f0100040 31     kern/init.c
14     OPT    0      0      00000000 43     gcc2_compiled.
15     LSYM   0      0      00000000 58     int:t(0,1)=r(0,1);-2147483648;2147483647;
16     LSYM   0      0      00000000 100    char:t(0,2)=r(0,2);0;127;
17     LSYM   0      0      00000000 126    long int:t(0,3)=r(0,3);-2147483648;2147483647;
18     LSYM   0      0      00000000 173    unsigned int:t(0,4)=r(0,4);0;4294967295;
19     LSYM   0      0      00000000 214    long unsigned int:t(0,5)=r(0,5);0;4294967295;
20     LSYM   0      0      00000000 260    long long int:t(0,6)=r(0,6);-0;4294967295;

切り分けて見てみます。

$ objdump -G obj/kern/kernel|grep ' SO '
0      SO     0      0      f0100000 1      {standard input}
13     SO     0      2      f0100040 31     kern/init.c
100    SO     0      0      f0100195 0      
101    SO     0      2      f01001a0 1320   kern/console.c
421    SO     0      0      f0100699 0      
422    SO     0      2      f01006a0 3663   kern/monitor.c
542    SO     0      0      f0100955 0      
543    SO     0      2      f0100958 4282   kern/printf.c
593    SO     0      0      f01009b8 0      
594    SO     0      2      f01009c0 4452   kern/kdebug.c
727    SO     0      0      f0100cc7 0      
728    SO     0      2      f0100cd0 4904   lib/printfmt.c
920    SO     0      0      f01012f9 0      
921    SO     0      2      f0101300 5630   lib/readline.c
974    SO     0      0      f01013d3 0      
975    SO     0      2      f01013e0 5761   lib/string.c
1224   SO     0      0      f01017ea 0      
$

ええと、kdebug.c の stab_binsearch 手続きに渡しているのは

  • N_SO
  • N_FUN

あと N_SOL とか N_PSYM も処理対象な模様。一応 grep したナニを以下に挙げておきます。

$ objdump -G obj/kern/kernel|grep SOL
1      SOL    0      0      f010000c 18     kern/entry.S
143    SOL    0      0      f01001a3 1335   ./inc/x86.h
145    SOL    0      0      f01001ac 1320   kern/console.c
161    SOL    0      0      f01001b1 1335   ./inc/x86.h
163    SOL    0      0      f01001b9 1320   kern/console.c
165    SOL    0      0      f01001c3 1335   ./inc/x86.h
167    SOL    0      0      f01001c9 1320   kern/console.c
225    SOL    0      0      f01002a5 1335   ./inc/x86.h

あるいは以下。

$ objdump -G obj/kern/kernel|grep PSYM
59     PSYM   0      0      00000008 1147   file:p(0,19)=*(0,2)
60     PSYM   0      0      0000000c 1167   line:p(0,1)
61     PSYM   0      0      00000010 1179   fmt:p(0,19)
70     PSYM   0      0      00000008 1231   file:p(0,19)
71     PSYM   0      0      0000000c 1167   line:p(0,1)
72     PSYM   0      0      00000010 1179   fmt:p(0,19)
83     PSYM   0      0      00000008 1267   x:p(0,1)
219    PSYM   0      0      00000008 3158   fdnum:p(0,1)
311    PSYM   0      0      00000008 3224   c:p(0,1)
460    PSYM   0      0      00000008 4009   argc:p(0,1)
461    PSYM   0      0      0000000c 4021   argv:p(0,22)=*(2,2)

うーん。

コンパイル

は未実施。

$ gcc -pipe -nostdinc -O2 -fno-builtin -I. -MD -Wall -Wno-format -DJOS_KERNEL -gstabs -c -S kern/init.c
$

ええと、init.s ができているので中身を見てみたのですが、.stab* な情報がやたらに出力されておりますな。よく考えたらデバッグシンボルなソレって完全に無関心だったぞ。

ええと、readelf -a してみました。出力の一部が以下。

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0xf010000c
  Start of program headers:          52 (bytes into file)
  Start of section headers:          70552 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         3
  Size of section headers:           40 (bytes)
  Number of section headers:         11
  Section header string table index: 8

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        f0100000 001000 001a55 00  AX  0   0 16
  [ 2] .rodata           PROGBITS        f0101a60 002a60 0006f4 00   A  0   0 32
  [ 3] .stab             PROGBITS        f0102154 003154 003979 0c   A  4   0  4
  [ 4] .stabstr          STRTAB          f0105acd 006acd 001930 00   A  0   0  1
  [ 5] .data             PROGBITS        f0108000 009000 008320 00  WA  0   0 4096
  [ 6] .bss              NOBITS          f0110320 011320 000660 00  WA  0   0 32
  [ 7] .comment          PROGBITS        00000000 011320 00002b 01  MS  0   0  1
  [ 8] .shstrtab         STRTAB          00000000 01134b 00004c 00      0   0  1
  [ 9] .symtab           SYMTAB          00000000 011550 000650 10     10  48  4
  [10] .strtab           STRTAB          00000000 011ba0 00035e 00      0   0  1

組込み OS 開発入門の 5 章とか再読の必要ありそげ。
で、実際に読みこんで云々してるのは boot/main.c の bootmain 手続きなのですが、ここでは

#define ELFHDR		((struct Elf *) 0x10000) // scratch space

0x10000 番地以降にセグメント単位で ELF なソレを展開してます。手続き定義の一部が以下。

#define SECTSIZE	512
#define ELFHDR		((struct Elf *) 0x10000) // scratch space

void readsect(void*, uint32_t);
void readseg(uint32_t, uint32_t, uint32_t);

void
bootmain(void)
{
	struct Proghdr *ph, *eph;

	// read 1st page off disk
	readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);

	// is this a valid ELF?
	if (ELFHDR->e_magic != ELF_MAGIC)
		goto bad;

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

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

で、

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

とのこと。kern/kdebug.c で debuginfo_eip と stab_binsearch という手続きが定義されているのですが、こっちの記述を把握というか理解するのが先ざんす。
そして The "stabs" representation of debugging information のあたりなどを確認しつつ、ソースを確認しつつ、もくもくします。