sim/arm
最近はドローンが何かと話題になっている。官邸に落ちてるのを発見されて大騒ぎになったり、 坊さんの行列に落ちたり、etc,etc。
行政は遅ればせながら大事な施設の回りで飛行禁止にしよう、違反したら罰金50万円だか 懲役1年だとか。大事な施設以外ならOKかと思うと、都の公園でも駄目って駄目だしが出るとか。
科学の向上心溢れる青少年の芽を摘み取ってよいものだろうか? 禁止とセットでドローン 解放区を作るぐらいの度量が無いものかね? それとも、うちの田舎ならドローン飛ばし放題 ですよって、地域振興の為に名乗りを上げる地方自治体は無いの?
この間落ちたドローンには、カメラが搭載されてて空撮出来る仕様だったらしい。音も無く 窓辺に近づき、情事を撮影。それを元に強請りってんじゃ困るので、情事に必携帯、電波撹乱装置とやらが 秋葉原で売られていたりして。何てったって、攻めには防御が肝心ですから。
カメラが載ってるって事は、きっと制御装置も載せてあるに違いない。日経Linuxを読めば 鼻歌を歌いながら、そういうドローンが作れそうですから。
制御基板は、日経お得意のラズパイだな。それともこれから流行りそうな Spark Coreかな。どうやらこちらの石はPICの親分っぽいな。 遊びがいはArduinoに軍配が上がるだろうね。IO端子がいっぱい付いているもの。
ラズパイってハードどうなってるのかな?Schematics によると、四角い箱が書いてあるだけで、面白くないな。なんだ、 カメラ・キットあるじゃん。 そして、Installing Ruby packages まで有るって事は、ドローン抱えて、松江へGO。
最初の試練は、ドローンを海上で運用する事。もし操縦に失敗したらそこでThe End。顔を 洗って出直してこいってスパルタ教育が待ってます。
目標は、撹乱電波によるジャミングにも耐えて、空撮する自立型盗撮機を開発する事。 これさえ完成すれば、日経の何とか賞を総ナメ出来るぞ。 なんたって、日本海の荒海荒風に鍛えられたマシンですから。都内で売ってるひ弱なマシンとは 大違いでっせ。ああ、あんちょこが有った。 カメラモジュール到着
そして我らが秋月電子は、時代を先取りしてまっせ。 PiNoir ラズベリーパイ 赤外線カメラモジュール ですって。暗い所でも撮影出来るんだね。ドローンは新宿公園で飛ばせませんが、木の上に 放り投げておく事は出来る・かな?
上記は怪しさ256倍だったので、少しトーンダウンして、 Raspberry PiからpifmでFMラジオに電波を飛ばしてみる とか Raspberry Piで航空機からの位置情報信号ADS-Bを受信 してみるなんてのが、電波少年には嬉しいな。
qemu
今までqemuを使ってarmなまねをさせてきた。頑張れば、 QEMU: 仮想ボード ARM Cortex-A9 マルチコアで Linux を動かす なんて事も出来るみたい。最近はipadなんかもマルチコアで動いているんで、アプル対抗 出来るんだな。
それから、ちょびっとarmのマネってんで、 QEMU でARM エミュレータ環境を作成する なんて事も出来るみたい。
qemuにこだわらなければ、エミュレータってくくりで、 ARMulatorなんていう記事がある。 また、 Free ARM Emulators に有った SkyEye が良さそうですよ。
検索の途中で見つけた、 モニタ書きbkpt,step なんてのも面白い。
qemu-arm
qemuのarmには、qemu-armってのと、今回使ったqemu-system-armの2種類が有る。Windows版では、 後者系しか入っていない。
だったらウブに入ってる前者は何よ? ユーザーモード版と俗に言うらしい。後者は、 armのパソコンを丸ごとまねするのに対し、前者は、石だけarmよって事みたいだ。そんなの 使い道有るのか? ぐぐったら、使ってる人がいた。先ほど見たね。
Ubuntu 12.04 で hello.c をARMバイナリとしてコンパイルして実行してみる なんて事らしい。ここで出て来るqemu-user と qemu-user-staticで、qemu-armが入る。 こいつに、linuxで使ってるライブラリィーとリンカーを足せば、i386なマシンでも、armの バイナリーが実行出来るとな。
と言う事は、qemu-armがlinuxのカーネルを内包してるのかな。だから後はライブラリィー経由で、 syscallを実行出来るんだな。だから、これの相当品は、Windowsでは搭載してないのか。 まてよ、そうじゃないだろう。syscallは、armだろうがi386だろうがやる事は一緒。ならば、 hostOSで動いているLinuxに委譲した方が楽か。どうせ文字を表示するにしても、ファイルを 扱うにしても、host側のリソースを借用する事になるんだから。
その理屈からすると、FreeBSDでも、 qemu-armは提供されないはずだな。残念でした。まてまて、FreeBSDにはLinuxのエミュレーションが 出来るしかけが有るから、大丈夫かな。後で調べてみよう。
* User mode emulation (Linux host only). In this mode, QEMU can launch Linux processes compiled for one CPU on another CPU. It can be used to launch the Wine Windows API emulator or to ease cross-compilation and cross-debugging. As QEMU requires no host kernel patches to run, it is very safe and easy to use.
調べてみたら、こんな説明が有ったぞ。でも何でこれがFreeBSDのportsなってるんだ。 やはりLinuxのエミュレーションが備わっているからだな。近頃は、fedora10の他、SentOS 6.5のマネも 出来るみたいだ。fedoraはそろそろ22だし、Centは7.1だかが全盛だから、大分古いな。 古いと言えば、Lisa2っていう往年も名機のまねも出来るみたいだ。vmips なんてのも有ったぞ。
sakae@uB:~/arm$ qemu-arm nimapp /lib/ld-linux.so.3: No such file or directory
リンカー入っていないんか。debina-armな方ではどうなってる?
sakae@debian-armel:~/nim$ ldd nimapp libdl.so.2 => /lib/arm-linux-gnueabi/libdl.so.2 (0xb6f3e000) libc.so.6 => /lib/arm-linux-gnueabi/libc.so.6 (0xb6e06000) /lib/ld-linux.so.3 (0xb6f4f000)
ふむ、リンカーとarm-linux-gnueabiをまとめて、ウブに持っていけば、動くとな。 どれぐらいの容量かと調べたら、8.2M有った。
リンカーにもライブラリィーにもお世話にならない、シングルバイナリーなら、問題なく 動くぞ。
sakae@uB:~/arm$ qemu-arm rvtl RVTL v.3.04.1eabi 2013/04/25, (C)2003-2013 Jun Mizutani RVTL may be copied under the terms of the GNU General Public License. <04AA> ~
Linux Zaurus、玄箱PRO、Raspberry Pi 用 rvtl ってBASIC似なやつを、動かした図。
spimのarm版?
mipsには、spimっていうお手軽なシュミレータが有る。armは上記で調べたものが代表的だけど、 他には無いの? 検索中に、gdbでsimなんてのが引っかかってきた。はるか昔、gdbでh8な CPUのマネをした事を思い出した。熱血!アセンブラ入門を 書いた人の紹介だったな。
ひょっとして、armも出来るんじゃないかい。gdb-7.9を落としてきて展開してみたら、 sim/armってのが確かに有ったぞ。gdbを作る時に、targetを指定すればいいらしい事は 分かったけど、どうやって指定すんの?
[sakae@fedora gdb-7.9]$ sh config.sub arm-elf arm-unknown-elf
こんな風に確認しておいてから、
./configure --target=arm-elf
で、行けた。と思ったら、termcapが入ってないから駄目だって。termcapとはncursesの事かえ? って思って、 ncurses-devel を入れたら、configure成功。長いmake実行の末、i386なマシン上に 突如、arm用のgdbが出来上がったよ。 実際に動くかやってみる。お題は、armなdebianで作ったfactをnimでコンパイルしたもの。
[sakae@fedora gdb-7.9]$ gdb/gdb ~/arm/nimapp : warning: A handler for the OS ABI "GNU/Linux" is not built into this configuration of GDB. Attempting to continue with the default armv4t settings. Reading symbols from /home/sakae/arm/nimapp...(no debugging symbols found)...done. (gdb) target sim Connected to the simulator. (gdb) load Loading section .interp, size 0x13 vma 0x8134 : Start address 0x8be0 Transfer rate: 1187080 bits in <1 sec. (gdb) b _start Breakpoint 1 at 0x8be0 (gdb) display/i $pc 1: x/i $pc <error: No registers.> (gdb) run Starting program: /home/sakae/arm/nimapp Breakpoint 1, 0x00008be0 in _start () 1: x/i $pc => 0x8be0 <_start>: mov r11, #0 (gdb) si 0x00008be4 in _start () 1: x/i $pc => 0x8be4 <_start+4>: mov lr, #0
ちゃんとシュミレータで動きましたねぇ。siで一命令づつ実行する時は、display/iでpcを指定おくと、 次に実行される命令が表示されて便利だぞ。
で、sim/armの中に下りて行くと、いかにもと言うrunってアプリが出来上がっていた。なんじゃらほいとばかり実行してみると、
[sakae@fedora arm]$ ./run Usage: run [options] program [program args] Options: -a args Pass `args' to simulator. -m size Set memory size of simulator, in bytes. -t Perform instruction tracing. Note: Very few simulators support tracing. -v Verbose output. program args Arguments to pass to simulated program. Note: Very few simulators support this. Target specific options: --swi-support=<list> Comma seperated list of SWI protocols to supoport. This list can contain: NONE, DEMON, ANGEL, REDBOOT and/or ALL. -d Enable disassembly of instructions during tracing. -z Trace entering and leaving functions.
取り合えず、先のfactを実行してみる。
pc: 0, [sakae@fedora arm]$ ./run -t -d -z ~/arm/nimapp pc: 8be0, mov fp, #0 pc: 8be4, mov lr, #0 pc: 8be8, pop {r1} ; (ldr r1, [sp], #4) pc: 8bec, mov r2, sp pc: 8bf0, push {r2} ; (str r2, [sp, #-4]!) pc: 8bf4, push {r0} ; (str r0, [sp, #-4]!) pc: 8bf8, ldr ip, [pc, #16] ; 0x00000018 pc: 8bfc, push {ip} ; (str ip, [sp, #-4]!) pc: 8c00, ldr r0, [pc, #12] ; 0x00000014 pc: 8c04, ldr r3, [pc, #12] ; 0x00000014 pc: 8c08, bl 0xfffffef4 pc changed to 8afc pc: 8afc, add ip, pc, #0, 12 pc: 8b00, add ip, ip, #176128 ; 0x2b000 pc: 8b04, ldr pc, [ip, #2428]! ; 0x97c pc changed to 8a1c pc: 8a1c, push {lr} ; (str lr, [sp, #-4]!) pc: 8a20, ldr lr, [pc, #4] ; 0x0000000c pc: 8a24, add lr, pc, lr pc: 8a28, ldr pc, [lr, #8]! pc changed to 0
armの場合0番地付近は割り込みベクターになっているはず。そこにアクセスするって事は、 SEGVが発生したんだな。まあ、当たり前の事だけど。
ちなみにsim/armのREADMEには
This directory contains the standard release of the ARMulator from Advanced RISC Machines, and was ftp'd from. ftp.cl.cam.ac.uk:/arm/gnu It likes to use TCP/IP between the simulator and the host, which is nice, but is a pain to use under anything non-unix. I've added created a new Makefile.in (the original in Makefile.orig) to build a version of the simulator without the TCP/IP stuff, and a wrapper.c to link directly into gdb and the run command.
こんな事が書いてあった。由緒ある祖先なわけね。ならば、どんな風に実行されてるか、 コールツリーでも眺めてみるか。
#0 ARMul_ReLoadInstr (state=0x8159240, address=35808, isize=4) at armvirt.c:197 #1 0x08067fbd in ARMul_Emulate32 (state=0x8159240) at ./armemu.c:560 #2 0x080556f4 in ARMul_DoProg (state=0x8159240) at arminit.c:240 #3 0x0804b9eb in sim_resume (sd=0x1, step=0, siggnal=0) at wrapper.c:269 #4 0x0804ba2a in sim_trace (sd=0x1) at wrapper.c:240 #5 0x0804a10b in main (ac=<optimized out>, av=<optimized out>) at ./../common/run.c:271
ふーん、arminst.cあたりを読むと、石の動きが分かるんか。面白そうだな。
先のSEGV相当で、arminit.cあたりのARMul_Abortの中に落ちるんだな。
switch (vector) { case ARMul_ResetV: /* RESET */ SETABORT (INTBITS, state->prog32Sig ? SVC32MODE : SVC26MODE, 0); break; case ARMul_UndefinedInstrV: /* Undefined Instruction */ SETABORT (IBIT, state->prog32Sig ? UNDEF32MODE : SVC26MODE, isize); break; case ARMul_SWIV: /* Software Interrupt */ SETABORT (IBIT, state->prog32Sig ? SVC32MODE : SVC26MODE, isize); break; case ARMul_PrefetchAbortV: /* Prefetch Abort */ :
そんでもって、上記が何処から呼ばれるかと言うと、armemu.cのARMul_Emulate32のトレース した後あたり。
if (! TFLAG && trace) { fprintf (stderr, "pc: %x, ", pc & ~1); if (! disas) fprintf (stderr, "instr: %x\n", instr); } if (instr == 0 || pc < 0x10) { ARMul_Abort (state, ARMUndefinedInstrV); state->Emulate = FALSE; }
PCが0x10以下か命令列が0だった時だな。つらつらと命令のシュミレータを見てたら 面白いのに出会った。
/* Branch and Link forward. */ case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7: /* Put PC into Link. */ state->Reg[14] = pc + 4; state->Reg[15] = pc + 8 + POSBRANCH; FLUSHPIPE; if (trace_funcs) fprintf (stderr, " pc changed to %x\n", state->Reg[15]); break;
サブルーチンを呼び出す命令。戻りアドレスをリンクレジスタにセーブして、飛び先を PCにセットして、パイプラインが乱れるのでフラッシュしてる。そしてPCの流れが分岐 するんで、それをユーザーに教えてる。
後、複雑なアドレス計算の末、データをメモリーの何処から持って来る(あるいは押し込む)か系の命令解読 実行ルーチンが有った。
switch ((int) BITS (20, 27)) { : case 0x6b: /* Load Word, WriteBack, Post Inc, Reg. */ if (BIT (4)) { if (state->is_v6 && handle_v6_insn (state, instr)) break; ARMul_UndefInstr (state, instr); break; } UNDEF_LSRBaseEQOffWb; UNDEF_LSRBaseEQDestWb; UNDEF_LSRPCBaseWb; UNDEF_LSRPCOffWb; lhs = LHS; temp = lhs + LSRegRHS; state->NtransSig = LOW; if (LoadWord (state, instr, lhs)) LSBase = temp; state->NtransSig = (state->Mode & 3) ? HIGH : LOW; break;
こいつらは、自分がもう少し石の動きを理解してからだな。
armos
ひたすらsim/armの下のソースを眺めていたら、armos.[ch]なんてのに出会った。 sim用にosと言うかBIOS相当の真似が出来るようになってるっぽい。
コードを丹念に見てけば、使い方が分かるだろうけど、どこかにサンプルが無いかな? 熱血アセンブラ本の一部がちらっと読めて、そこに例が載ってたぞ。
[sakae@fedora sim]$ ls testsuite/sim/arm/*.ms testsuite/sim/arm/hello.ms testsuite/sim/arm/misaligned3.ms testsuite/sim/arm/misaligned1.ms testsuite/sim/arm/movw-movt.ms testsuite/sim/arm/misaligned2.ms
サフィックスがmsとなってるのは、マクロを含んだアセンブラソースだからのようだ。 hello.msがどんなarmの石でも動くように書かれているっぽい。armの石の進化した命令を 使ったやつは、movw-movt.msっぽい。
早速、debian-armelに持って行ってアセンブル・実行してみるとIllegal instructionで 実行を拒否された。どんなソースかと言うと
_start: # Run some simple insns to confirm the engine is at least working. nop # Skip over output text. bl skip_output hello_text: .asciz "Hello, world.\n" .p2align 2 skip_output: movw r4, #:lower16:hello_text movt r4, #:upper16:hello_text
armの場合は、データをtext領域へ埋め込むのが普通みたいだ。で、その絶対アドレスを 32Bitで得る必要が有る。だけど、32Bitマシンで、32Bitの即値は埋め込めない。2回に 分けて、r4レジスタへロードしてる。movtが無効命令とみなされたんだ。
しょうがないので、gdb付属のシュミレータにかけてみる。
pc: 8054, nop ; (mov r0, r0) pc: 8058, bl 0x00000014 pc: 806c, movw r4, #32860 ; 0x805c pc: 8070, movt r4, #0 pc: 8074, mov r0, #3 pc: 8078, mov r1, r4 pc: 807c, svc 0x00123456 pc: 8080, add r4, r4, #1 pc: 8084, sub r3, r3, r3 pc: 8088, ldrb r5, [r4, r3] pc: 808c, teq r5, #0 pc: 8090, bne 0xffffffe4 pc: 8074, mov r0, #3 pc: 8078, mov r1, r4 pc: 807c, svc 0x00123456 pc: 8080, add r4, r4, #1 : pc: 8090, bne 0xffffffe4 pc: 8094, mov r0, #24 pc: 8098, ldr r1, [pc, #8] ; 0x00000010 pc: 809c, svc 0x00123456 Hello, world.
システムコール番号3ってのをr0に設定し、表示したい文字のポインタをr1に入れて、svc するのが流儀だな。r5で文字列の終わりを判定してんのか。
で、上のコードは、特殊な命令を使ってた。どんなarmでも動くようにするにはどうするか?
_start: # Run some simple insns to confirm the engine is at least working. nop # Skip over output text. bl skip_output hello_text: .asciz "Hello, world.\n" .p2align 2 skip_output: # Prime loop. mov r4, r14 output_next:
hello_textのアドレスを取り出すのに、bl(branch with link)を使ってる。こうすると、 r14には、戻りアドレスとして、hello_textのアドレスが入ってくる。本来の使い方ではないだろうけど、 arm特有なアセンブラのテクニックとして覚えておこう。