dc

女房にせかされて、ipad用のカバーを買いに行ってきた。ipadを買った時に 同時に購入したものだ。イタリア製とかで、6000円ぐらいしたと思う。

3年半の酷使で、端の方はボロボロ。かっこ良かった伊達なカバーも今や みすぼらしいものに成り下がってしまったから。貧乏臭いとぬかす。 どうせ、貧乏だから分相応と思うんだけど、変な所に気を使う。 (オイラーは、ボロは着てても、心は錦ですから、女房とは対極に位置します)

ipadもあれから数度の改定を経て、カバーも2016年式とか2014年式とか 言って売ってるのね。脇にすまなそうに、ipad Air2対応とかPro対応とか 書いてある。

もう古いのは売っていないのか? 店員さんに持って行ったipadを見せると 良く使いこまれましたねぇと感心された。その店員さんも同じカバーを 使っていたとか。隅っこの方に、申し訳なさそうに、2012年式ってやつが 置いてあり、これが昔のipadに対応するとの事。

色も形も選択の余地無し。エレコムさん、昔のユーザーも大事にしてよね。 でも、当時と比べて半額の値段で買えたから、佳しとするか。

ついでに、バッテリー交換の窓口電話を教わってきた。0120-277-535

電池の有るうちに交換しておこうと連絡を取ってみた。まず診断をしましょって 言われて、プライバシーの所に有る、アプルの診断をクリック。

ああ、その前にシリアルを教えろと言われたな。やっぱり、フォネテックス コードのやり取りが有ったぞ。アプルのフォネティクス表現は国名なんか。 ブラジルのBとかフランスのFとかイングランドのEとかね。 地名のモナコとかニューヨークとかシカゴとかやっちゃうと、昔のフォント名 みたいだから、国名に進化させたんだな。ああ、モナコってフォント好きだったよ。 オオサカは唯一の漢字フォントだったと、昔を思い出したぞ。

で、アプルケアのおねーさんの言う事には、まだ電池はもりもりしてるんで 交換の必要はありませんです、との事だった。もう少し現状のまま使ってみるぞ。

数日して、アンケートの依頼メールがやってきた。今後のサポート向上に 役立てますから、是非ご協力を、ですって。あんなアンケートで、向上するの かね?

ああ、電話した時に、一発目で製品毎に振り分けられるんだけど、一位が iphone、二位がwatch、三位がipad、四位がmacだったよ。アプルの優先順位が 垣間見られて、興味深いな。

Device tree

前回やって失敗した、armv7版のOpenBSDをqemuから動かす企み、まだ、残骸が 残っているので、検証しておくか。

対象のボードがどうなってるのってのは、Device Treeで表現して、それを 起動時に与えるって事だった。その方式により、カーネルは単一な物を使い 回せる事になった。

そのデバイスツリーを取り出してきて、逆変換すれば、ハードの構成が分かる はず。

[ob: arm]$ fdtdump sun4i-a10-cubieboard.dtb >cubie.dts
[ob: arm]$ wc cubie.dts
    1234    3782   49340 cubie.dts

1200行余りで、カーネル側が知りたいハードウェア情報を記述出来ているとな。 それも、ボードに載っているかも知れない全ての石を網羅してるのかな。 物は試し、ソースを眺めてみろ。

/dts-v1/;
// magic:               0xd00dfeed
// totalsize:           0x62a5 (25253)
// off_dt_struct:       0x38
// off_dt_strings:      0x5e6c
// off_mem_rsvmap:      0x28
// version:             17
// last_comp_version:   16
// boot_cpuid_phys:     0x0
// size_dt_strings:     0x439
// size_dt_struct:      0x5e34

