貧乏人のラズパイ(もどき) (3)

以前、秋月からACアダプターを買った友人の事を書いた。この間、その友人から 連絡が有ったんで、顛末を聞いてみた。

差し込むプラグの大きさが微妙に違っていて、接続出来なかったとか。 ACアダプタ - Wikipedia を見ると、確かに、1mmぐらいの刻みでサイズが異なっているな。

そんなの、事前にノギスで測って、確認しておけばいいじゃん。ブブー。普通の家に ノギスなんて有りません。とか言うと、女房なら、まずは百均見て来いと言われそう。

この間も、新潟は三条からやってきた刃物のデパートって屋台で、ビンとかペット ボトルの首に付いているプラスチックの帯を外す、エコ鋏ってのを480円で売ってる のを見つけた。

取り外しに苦労してる女房のために、買おうと思ったけど、またこんなの買って きて、百均見たかよって、怒られそうなので、止めた。 話が逸れた。

ノギス無く、プラグの直径をどうやって測る? 3秒考えて、思い付いたぞ。 糸を一巻き撒きつけて、印を付ける。その印の間の長さを測って、3.14で割れ ばいいじゃん。それってメタボ診断の応用じゃん。誰でも思い付くぞ。

で、友人曰く、プラグを取り替えればいいんだけど、導通確認のテスター、半田コテ、 ニッパーとかが無い。(会社から、退職記念に持ってくる訳にはいかないね)

この間、ホームセンターで見たら、デジタルテスターが、1000円で買えて、 アナログテスターが3000ぐらいとか。出来たら、針が振れるアナログ式が良いとか (これ、オイラーも激同意)

コテもチップの温度管理が出来るやつって言ったら、べらぼうな値段がする。 なんだかんだ、道具を集めるだけで、とんでもない事になりそう。

そうなんだよな。ハードに手を出すと!

今記事にしてるラズパイもそう。ブレッドボードでLEDをチカチカさせようったって、 抵抗は100本単位になっちゃうし、これはもう、どろ沼ですよ。ハードに身を 沈めないと、きっちりとリソースを消費出来ないぞ。

これを考えると、電子工作って、覚悟が要る趣味と思うぞ。

ああ、結局、ACアダプターのプラグをどうしたか、聞きそびれたな。

ちょっと整理

暫く前からarmやってる。ハローHello,World本に紹介されてた、cross2-20130826.zip から、FreeBSDに環境を作ったりした。

gccとかgdbがちょっとレトロだけど、練習するにはこれで十分な感じがする。 gccのコンパイルに膨大な時間がかかるかと思っていたけど、昔のgccは最適化には 手が回らなかったみたいで、すんなりコンパイル出来て嬉しい。 貧乏人には嬉しいですよ。

このcross2のREADMEには、富豪ならコンパイルする時、make -J10ぐらいは 出来るでしょって書いてあったけど、おいらは素直に まけ ですよ。

そんなオイラーでも、古いgdbはちょっと御免なさいしたい。OpenBSDのarm系では、 gdb-7.9.1が採用されてて、これがすこぶる便利。

一番新しい、gdb-7.11.1とかになると、便利さが削減されちゃってるので、armな人 には、幻のgdbって呼ばれるに違いない。

で、gdbのソースをOpenBSDから取ってきて、portsの所でmake fetchすれば、適当な 所から落としてくれるので、ソースの在り処を探し回る必要無し。

次は手動で、conigure。与えるパラメータは、

$ arm-elf-gdb
GNU gdb (GDB) 7.3.1
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i386-unknown-freebsd10.2 --target=arm-elf".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
(gdb)

起動画面に出てるのを、そのままパクレばいい。おいらは、こうして出来た、 gdb/gdbと sim/arm/run を、/usr/local/cross/binにmvした。今考えたら、gdbって 名前、前から鎮座してる糞石用のやつと被るな。命名権は先の物が優先なんで、 後から出来たのを、agdb、arunに変更しといた。

$ arun -d -t -z a.out
  :
