« rx-elf-gccでprintf等のライブラリを使う | トップページ | MITOUJTAGにICEを統合します! »

2011.05.14

rx-elf-gccの問題点

rx-elf-gccを使用していて、困った現象に遭遇しました。

一言で言うと、RX用のGCCにはバグ(仕様?)があって、プログラムをそのままではROM化できないということです。

これは、自前でビルドしたGCCでも、ルネサス推奨のKPITのGCCでも同じです。
具体的にはどういうことかというと、初期値を持ったグローバル変数の初期値が正しくセットされないという現象が起きています。

例えば、初期値を持ったグローバル変数を作ります。


char message[] = "hogehoge";
int g_state = 1;

すると、これらの変数は.dataというセクションに配置されます。

.dataというセクションはなかなか扱いが難しいセクションで、スタートアッププログラムでROM上からRAM上へと転送してやらねばなりません。具体的には下記のようなコードで行います。


mov #__datastart, r1
mov #__romdatastart, r2
mov #__romdatacopysize, r3
smovf

したがって、プログラムをROM化する場合、.dataセクションに配置されたシンボルは、実行時の仮想アドレス(VMA)と、ROM上の配置されるアドレス(LMA)は異なるのが普通です。

さて、GCCでは、このようなことを実現するためにはリンカスクリプトで、


.data 0x00000000 : AT (0xffff0000) {
. = ALIGN(4);
PROVIDE(__romdatastart = 0xffff0000);
PROVIDE (__datastart = .); /* IF_ROROM */
PROVIDE (__preinit_array_start = .);
 ・・・

と書くことによって、実行時に参照されるアドレスは0x00000000、ROM上では0xffff0000番地と、分けることができます。実際には前のセクションの開始アドレスと長さから計算するように書くのですが、ここではわかりやすいように直接値を書いています。

RX用のあるプログラムをコンパイルしたところ、GNUリンカが吐き出すMAPファイルでは、


名前 Origin 長さ 属性
.data 0x00000000 0x898 load address 0xffff0000

となっていたので、仮想アドレス0x0000000はで、0xffff0000番地に配置されたことがわかります。

しかし、rx-elf-objdump -f ファイル名とやってヘッダ情報を見てみると、


セクション:
索引名 サイズ VMA LMA File off Algn
0 .fvectors 00000030 ffffffd0 ffffffd0 00009628 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .rvectors 00000400 fff80000 fff80000 00000094 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .text 00007ed0 fff80400 fff80400 00000494 2**0
CONTENTS, ALLOC, LOAD, READONLY, CODE
3 .rodata 00000a2c fff882d0 fff882d0 00008364 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .data 00000898 00000000 00000000 00008d90 2**2
CONTENTS, ALLOC, LOAD, DATA
5 .bss 0000026c 00000898 00000898 00009628 2**2
ALLOC

となっていて、VMA=LMAになってしまっています。このため、rx-elf-objcopyを使ってELFファイルをMOTファイルに変換したときに、.dataの中身がFFFF0000番地に配置されず、00000000番地に配置されてしまいます。

実際にMOTファイルでは、


S3250000000000000000686F6765686F67650000000001000000040B00001C0000000000000068
S325000000200803000070030000D8030000000000000000000000000000000000000000000061
S32500000040000000000000000000000000000000001484F8FF0000000000000000000000000B
S3250000006000000000000000000000000000000000000000000000000000000000000000007A
S3250000008000000000000000000000000000000000000000000000000000000000000000005A
S325000000A000000000000000000000000000000000000000000000000000000000000000003A
S325000000C001000000000000000E33CDAB34126DE6ECDE05000B0000000000000000000000ED

見事に0番地に配置されました。

こういう理由のため、.dataセクション、つまりグローバル変数の初期値が正しいアドレスに配置されないのでROM化したプログラムは正常に動作しないのです。

では、どうしたらいいかというと、MOTファイルを生成する段階、つまりrx-elf-objcopyにオプションをつけてLMAを手動で指定してやります。

具体的には次のようにします。
rx-elf-objcopy --srec-forceS3 --srec-len 32 -O srec --change-section-lma .data=0xffff0000 testapp.elf testapp.mot

このようにしてLMAのアドレスを手動で指定してやれば、うまくROM化できます。
ただし、全自動ではできません。全自動化する方法は只今検討中です。



■追記
SHの場合はどうかというと、リンカスクリプトを

.data 0x0c400000 : AT( 0x0000c520))
{

としておいて、あるプログラムをコンパイル&リンクしたところ、GNUリンカが吐き出すMAPファイルでは、


名前 Origin 長さ 属性
.data 0x000000000c400000 0xec load address 0x000000000000c520

となっていました。objdump -hで見てみると、

セクション:
索引名 サイズ VMA LMA File off Algn
・・
4 .data 000000ec 0c400000 0000c520 0000c5c0 2**2
CONTENTS, ALLOC, LOAD, DATA

となっていて、VMAとLMAが適切に設定されています。
つまり、RXのリンカだけ、なぜかLMA=VMAにされてしまうようです。

|

« rx-elf-gccでprintf等のライブラリを使う | トップページ | MITOUJTAGにICEを統合します! »

コメント

binutils 2.21のbfd/elf32-rx.c 3335行目以降に「この挙動は仕様である」旨が書かれています。

# リンカのスイッチで挙動が替えられるとよいのですが。

投稿: kikairoya | 2011.05.14 16:58

kikairoya 様

貴重な情報ありがとうございます。
elf32-rx.cのこのあたりのルーチンをコメントアウトしてみたくなりました。後で試してみます。

投稿: なひたふ | 2011.05.14 19:46

elf32-rx.cのelf32_rx_modify_program_headersのルーチンの中の
 phdr[i].p_vaddr = phdr[i].p_paddr;
をコメントアウトしてビルドしたら、うまくMOTファイルが作成されるようなELFを生成するリンカが作れました。やはりここが原因でした。

・・と思っていたら、kikairoyaさんがパッチを作成されて、bugzillaに投稿していただいたのですね。ありがとうございます。
オプション指定可能なパッチを作っていただいて、的確な英文で問題を指摘していただいて、感激です。

投稿: なひたふ | 2011.05.14 21:08

パッチが採用され、mainlineにマージされました。 2.22から使えるようになります。

投稿: kikairoya | 2011.05.18 07:43

すばらしい!
GCCの本流が進化していく過程を目の当たりにしました。
貴重な経験でした。

投稿: なひたふ | 2011.05.18 08:16

コメントを書く



(ウェブ上には掲載しません)




« rx-elf-gccでprintf等のライブラリを使う | トップページ | MITOUJTAGにICEを統合します! »