/ {
    #address-cells = <0x00000001>;
    #size-cells = <0x00000001>;
    interrupt-parent = <0x00000001>;
    model = "Cubietech Cubieboard";
    compatible = "cubietech,a10-cubieboard", "allwinner,sun4i-a10";
    chosen {
        #address-cells = <0x00000001>;
        #size-cells = <0x00000001>;
        ranges;
        stdout-path = "serial0:115200n8";
        framebuffer@0 {
            compatible = "allwinner,simple-framebuffer", "simple-framebuffer";
            allwinner,pipeline = "de_be0-lcd0-hdmi";
            clocks = <0x00000002 0x00000001 0x00000003 0x00000024 0x00000003 0x0000002b 0x00000003 0x0000002c 0x00000004 0x0000001a>;
            status = "disabled";
        };
        framebuffer@1 {
            compatible = "allwinner,simple-framebuffer", "simple-framebuffer";
            allwinner,pipeline = "de_fe0-de_be0-lcd0-hdmi";

これ、冒頭付近のもの。chosenって、日本語に約すと、選んでちょって事。lcd用の フレームバッファーとかhdmi用のフレームバッファーとかが有るんだな。 statusがdisabledになってるんで、hdmiは選ばれていない(ハード的に搭載されてない) って事か。

    memory {
        device_type = "memory";
        reg = <0x40000000 0x80000000>;
    };
    cpus {
        #address-cells = <0x00000001>;
        #size-cells = <0x00000000>;
        cpu@0 {
            device_type = "cpu";
            compatible = "arm,cortex-a8";
            reg = <0x00000000>;
            clocks = <0x00000005>;
            clock-latency = <0x0003b9b0>;
            operating-points = <0x000f6180 0x00155cc0 0x000dea80 0x00149970 0x000d2f00 0x0013d620 0x00098580 0x001312d0>;
            #cooling-cells = <0x00000002>;
            cooling-min-level = <0x00000000>;
            cooling-max-level = <0x00000003>;
            cpu-supply = <0x00000006>;
            linux,phandle = <0x00000009>;
            phandle = <0x00000009>;
        };
    };

メモリーとかcpuの宣言。載ってる石の種別とかが分かる。linux用の宣言て何だろう。 石の特性までLinuxはちょっかい出してるんか? それじゃ、クリーンな石じゃなくなっちゃうじゃん。

        serial@01c28000 {
            compatible = "snps,dw-apb-uart";
            reg = <0x01c28000 0x00000400>;
            interrupts = <0x00000001>;
            reg-shift = <0x00000002>;
            reg-io-width = <0x00000004>;
            clocks = <0x00000034 0x00000010>;
            status = "okay";
            pinctrl-names = "default";
            pinctrl-0 = <0x00000035>;
        };

これはシリアルの宣言が。コントロールするためのレジスターがどのアドレスに 割付られているか、OSが知る必要があるんで、こういう風に定義されてるのね。 まさに、ハードとソフトの接点だな。

Linuxのソースツリーを覗くと、ラズパイ用の定義も載ってるかな。載ってたら、 それをぱくってきて差し替えちゃえば、理論的にはOpenBSDも動くはず。 まだ、ラズパイでOpenBSDを動かしたって報告は無いから、動けば世界初の 快挙になるでしょう。qemuなんかにしがみ付いていないで、ハード買えよ >俺。

Debianに入れてるカーネルを覗いてみた。

sakae@debian:/usr/src/linux-source-3.16/arch/arm/boot/dts$ ls -l bcm283*
-rw-r--r-- 1 root root  853  3月  2  2016 bcm2835-rpi-b.dts
-rw-r--r-- 1 root root 3877  3月  2  2016 bcm2835.dtsi

ざっと見、そのままでは使うの難しそう。それより、bcm2836とかは、何処に 有るんだろう? Debianの拡張を施したソースという触れ込みだから、ここに 有ってもおかしくないと思うんだけど。

FreeBSDにもdtsファイルが置いてあった。そもそもDevice Treeの発想って NetBSDに遡るんではなかろうか。色々な石、アークテクチャなシステムを NetBSDで侵食したい。

それぞれ用にOSを書き換えていたら、効率よく征服出来ない。どうしよう? OSは一つにしておいて、それぞれに異なるデバイスの差異は、別ファイルに 書いておけばいいじゃん。そうすれば、そのファイルを差し替えるだけで 新しいものに対応する。

/sys/boot/fdt/dts/armとか/sys/gnu/dts/armに色々堆積されてたぞ。 中を探ってみたけど、bcm2836系は無かった。何かトリックが有って、2836系に 対応させているのだろうか?

dc 使い

直流電源の話じゃないよ。

NAME
     dc – desk calculator

SYNOPSIS
     dc [-x] [-e expression] [file]

DESCRIPTION
     dc is an arbitrary precision arithmetic package.  The overall structure
     of dc is a stacking (reverse Polish) calculator i.e. numbers are stored
     on a stack.  Adding a number pushes it onto the stack.  Arithmetic
     operations pop arguments off the stack and push the results.  See also
     the bc(1) utility, which is a preprocessor for dc providing infix
     notation and a C-like syntax which implements functions and reasonable
     control structures for programs.  The options are as follows:

unixで計算機と言ったら、普通の人はbcを選択するけど、forth好きな人はdcを 選択します。

[ob: ~]$ dc
123 d * p
15129
1111111111111111111111111111111111 d * p
1234567901234567901234567901234567654320987654320987654320987654321
q

上記は簡単な使用例。入力した数値を2乗した結果を表示する。lispよろしく 無限精度演算が可能。勿論、少数点付き数値の演算も出来る。(1.2e2みたいな E表現の浮動小数点はだめだけど)

[la1+dsa*pla10>y]sy
0sa1
lyx
1
2
6
24
120
720
5040
40320
362880
3628800

こんな風に、マクロを使って、1から10までの階乗も計算出来るようだ。 どういう仕組みか、解析してみる。

括弧内に文字列を書く。この文字列は、マクロとして通常は使う。 文字列は、スタックに置かれる。srで、その文字列を取り出し、レジスタrに 保存する。

0sa1で、0をaレジの保存。1をpush。スタックの内容をモニターするには、f コマンドが便利。 lyx は、yレジスタ(最初に定義したマクロ)を、スタックに積む。xで スタックから値を取り出して、マクロを実行。

問題はマクロの中身。laで、aレジスタから値(0が入っているはず)を取り出し、 1をpush。足し算、dで複製、値をaレジスタに保存、掛け算して、結果を表示。 レジスタaからスタックに呼び戻し、10>yは、10と比較して、 もともとのスタックの先頭が大きい場合、レジスタy(マクロが入っている)を 実行。

図に描いてみないと、理解出来ないな。いや、そんな事は無いぞ。yレジスタは マクロの保存。aレジスタは、nの階乗のnを保存しとく役割。マクロを呼び出す時の スタックトップには、結果の初期値が入っている。 こういう条件で、マクロを追跡すれば、何ら難しい事は無い。なお、一点注意 しなければいけないのは、saとかでレジスタにtopの値をセーブすると、topの 値は消費されて無くなってしまう事。主戦場は常にスタックに有りだ。

dc の動きを追う

例によってどう動くか、追ってみる。ソースは、/usr/src/usr.bin/dcに鎮座してる。 emacsで追えるように、Makefileに小細工(CFLANGSを追加)

# cat Makefile
#       $OpenBSD: Makefile,v 1.3 2015/10/10 19:28:54 deraadt Exp $

PROG=   dc
SRCS=   main.c dc.c bcode.c inout.c mem.c stack.c
COPTS+= -Wall
LDADD=  -lcrypto
DPADD=  ${LIBCRYPTO}
CFLAGS+= -O0 -gdwarf-2 -g3

.include <bsd.prog.mk>

そしてmakeする。出来上がったdcは適当な所へ移動させとく。

# make
cc -O2 -pipe  -O0 -gdwarf-2 -g3 -Wall -Werror-implicit-function-declaration  -c main.c
cc -O2 -pipe  -O0 -gdwarf-2 -g3 -Wall -Werror-implicit-function-declaration  -c dc.c
cc -O2 -pipe  -O0 -gdwarf-2 -g3 -Wall -Werror-implicit-function-declaration  -c bcode.c
cc -O2 -pipe  -O0 -gdwarf-2 -g3 -Wall -Werror-implicit-function-declaration  -c inout.c
cc -O2 -pipe  -O0 -gdwarf-2 -g3 -Wall -Werror-implicit-function-declaration  -c mem.c
cc -O2 -pipe  -O0 -gdwarf-2 -g3 -Wall -Werror-implicit-function-declaration  -c stack.c
cc   -o dc main.o dc.o bcode.o inout.o mem.o stack.o -lcrypto
# cp dc /tmp

単なる計算器なのに、暗号ライブラリィーを使うって、どゆ事? manを見ていたら、答えが出てた。

HISTORY
     The dc command first appeared in Version 6 AT&T UNIX.  A complete rewrite
     of the dc command using the bn(3) big number routines first appeared in
     OpenBSD 3.5.

由緒あるコマンドだけど、ある時、無限数を扱えるbn(3)を導入して、書き直したよ。 そのルーチンは、opensslの一部。だから、暗号に関わってくるとな。 あれ、無限精度と言ったら、昔やったな。確かgmpだったか。但し、あちらは、 積極的にライブラリィーを入れないと使えないし、GPLはいやだって拘りが BSD界隈には有るのよね。

sakae@ub:~$ ldd /usr/bin/dc
        linux-gate.so.1 =>  (0xb77ff000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7638000)
        /lib/ld-linux.so.2 (0x800c7000)
sakae@ub:~$ echo '11111111111111111111111111111111d*p' | dc
123456790123456790123456790123454320987654320987654320987654321

ウブではどうかと調べてみたら、libcしか取り込んでいない。でも、BigNumが ちゃんと扱えている。何か手がかりが掴めるかとnmとかstringsしたけど、 手がかりになりそうなものは全て削除されてた。つまんないOSだな。

そんじゃ、例のごとくgdbで追ってみる。与えるデータは、ファイルに書いた 5d*p と言うもの。動きを知るには、これぐらいが適当かな。

起動すると、初期化の後、evalへ飛んでくる素直な作り。

(gdb) bt
#0  eval () at bcode.c:1712
#1  0x16c95b91 in dc_main (argc=1, argv=0xcf7e3a48) at dc.c:103
#2  0x16c957fa in main (argc=2, argv=0xcf7e3a44) at main.c:33

このevalの中に文字応じたジャンプテーブルが用意されてて、細かい処理は それに委ねているようだ。

=>              if (0 <= ch && ch < UCHAR_MAX)
                        (*jump_table[ch])();

ちょっと先へ進めると、掛け算が出てきた。

(gdb) p/c ch
$7 = 42 '*'
(gdb) s
1736                            (*jump_table[ch])();
static void
bmul(void)
{
        struct number   *a, *b;
        struct number   *r;

=>      a = pop_number();
        if (a == NULL)
                return;
        b = pop_number();
        if (b == NULL) {
                push_number(a);
                return;
        }

        r = new_number();
        bmul_number(r, a, b, bmachine.scale);

        push_number(r);
        free_number(a);
        free_number(b);
}

典型的な事をやってるな。相手が無限精度数字なんで、それなりの対応で、 結果を返す場所を用意して、掛け算実施、いらなくなったエリアを開放とか してる。肝心の掛け算ルーチンは、

void
bmul_number(struct number *r, struct number *a, struct number *b, u_int scale)
{
        BN_CTX          *ctx;

        /* Create copies of the scales, since r might be equal to a or b */
        u_int ascale = a->scale;
        u_int bscale = b->scale;
        u_int rscale = ascale + bscale;

        ctx = BN_CTX_new();
        bn_checkp(ctx);
        bn_check(BN_mul(r->number, a->number, b->number, ctx));
        BN_CTX_free(ctx);

        r->scale = rscale;
        if (rscale > bmachine.scale && rscale > ascale && rscale > bscale)
                normalize(r, max(scale, max(ascale, bscale)));
}

ここで出てきてるスケールは、少数点以下の桁数。内部は整数で扱ってて、どこに 少数点が有るかを、別管理してるのだな。

ついでに、ジャンプテーブルを見ておくと

static const struct jump_entry jump_table_data[] = {
        { ' ',  nop             },
        { '!',  not_compare     },
        { '#',  comment         },
        { '%',  bmod            },
        { '(',  less_numbers    },
        { '*',  bmul            },
        { '+',  badd            },
        { '-',  bsub            },
        { '.',  parse_number    },
        { '/',  bdiv            },
        { '0',  parse_number    },
        { '1',  parse_number    },
        { '2',  parse_number    },
           :
        { 'v',  bsqrt           },
        { 'x',  eval_tos        },
        { 'z',  stackdepth      },
        { '{',  lesseq_numbers  },
        { '~',  bdivmod         }
};

こんな分かり易い風になってた。

Linuxではどうよ

今までソースを見るのは主としてBSD系にしてた。だって、そこにソースが有るなら 見るに限るってね。でも、これ直交性が無いよね。

リナ系でソース読むのはどうしたらいい? Debian 管理者ハンドブック こんな素晴らしいハンドブックが有ったぞ。そして、こんな例も、 DebianやUbuntuで公式パッケージのソースをダウンロード/ビルドする

まずは、ソースのお取り寄せ

sakae@debian:~/testme$ apt-get source dc
Reading package lists... Done
Building dependency tree
Reading state information... Done
Picking 'bc' as source package instead of 'dc'
NOTICE: 'bc' packaging is maintained in the 'Git' version control system at:
git://anonscm.debian.org/collab-maint/bc.git
Need to get 387 kB of source archives.
Get:1 http://ftp.jp.debian.org/debian/ jessie/main bc 1.06.95-9 (dsc) [2906 B]
Get:2 http://ftp.jp.debian.org/debian/ jessie/main bc 1.06.95-9 (tar) [361 kB]
Get:3 http://ftp.jp.debian.org/debian/ jessie/main bc 1.06.95-9 (diff) [23.3 kB]
Fetched 387 kB in 1s (256 kB/s)
gpgv: keyblock resource `/home/sakae/.gnupg/trustedkeys.gpg': file open error
gpgv: Signature made Sat Jun 14 19:48:44 2014 JST using RSA key ID 4A11C97A
gpgv: Can't check signature: public key not found
dpkg-source: warning: failed to verify signature on ./bc_1.06.95-9.dsc
dpkg-source: info: extracting bc in bc-1.06.95
dpkg-source: info: unpacking bc_1.06.95.orig.tar.gz
dpkg-source: info: unpacking bc_1.06.95-9.debian.tar.xz
dpkg-source: info: applying 01_typo_in_bc.diff
dpkg-source: info: applying 02_hyphens_as_minus_in_man.diff
dpkg-source: info: applying 03_array_initialize.diff
dpkg-source: info: applying 04_info_dircategory.diff
dpkg-source: info: applying 05_notice_read_write_errors.diff
dpkg-source: info: applying 06_read_dcrc.diff
dpkg-source: info: applying 07_bc_man.diff

どんな物がやって来たか、一応確認。dcを頼んだのにbcがやってきた。 bcとdcは対になってるんか。

sakae@debian:~/testme$ ls
bc-1.06.95                  bc_1.06.95-9.dsc
bc_1.06.95-9.debian.tar.xz  bc_1.06.95.orig.tar.gz

もう、ソースは展開されてた。

sakae@debian:~/testme$ cd bc-1.06.95/
sakae@debian:~/testme/bc-1.06.95$ ls
AUTHORS      FAQ          README       configure     doc
COPYING      INSTALL      Test         configure.in  h
COPYING.LIB  Makefile.am  aclocal.m4   dc            install-sh
ChangeLog    Makefile.in  bc           debian        lib
Examples     NEWS         config.h.in  depcomp       missing

dcをコンパイルするのに必要な関係者を召還する。

sakae@debian:~/testme/bc-1.06.95$ sudo apt-get build-dep dc
Reading package lists... Done
Building dependency tree
Reading state information... Done
Picking 'bc' as source package instead of 'dc'
The following NEW packages will be installed:
  bison debhelper flex gettext intltool-debian libbison-dev libfl-dev
  libreadline-dev libreadline6-dev libtinfo-dev libunistring0 po-debconf
0 upgraded, 12 newly installed, 0 to remove and 2 not upgraded.
Need to get 4466 kB of archives.
After this operation, 13.6 MB of additional disk space will be used.
  :
Setting up libreadline6-dev:i386 (6.3-8+b3) ...
Setting up libreadline-dev:i386 (6.3-8+b3) ...
Processing triggers for libc-bin (2.19-18+deb8u4) ...

ソース内で、コンパイル開始

sakae@debian:~/testme/bc-1.06.95$ dpkg-buildpackage -b -uc
  :
dpkg-deb: building package `dc' in `../dc_1.06.95-9_i386.deb'.
 dpkg-genchanges -b >../bc_1.06.95-9_i386.changes
dpkg-genchanges: binary-only upload (no source code included)
 dpkg-source --after-build bc-1.06.95
dpkg-buildpackage: binary-only upload (no source included)

ソースの上の階に、パッケージが出来上がっていた。

sakae@debian:~/testme$ ls
bc-1.06.95                  bc_1.06.95-9_i386.changes  dc_1.06.95-9_i386.deb
bc_1.06.95-9.debian.tar.xz  bc_1.06.95-9_i386.deb
bc_1.06.95-9.dsc            bc_1.06.95.orig.tar.gz

ここで、bcとdcが分離されるのね。

dc: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e51112b241e7486c253f4426620d48f3244ab9ca, not stripped

ソース内に出来上がった物は、debug出来るようにコンパイルされてた。パッケージに する時に、要らない物を全部そぎ落としているんだな。