pc: 8cb4,  mov  r0, r7
pc: 8cb8,  ldm  sp, {r4, r5, r6, r7, r8, fp, sp, pc}
 pc changed to 82ec
pc: 82ec,  b    0xfffffff0
pc: 82dc,  mov  r0, r4
pc: 82e0,  bl   0x000022cc
 pc changed to a5ac
pc: a5ac,  mvn  r1, #0
pc: a5b0,  b    0xffffffa8
pc: a558,  push {r4, r5, r6, lr}
pc: a55c,  cmp  r1, #6
  :

こんな具合に、dオプションで、逆アセンブル、tオプションでトレース、zオプションで pcの流れが変わった(サブルーチン呼び出し等)時に、報告。と言った具合に、 昔のBASIC並みな事が出来る。なお、このログはstderrに対してなされるので、 ファイルに落とすなら、

$ arun -d -t -z a.out 2>TRACE.log
Hello ARM, input pls.
fuga
fuga
$ head -3 TRACE.log
pc: 8110,  movs r0, #22
pc: 8114,  add  r1, pc, #272    ; 0x110
pc: 8118,  svc  0x00123456

こんな風にやればよい。

不快 PATH

前回の最後で、オブジェクトのコードがどのソース由来か知るために、gdbで btしてみた。再度そのデータを載せる。

#0  initialise_monitor_handles ()
    at ../../../../../../../../toolchain/gcc-3.4.6/newlib/libc/sys/arm/syscalls.c:141
#1  0x00008174 in start ()
    at ../../../../../../../../toolchain/gcc-3.4.6/newlib/libc/sys/arm/crt0.S:274

このデータって、そのままの形で格納されてるの?

$ arm-elf-readelf -a a.out |grep -e \\.c$
  :
   359: 00000000     0 FILE    LOCAL  DEFAULT  ABS wbuf.c
   363: 00000000     0 FILE    LOCAL  DEFAULT  ABS wcrtomb.c
   369: 00000000     0 FILE    LOCAL  DEFAULT  ABS wctomb_r.c

readelf じゃ、ファイル名しか出てこない。

$ arm-elf-strings -a a.out | grep \\.c
  :
../../../../../../../toolchain/gcc-3.4.6/newlib/libc/stdio/stdio.c
../../../../../../../../toolchain/gcc-3.4.6/newlib/libc/machine/arm/strlen.c
../../../../../../../../toolchain/gcc-3.4.6/newlib/libc/sys/arm/syscalls.c
../../../../../../../toolchain/gcc-3.4.6/newlib/libc/stdio/vfprintf.c
../../../../../../../toolchain/gcc-3.4.6/newlib/libc/reent/writer.c
  :

stringsでも、全エリア対象って -a を付けて、やっと出てきた。

$ arm-elf-strings -a a.out | grep crt0.S
../../../../../../../../toolchain/gcc-3.4.6/newlib/libc/sys/arm/crt0.S
crt0.S

readelfでは、crt0.Sは出てこなかったけど、こちらは正直でしたよ。 それにしても、相対PATHの表現が不快ですな。ああ、深いが正解かな。

それぞれのオブジェクトが置いてある所から、上(root)に向かって登って行って、 Topに辿り着いたら、そこからソースの有る所を目指して下りて行く。 こんなのが、gdbへのサービスのために、記録されてるんだな。

頂上目指して登って行くのはいいけど、そこから下るのはどうするの? 親dirを 意味する .. は、言わば匿名dir、何でも良いと言ってるのと同じ。そこんとこ どうなってるんだろう?

少なくても、/home/sakae/cross2ぐらいの道標が無いと下れんわな。

$ arm-elf-strings -a a.out | grep sakae
/home/sakae/cross2/build/gcc/arm-elf/arm-elf/libgloss/arm
  :

やっぱり、思った通りだ。ちゃんと署名が入っているよ。これって、gdb用の 付属情報? gdb情報無しでコンパイルしてみる。

$ arm-elf-gcc hello.c
$ arm-elf-strings -a a.out | grep sakae
/home/sakae/cross2/build/gcc/arm-elf/arm-elf/libgloss/arm
  :

