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 のあたりなどを確認しつつ、ソースを確認しつつ、もくもくします。