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特有なアセンブラのテクニックとして覚えておこう。