組み込みOS自作入門(3章)
- 3回目です。ちょっとずつ複雑になってきました。。。
LinuxなどのOS上で動くアプリを作る時はmain()の中にプログラムを書けば動作します。これはOSやOS付属の環境がmain動作まえにすべき初期化処理をこっそりと処置しているからです。
一方で、OSのない組み込み系のプログラムでは、main実行前の初期化処理を自分で作らないとプログラムは期待した動作になりません。
3章では初期値を持つ静的変数を正しく設定します。4章ではスタックポインタの解説をしてます。(設定自体はこっそりと1章のstartup.sでやってます。)
初期値を持つ静的変数はROMに配置すると書き換えができなくなります。RAMに配置すると、初期値を設定できなくなります。。さあ困った。。
というわけで、
3章ですること
- 初期値を持つ静的変数をROMとRAM両方に配置する。
プログラム動作時に読み書きする領域をRAMに配置する。(論理アドレス)
定数の実体としての初期値をROMも配置する。(物理アドレス) - main実行前の初期化処理で初期値をROM(物理アドレス)からRAM(論理アドレス)にコピーする。
上に書いたことを実現するために、リンカスクリプトを修正していきます。
【ld.scrのソース】
MEMORY
{
〜
}
SECTIONS
{
.data : {
_data_start = . ;
*(.data)
_edata = . ;
} > data AT> rom
_data_start_load = LOADADDR(.data);
〜
}
- MEMORYコマンド
大まかな分類分けとして『メモリ領域』を定義しておく。 - SECTIONSコマンド
ソースコンパイル後に出てくる各セクションの配置先を『メモリ領域』のどれにするか決める。 - 初期値ありの静的変数はコンパイル後に.dataセクションとして出力される。
.dataセクションの配置先は『> data』とすることで、実行プログラムからはdata領域(実体はRAM)に領域が確保されて、ここに読み書きされる。
更に、『 AT> rom』とすることで、初期値として使いたい定数データはrom領域(実体はROM)にも配置される。 - 初期値ありの静的変数を初期化するには
main実行前に、コピー元:_data_start_load、コピー先:_data_startとして、サイズ:.data分コピーすれば良い。 - ロケーションカウンタはSECTIONSコマンドの中で使える変数で、論理アドレスを指している。
- LOADADDR()は引数に指定したセクションの物理アドレスを返す。
余談だが、ADDR()は論理アドレスを返す。SIZEOF()はセクションのサイズを返す。
LOADADDR()を使って物理アドレスを取得するのは必須です(書籍の間違い)。これは公式サイトのサポートページでも書かれています。
このあたりが本書で一番ややこしいのかもしれませんね。。OS自作入門は少しずつ改造して内容を理解するタイプの書籍なので、同じ情報が小出し小出しになっています。体系的に理解しようとしたら、ノートを作っておくのがいいかもしれません。