GNU ldのマッピング

シモネタではありませんシタネタです.「奴らの下を行け」系列な話.
ldscriptには,MEMORYコマンドっていうものがあります.ファーム寄りの開発をしなければ,一生使うことはないかと思うのですが.
ROMやRAMを意識してオブジェクトを配置したいときに使います.

MEMORY {
        REGION_ROM1     : ORIGIN = 0x0000, LENGTH = 0x080
        REGION_ROM2     : ORIGIN = 0x0100, LENGTH = 0x800
        REGION_OTHER    : ORIGIN = 0x2000, LENGTH = 0x080
}

SECTIONS {
        .begin : {
                begin.o(.text);
        } > REGION_ROM1
        .tail : {
                tail.o(.text);
        } > REGION_ROM2

        .data : { } > REGION_OTHER
        .bss : { } > REGION_OTHER
}

例えばこんな感じ.この例では,ROM1とROM2が不連続な位置にあり,その間にはメモリが存在しません.
そこにアクセスしたらバスエラー例外が出るとします*1
さて,これをリンクしてみます.すると,こんな結果に….

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000054 0x00000000 0x00000000 0x00204 0x00204 R E 0x4

Section to Segment mapping:
  Segment Sections...
   00     .begin .tail

読めば判ると思いますが,ROM1とROM2の間を跨いでprogram headersが設定されてしまっています.
これを単純にロードすると,例で仮定したとおりならバスエラーです.無害な場合もあるでしょうし,例えば間にメモリマップドレジスタがあればクリティカルな状態になるかもしれません.
てなわけで,たぶんこれってバグだと思う.本家MLにメール出しておきました.

逃げ道を考える.

プログラムヘッダでなくセクションヘッダを使えばいいじゃんという逃げ道はあって,実際にobjcopyはS-recordを生成するときにセクションヘッダを使っている様子.ROM焼きなんかは大抵はS-recordを使うので,顕在化していない可能性が高い.もちろん,私がldの仕様を理解していない可能性もありますが.
判っているならセクションヘッダを使えばいいじゃんと必ずしも割り切れなくて,件の本の影響で増えているELFゴルファー系の方々の記述の中には,セクションヘッダの削除がサイズ抑制のノウハウとして鎮座*2していたりするわけで….いや,ゴルファーの皆様は知っていてやっていらっしゃるのだからそれはそれでよいのですが,上辺だけ真似る人には,この問題は理解できないでしょう.たぶん.

試してみたのは mips64-elf (GNU ld version 2.17.50 20060513).でもまあたぶん,本件にターゲット依存性はないでしょう.

困ったなぁ.

*1:あくまでも例で,実際はターゲットによりケースバイケースですが

*2: id:yupo5656:20061112