AdafruitのRaspberry Piケースを購入

Adafruitの通信販売でRaspberry Piケースを購入。

お値段 $14.95、送料が$10.28 で合計 $25.23。Pay Palで支払って 2,035円でした。
注文して2日で発送されて、そこから7日で、日本郵便で自宅まで届きました。


開封すると、レーザー加工でアクリルの保護紙が焦げた匂いが少し漂う。


保護紙をテープでくっつけて剥がしていくとこんな感じ。


あとはパーツを順番にはめていくだけ。下部の切れ込みが入っている部分を押しながらはめ込むと、アクリルの弾力で固定されるようになっているので、接着剤などはいらず簡単に組み立たり外したりできます。Ethernetポートの下にスペーサ(Ethernetポートのフタ部分にくっついてる部品)を入れ忘れないように注意。

スリットが入っているのでGPIO端子へのアクセスも問題なし。


詳しい組み立て方はYouTubeで公開されています。

単体でも実行可能な共有ライブラリの作り方

Fedora 17に含まれる実行可能な共有ライブラリ - Okiraku Programming の続きです。


さて、前記事で説明した glibc のような実行可能なshared libraryを作るにはどうすれば良いか? が気になるところですね?


基本的にはentry pointを指定して共有ライブラリを作成すればOKです。

が、ちょっと罠があって、普通に共有ライブラリを作ってしまうと、プログラムヘッダ中に存在するインタプリタ情報(INTERP)が設定されないため、他のライブラリといっさいリンクされない状態で実行が開始されてしまい、printf などのライブラリ関数が全く呼べない状態になってしまいます。これでもアセンブリで直接システムコール呼び出しすれば、メッセージ表示などもできないことはないのですが…(もちろんglibcはそうしてます)。


test.c

#include <stdio.h>
#include <stdlib.h>
void msg() {
	puts("libtest-0.1");
	exit(0);
}
% gcc -shared -fPIC -e msg test.c -o libtest.so      # -e msg でエントリポイントを指定
% ./libtest.so
zsh: segmentation fault  ./libtest.so

↑落ちた… (gdbでトレースしてみると、puts@pltを解決しようとして、未リンクの場所に飛んでしまっています。)

% readelf -l libtest.so

Elf file type is DYN (Shared object file)
Entry point 0x6b0
There are 6 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000764 0x0000000000000764  R E    200000
  LOAD           0x0000000000000768 0x0000000000200768 0x0000000000200768
                 0x0000000000000238 0x0000000000000240  RW     200000
  DYNAMIC        0x0000000000000788 0x0000000000200788 0x0000000000200788
                 0x00000000000001c0 0x00000000000001c0  RW     8
  NOTE           0x0000000000000190 0x0000000000000190 0x0000000000000190
                 0x0000000000000024 0x0000000000000024  R      4
  GNU_EH_FRAME   0x00000000000006e4 0x00000000000006e4 0x00000000000006e4
                 0x000000000000001c 0x000000000000001c  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     8
...

インタプリタ情報(INTERP)がない。


それならと、ld に --dynamic-linker=/lib64/ld-linux*.so でも渡せば良いのかと思いきや、-shared指定があるとこれでもうまくいきません。


