最近、リンカスクリプトをいろいろいじっていたのだが、
その中で少し困ったことがあって、それはオール0の初期値あり変数セクションが生成されてしまうことである。
一般的に使われるセクション名としては下記のものがある。
- .text : マシンコード
- .rodata : 定数
- .data : 初期化データのある変数
- .bss : 初期値0の変数
.text と .rodata は不変なのでマイコンではROMに割り付けることができて、
.data と .bss は変数なのでRAMに割り付ける必要があり、
その中で.dataは起動時に初期化データをRAMに転送しなければならない。
おおざっぱにはこういう理解でよい。
ただ、いろいろな都合でこれ以外のセクション名を指定することもある。
今回問題となったのはスタック領域について .stack というセクション名を付けたものだった。
T_STK __stack[STACK_SIZE] __attribute__((section(".stack"),nocommon));
こんなコードが自動生成されていた。
これにより .stackセクションに__stack[]という配列が確保されるわけだが、
なぜか初期値0の変数ではなく、オール0の初期化データのある変数という扱いになってしまう。
よくわからないのでいろいろ実験してみたのだが、
attributeで指定しているセクション名を .bss.stack としたら、初期値0の変数として扱われることがわかった。
自分で指定するセクション名が.bssから始まると、.bss同様の扱いになるが、
それ以外の名前を指定すると変数の場合は .dataセクション同様の扱いになってしまうと。
どうもそういうことらしい。
コンパイラ(gcc)で生成されたオプジェクトファイルの解析結果からすると、
上記の差異はgccで発生しているように見えるのだが、対策はリンカスクリプトで行う。
.stack (NOLOAD) :{
*(.stack*)
} > RAM_WORK
.stackセクションをRAMに割り付けるときにNOLOADと指定すると、
初期化データを転送する処理がいらないという意味になるらしい。
特別な事情がなければ冒頭に書いたセクション名を使うとよくて、
エントリポイントのセクション名として .text.startup を指定して、
.text.startupセクションをROM先頭に割り付けるという使い方があった。
こういうやり方にした方がいろいろな問題を回避しやすいように思える。
.bss.stackセクションならば今回の問題は発生しなかったわけだしね。
とはいえ、最終的にはリンカスクリプトでなんとでもという話のようだ。
最初はコンパイラ側でどうにかするのかと思ったのだが、
リンカ側で対応するべきことであると気づくまでに時間を要してしまった。
サンプルコードに付属していたリンカスクリプトはこのあたり対策されてなくて、
オール0の初期化データが大量発生していたのだが、サンプル書いた人はおかしいと思わなかったのだろうか?
動くという点では当然問題ないのだけど……