この問題は1年半くらい前に引っかかったはずなのに、また今回ハマった!
「GCCでCortex-M3用のコードをはき出させるとHardHalutになる件」
結論から言うと、binutilsのリンカがthumb命令のBLインストラクションをBLXに変換してしまうのが原因。Cortex-M3はthumb2命令に対応してるとはいえ、オフセット値を引数に取るBLX命令はサポートしていないんだよなぁ。GCCやLDで-mcpu=cortex-m3 -mthumbを指定しているにもかかわらず、この変換をしてしまう場合がある。で、(再び)いろいろ調べてみた・・・
時間をかけて調べるといろいろと勉強になる。特に今回はリンカは結構いろんなことをやっていることを実感した。
関数呼び出しなど分岐が発生する部分でGCCがはき出すコードはBL (-1)という命令だけ。その-1のところはLDが各関数のロケーションが決定した段階で適宜埋める、という仕掛け。これは、普通に想像できる範囲でした。
ARMの場合ややこしいのは、ARMモードとTHUMBモードの切り替えが存在するということ。LDはそのモード切替が必要と判断したら、GCCがはき出したBL (-1)の部分をスタブへのブランチ命令に書き換えて、そのブランチ先のスタブでARM<-->THUMBモードの切り替えと、目的の関数へのブランチを行うようになっている。更にBL命令ではPC相対で前後2^24ビットまでの範囲でしかジャンプできないから、もしその範囲を超えるBL命令であった場合もいったんスタブへブランチさせて、その中でBL Rn命令を使って任意のアドレスにジャンプできるようなコードをLD自体が生成する。ELFファイルに含まれるアドレスのアトリビュートはもっとたくさん種類があるのだけれど、おおざっぱに言って、そういうことをやっているようだった。
さて、今回のポイントはCortex-M3でリンクするときにBL命令を勝手にBLX命令に変更させないようにすることだ。調べてみると、binutilsのbfd/elf32-arm.cにusing_thumb_only()という便利そうな関数が用意されている。この関数がTRUEを返すときはCortex-M3用だと(勝手に)思い込ませて、BL->BLX変換やTHUMB->ARMモードへの移行を起こさせないようにパッチを当てる。ついでに、THUMB<-->ARMへ移行するようなスタブの生成も抑制する。
という訳で作ったパッチがこれ。ちょっと強引かな~。Cortex-M3専用ということでご勘弁を。
--- binutils-2.21.1/bfd/elf32-arm.c 2011-05-11 16:29:12.000000000 +0900
+++ binutils-2.21.1patch/bfd/elf32-arm.c 2011-11-16 17:58:46.718879900 +0900
@@ -3138,7 +3138,12 @@
|| (r_type == R_ARM_THM_JUMP24))
&& !use_plt))
{
- if (st_type == STT_ARM_TFUNC)
+ if (thumb_only) {
+ stub_type =
+ (THM_MAX_BWD_BRANCH_OFFSET <= branch_offset && branch_offset <= THM_MAX_FWD_BRANCH_OFFSET) ?
+ arm_stub_none : arm_stub_long_branch_thumb_only;
+ }
+ else if (st_type == STT_ARM_TFUNC)
{
/* Thumb to thumb. */
if (!thumb_only)
@@ -7556,7 +7561,8 @@
the PLT do not require stubs. */
if (sym_flags != STT_ARM_TFUNC && sym_flags != STT_SECTION
&& (h == NULL || splt == NULL
- || h->plt.offset == (bfd_vma) -1))
+ || h->plt.offset == (bfd_vma) -1)
+ && !using_thumb_only(globals))
{
if (globals->use_blx && r_type == R_ARM_THM_CALL)
{
0 件のコメント:
コメントを投稿