前置き
PCではなく、小さなマイコンの世界で使われます。
たいていはデフォルト設定のままでよいのですが、設定の意味を理解したい人や
特殊な設定をしたい人向けに。
本題
GNUリンカ(ld)の設定で、VMA(Virtual Memory Address)とLMA(Load Memory Address)があります。
名前からはイメージが湧きづらいですが、大雑把に言えば、RAMに変数を配置するために必要になるものです。
SECTIONS
{
.data 0x1000: AT(0x2000)
{
ここで0x1000がVMAで、0x2000がLMAです。
0x1000がRAMで0x2000がROM、と考えてもよいです。
どこがROMでどこがRAMか(メモリマップ)はマイコンによって決まります。
例えば、ルネサスのRL78/G13というマイコンなら、ROM 0x00000,RAM 0xFF700です。
Virtualと言ってもいわゆる仮想アドレスではなく、むしろ再配置(relocation)あるいは参照アドレスと言った方がよいと思います。
(MMUを積んでいないマイコンも多いです)
再配置というのは、プログラムのリンク時に具体的なアドレスを割り当てることで
int var = 1;
printf("var = %d\n", var);
この場合、printfは0x1000~の領域を参照します。
これに対して、LMAはデータを書き込む(焼く)アドレスを指定します。プログラムを実行する側にとっては、データが最初そのアドレスにあるという状態を意味します。
仮にこの部分だけを実行するとしたら、実行結果は
var = 0
になります。
(printfが存在すると仮定して)
varの初期値1は0x2000~の領域に存在するのであって、0x1000~にはないからです。
なので、実行時(main関数の前)にLMAからVMAにコピーするコードが必要なのですが
たいていはIDEが自動生成してくれます。
RL78のgccでは以下のようなコードが生成されます。この部分をまるっとコメントアウトすると、仮に~と書いた実行結果になります。
/* load data section from ROM to RAM */
;; block move to initialize .data
mov es, #0
movw de, #_mdata /* src ROM address of data section in de */
movw hl, #_data /* dest start RAM address of data section in hl */
最初からRAMにロードすればいいじゃないかと思うかもしれませんが、組み込みではそうもいかないのです。
ロードするのはプログラム実行時ではなく、ROMに焼くときだからです。ROMに焼いた時点でELF情報は失われます。
また、RAMに再配置せずにROMのままにするのもよくないです。
SECTIONS
{
.data 0x2000: AT(0x2000) /* ROM */
{
この場合、
var = 2;
printf("var = %d\n", var);
実行結果
var = 1
ROMなので。
アクセス違反にはなりませんが(マイコンによる)、値は変わってくれません。
補足
これは初期値ありの変数に限った話で、初期値なしの変数(bss)の場合はROMに焼く必要はないので、VMA = LMA = RAMで構いません。