Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
マイコン向けのただのリンカを自作してみた話
Search
simotin13
August 13, 2024
0
31
マイコン向けのただのリンカを自作してみた話
カーネル/VM探検隊@関西 9回目で発表させて頂いたときの資料です。
ルネサスのRXマイコン向けのリンカを書いてみたというネタです。
simotin13
August 13, 2024
Tweet
Share
More Decks by simotin13
See All by simotin13
C/C++用のコードカバレッジツールを自作してみた話
simotin13
0
18
Pinでコードカバレッジツールを自作してみた話
simotin13
0
1.3k
Featured
See All Featured
Navigating Team Friction
lara
190
15k
Practical Orchestrator
shlominoach
190
11k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.5k
Six Lessons from altMBA
skipperchong
29
4k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.2k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
16k
Designing for Performance
lara
610
69k
Writing Fast Ruby
sferik
630
62k
Building Better People: How to give real-time feedback that sticks.
wjessup
369
20k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
359
30k
What's in a price? How to price your products and services
michaelherold
246
12k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.2k
Transcript
マイコン向けの ただのリンカを書いてみた話 @simotin13
Who am I? Hiroyuki Miyazaki @simotin13 ・関西で主に組込系のコード書いてます。 ・Ruby,組込,低レイヤが好きです。
Abstract~発表の概要~ ・なぜマイコン向けのリンカ? ・簡単なところから始めてみた ・Rubyで実装してみた ルネサスRXマイコン向けのリンカを書いてみました Github https://github.com/simotin13/rb_linker →複数のオブジェクトファイルをリンクして、ターゲットボードでLチカできた!
Why RX linker? ・マイコンを使った製品開発の仕事をしているにも関わらず、 ライセンスを購入して貰えない… ▪問題 仕事で使っている商用開発環境(CS+)のライセンスが足りない・・・ 「1ライセンス用意してやる。あとは何とかしろ!」
Why RX linker? ・とりあえずその辺のPCに無償評価版入れて使い倒し ・最後はライセンスの使用権をめぐる争いに… →作業効率悪くてストレスたまる! ライセンスが足りてないと何が起こるか?
Why RX linker? どうやって解決するか?
Why RX linker? 「ライセンスが足りないならフローティングライ センスを買えばいいじゃない」 「ライセンスが足りないならクラッキングすれば いいじゃない」 一般的な解決方法
Why RX linker? [CS+ FAQページ] https://ja-support.renesas.com/knowledgeBase/17796817 無償評価版の機能制限について
Why RX linker? [CS+ FAQページ] https://ja-support.renesas.com/knowledgeBase/17796817 無償評価版の機能制限について コンパイラは無償で提供するけど、リンカ使いたけれ ば金払えよ!
Why RX linker? ライセンスが足りないならリンカを 書けばいいじゃない! ・コンパイルは無償評価版がやってくれる →リンカを書けば無償評価版定額使い放題! ただのリンカ =タダ(無料)のリンカ an
ordinary linker = a free linker
spec 諸々の仕様について アーキテクチャ RX(32bit CISC) バイナリフォーマット ELF(32bit 再配置可能オブジェクトファイル) ターゲットボード GR-SAKURA
デバッガ・エミュレータ E1 入力 .clnkファイル (コマンドラインオプションとリンカスクリプトを混ぜたようなテキスト ファイル) 出力 ELF(32bit 実行形式)
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やコンパイラによって異なります
Incremental development ~簡単なものから作る~ ELF header .text .bss .shstrtab Section Headers
hoge.o Program Header ELF Header Section Headers まずは1ファイルを「リンク」してみる。 リンク=プログラムヘッダの追加とELFヘッダの更新。 プログラムヘッダが入る分、諸々のオフセット位置の値が変わってくる。
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ファイルによりリ ンカに渡される。
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も汎用レジスタへの割り当てを期待
Incremental development ~簡単なものから作る~ $cat sakura2.clnk -input=DefaultBuild¥resetprg.obj -noprelink -nodebug -output=DefaultBuild¥sakura2.abs -nooptimize
-start=PResetPRG/0FFF00000,FIXEDVECT/FFFFFFFC -nologo-exit 入力となる .clnkファイル リンクするオブジェクトファイル 出力するファイル名 セクション設定
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) オブジェクトファイルのセクション情報を眺めてみる
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) オブジェクトファイルのセクション情報を眺めてみる えっ!?リロケーション発生してるし! なんで!?
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 オブジェクトファイルのリロケーション情報を眺めてみる
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飛ばしていいかわからんか ら教えてやで。
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<*%&~!)やで。
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>
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){ …} →ループの最後まで行ったら何して いいかわからんやで。
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
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 うんうん、機械語出力してみないことには分岐先ア ドレスは決まらないしこれは仕方がな・・・ってこれ は仕方なくないわ! お前らローカルスコープのくせにリンカ頼ってんじゃ ねぞゴラァ!
Don’t think,Feel.~考えるな、感じろ! ~ ・Rel(a)セクションはコンパイラが一人でどうすることもで きなかった悩みをお前に託した独白録だ! 考えるな! コンパイラのお気持ちになれ! 感じろ! リロケーションについて一言
リンカをRubyで書いた
リンカをRubyで書いた理由 手っ取り早くスクリプト言語で書きたい! やる気ないけど、進捗はほしい! 低レイヤやりたいけど C・C++は書きたくない
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みたいなクラスを自作して今後の低レイヤ開発のツールと したい。
まとめ ・デバッグ情報のリンク →リンクはできてプログラムは動いているけどデバッグシンボルが入ったセクションのリンクを試してい ない。デバッグできなければ実用性は低いので取り込みたい。 ・他のCPU・OSのリンカも書いてみたい RXで実用性のめどがついたらRL78とかARMにも移植して、自作の無料リンカで無双したい。 OS(Unix,Linux系)ありのリンカも書いてみたいけど、ハードルが急激に高くなる(libc,ld.so,plt/got…etc)の が辛い・・・ →難易度という意味ではマイコン向けのリンカ自作はリンカを書くよい練習になると思うのでおススメ! エミュレータの値段も個人で手に入るような価格になってきているので是非!
References ~参考文献など~ ▪参考文献 ・リンカ・ローダ実践開発テクニック ・RXマイコンのすべて 基礎編 ▪マニュアル ・RXマイコンソフトウェアマニュアル( r01us0032jj0120_rxsm.pdf) ▪拡張モジュールの作り方
https://ruby-doc.org/core-2.3.0/doc/extension_ja_rdoc.html http://ruby.gfd-dennou.org/tutorial/ruby-ext/
ご清聴ありがとうございました。 ご意見・ご質問がありましたらお気軽にどうぞ。 ¥(^o^)¥