"12ステップで作る組み込みOS自作入門" で勝手にはまったことをメモ


はじめに

表題の本[1]はとても親切に書かれていて,順に追っていけば問題なく進めていけるのですが,タイプミスなど自分の不注意でエラーになることがあります.そんなときの対応も含めて勉強になるので,恥を忍んでメモをしていきます.
内容は,随時追加してきます.

なお,私が自爆した内容であり,書籍の記載のせいではないことをここで強調しておきます.

6th step もう一度,Hello World!

6-1 make エラー

症状

> cd os
> make
/usr/local/bin/h8300-elf-gcc startup.o main.o lib.o serial.o -o kozos -Wall -mh -nostdinc -nostdlib -fno-builtin -I. -Os -DKOZOS -static -T ld.scr -L.
/usr/local/lib/gcc/h8300-elf/8.2.0/../../../../h8300-elf/bin/ld:ld.scr:33: warning: memory region `data' not declared
/usr/local/lib/gcc/h8300-elf/8.2.0/../../../../h8300-elf/bin/ld: kozos section `.text.startup' will not fit in region `ram'
/usr/local/lib/gcc/h8300-elf/8.2.0/../../../../h8300-elf/bin/ld: region `ram' overflowed by 122 bytes
collect2: error: ld returned 1 exit status
Makefile:42: ターゲット 'kozos' のレシピで失敗しました
make: *** [kozos] エラー 1

原因と対策

"region `ram' overflowed" と言われている通りで,リンカスクリプトのRAM領域のサイズを間違えて小さくしすぎていたため,収まらないとエラーを出してくれていました.

os/ld.scr
MEMORY
{
    ram(rwx)    : o = 0xfffc20, l = 0x000300 /* ここは,0x003f000 が正しい */
    stack(rw)   : o = 0xffff00, l = 0x000000 /* end of RAM */
}

6-2 エントリーポイントのアドレスが 0x0 になってしまう

症状

> readelf -a kozos.elf
ELF ヘッダ:
  マジック:   7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00
  クラス:                            ELF32
  データ:                            2 の補数、ビッグエンディアン
  バージョン:                        1 (current)
  OS/ABI:                            UNIX - System V
  ABI バージョン:                    0
  型:                                EXEC (実行可能ファイル)
  マシン:                            Renesas H8/300
  バージョン:                        0x1
  エントリポイントアドレス:               0x0
  プログラムの開始ヘッダ:          52 (バイト)
  セクションヘッダ始点:          2932 (バイト)
  フラグ:                            0x810000
  このヘッダのサイズ:                52 (バイト)
  プログラムヘッダサイズ:            32 (バイト)
  プログラムヘッダ数:                4
  セクションヘッダ:                  40 (バイト)
  セクションヘッダサイズ:            9
  セクションヘッダ文字列表索引:      8

原因と対策

  • .SECTIONS 内の ROM 領域の定義は削除していた.(これは正しい)
  • かつ,リンカスクリプトの .text セクションの配置先指定が, > ROM のままになっていた

現象としては,リンカが,未定義の領域を 0x0 として処理していたようです.


6.3 OSが実行できない

OS の ELF をロードして run すると,エントリーポイントの表示が正しくなく,そのまま固まります.readelf -a kozos で表示されるエントリーポイントは正しいことを確認しました (0x00ff0c20).

kzload> load

XMODEM receive succeeded.
kzload> run
starting from entry point: 20kzload (kozos boot loader) started.
kzload>

原因と対策

エントリーポイントが 20 と表示されるのは, 0x00ff0c20 のLSB側だけが切り取られて表示されているように見えます.elf_read() の返値がおかしいのではないかと予想し,ソースを確認したところ,返値の型に間違いが見つかりました.

bootloader/elf.c
char elf_load(char *buf)  /* 正しくは char *elf_load(char *buf) */

ポインタを返すべきところが char になっていたので,MSB側が切り捨てられていたようです.ヘッダも同様に誤りがあったので修正したところ,うまく動きました.

7th step 割込み処理を実装する

7.1 kozos を load すると bootloader に戻ってしまう

症状

kzload> load

XMODEM receive succeeded.
kzload> run
starting from entry point: ffc020
kozos boot succeeded!
> kzload (kozos boot loader) started.
kzload> a

原因と対策

割込みハンドラの入り口で,_interrupt に渡すスタックポインタを間違えて,@ が余計についていました.さらに,割込みベクタを設定する vector.c の NULL の数が8個少なかったために,SCIの割込みベクタがずれていました.おそらく 0000 に飛ばされて,bootloader に戻っていたのだと思います.

intr.S
  .global _intr_serintr
  .type _intr_serintr,@function
_intr_serintr:
  mov.l er6,@-er7
  mov.l er5,@-er7
  mov.l er4,@-er7
  mov.l er3,@-er7
  mov.l er2,@-er7
  mov.l er1,@-er7
  mov.l er0,@-er7
  mov.l er7,@er1  ; 正しくは mov.l er7,er1
  mov.w #SOFTVEC_TYPE_SOFTERR,r0
  jsr @_interrupt
  mov.l @er7+,er0
  mov.l @er7+,er1
  mov.l @er7+,er2
  mov.l @er7+,er3
  mov.l @er7+,er4
  mov.l @er7+,er5
  mov.l @er7+,er6
  rte

参考文献

[1] 坂井弘亮, 「12ステップで作る組み込みOS自作入門」, カットシステム