やっぱり、入っているなあ。じゃ、stripしてみるか。

$ arm-elf-strip a.out
$ arm-elf-strings -a a.out | grep sakae
$

これで、やっと消えてくれた。情報漏洩には気を付けましょう。>ウィルス作者さん

シュミレータがOSの振りをする

arunがOSの振りをして、サービスを行う。どんなシステムコールもどきが発せられるか 確認してみる。

$ gdb -q arun
Reading symbols from arun...done.
(gdb) b ARMul_OSHandleSWI
Breakpoint 1 at 0x8056b20: file armos.c, line 450.
(gdb) run a.out
Starting program: /usr/local/cross2/bin/arun a.out

Breakpoint 1, ARMul_OSHandleSWI (state=state@entry=0x28814400,
    number=number@entry=1193046) at armos.c:450
450     {

armos.cは、gdb/sim/arm/配下にある、割り込みハンドラー相当だ。ここを 見張っていると、どんなシステムコールが発せられたか、確認出来る。

690                 case AngelSWI_Reason_HeapInfo: 
728                 case AngelSWI_Reason_Open: 
728                 case AngelSWI_Reason_Open: 
686                 case AngelSWI_Reason_GetCmdLine: 
748                 case AngelSWI_Reason_IsTTY: 
741                 case AngelSWI_Reason_Write: 
748                 case AngelSWI_Reason_IsTTY: 
734                 case AngelSWI_Reason_Read: 
741                 case AngelSWI_Reason_Write: 
670                 case AngelSWI_Reason_Close: 
670                 case AngelSWI_Reason_Close: 
670                 case AngelSWI_Reason_Close: 
724                 case AngelSWI_Reason_Errno: 
706                 case AngelSWI_Reason_ReportException: 

こんな形で、呼ばれて、そしてアプリ a.out が終了した。Openを2回呼んでる のは、stdinとstdoutでしょうかね。追ってけば分かるだろうけど、今回はpass。

それに対してCloseを3回実行してるのは、3回目にわざとエラーを起して、 例外を上げ、それをきっかけに、アプリを終了させようって言う、苦肉な 作戦なんだろうね。

初回のHeapInfoは、スタックをどのあたりに設定したら良いかの情報を得て いるっぽいな。なんとなく、軽めのOSっぽい処理してんな。

次はユーザーランド側

やっぱり、システムコールがどう発せられるかあたりが、観賞ポイントになるかな。 それ以外は、後ほどやってみよう。

$ agdb -q a.out
Python Exception <type 'exceptions.AttributeError'> 'module' object has no attribute 'Command':
agdb: warning:
Could not load the Python gdb module from `/usr/local/cross2/share/gdb/python'.
Limited Python support is available from the _gdb module.
Suggest passing --data-directory=/path/to/gdb/data-directory.

Reading symbols from a.out...done.
(gdb) target sim
Connected to the simulator.
(gdb) load
Loading section .init, size 0x1c vma 0x8000
  :
Loading section .data, size 0x9d0 vma 0x19b44
Start address 0x8110
Transfer rate: 338080 bits in <1 sec.
(gdb) r
Starting program: /usr/home/sakae/hello/a.out
Hello ARM, input pls.
^C
Program received signal SIGINT, Interrupt.
0x0000a1cc in _swiread (file=-1, ptr=0x6 "", len=2096944)
    at ../../../../../../../../toolchain/gcc-3.4.6/newlib/libc/sys/arm/syscalls.c:103
103       asm volatile ("mov r0, %1; mov r1, %2; " AngelSWIInsn " %a3; mov %0, r0"

作ったagdb本体だけをコピーしちゃったので、Pythonサポートに必要な付属品が 無いと文句を言われたけど、そんなのガッテン承知。 いきなりアプリを走らせて、アプリの入力待ちで、Ctrl-Cでdebuggerに入る。 そして、

(gdb) bt
#0  0x0000a1cc in _swiread (file=-1, ptr=0x6 "", len=2096944)
    at ../../../../../../../../toolchain/gcc-3.4.6/newlib/libc/sys/arm/syscalls.c:103
#1  0x0000a214 in _read (file=0, ptr=0x1ab38 "", len=1024)
    at ../../../../../../../../toolchain/gcc-3.4.6/newlib/libc/sys/arm/syscalls.c:232
#2  0x0000f540 in _read_r (ptr=0x19c5c <impure_data>, fd=2096944,
    buf=0x19c5c <impure_data>, cnt=107880)
    at ../../../../../../../toolchain/gcc-3.4.6/newlib/libc/reent/readr.c:58
#3  0x00009eb8 in __sread (ptr=0xffffffff, cookie=0x19f48 <impure_data+748>,
    buf=0x19c5c <impure_data> "", n=107880)
    at ../../../../../../../toolchain/gcc-3.4.6/newlib/libc/stdio/stdio.c:48
#4  0x00009d28 in __srefill_r (ptr=0x19c5c <impure_data>,
    fp=0x19f48 <impure_data+748>)
    at ../../../../../../../toolchain/gcc-3.4.6/newlib/libc/stdio/refill.c:119
#5  0x000083ac in _fgets_r (ptr=0x19c5c <impure_data>, buf=0x1a624 <buff> "",
    n=254, fp=0x19f48 <impure_data+748>)
    at ../../../../../../../toolchain/gcc-3.4.6/newlib/libc/stdio/fgets.c:132
#6  0x00008274 in main () at hello.c:7

いいねぇ。ソースが有るてとっても素敵。

on Debian

調子こいて、Debianにも環境を作っておいたので、試してみる。

sakae@debian:~/hello$ arm-none-eabi-gcc hello.c
/usr/lib/gcc/arm-none-eabi/4.8/../../../arm-none-eabi/lib/libc.a(lib_a-exit.o): In function `exit':
/home/tin/projects/debian/arm-toolchain/collab-maint/newlib/build/arm-none-eabi/newlib/libc/stdlib/../../../../../newlib/libc/stdlib/exit.c:70: undefined reference to `_exit'
  :

こんな具合に、見事にエラーですよ。おい、tinさん、名前が割れてるから、文句を 垂れるか。チンさんだろうから台湾の人? それとも上海あたりの人?

日本のこんな片田舎で悪態ついていても、何の解決にもならないので、ぐぐる先生に 聞いてみたよ。

gcc - Linker error on a C project using eclipse こんなのを提示された。みんな悩みは一緒って事で、思わずほっとする。 (そんな事で、ほっとするんじゃねぇ!)

カリカリなハード屋さん向けと言うか、組み込み屋さん向けにパッケージを作ったんで 、newlibなんてヤワなやつは使わないようにしてるんよーー、って事らしい。 確かに、gcc -vして出て来る、作成仕様には、 --without-newlib なのやら、 色々なものが、disableされてた。必要な物が有ったら、自分でカスタマイズ しろ、その為の土台を提供してるんよ。

その壁を乗り越えれなかったら、ソフト開発なんてやめてしまえって言う、愛の鞭が 隠されてましたよ。

軟弱者は、次のようにして、newlibを使えとな。これって、rdimon用のスペックを 使ってコンパイル。リンカーには、start-groupとeng-groupってのを渡して るんだな。なんたらスペックでコンパイラーの性格を切り替えるのか。

sakae@debian:~$ locate rdimon.specs
/usr/lib/arm-none-eabi/newlib/rdimon.specs
/usr/lib/arm-none-eabi/newlib/armv6-m/rdimon.specs
  :
sakae@debian:~$ cat /usr/lib/arm-none-eabi/newlib/rdimon.specs
%rename link_gcc_c_sequence                rdimon_link_gcc_c_sequence

*rdimon_libc:
%{!specs=nano.specs:-lc} %{specs=nano.specs:-lc_nano}

*rdimon_libgloss:
%{!specs=nano.specs:-lrdimon} %{specs=nano.specs:-lrdimon_nano}

*link_gcc_c_sequence:
%(rdimon_link_gcc_c_sequence) --start-group %G %(rdimon_libc) %(rdimon_libgloss) --end-group

*startfile:
crti%O%s crtbegin%O%s %{!pg:rdimon-crt0%O%s} %{pg:rdimon-crt0%O%s}

ほとんどが、リンカーに対する指示をしてるんだな。

取りあえず、コンパイルしてみる。

sakae@debian:~/hello$ arm-none-eabi-gcc --specs=rdimon.specs   -Wl,--start-group -lgcc -lc -lm -lrdimon -Wl,--end-group hello.c
sakae@debian:~/hello$ ls
a.out  hello.c
sakae@debian:~/hello$ arm-none-eabi-run a.out
Hello ARM, input pls.
HOGEFUGA
HOGEFUGA

素直に例の通りにオプションを与えたけど、スペック表に、使うライブラリィー等が 指示されてるじゃん。よって、与えるオプションは、--specs=rdimon.specs だけで いいんちゃう? -> 正にその通りでしたよ。一手間かけると、後が楽に なるね。なお、このオプションの念力はどこでも通用するようで、Fedoraでもリナ でも、大丈夫だったぞ。

次は、gdbで観察

sakae@debian:~/hello$ arm-none-eabi-gdb a.out
GNU gdb (7.7.1+dfsg-1+6) 7.7.1
  :
This GDB was configured as "--host=i586-linux-gnu --target=arm-none-eabi".
Reading symbols from a.out...done.
(gdb) target sim
Connected to the simulator.
(gdb) load
Loading section .init, size 0x18 vma 0x8000
  :
Start address 0x8198
Transfer rate: 150592 bits in <1 sec.
(gdb) b main
Breakpoint 1 at 0xa8cc
(gdb) run
Starting program: /home/sakae/hello/a.out

Breakpoint 1, 0x0000a8cc in main ()
(gdb) s
Single stepping until exit from function main,
which has no line number information.
puts (s=0xbffc "Hello ARM, input pls.")
    at ../../../../../newlib/libc/stdio/puts.c:138
138     ../../../../../newlib/libc/stdio/puts.c: No such file or directory.
(gdb) c
Continuing.
Hello ARM, input pls.
^C
Program received signal SIGINT, Interrupt.
0x000088d4 in do_AngelSWI (arg=0x1fff74, reason=6)
    at ../../../../libgloss/arm/swi.h:78
78      ../../../../libgloss/arm/swi.h: No such file or directory.
(gdb) bt
#0  0x000088d4 in do_AngelSWI (arg=0x1fff74, reason=6)
    at ../../../../libgloss/arm/swi.h:78
#1  _swiread (fh=-1, ptr=0x1fff74 "", ptr@entry=0x1cfa0 "", len=len@entry=1024)
    at ../../../../libgloss/arm/syscalls.c:252
#2  0x0000892c in _read (fd=<optimized out>, ptr=0x1cfa0 "", len=1024)
    at ../../../../libgloss/arm/syscalls.c:285
#3  0x0000a778 in _read_r (ptr=0x1c150 <impure_data>, fd=<optimized out>,
    buf=<optimized out>, cnt=<optimized out>)
    at ../../../../../newlib/libc/reent/readr.c:58
#4  0x00009e24 in __sread (ptr=<optimized out>,
    cookie=0x1c43c <impure_data+748>, buf=<optimized out>, n=<optimized out>)
    at ../../../../../newlib/libc/stdio/stdio.c:48
#5  0x0000ba28 in __srefill_r (ptr=ptr@entry=0x1c150 <impure_data>,
    fp=fp@entry=0x1c43c <impure_data+748>)
    at ../../../../../newlib/libc/stdio/refill.c:119
#6  0x0000a9d0 in _fgets_r (ptr=0x1c150 <impure_data>, buf=0x1ca90 <buff> "",
    n=254, fp=0x1c43c <impure_data+748>)
    at ../../../../../newlib/libc/stdio/fgets.c:132
#7  0x0000a8f8 in main ()

今回は、これぐらいにしておくか。