Upgrade to Pro — share decks privately, control downloads, hide ads and more …

マイコン向けのただのリンカを自作してみた話

Avatar for simotin13 simotin13
August 13, 2024
29

 マイコン向けのただのリンカを自作してみた話

カーネル/VM探検隊@関西 9回目で発表させて頂いたときの資料です。
ルネサスのRXマイコン向けのリンカを書いてみたというネタです。

Avatar for simotin13

simotin13

August 13, 2024
Tweet

Transcript

  1. spec 諸々の仕様について アーキテクチャ RX(32bit CISC) バイナリフォーマット ELF(32bit 再配置可能オブジェクトファイル) ターゲットボード GR-SAKURA

    デバッガ・エミュレータ E1 入力 .clnkファイル (コマンドラインオプションとリンカスクリプトを混ぜたようなテキスト ファイル) 出力 ELF(32bit 実行形式)
  2. What is linker? ~リンカ何するものぞ?~ ELF header .text .bss .shstrtab Section

    Headers hoge.o foo.o ELF Header .text .bss .shstrtab Section Headers Program Header ELF Header Section Headers 注:セクション名はCPUやコンパイラによって異なります
  3. Incremental development ~簡単なものから作る~ ELF header .text .bss .shstrtab Section Headers

    hoge.o Program Header ELF Header Section Headers まずは1ファイルを「リンク」してみる。 リンク=プログラムヘッダの追加とELFヘッダの更新。 プログラムヘッダが入る分、諸々のオフセット位置の値が変わってくる。
  4. Incremental development ~簡単なものから作る~ #include "iodefine.h" #pragma section ResetPRG #pragma entry

    PowerON_Reset_PC void PowerON_Reset_PC(void) { int i = 0; unsigned char reg; PORTA.PDR.BIT.B0 = 1; while(1){ if (i % 1000 == 0) { reg = PORTA.PODR.BIT.B0; PORTA.PODR.BIT.B0 = ~reg; i = 0; } i++; } } #pragma section C FIXEDVECT const void (*const func)(void)= PowerON_Reset_PC; ソースファイル resetprg.c 例外ベクタテーブルの登録 セクション設定(CS+) ループとカウントによるLチカ! セクションの割り当ては開発環境(CS+)で 行う。 ここで設定した値が.clnkファイルによりリ ンカに渡される。
  5. Incremental development ~簡単なものから作る~ #include "iodefine.h" #pragma section ResetPRG #pragma entry

    PowerON_Reset_PC void PowerON_Reset_PC(void) { int i = 0; unsigned char reg; PORTA.PDR.BIT.B0 = 1; while(1){ if (i % 1000 == 0) { reg = PORTA.PODR.BIT.B0; PORTA.PODR.BIT.B0 = ~reg; i = 0; } i++; } } #pragma section C FIXEDVECT const void (*const func)(void)= PowerON_Reset_PC; ソースファイル resetprg.c 例外ベクタテーブルの登録 セクション設定(CS+) ループとカウントによるLチカ! セクションの割り当ては開発環境(CS+)で 行う。 ここで設定した値が.clnkファイルによりリ ンカに渡される。 ・スタックポインタ初期化していない →ローカル変数使えない・関数呼べない int i も unsigned char regも汎用レジスタへの割り当てを期待
  6. Incremental development ~簡単なものから作る~ $cat sakura2.clnk -input=DefaultBuild¥resetprg.obj -noprelink -nodebug -output=DefaultBuild¥sakura2.abs -nooptimize

    -start=PResetPRG/0FFF00000,FIXEDVECT/FFFFFFFC -nologo-exit 入力となる .clnkファイル リンクするオブジェクトファイル 出力するファイル名 セクション設定
  7. Incremental development ~簡単なものから作る~ $ readelf resetprg.obj –S There are 9

    section headers, starting at offset 0x377: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] $iop LOUSER+0 00000000 000034 0001ad 00 0 0 1 [ 2] .shstrtab STRTAB 00000000 0001e1 000052 00 0 0 1 [ 3] .symtab SYMTAB 00000000 000233 000060 10 4 4 4 [ 4] .strtab STRTAB 00000000 000293 00008a 00 0 0 1 [ 5] PResetPRG PROGBITS 00000000 00031d 000032 00 AX 0 0 1 [ 6] FIXEDVECT PROGBITS 00000000 000367 000004 00 A 0 0 4 [ 7] .relaPResetPRG RELA 00000000 00034f 000018 0c 3 5 4 [ 8] .relaFIXEDVECT RELA 00000000 00036b 00000c 0c 3 6 4 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), p (processor specific) オブジェクトファイルのセクション情報を眺めてみる
  8. Incremental development ~簡単なものから作る~ $ readelf resetprg.obj -S There are 9

    section headers, starting at offset 0x377: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] $iop LOUSER+0 00000000 000034 0001ad 00 0 0 1 [ 2] .shstrtab STRTAB 00000000 0001e1 000052 00 0 0 1 [ 3] .symtab SYMTAB 00000000 000233 000060 10 4 4 4 [ 4] .strtab STRTAB 00000000 000293 00008a 00 0 0 1 [ 5] PResetPRG PROGBITS 00000000 00031d 000032 00 AX 0 0 1 [ 6] FIXEDVECT PROGBITS 00000000 000367 000004 00 A 0 0 4 [ 7] .relaPResetPRG RELA 00000000 00034f 000018 0c 3 5 4 [ 8] .relaFIXEDVECT RELA 00000000 00036b 00000c 0c 3 6 4 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), p (processor specific) オブジェクトファイルのセクション情報を眺めてみる えっ!?リロケーション発生してるし! なんで!?
  9. Incremental development ~簡単なものから作る~ $ readelf resetprg.obj -r Relocation section '.relaPResetPRG'

    at offset 0x34f contains 2 entries: Offset Info Type Sym.Value Sym. Name + Addend 00000019 0000020b R_RX_DIR8S_PCREL 00000000 PResetPRG + 2e 00000031 0000020b R_RX_DIR8S_PCREL 00000000 PResetPRG + a Relocation section '.relaFIXEDVECT' at offset 0x36b contains 1 entries: Offset Info Type Sym.Value Sym. Name + Addend 00000000 00000401 R_RX_DIR32 00000000 _PowerON_Reset_PC + 0 オブジェクトファイルのリロケーション情報を眺めてみる
  10. Incremental development ~簡単なものから作る~ $ readelf resetprg.obj -r Relocation section '.relaPResetPRG'

    at offset 0x34f contains 2 entries: Offset Info Type Sym.Value Sym. Name + Addend 00000019 0000020b R_RX_DIR8S_PCREL 00000000 PResetPRG + 2e 00000031 0000020b R_RX_DIR8S_PCREL 00000000 PResetPRG + a Relocation section '.relaFIXEDVECT' at offset 0x36b contains 1 entries: Offset Info Type Sym.Value Sym. Name + Addend 00000000 00000401 R_RX_DIR32 00000000 _PowerON_Reset_PC + 0 オブジェクトファイルのリロケーション情報を眺めてみる ベクタテーブルに配置したパワーオンリ セット用関数のアドレスを探してる・・・ まぁそりゃそうか。 @.relaFIXEDVECT どこにPC飛ばしていいかわからんか ら教えてやで。
  11. Incremental development ~簡単なものから作る~ $ readelf resetprg.obj –r Relocation section '.relaPResetPRG'

    at offset 0x34f contains 2 entries: Offset Info Type Sym.Value Sym. Name + Addend 00000019 0000020b R_RX_DIR8S_PCREL 00000000 PResetPRG + 2e 00000031 0000020b R_RX_DIR8S_PCREL 00000000 PResetPRG + a Relocation section '.relaFIXEDVECT' at offset 0x36b contains 1 entries: Offset Info Type Sym.Value Sym. Name + Addend 00000000 00000401 R_RX_DIR32 00000000 _PowerON_Reset_PC + 0 オブジェクトファイルのリロケーション情報を眺めてみる こいつら何言ってるのかわからん・・ 逆アセみるか… @.relaPResetPRG PResetPRGが#(0aっfa!~!>7で19と31 がR_RX”a<*%&~!)やで。
  12. Incremental development ~簡単なものから作る~ $ rx-elf-objdump.exe resetprg.obj -S 00000000 <_PowerON_Reset_PC>: 0:

    fb ee 0a c0 08 mov.l #0x8c00a, r14 5: f0 e0 bset #0, [r14].b 7: 75 4f fe mov.l #254, r15 a: ef 54 mov.l r5, r4 c: fd 78 84 e8 03 div #0x3e8, r4 11: 76 14 e8 03 mul #0x3e8, r4 15: ff 04 45 sub r4, r5, r4 18: 21 00 bne.b 18 <_PowerON_Reset_PC+0x18> 1a: 59 e5 20 movu.b 32[r14], r5 1d: ef f4 mov.l r15, r4 1f: 51 e4 20 and 32[r14].ub, r4 22: 64 15 and #1, r5 24: 57 54 or r5, r4 26: fd e0 f4 bnot #0, r4 29: c7 e4 20 mov.b r4, 32[r14] 2c: 66 05 mov.l #0, r5 2e: 62 15 add #1, r5 30: 2e 00 bra.b 30 <_PowerON_Reset_PC+0x30>
  13. Incremental development ~簡単なものから作る~ $ rx-elf-objdump.exe resetprg.obj -S 00000000 <_PowerON_Reset_PC>: 0:

    fb ee 0a c0 08 mov.l #0x8c00a, r14 5: f0 e0 bset #0, [r14].b 7: 75 4f fe mov.l #254, r15 a: ef 54 mov.l r5, r4 c: fd 78 84 e8 03 div #0x3e8, r4 11: 76 14 e8 03 mul #0x3e8, r4 15: ff 04 45 sub r4, r5, r4 18: 21 00 bne.b 18 <_PowerON_Reset_PC+0x18> 1a: 59 e5 20 movu.b 32[r14], r5 1d: ef f4 mov.l r15, r4 1f: 51 e4 20 and 32[r14].ub, r4 22: 64 15 and #1, r5 24: 57 54 or r5, r4 26: fd e0 f4 bnot #0, r4 29: c7 e4 20 mov.b r4, 32[r14] 2c: 66 05 mov.l #0, r5 2e: 62 15 add #1, r5 30: 2e 00 bra.b 30 <_PowerON_Reset_PC+0x30> @18: 21 00 if (i % 1000 == 0) →else側の分岐先がわからんやで。 @30: 2e 00 while(1){ …} →ループの最後まで行ったら何して いいかわからんやで。
  14. Incremental development ~簡単なものから作る~ $ rx-elf-objdump.exe resetprg.obj -S 00000000 <_PowerON_Reset_PC>: 0:

    fb ee 0a c0 08 mov.l #0x8c00a, r14 5: f0 e0 bset #0, [r14].b 7: 75 4f fe mov.l #254, r15 a: ef 54 mov.l r5, r4 c: fd 78 84 e8 03 div #0x3e8, r4 11: 76 14 e8 03 mul #0x3e8, r4 15: ff 04 45 sub r4, r5, r4 18: 21 00 bne.b 18 <_PowerON_Reset_PC+0x18> 1a: 59 e5 20 movu.b 32[r14], r5 1d: ef f4 mov.l r15, r4 1f: 51 e4 20 and 32[r14].ub, r4 22: 64 15 and #1, r5 24: 57 54 or r5, r4 26: fd e0 f4 bnot #0, r4 29: c7 e4 20 mov.b r4, 32[r14] 2c: 66 05 mov.l #0, r5 2e: 62 15 add #1, r5 30: 2e 00 bra.b 30 <_PowerON_Reset_PC+0x30> #include "iodefine.h" #pragma section ResetPRG #pragma entry PowerON_Reset_PC void PowerON_Reset_PC(void) { int i = 0; unsigned char reg; PORTA.PDR.BIT.B0 = 1; while(1){ if (i % 1000 == 0) { reg = PORTA.PODR.BIT.B0; PORTA.PODR.BIT.B0 = ~reg; i = 0; } i++; } } #pragma section C FIXEDVECT const void (*const func)(void)= PowerON_Reset_PC; リロケーション値=0x2E – 0x18=0x16 リロケーション値=0x0A – 0x30=0xDA
  15. Incremental development ~簡単なものから作る~ $ rx-elf-objdump.exe resetprg.obj -S 00000000 <_PowerON_Reset_PC>: 0:

    fb ee 0a c0 08 mov.l #0x8c00a, r14 5: f0 e0 bset #0, [r14].b 7: 75 4f fe mov.l #254, r15 a: ef 54 mov.l r5, r4 c: fd 78 84 e8 03 div #0x3e8, r4 11: 76 14 e8 03 mul #0x3e8, r4 15: ff 04 45 sub r4, r5, r4 18: 21 00 bne.b 18 <_PowerON_Reset_PC+0x18> 1a: 59 e5 20 movu.b 32[r14], r5 1d: ef f4 mov.l r15, r4 1f: 51 e4 20 and 32[r14].ub, r4 22: 64 15 and #1, r5 24: 57 54 or r5, r4 26: fd e0 f4 bnot #0, r4 29: c7 e4 20 mov.b r4, 32[r14] 2c: 66 05 mov.l #0, r5 2e: 62 15 add #1, r5 30: 2e 00 bra.b 30 <_PowerON_Reset_PC+0x30> #include "iodefine.h" #pragma section ResetPRG #pragma entry PowerON_Reset_PC void PowerON_Reset_PC(void) { int i = 0; unsigned char reg; PORTA.PDR.BIT.B0 = 1; while(1){ if (i % 1000 == 0) { reg = PORTA.PODR.BIT.B0; PORTA.PODR.BIT.B0 = ~reg; i = 0; } i++; } } #pragma section C FIXEDVECT const void (*const func)(void)= PowerON_Reset_PC; リロケーション値=0x2E – 0x18=0x16 リロケーション値=0x0A – 0x30=0xDA うんうん、機械語出力してみないことには分岐先ア ドレスは決まらないしこれは仕方がな・・・ってこれ は仕方なくないわ! お前らローカルスコープのくせにリンカ頼ってんじゃ ねぞゴラァ!
  16. Rubyで書いた結果 ・Rubyにはバイト型がないので低レイヤに向かない。 →バイナリ触りたければ String#unpack,Array#packを使う必要があるが、モンキーパッチで取り繕えばあ まり気にならない。1ファイルのリンクまではとりあえずRubyのみで書けた。 ・2ファイルのリンクに取り組み始めて、様子がおかしくなり始めた。 →複数ファイルをリンクする場合、オフセット位置やインデックスなど諸々の値を本格的に書き換える必 要があるが、RubyからELFの構造体(Elf32_Sym, Elf32_Rel…etc)のメンバーを気軽に書き換えられない。 ・仕方がないので、

    Elf32_Sym, Elf32_Rel構造体を操作するクラスをC言語で拡張モジュールとして実装す る。構造体をラップするRubyの拡張モジュールを書くのは割と簡単。 →Cで書いたクラスもRubyで書いたクラスも同じクラスになるので便利。 結論 Rubyの方が得意な部分(文字列処理・ハッシュ)はRubyで書けたので悪くはないと思うが、バイト列が扱 いにくいのは何ともできなかった。→ByteArrayみたいなクラスを自作して今後の低レイヤ開発のツールと したい。