仕方ないので、ライブラリのソース中に .interp セクションを指定してINTERPを作らせるようにします。(→参考: http://gcc.gnu.org/ml/gcc-help/2003-07/msg00232.html )

test.c

#include <stdio.h>
#include <stdlib.h>
const char interp[] __attribute__((section(".interp"))) = "/lib64/ld-linux-x86-64.so.2";
void msg()
{
	puts("libtest-0.1");
	exit(0);            /* この関数を抜ける前に終了しないとSEGVで異常終了する */
}
% gcc -shared -fPIC -e msg test.c -o libtest.so      # -e msg でエントリポイントを指定
% ./libtest.so
libtest-0.1

↑実行できた! (もちろんshared libraryとして、他のプログラムから gcc -ltest 〜でリンクして関数呼び出しもできます。)

% readelf -l libtest.so

Elf file type is DYN (Shared object file)
Entry point 0x740
There are 8 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000001c0 0x00000000000001c0  R E    8
  INTERP         0x0000000000000780 0x0000000000000780 0x0000000000000780
                 0x000000000000001c 0x000000000000001c  R      10
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x000000000000081c 0x000000000000081c  R E    200000
  LOAD           0x0000000000000820 0x0000000000200820 0x0000000000200820
                 0x0000000000000238 0x0000000000000240  RW     200000
  DYNAMIC        0x0000000000000840 0x0000000000200840 0x0000000000200840
                 0x00000000000001c0 0x00000000000001c0  RW     8
  NOTE           0x0000000000000200 0x0000000000000200 0x0000000000000200
                 0x0000000000000024 0x0000000000000024  R      4
  GNU_EH_FRAME   0x000000000000079c 0x000000000000079c 0x000000000000079c
                 0x000000000000001c 0x000000000000001c  R      4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     8
...

今度はINTERPが指定されており、ちゃんとライブラリ呼び出しも含めて実行できいることが分かります。

Fedora 17に含まれる実行可能な共有ライブラリ

一般的な共有ライブラリ(shared library。lib*.so)は実行可能ではありませんが、その構造は実行ファイルと同じELF形式で、大きな違いはありません。( →参考: http://mkosaki.blog46.fc2.com/blog-entry-107.html )
例外的に、glibc ( /lib64/libc-*.so ) は、ほぼあらゆる実行バイナリからリンクされる共有ライブラリですが、実はそれ自身が実行可能でもあります。
実際に実行してみると、自分自身のバージョンを表示して終了します。

% /lib64/libc-2.15.so
GNU C Library stable release version 2.15, by Roland McGrath et al.
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.7.0 20120504 (Red Hat 4.7.0-4).
Compiled on a Linux 3.3.4 system on 2012-05-11.
Available extensions:
	Support for some architectures added on, not maintained in glibc core.
	The C stubs add-on version 2.1.2.
	crypt add-on version 2.1 by Michael Glad and others
	GNU Libidn by Simon Josefsson
	Native POSIX Threads Library by Ulrich Drepper et al
	BIND-8.2.3-T5B
	RT using linux kernel aio
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.


何となく、Fedora 17に、こういったライブラリがどれくらいあるか調べてみました。調べ方はこんな感じ。"(*)" というのはzshの拡張グロブで実行可能なものだけを取り出しています。なお、Fedora 17からは /lib64 は /usr/lib64 へのsymbolic linkになったので、/usr/lib64 だけで十分です。

zsh% for x in /usr/lib{,64}/*(*); do echo "Trying $x ..."; $x && echo $x >> /tmp/lib-exectable.txt; done

/tmp/lib-exectable.txt には、こんな感じのものが表示されました。...

続きを読む

hidden symbolの関数呼び出しをフックする

Linuxでは一般的なライブラリ関数の呼び出しはLD_PRELOADを使ってフックして、別の動作をさせる(オーバーライドする)ことができます。
(参考: LD_PRELOADでBrainf*ck - Okiraku Programming


しかし、フックできる関数には条件があって、PLT(procedure linkage table)と呼ばれるテーブルを経由して呼び出されているものに限られます。
ライブラリによってはhidden symbolといって、同一ライブラリ内での関数呼び出しがstatic linkになっている場合があり、こうしたものはフック対象外になります。(リンカスクリプトでそういう指定が可能になっている。)
一例としては、libcに含まれる malloc(3) 関数は内部で mmap(2) や brk(2) を呼び出しますが、こうしたライブラリ内部から自身の関数呼び出しする場合は一部static linkとなっておりPLTを経由しないので、LD_PRELOADではオーバーライドできません。
(普通にプログラムが mmap(2) や brk(2) を読んでいる場合にはPLT経由になるのでフック可能です。)


こうしたstaticな関数を元のプログラムやライブラリを改変せずにオーバーライドしたい場合があります。
(例えば他のベンダが作っているライブラリの動作がマズいのだが既にメンテされてなくてソースもない場合とか)
そんなときは、無理やりコードの一部を書き換えてフックを挿入してしまうという手があります。


具体的には、jmp命令をフックしたいライブラリ関数の先頭に埋め込み、LD_PRELOADで挿入したフック用のライブラリの関数に飛ばしてしまえばOK。
シングルスレッド限定ではありますが、フック内部で埋め込んだjmp命令を元の命令列に戻してから元の関数を呼び出せば、前処理/後処理をすることも可能です。


いくつかの関数をフックするために何度も同じような処理を書くのが面倒だったため、C++とマクロでフックを簡単に埋め込めるようにする仕掛けを作ってみました。
このコードはx86_64でのみ動作します(命令列で jmp *(%rip); を埋め込んでいるため)。また、左記の命令は14byteあるため、14byte以上の関数でないと正常にオーバーライドできません(でないと隣にある関数を破壊してしまう。。)


例えばbrkをフックするコードは以下。ヘッダやMakefileを含む全体は以下のgistに。
https://gist.github.com/1519275

hook_brk.cpp

#include "LibHook.h"    // 面倒なことを全部押し込んだヘッダ
#include <stdio.h>
#include <unistd.h>

// フック関数
int brk_hook(void *addr)
{
	int ret = brk(addr);
	printf("brk(%p) => %d\n", addr, ret);
	return ret;
}
HookInstaller(brk, brk_hook);  // brk(2) を brk_hook()でオーバーライド

このように書くと、対象プログラムのmain関数の実行前(つまりconstructor実行時)に brk の先頭にjmp命令が埋め込まれ、brkを呼び出すとjmp命令を外した後に brk_hook が呼ばれるようになります。brk_hook内でbrkを呼び出しても無限ループになったりせずにオリジナルのbrkが実行されます。brk_hookからreturnすると、jmp命令が再び埋め込まれたのち、hookからの返り値がbrkの返り値であるかのように返されます。*1

これをビルドするには、

g++ -fPIC -shared -o hook_brk.so hook_brk.cpp -std=c++0x -ldl

のようにします。なおC++11 なのはフックの自動挿入/解除の記述を簡単にするためにvariadic templateを使って関数型の指定を自動でやらせているため。フック挿入自体*2とは本質的には関係ありません。


何度か malloc するプログラムを、
malloc_hook.c

#include <stdlib.h>
int main(void)
{
	free(malloc(256<<10));
	free(malloc(256<<10));
	return 0;
}

これを

gcc malloc_hook.c && LD_PRELOAD=hook_brk.so ./a.out

のようにビルド & 実行すると、

brk((nil)) => 0
brk(0x240d000) => 0

とフック関数内でprintfしているメッセージが表示され、mallocからの呼び出しでもフックできていることが確認できます。*3


なおフックは

    NSHook_brk::hook.uninstall();

とかやると途中で解除することも可能です。


一種の自己書き換えのような無茶をしているので安全とは言いがたいですが、いざとなったらこのようなことも可能だという例として。


全コード
Hook hidden symbol call (x86_64) · GitHub

ところで

C++で関数ポインタを扱うテンプレートを書く際、template引数に指定された関数型から、その返り値や引数の型をtemplate引数にもつテンプレートクラスAをインスタンス化したい場合って、一般的な方法ってあるのでしょうか。

よくわからなかったので、しかたなくダミーのtemplate関数を用意して、

// インスタンス化したいテンプレートクラス
template <class RET, class... ARGS>
class A {
};

// ダミー関数
template <class RET, class... ARGS>
A<RET,ARGS...> resolve_ftype(RET(*f)(ARGS...))
{
	throw "This function must not be called.";
	return A<RET,ARGS...>();
}

int func(const char*);

typedef decltype(resolve_ftype(func)) A_T;  // => A_T は A<int, const char*> のこと

などとしているのですが、もっといい方法はないのかな。

*1:マルチスレッドだと他スレッドが関数実行中にjmp命令で上書きする恐れがあるので不可。jmp命令の自動挿入/解除を止めれば大丈夫ですが、この場合はオリジナル関数は破壊されてしまうため、自分で1から再実装する必要があります。

*2:要するにdlopen/dlsymで関数アドレスを取得して、オリジナルの命令列を保存した後、フックへのjmp命令列を構築しmemcpyで上書き、という処理です。

*3:ちなみにフック内のprintf内でもバッファ確保のためmallocが呼ばれることがありますので、brkに再突入することがありますが、その場合はjmp命令がはずれているのでフックは呼ばれません。

AFPサーバ(netatalk)のLion対応状況

Macとファイルサーバの間でnetatalkのafpdを利用してAFPでファイル共有している場合、Mac OS X 10.7 LionはSnow Leopard以前と対応状況が異り、新しいバージョンが必要になっています。

netatalkを使ったNAS製品等でも、Lionだと接続に制限が出たりしています。

例: http://buffalo.jp/taiou/os/macosx107/01/macosx107_01.html


具体的には、次のような機能に対応している必要があるようです。

用途 必要機能 netatalkバージョン
認証あり(ゲスト以外)の接続: DHX2認証のサポートが必須に (セキュリティ向上) netatalk-2.0.4以降
Time Machine サポート: AFP3.3 (AFP Replay Cache等)への対応要 netatalk-2.2beta3以降


netatalkが古いバージョンだと、「このバージョンのAFPには対応していません」「Time Machineに必要なAFPの機能に対応していません」といったエラーが表示され、接続やTime Machineのバックアップが利用できなくなります。


具体的には、DHX2認証にはnetatalk-2.0.4以降で対応しているようです。 ただし、uams_dhx2.so がビルド*1され、かつafpd.confの-uamlistに含まれている必要があります。
なお、Lion側の設定変更で、セキュリティを甘くして認証をパスする方法もあるようです。
http://www.alexanderwilde.com/2011/04/os-x-lion-connection-error-with-afp-and-workaround/


また、Time Machineに必要なAFP3.3はnetatalk-2.2beta3以降 (現在 2.2beta4 が公開されているのでこちらの方が良いでしょう) が必要です。


新しめのディストリビューションであれば、上記対応バージョンが yum や apt 等でインストールできると思います。


個人的には、古いLinkStation (LS-GL) にシリアルポートをつけて改造して使っていますが、これがLionに対応していなかったため、netatalk-2.2beta4をビルドして入れ替えしたところ、Time Machineも含めて利用できるようになりました。
( 具体的には、Berkeley DB-5.0.32.NC, libgpg-error-1.7, libgcrypto-1.2.4, netatalk-2.2-beta4 を導入。)

*1:libgcryptoに依存、さらにlibgcryptoはlibgpg-errorに依存

LinuxのYUREXドライバ

このエントリはカーネル/VM Advent Calendarの48日目向けです。


2011/1/4にLinuxカーネル2.6.37がリリースされました。BKL(Big Kernel Lock)の除去やファイルシステムのスケーラビリティなど、多くの改善が行われています。


そんな中、カーネルをビルドしようとしてmake oldconfigしていると、

USB YUREX driver support (USB_YUREX) [N/m/y/?] (NEW)

と聞かれてはっと気付いた方もいたかと思います。
そう、あの"貧乏ゆすりを科学する"YUREXに、Linuxカーネル2.6.37で対応したのです。


このエントリでは、このLinuxのYUREXドライバの使い方をご紹介したいと思います。(持ってない人には役に立たない情報ですみません…。既に完売してるので入手困難だし。。)

1. 準備

  1. 2.6.37以降のカーネルを、make menuconfig等でCONFIG_USB_YUREXをM(Yでもいいけど…)に設定して、ビルド・インストールします。
  2. YUREXのスイッチを下にスライドし、USBで接続します。*1
  3. udevによって /dev/yurex0 というデバイスが作られます。


Linux2.6.37以前でも、こちらのドライバをダウンロードしてmake installすれば利用できます。ただし、そのままだとhidrawという別のドライバにUSB接続をとられてしまうため、別途udevの設定が必要です。詳しくは上記githubのREADMEをみてください。

2. 使ってみる

/dev/yurex0 に対して read/write することで、YUREXにアクセスできます。例えば、

  • /dev/yurex0 から貧乏ゆすりカウンタの読み出しができます。
# cat /dev/yurex0
12873
  • 逆に echo 12345 > /dev/yurex0 とすれば値を書き込めます。

他にも、

  • "A" を書き込むとアニメーション表示 (数字を書き込むと停止します)
  • "L0" "L1" を書き込むとLEDを付けたり消したりできる

という機能もあります。


こんなちょっとしたプログラムを動かせば、YUREXがお洒落(?)な時計に早変わり。

効果音はなりませんけど。

3. その他の機能

単にカウントの読み出ししかできないと、/dev/yurex0の値をreadし続けないと貧乏ゆすりがあったことが分かりません。それではCPUの無駄遣い。そこでこのドライバではfasyncという機能に対応しています*2。fasyncを使うと、貧乏ゆすりがあったときにシグナルで通知を送ってもらうことができます。

fasyncを有効にするには、fcntlを使って以下のようにします:

	fd = open("/dev/yurex0", O_RDONLY);
        sigaction(SIGIO, &sa, NULL);
	fcntl(fd, F_SETOWN, getpid());
	fcntl(fd, F_SETFL, O_ASYNC);

これで、貧乏ゆすりのたびにこのプロセス宛にSIGIOが送られて来ます。

詳しくはこちらのサンプルを見て下さい。


それと、複数のYUREXを接続した場合には /dev/yurex1 などになります。最大16台まで接続可能。って、そんなにYUREXを持ってる人いるの?

余談

YUREXのUSBベンダーIDは0x0c45、プロダクトIDは0x1010ですが、この番号はGeneralKeysというドイツの会社(?)が作っているワイヤレスプレゼンターと重複してしまっていたようです(Bugzillaに上げられた報告)。こんなことあるんですね…。


そもそもUSB.orgのリストによると、ベンダーID 0x0c45(=3141)って、台湾のSonix Technologyという会社に割り振られているはずなのですよね。どちらもこの会社のUSBチップか何かを使ってて適当なプロダクト番号振ってしまった、とかなのかなあ…?


YUREXドライバの運命や如何に…。

*1:VMware等では、デフォルトでUSB HIDデバイスをゲストに接続できない設定となっているため、YUREXをゲストに繋げないようです。この場合、設定の変更が必要とのこと。

*2:これがわざわざ /dev/yurex0 なんていうキャラクタデバイスになってる理由。^^;

PPTPサーバ立てた

最近カンファレンスなどに出かけてオープンな無線LANアクセスポイントが提供されている場合、よく「セキュリティに気をつけて! VPNを使って下さい」てな掲示を見かけます。
そうだよねーと思って、ARMなLinuxマシン(NAS)をPPTPサーバに仕立ててみた時のメモです。(今時PPTP?とも思ったが、今使っているNASだとカーネルの都合上、IPsecはモジュールだけでなくカーネル自体の入れ替えが必要そう(?)だったので断念。)

PPPカーネルモジュールの導入

今回使うのはARM搭載のNAS上で動くLinuxですが、標準状態ではpppドライバが入っていませんでしたので、まずはこれを導入。

続きを読む