Rustのlockとスコープのはなし
こういうコードを書くと当然deadlockする。
use std::sync::{Mutex, Arc}; use std::thread; fn main() { let mut lock = Arc::new(Mutex::new(false)); let mut cloned_lock = lock.clone(); let th = thread::spawn(move|| { loop { let mut quit = cloned_lock.lock().unwrap(); if *quit { break; } } }); let mut quit = lock.lock().unwrap(); *quit = true; th.join(); }
ロックの開放タイミングをいいかんじにするとちゃんと終了する。
use std::sync::{Mutex, Arc}; use std::thread; fn main() { let mut lock = Arc::new(Mutex::new(false)); let mut cloned_lock = lock.clone(); let th = thread::spawn(move|| { loop { let mut quit = cloned_lock.lock().unwrap(); if *quit { break; } } }); { let mut quit = lock.lock().unwrap(); *quit = true; } th.join(); }
さらに以下のように書いても正常に終了する。 おそらく変数スコープのところで獲得・開放してくれているのだろう。
use std::sync::{Mutex, Arc}; use std::thread; fn main() { let mut lock = Arc::new(Mutex::new(false)); let mut cloned_lock = lock.clone(); let th = thread::spawn(move|| { loop { let mut quit = cloned_lock.lock().unwrap(); if *quit { break; } } }); *lock.lock().unwrap() = true; th.join(); }
Rustいいなあ。
D言語のfizzbuzz
FizzBuzzでD言語っぽいコードってどんな感じだろう、みたいな話があったので。
ふつうに実行時に書くならこんな感じだろうか。
import std.algorithm; import std.conv : to; import std.range; import std.stdio; void main() { 1.iota(16) .map!(a => (! (a % 15)) ? "fizzbuzz" : (! (a % 5)) ? "buzz" : (! (a % 3)) ? "fizz" : a.to!string ) .each!writeln; }
コンパイル時fizzbuzzの愚直な例。 dmd -c fizzbuzz.d
で動かせばコンパイル時出力確認できる。
string fizzbuzz(alias n)() { import std.conv : to; static if (!(n % 15)) return "fizzbuzz"; static if (!(n % 3)) return "fizz"; static if (!(n % 5)) return "buzz"; else return n.to!string; } pragma(msg, fizzbuzz!1); pragma(msg, fizzbuzz!2); pragma(msg, fizzbuzz!3); pragma(msg, fizzbuzz!4); pragma(msg, fizzbuzz!5); pragma(msg, fizzbuzz!6); pragma(msg, fizzbuzz!7); pragma(msg, fizzbuzz!8); pragma(msg, fizzbuzz!9); pragma(msg, fizzbuzz!10); pragma(msg, fizzbuzz!11); pragma(msg, fizzbuzz!12); pragma(msg, fizzbuzz!13); pragma(msg, fizzbuzz!14); pragma(msg, fizzbuzz!15);
再帰で書くのは逆順しか思いつかなかった。
auto fizzbuzz(alias n)() { import std.conv : to; static if (n == 1) { return 1; } else { pragma(msg, (! (n % 15)) ? "fizzbuzz" : (! (n % 5)) ? "buzz" : (! (n % 3)) ? "fizz" : n.to!string ); return fizzbuzz!(n - 1); } } pragma(msg, fizzbuzz!15);
Dconf 2017
1日目
Pointers Gone Wild: Memory Safety and D
Dの作者ことうぉるたん。コンパイラの恩恵を受けるために生ポインタを使うよりref使おうとか、return refやscope refとか追加した理由とか。スライドにD言語くんがいっぱい登場していた。
D as a Better C
基本CとC++のdisで、後半はプロジェクトの事例紹介。
あまり期待していたような話はなかった。
Where Art Thou, LDC?
LDCのコアコミッタの人。発表はみてなくて後でスライドみただけ。ここ最近のLDCの活動(PGOとかLTOとか最適化のためのオプション追加したとか)、将来のビジョン(リンカにLLD使いたいとか、RISC-Vターゲットとか)。
What’s GNU With You?
GDCの中の人の話。GDBのD言語サポートがんばってる話(DWARF対応まわりとか、addr2line -C dlangとかでdemangleできるよ、とか)。
ABIまわりとか大変そうな雰囲気だった。ここは難しくてあまり理解できていない。
2日目
Things that Matter
C++界の巨人、Scott Mayer。効率/速度、ポータビリティ、ツーラビリティ(リファクタリングの話は笑った)、一貫性、インターフェース、コミットメントがこれまでの経験を通して大事であるという話。
Abstraction Cost and Optimization (LDC)
LDCでどういう風に書けば効率的なアセンブラを吐けるかという話。
いろいろなケースが紹介されている。単純な例でいうと、以下のコードはコンパイル時に定数になって効率的なアセンブラになっている。
import std.algorithm; import std.range; auto hoge() { return 1.iota(10) .map!(a => a * 2) .reduce!((a, b) => a + b); } /* $ ldc2 -c -O3 hoge.d $ objdump -Cd -Mintel hoge.o ... 0000000000000000 <_D4hoge4hogeFNaNfZi>: 0: b8 5a 00 00 00 mov eax,0x5a 5: c3 retq ... */
また、以下のコードはポインタでなくrefを使うとループのところでSIMDを使ったアセンブラを生成してくれる。
extern(C): alias T = int; T byPtr(int n, T *c) { T sum; for (int i=0; i < n; ++i) if (i > 0) sum += *c; return sum; } T byRef(int n, ref T c) { T sum; for (int i=0; i < n; ++i) if (i > 0) sum += c; return sum; } int f(int);
生成されるアセンブリ (これもobjdump -Cd -Mintel)
ref.o: file format elf64-x86-64 Disassembly of section .text.byPtr: 0000000000000000 <byPtr>: 0: 31 c0 xor eax,eax 2: 85 ff test edi,edi 4: 7e 56 jle 5c <byPtr+0x5c> 6: 8d 47 ff lea eax,[rdi-0x1] 9: 89 f9 mov ecx,edi b: 83 e1 03 and ecx,0x3 e: 83 f8 03 cmp eax,0x3 11: 73 06 jae 19 <byPtr+0x19> 13: 31 d2 xor edx,edx 15: 31 c0 xor eax,eax 17: eb 28 jmp 41 <byPtr+0x41> 19: 29 cf sub edi,ecx 1b: 31 d2 xor edx,edx 1d: 31 c0 xor eax,eax 1f: 90 nop 20: 85 d2 test edx,edx 22: 7e 02 jle 26 <byPtr+0x26> 24: 03 06 add eax,DWORD PTR [rsi] 26: 85 d2 test edx,edx 28: 78 02 js 2c <byPtr+0x2c> 2a: 03 06 add eax,DWORD PTR [rsi] 2c: 83 fa ff cmp edx,0xffffffff 2f: 7c 02 jl 33 <byPtr+0x33> 31: 03 06 add eax,DWORD PTR [rsi] 33: 83 fa fe cmp edx,0xfffffffe 36: 7c 02 jl 3a <byPtr+0x3a> 38: 03 06 add eax,DWORD PTR [rsi] 3a: 83 c2 04 add edx,0x4 3d: 39 d7 cmp edi,edx 3f: 75 df jne 20 <byPtr+0x20> 41: 85 c9 test ecx,ecx 43: 74 17 je 5c <byPtr+0x5c> 45: f7 d9 neg ecx 47: 66 0f 1f 84 00 00 00 nop WORD PTR [rax+rax*1+0x0] 4e: 00 00 50: 85 d2 test edx,edx 52: 7e 02 jle 56 <byPtr+0x56> 54: 03 06 add eax,DWORD PTR [rsi] 56: ff c2 inc edx 58: ff c1 inc ecx 5a: 75 f4 jne 50 <byPtr+0x50> 5c: c3 ret Disassembly of section .text.byRef: 0000000000000000 <byRef>: 0: 31 c0 xor eax,eax 2: 85 ff test edi,edi 4: 0f 8e 58 01 00 00 jle 162 <byRef+0x162> a: 8b 0e mov ecx,DWORD PTR [rsi] c: 31 f6 xor esi,esi e: 83 ff 08 cmp edi,0x8 11: b8 00 00 00 00 mov eax,0x0 16: 0f 82 34 01 00 00 jb 150 <byRef+0x150> 1c: 89 fa mov edx,edi 1e: 83 e2 f8 and edx,0xfffffff8 21: b8 00 00 00 00 mov eax,0x0 26: 0f 84 24 01 00 00 je 150 <byRef+0x150> 2c: 66 0f 6e c1 movd xmm0,ecx 30: 66 0f 70 c0 00 pshufd xmm0,xmm0,0x0 35: 8d 42 f8 lea eax,[rdx-0x8] 38: c1 e8 03 shr eax,0x3 3b: 44 8d 40 01 lea r8d,[rax+0x1] 3f: 41 83 e0 01 and r8d,0x1 43: 85 c0 test eax,eax 45: 0f 84 a4 00 00 00 je ef <byRef+0xef> 4b: 41 8d 70 ff lea esi,[r8-0x1] 4f: 29 c6 sub esi,eax 51: 66 0f ef e4 pxor xmm4,xmm4 55: 66 0f 6f 2d 00 00 00 movdqa xmm5,XMMWORD PTR [rip+0x0] # 5d <byRef+0x5d> 5c: 00 5d: 66 44 0f 6f 05 00 00 movdqa xmm8,XMMWORD PTR [rip+0x0] # 66 <byRef+0x66> 64: 00 00 66: 66 44 0f 6f 0d 00 00 movdqa xmm9,XMMWORD PTR [rip+0x0] # 6f <byRef+0x6f> 6d: 00 00 6f: 66 44 0f 6f 15 00 00 movdqa xmm10,XMMWORD PTR [rip+0x0] # 78 <byRef+0x78> 76: 00 00 78: 66 44 0f 6f 1d 00 00 movdqa xmm11,XMMWORD PTR [rip+0x0] # 81 <byRef+0x81> 7f: 00 00 81: 66 0f ef f6 pxor xmm6,xmm6 85: 66 0f ef db pxor xmm3,xmm3 89: 0f 1f 80 00 00 00 00 nop DWORD PTR [rax+0x0] 90: 66 0f 6f cd movdqa xmm1,xmm5 94: 66 41 0f fe c8 paddd xmm1,xmm8 99: 66 0f 6f d5 movdqa xmm2,xmm5 9d: 66 41 0f fe d1 paddd xmm2,xmm9 a2: 66 0f 6f fd movdqa xmm7,xmm5 a6: 66 0f 66 fc pcmpgtd xmm7,xmm4 aa: 66 0f 66 cc pcmpgtd xmm1,xmm4 ae: 66 0f db f8 pand xmm7,xmm0 b2: 66 0f db c8 pand xmm1,xmm0 b6: 66 0f fe fe paddd xmm7,xmm6 ba: 66 0f fe cb paddd xmm1,xmm3 be: 66 0f 6f dd movdqa xmm3,xmm5 c2: 66 41 0f fe da paddd xmm3,xmm10 c7: 66 41 0f fe eb paddd xmm5,xmm11 cc: 66 0f 66 d4 pcmpgtd xmm2,xmm4 d0: 66 0f 66 dc pcmpgtd xmm3,xmm4 d4: 66 0f db d0 pand xmm2,xmm0 d8: 66 0f db d8 pand xmm3,xmm0 dc: 66 0f fe d7 paddd xmm2,xmm7 e0: 66 0f fe d9 paddd xmm3,xmm1 e4: 83 c6 02 add esi,0x2 e7: 66 0f 6f f2 movdqa xmm6,xmm2 eb: 75 a3 jne 90 <byRef+0x90> ed: eb 10 jmp ff <byRef+0xff> ef: 66 0f ef d2 pxor xmm2,xmm2 f3: 66 0f 6f 2d 00 00 00 movdqa xmm5,XMMWORD PTR [rip+0x0] # fb <byRef+0xfb> fa: 00 fb: 66 0f ef db pxor xmm3,xmm3 ff: 45 85 c0 test r8d,r8d 102: 74 28 je 12c <byRef+0x12c> 104: 66 0f 6f 0d 00 00 00 movdqa xmm1,XMMWORD PTR [rip+0x0] # 10c <byRef+0x10c> 10b: 00 10c: 66 0f fe cd paddd xmm1,xmm5 110: 66 0f ef e4 pxor xmm4,xmm4 114: 66 0f 66 cc pcmpgtd xmm1,xmm4 118: 66 0f db c8 pand xmm1,xmm0 11c: 66 0f fe d9 paddd xmm3,xmm1 120: 66 0f 66 ec pcmpgtd xmm5,xmm4 124: 66 0f db e8 pand xmm5,xmm0 128: 66 0f fe d5 paddd xmm2,xmm5 12c: 66 0f fe d3 paddd xmm2,xmm3 130: 66 0f 70 c2 4e pshufd xmm0,xmm2,0x4e 135: 66 0f fe c2 paddd xmm0,xmm2 139: 66 0f 70 c8 e5 pshufd xmm1,xmm0,0xe5 13e: 66 0f fe c8 paddd xmm1,xmm0 142: 66 0f 7e c8 movd eax,xmm1 146: 39 fa cmp edx,edi 148: 89 d6 mov esi,edx 14a: 74 16 je 162 <byRef+0x162> 14c: 0f 1f 40 00 nop DWORD PTR [rax+0x0] 150: 85 f6 test esi,esi 152: ba 00 00 00 00 mov edx,0x0 157: 0f 4f d1 cmovg edx,ecx 15a: 01 d0 add eax,edx 15c: ff c6 inc esi 15e: 39 f7 cmp edi,esi 160: 75 ee jne 150 <byRef+0x150> 162: c3 ret
あと最適化で消されたくないときは import ldc.attributes;
して @weak
を使うとかけっこう盛りだくさんでよかった。
まとめ?
システムプログラミング言語らしく比較的低レイヤの話が多かった印象。プロダクション事例とかはあまりない気がするが、自分のみるセクションが偏っているだけな気もする。
LDCの最適化とか
http://robert.ocallahan.org/2017/04/rust-optimizations-that-c-cant-do_5.html これを読んで、なるほどRustだとこういう最適化できるのね、というのを学んだ。
Here it’s really clear that the semantics of Rust are making this optimization possible. In C++ v could be a reference to a global variable which is modified by the callback function, in which case hoisting the load would be incorrect.
とあるようにC++だとvがグローバル変数への参照として渡ってきて、callbackの中でそのグローバル変数の書き換えが起こるかもしれない。 なんで、 [qword ptr [r15]] って感じでメモリをloadしにいく必要がある。(下にあるLink-Time Optimizationうんぬんは無視する)。 まあそのへんはsharedを型に使ってるD言語、LDCあたりだとどうなるんだろう(まあなんとなくC++と同じ気はする、グローバル変数触らないという制約はもてない気する)と思ってざっとみてみる。
こういうコードを書く。
ulong foo(ref ulong v, void function() callback) { auto sum = 0; foreach (_; 0 .. 100) { sum += v; callback(); } return sum; }
ldc2 -O -c -ofldc.o hoge.d でコンパイルした結果:
... 20: 41 03 1e add (%r14),%ebx 23: 41 ff d7 callq *%r15 26: 48 63 db movslq %ebx,%rbx 29: ff cd dec %ebp 2b: 75 f3 jne 20 <_D4hoge3fooFKmPFZvZm+0x20>
ループ内部はこんな感じになっていて、まあ期待(?)した結果になった。Intel記法だとdword ptrとかだしてくれるんでそっちしたほうがよかったかもしれない。
ただRust側で addとcallが並び変わってる理由がよくわかってない。
追記
pure
を使えばいけるかも、と思ったが
$ ldc2 -O -c -ofldc.o hoge.d hoge.d(7): Error: pure function 'hoge.foo' cannot call impure function pointer 'callback'
のように怒られてしまった。
ちょっと書き換えてみる。
ulong foo(ref ulong v, int function() pure nothrow @safe @nogc callback) { auto sum = 0; foreach (_; 0 .. 100) { sum += v; callback(); } return sum; }
それでもだめなようだ
$ ldc2 -O -c -ofldc.o hoge.d $ objdump -Mintel -Cd ldc.o # Intel記法にした ... 20: 41 03 1e add ebx,DWORD PTR [r14] 23: 41 ff d7 call r15 26: 48 63 db movsxd rbx,ebx 29: ff cd dec ebp 2b: 75 f3 jne 20 <_D4hoge3fooFKmPFNaNbNiNfZiZm+0x20>
STM32F3DISCOVERYをD言語から動かすライブラリを書いている
https://github.com/kubo39/stm32f3discovery で開発中です。LDC前提。とりあえずのLチカと例外のハンドリングとかくらいしかない。
Lチカのプログラムはこんな感じの抽象度で書けます。
import stm32f3discovery; import led; extern(C): @nogc: nothrow: void main() { pragma(LDC_never_inline); initLED(); auto ticks = 100000; while (true) { foreach (led; LEDS) { led.on; delay(ticks); led.off(); delay(ticks); } } } void delay(uint n) { pragma(LDC_never_inline); foreach (_; 0 .. n) { // nop } }
作り途中なので課題はまだまだありますが、とりあえず見えてる範囲だと
- 実装量が全然足りてない(タイマーとかusartとか追加していく)
- モジュール構成がいけてない
- 内部の抽象化がまだまだきれいになってない(生ポインタでやってたり)
- パッケージングどうするか(dubが貧弱に感じてるのでMakefileにしてるけど別によかったのかもしれない)
- ARM Cortex-Mで共通のものはさらにライブラリとしてくくりだしたい
とかです。
こういうことをやってるとグローバルな変数参照しようとしてlibcの __tls_get_addr
を参照しようとしてリンクエラーになったり、extern(D) void mainにしてて_Dmainとして扱われて死亡したりD言語特有の現象に遭遇して詳しくなれたりするようなしないような気がします。
他にあとD言語のいいところ、、
- C言語との連携
- @nogcやnothrowのチェックがある
- Cよりずっとstrictな型検査
- typeofとか使うと便利
今のところこのへんですかね。
STM32F3DISCOVERYでD言語のプログラムを実行する
LDCコンパイラでSTM32F3DISCOVERYで動く実行バイナリをいろいろ苦労して作成した。
いろいろ罠
LDCのオプションは罠だらけ
march,mcpuの指定が罠
- float ABIの指定でhardが効かない、soft ABIとなる
tripleの指定が罠
- eabihfだとsoft-float ABIになってしまう、gnueabihfにしないとhard-float ABIにならない
- OSにunknownが指定できない、とりあえずLinuxを指定して対処
リンカまわり
- エントリポイントが0x1バイトずれる
- 外から-eオプションで渡して回避
最終的な実行バイナリ生成コマンド
$ ldc2 -mtriple=thumbv7em-none-linux-gnueabihf -c -g main.d $ arm-none-eabi-ld main.o -e 0x8000009 -T layout.ld -nostartfiles -o main.bin
readelfの出力
- エントリポイントアドレスが
0x8000009
になっていればよい - Float ABIが
hard-float ABI
になっていればよい
$ arm-none-eabi-readelf -h main.bin ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: ARM Version: 0x1 Entry point address: 0x8000009 Start of program headers: 52 (bytes into file) Start of section headers: 1548 (bytes into file) Flags: 0x5000400, Version5 EABI, hard-float ABI Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 2 Size of section headers: 40 (bytes) Number of section headers: 16 Section header string table index: 13
objdumpの出力
このような内容になった
$ arm-none-eabi-objdump -Cd main.bin main.bin: file format elf32-littlearm Disassembly of section .text: 08000000 <_reset-0x8>: 8000000: 2000a000 .word 0x2000a000 8000004: 08000009 .word 0x08000009 Disassembly of section _reset: 08000008 <_reset>: 8000008: b082 sub sp, #8 800000a: 202a movs r0, #42 ; 0x2a 800000c: 9000 str r0, [sp, #0] 800000e: 9001 str r0, [sp, #4] 8000010: e7ff b.n 8000012 <_reset+0xa> 8000012: 2000 movs r0, #0 8000014: 2800 cmp r0, #0 8000016: d102 bne.n 800001e <_reset+0x16> 8000018: e7ff b.n 800001a <_reset+0x12> 800001a: e7ff b.n 800001c <_reset+0x14> 800001c: e7f9 b.n 8000012 <_reset+0xa> 800001e: b002 add sp, #8 8000020: 4770 bx lr
OpenOCD
APTで入れたものであればこのような表示になる。
$ openocd -f board/stm32f3discovery.cfg Open On-Chip Debugger 0.9.0 (2015-09-02-10:42) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html adapter speed: 1000 kHz adapter_nsrst_delay: 100 Info : The selected transport took over low-level target control. The results mi ght differ compared to plain JTAG/SWD none separate srst_only separate srst_nogate srst_open_drain connect_deassert_srst Info : Unable to match requested speed 1000 kHz, using 950 kHz Info : Unable to match requested speed 1000 kHz, using 950 kHz Info : clock speed 950 kHz Info : STLINK v2 JTAG v16 API v2 SWIM v0 VID 0x0483 PID 0x3748 Info : using stlink api v2 Info : Target voltage: 2.917647 Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
GDB
GDBでリモート接続する。
$ arm-none-eabi-gdb -q main.bin Reading symbols from main.bin...done. (gdb) target remote :3333 Remote debugging using :3333
loadしてSTM32F3DISCOVERYボード上で実行できるようにする。
(gdb) load Loading section .text, size 0x8 lma 0x8000000 Loading section _reset, size 0x1a lma 0x8000008 Start address 0x8000008, load size 34 Transfer rate: 244 bytes/sec, 17 bytes/write.
エントリポイントは 0x08000009
となっているが、プログラムの先頭は 0x08000008
となっている。
(gdb) disas Dump of assembler code for function main: => 0x08000008 <+0>: sub sp, #8 0x0800000a <+2>: movs r0, #42 ; 0x2a 0x0800000c <+4>: str r0, [sp, #0] 0x0800000e <+6>: str r0, [sp, #4] 0x08000010 <+8>: b.n 0x8000012 <main+10> 0x08000012 <+10>: movs r0, #0 0x08000014 <+12>: cmp r0, #0 0x08000016 <+14>: bne.n 0x800001e <main+22> 0x08000018 <+16>: b.n 0x800001a <main+18> 0x0800001a <+18>: b.n 0x800001c <main+20> 0x0800001c <+20>: b.n 0x8000012 <main+10> 0x0800001e <+22>: add sp, #8 0x08000020 <+24>: bx lr End of assembler dump.
ステップ実行で確認。たしかに動いているようだ。
(gdb) step main () at main.d:13 13 auto x = 42; (gdb) print x $1 = 42 (gdb) step 14 y = x; (gdb) print y $2 = 42 (gdb) print &x $3 = (int *) 0x20009ff8 (gdb) print &y $4 = (int *) 0x20009ffc
LDCでSTM32F103のバイナリを作ろうとしてできなかった
Cortex-M3な環境を試してみる。
コードを以下のようなかんじで用意する。
import ldc.attributes; extern(C): @nogc: nothrow: pragma(LDC_no_moduleinfo); @(section("_reset")) void _reset() { int y = void; auto x = 42; y = x; while (true) {} }
ENTRY(_reset); MEMORY { FLASH : ORIGIN = 0x08000000, LENGTH = 128K RAM : ORIGIN = 0x20000000, LENGTH = 20K } SECTIONS { .text : { /* Vector table */ LONG(ORIGIN(RAM) + LENGTH(RAM)) LONG(_reset + 1); /* Reset handler */ _reset = .; *(.text._reset) *(.text*) } > FLASH /DISCARD/ : { *(.ARM.exidx*) *(.note.gnu.build-id*) } }
実行バイナリを作る。
$ ldc2 -march=thumb -mcpu=cortex-m3 -c -g main.d $ arm-none-eabi-ld main.o -T layout.ld -nostartfiles -o main.bin
OpenOCDを起動。
$ openocd -f interface/stlink-v2-1.cfg -f target/stm32f1x.cfg
gdbからプログラムを流しこもうとするとコネクションが閉じられる。
$ gdb -q main.bin Reading symbols from main.bin...done. (gdb) target remote :3333 Remote debugging using :3333 warning: Architecture rejected target-supplied description 0xffbefdde in ?? () (gdb) load Loading section .text, size 0x8 lma 0x8000000 Loading section _reset, size 0x1a lma 0x8000008 Start address 0x8000008, load size 34 Remote connection closed
OpenOCD側の出力はこのような感じ。
他のボードと同じように Error: gdb requested a non-existing register
が出力されている。
Info : accepting 'gdb' connection on tcp/3333 target state: halted target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x08000008 msp: 0x20005000 target state: halted target halted due to breakpoint, current mode: Thread xPSR: 0x61000000 pc: 0x2000003a msp: 0x20005000 target state: halted target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x08000008 msp: 0x20005000 Error: gdb requested a non-existing register Info : dropped 'gdb' connection
objdumpで実行バイナリをみる。があまりよくわからない。
$ arm-none-eabi-objdump -Cd main.bin main.bin: file format elf32-littlearm Disassembly of section .text: 08000000 <_reset-0x8>: 8000000: 20005000 .word 0x20005000 8000004: 08000009 .word 0x08000009 Disassembly of section _reset: 08000008 <_reset>: 8000008: b082 sub sp, #8 800000a: 202a movs r0, #42 ; 0x2a 800000c: 9000 str r0, [sp, #0] 800000e: 9001 str r0, [sp, #4] 8000010: e7ff b.n 8000012 <_reset+0xa> 8000012: 2000 movs r0, #0 8000014: 2800 cmp r0, #0 8000016: d102 bne.n 800001e <_reset+0x16> 8000018: e7ff b.n 800001a <_reset+0x12> 800001a: e7ff b.n 800001c <_reset+0x14> 800001c: e7f9 b.n 8000012 <_reset+0xa> 800001e: b002 add sp, #8 8000020: 4770 bx lr
Rustで生成したものとreadelfを比較してみると、エントリポイントが0x1ずれているのが問題であることに気づいた。
以前書いた記事 で書いたfloat ABIの違いは関係ないようだ。さらに調べるとLLVMに渡すtripleのオプションでhardware floatを渡すことができた。ただしOSの関してはunknownを渡すことはできないようだ。
エントリポイントを外からいじってみる。
$ ldc2 -mtriple=thumbv7m-none-linux-eabi -c -g main.d $ arm-none-eabi-ld main.o -e 0x8000009 -T layout.ld -nostartfiles -o main.bin
$ arm-none-eabi-objdump -Cd main.bin main.bin: file format elf32-littlearm Disassembly of section .text: 08000000 <_reset-0x8>: 8000000: 20005000 .word 0x20005000 8000004: 08000009 .word 0x08000009 Disassembly of section _reset: 08000008 <_reset>: 8000008: b082 sub sp, #8 800000a: 202a movs r0, #42 ; 0x2a 800000c: 9000 str r0, [sp, #0] 800000e: 9001 str r0, [sp, #4] 8000010: e7ff b.n 8000012 <_reset+0xa> 8000012: 2000 movs r0, #0 8000014: 2800 cmp r0, #0 8000016: d102 bne.n 800001e <_reset+0x16> 8000018: e7ff b.n 800001a <_reset+0x12> 800001a: e7ff b.n 800001c <_reset+0x14> 800001c: e7f9 b.n 8000012 <_reset+0xa> 800001e: b002 add sp, #8 8000020: 4770 bx lr
エントリポイントが0x8000009になっていることを確認。
$ arm-none-eabi-readelf -h main.bin ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: ARM Version: 0x1 Entry point address: 0x8000009 Start of program headers: 52 (bytes into file) Start of section headers: 1548 (bytes into file) Flags: 0x5000200, Version5 EABI, soft-float ABI Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 2 Size of section headers: 40 (bytes) Number of section headers: 16 Section header string table index: 13
しかし結果は変わらず、うまくいかなかった。