4GSD へ ipk インストールの道 – その2

前回の続き。

qinstall.so に InstallUtil::getAvailableSize(QString) という関数があることが分かった。この辺をなんとかすればいけるかも?

InstallUtil::getAvailableSize の抽出

まず、この関数がどこからどこまでなのかを調べてみる。開始アドレスは前回の objdump の結果から、

00026d20 g    DF .text  000001bc  Base        InstallUtil::getAvailableSize(QString)

0x26d20 だと分かっているので、そこから逆アセンブルしてみる。

$ arm-linux-objdump -d --start-address=0x26d20 qinstall.so | less
      :
00026d20 <.text+0x14bd4>:
   26d20:       e1a0c00d        mov     ip, sp
   26d24:       e92ddc70        stmdb   sp!, {r4, r5, r6, sl, fp, ip, lr, pc}
   26d28:       e24cb004        sub     fp, ip, #4      ; 0x4
   26d2c:       e1a04000        mov     r4, r0
   26d30:       e24b0020        sub     r0, fp, #32     ; 0x20
:

そういえばARMはこんな感じだったっけ。
ARMでは関数の先頭で stmdb でレジスタを保存して、関数の最後に ldmdb でレジスタを復元してリターンするのが典型的なパターン。

そのまま先を見ていくと・・・。

     : 
26ed0: ea000000 b 0x26ed8
26ed4: 00000800 andeq r0, r0, r0, lsl #16
26ed8: e91bac70 ldmdb fp, {r4, r5, r6, sl, fp, sp, pc}

あった ldmdb。

脱線するけど、初めて ARM に触ったときにこの stmdb と ldmdb の仕組みが良くできてて感心した記憶がある。
関数の入り口と出口をならべてみるとよく分かる。

   26d20:       e1a0c00d        mov     ip, sp
   26d24:       e92ddc70        stmdb   sp!, {r4, r5, r6, sl, fp, ip, lr, pc}
   26ed8:       e91bac70        ldmdb   fp, {r4, r5, r6, sl, fp, sp, pc}

r4〜r6, sl, fp は素直に保存・復元が行われて、特に lr が見事で、コール時に保存した戻りアドレスの lr を pc に戻すことでリターンを実現している。
レジスタの復元とリターンが同時にできるなんて、なんてスマートなんだ!!  しかも1命令で!! と感動したのは私だけ?? (^^;

・・・・話を戻すと、getAvailableSize の最後は 0x26ed8 で良さそう。
最適化を行うと、関数の途中にも ldmdb が出てきたりするんだけど、ジャンプを追いかけても 0x26d20〜0x26ed8 の間に収まってるみたい。

とりあえず関数の抽出ができた。

getAvailableSize の調査

本当にこの関数で statfs が呼ばれているのか調べてみる。

関数コールは bl 命令を探すだけでOK。

   26d4c:       ebffa9b6        bl      0x1142c
26d60: ebffaa05 bl 0x1157c
26d9c: ebffa7f2 bl 0x10d6c
26db8: ebffa9e3 bl 0x1154c
26dd0: ebffa995 bl 0x1142c
26de4: ebffa9e4 bl 0x1157c
26e1c: ebffa7d2 bl 0x10d6c
26e34: ebffa9c4 bl 0x1154c
26e48: ebffa9bf bl 0x1154c
26e50: ebffa6c1 bl 0x1095c
26e58: ebffa617 bl 0x106bc
26ec8: ebffa7a7 bl 0x10d6c

コール先がダブっているものを省くと、

   26d4c:       ebffa9b6        bl      0x1142c
26d60: ebffaa05 bl 0x1157c
26d9c: ebffa7f2 bl 0x10d6c
26db8: ebffa9e3 bl 0x1154c
26e50: ebffa6c1 bl 0x1095c
26e58: ebffa617 bl 0x106bc

この6つに絞られた。これを1つ1つ調べてみる。

まずは 0x1142c。このアドレスはどこかというと、

$ LANG=C arm-linux-readelf -S qinstall.so
There are 23 section headers, starting at offset 0x3918c:

Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
:
[10] .init PROGBITS 00010194 010194 000018 00 AX 0 0 4
[11] .plt PROGBITS 000101ac 0101ac 001fa0 04 AX 0 0 4
[12] .text PROGBITS 0001214c 01214c 020da4 00 AX 0 0 4
:

.plt セクションのようだ。.plt セクションは動的リンクしたライブラリへジャンプするためのコードが書かれたもの。

早速 0x1142c を逆アセンブルすると、 

   1142c:       e59fc004        ldr     ip, [pc, #4]    ; 0x11438
   11430:       e08fc00c        add     ip, pc, ip
   11434:       e59cf000        ldr     pc, [ip]
   11438:       0002ef98        muleq   r2, r8, pc

こんな感じのコードだった。C言語風に訳すと、

  • ip = *((unsigned int *)0x11438);
  • ip += 0x11438;
  • pc = *((unsigned int *)ip)    // ipに書かれたアドレスへジャンプ

つまりは、0x2ef98 と 0x11438 を足したアドレスに書かれているアドレスにジャンプするというもの。

足し合わせると、 0x403d0 となる。このアドレスは・・・

$ LANG=C arm-linux-readelf -S qinstall.so
There are 23 section headers, starting at offset 0x3918c:

Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
:
[17] .dtors PROGBITS 0003ff20 037f20 000008 00 WA 0 0 4
[18] .got PROGBITS 0003ff28 037f28 0010d0 04 WA 0 0 4
[19] .dynamic DYNAMIC 00040ff8 038ff8 0000d0 08 WA 3 0 4
:

.got セクションのようだ。.got セクションは、シンボル解決をしたアドレスが書かれるテーブル。

まぁ、白々しくこう書いたけども、.plt がジャンプコードになっていて、.got がそのジャンプアドレステーブルになっているのは ELF の動的リンクの仕組みそのもの。うん。いい勉強になるね。(また白々しい・・・)

で、例の 0x403d0 には何が書かれるかというと、

$ LANG=C arm-linux-objdump -RC qinstall.so | less
qinstall.so:     file format elf32-littlearm

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
00032f5c R_ARM_ABS32       QTextStream::width(int)
    :
000403cc R_ARM_JUMP_SLOT   QCString::QCString(char const *)
000403d0 R_ARM_JUMP_SLOT   QString::fromLatin1(char const *, int)
000403d4 R_ARM_JUMP_SLOT   QFile::exists(void) const
   :

QString::fromLatin1 のようだ。リンカがアドレス解決すると、0x403d0 に QString::fromLatin1 のアドレスが書き込まれる。
つまり、まとめると、bl  0x1142c は QString::fromLatin1 をコールしていたというわけだ。

といった感じで、残りのコール先も調べると、以下の様にになった。

   26d4c:       ebffa9b6        bl      0x1142c		; 0x403D0 QString::fromLatin1(char const *, int)
26d60: ebffaa05 bl 0x1157c ; 0x40424 QString::find(QString const &, int, bool) const
26d9c: ebffa7f2 bl 0x10d6c ; 0x40220 QStringData::deleteSelf(void)
26db8: ebffa9e3 bl 0x1154c ; 0x40418 QString::operator=(char const *)
26e50: ebffa6c1 bl 0x1095c ; 0x4011C QString::latin1(void) const
26e58: ebffa617 bl 0x106bc ; 0x40074 statfs

お、ちゃんと statfs を呼んでいるようだ。

qinstall.so 内で他に statfs を呼んでないかを調べてみる。

$ LANG=C arm-linux-objdump -d qinstall.so | grep 0x106bc
arm-linux-objdump: qinstall.so: no symbols
   26e58:    ebffa617     bl    0x106bc

うん。getAvailableSize 関数でしか呼んでないみたい。この関数でビンゴだ。

(まだまだ続く・・・)

4GSD へ ipk インストールの道 – その1

前のブログで 4GB の SD カードについて書きましたが、この4GSDを使っていて一番不便なのは zaurus 標準の「アプリケーションの追加と削除」から ipk パッケージをインストールでできないことです。

ipkg コマンドをシェルから実行することで一応インストールは可能らしいのですが、シンボリックリンクを自分で張らないといけない(?)など、ちょっと面倒そう。。

ということで、標準アプリでどうにか ipk をインストールできないか試行錯誤してみました。
以下、個人的なメモなので、口調を変えてます。

症状から推測

まず、症状はSDカードへインストールすると「 アプリケーション追加先のメモリーが足りません」と表示される。(その時の画面が張られたブログ

その症状から空き容量の計算がうまくいっていないことが分かる。また、2GB SD カードではエラーは出ないことから、2GB超えに問題がありそう。

2GB/4GBというと、16進数にすると、0x80000000 Byte/0x100000000 Byte なので、signed long int / unsigned long int 型で扱えない値ということで、この辺がクサい。

おそらく、(unsinged?) long 型で残容量を取得して、パッケージの必要容量を差し引いてマイナスだったらという判定をしてると思われる。何となく以下のような処理になっているのではないか。(ソースがないのでかなり適当)

unsigned long available_size = get_available_size(path);
int package_size = get_package_size(ipk_path);

if ( available_size - package_size < 0 ) {
容量が足りない!
}

この辺を何とかゴニョゴニョできれば良いのではないかと。

バイナリ探し

さて、指定したファイルシステムの空き容量を調べるには通常 statfs 関数を使う(MANページ)ので、強引に grep で statfs を使っているファイルを検索してみた。

 # grep -c statfs /opt/QtPalmtop/*/* | grep -v :0
# grep -c statfs /opt/QtPalmtop/*/*/* | grep -v :0

(zaurus の grep には -r オプションがないので・・・)

すると、/opt/QtPalmtop/binlib/qinstall.so が見つかった。名前を見ても合ってそうな雰囲気。

「ソフトウェアの追加/削除」アプリは /opt/QtPalmtop/bin/qinsall を実行すれば起動するが、シンボリックリンクになっており、実際は quickexec というコマンドが実行されるらしく、実際のインストール処理は qinstall.so が行っているようだ。

ちなみに、qinstall.so は常駐している qeserver プロセスがリンクしているようで、おそらくquickexec がプロセス間通信を行って、qeserver のインストール処理を起動しているのではないかと。

初めは、qinstall が単体のアプリになっていると思っていたので、LD_PRELOAD を使って statfs を置き換えるようなライブラリをリンクしてしまおうかと思ったんだけど、qeserver は qinstall.so だけでなく、多くの設定アプリ系の *.so をリンクしているので、qeserver 全体で statfs を置き換えるのはちょっと影響が大きそうだ。

qinstall.so の調査

Linux-PC に qinstall.so を取り出して、arm-linux-objdump を使ってシンボルを調べてみる。

 # arm-linux-objdump -TC qinstall.so | less
:
00000000 DF *UND* 00000010 GLIBC_2.0 statfs
:

確かに statfs がリンクされているようだ。
シンボルをざっと見ていたら、気になるシンボルを発見。

 00026d20 g    DF .text  000001bc  Base        InstallUtil::getAvailableSize(QString)

お、なんかそれっぽい関数発見。
名前から想像するに、パス名を与えるとその空き容量を返すような関数なのだろう。

この関数に手を入れて空き容量をごまかせばいけるかも。

(続く・・・かな?)

qgmap 0.1.5 リリース

qgmap の新版をリリースします。→ダウンロードページ

今回は主に GPS 関連の変更です。

  • GPS位置追従/追従解除が可能に
    (v0.1.4では強制追従でした)
  • GPS位置が取得できていないときは移動しないように変更
    (南米に飛ばされなくなりました)
  • 三角マーカでGPS取得位置と移動方向を表示(カーナビのように)
  • GPS情報ウィンドウに移動速度、移動方向、標高を追加
  • 地図閲覧の邪魔にならないようにGPS情報ウィンドウを右下隅に表示
  • 「Menu」キーでポップアップメニュー表示
  • 「Map Path」メニューの地図データディレクトリ選択ダイアログで
    SDカードとCFの認識が間違っていたバグを修正

項目は多いですが、細々とした変更が多いです。

今回の目玉は、三角マーカによる現在位置表示機能です。 目玉というほどの機能ではないですが(^^;

マーカ表示
現在位置のマーカ表示

カーナビのようにマーカの向きは移動方向を示しています。

0.1.4 の時はGPSで取得した現在位置で強制的にセンタリングされていましたが、現在位置の追従/非追従を切り換えられるようになりました。

「G」キーを押した時は追従モードになっています。右下のGPS情報ウィンドウの右上にロックアイコンが表示されます。
スタイラスやカーソルキー、0〜9キーで画面を移動させるとロックが解除され、GPSの取得位置に追従しなくなります。(ロックアイコンも消えます)
スペースキーを押せば再び追従します。

GPS情報ウィンドウ
右上にロックアイコン 下に移動速度・方向、標高表示を追加

また、地味に移動速度、移動方向、標高表示に対応しました。

あと、ディレクトリ選択ダイアログを実装してから今までまったく気づいていなかったのですが、「Map Path」で開くディレクトリ選択ダイアログで CF と SD カード認識の判定が逆になってたので修正しました。
SDカードを挿しているとCFとして認識し、CFを挿しているとSDと認識するという、何ともお粗末なバグでした。

ようやくこれでなんとかGPS端末として最低限の機能が実装できたかな・・・?

qgmap 0.1.4 リリース

本当に久しぶりになってしまいましたが、qgmap をリリースします。→ダウンロードページ
2ヶ月ぶりですか。あいだ空きすぎですね (^^;

今回ようやくGPSに対応しました。といっても最低限の機能しかありませんが。。。

ひとまず現在位置を取得して、そこに移動する機能のみを実装しました。
ログ機能すらありません。いろいろな機能はこれから追加していきます。
GPSのログ取得やスレッド関係は HB1 様の Zega を参考にさせていただきました(^^)
すばらしいソフトとそのソースを公開していだたき感謝感謝です。

GPS機能を使用するには、まずGPSの設定が必要です。
左上のアイコンをクリックすると表示されるメニューの「GPS Settings」を選択します。

GPS設定メニュー
GPS設定メニュー

すると、次のようなダイアログが表示されます。

GPS設定ダイアログ
GPS設定ダイアログ

各設定の意味は以下の通りです。

  • GPS Device
    • GPS機器が接続されているデバイスファイルを指定します。
      Zega でいう、devFile 相当です。
  • PreCommand
    • GPS接続を行う前に実行するコマンドです。
      Zegaでいう、startGpsCommand 相当です。
  • PostCommand
    • GPS接続を切断した後に実行するコマンドです。
      Zegaでいう、stopGpsCommand 相当です。
  • 更新間隔
    • 画面を更新する間隔です。GPSが出力する間隔に関係なく、この設定で指定した間隔で画面が更新されます。
  • 測地系
    • GPS機器が出力する緯度・経度の測地系を指定します。
  • 時差
    • 現在地の時差を指定します。この時差を補正して測位時間を表示します。

基本的に、Zega で使用できている方は、devFile 指定を GPS Device に、startGpsCommand を PreCommand に、stopGpsCommand を PostCommand に指定すれば動くと思います。たぶんですが。

設定を行ったら、「G」キーで GPS 受信を開始します。
GPS受信モードに入ると、画面の左上に以下のような小窓が表示されます。

GPS情報表示
GPS情報表示

左上から右方向に、受信している衛星数、受信時刻、緯度、経度が表示されます。

緯度・経度が受信できると現在地を中心に表示します。スクロールしても強制的に現在地に戻されます。この辺は徐々に改良していきます。

GPS関連では今後、以下の項目を追加・改良したいと思っています。

  • 標高、移動速度、移動方位表示
  • 現在地自動センタリングのON/OFF切り替え
  • 現在地に移動方向を三角マーカで表示(カーナビのように)
  • NMEAログ保存機能
  • 移動軌跡表示

GPS機器は、M-241 と uPod で確認していますが、他の機器で使えるか不安ですので、試してみたかたは、動作した/しない関わらず、コメントに動作報告を頂けるとありがたいです。よろしくお願いします!

そろそろ機能が増えてきたので使い方をまとめないと・・・。

Zaurus で Bluetooth + M-241

前のエントリでチラッと Bluetooth ドングルを買ったと書きましたが、Bluetooth 機器を扱うのが初めてだったので勝手が分からず、使えるようになるまで大変だったので、自分用メモとして使い方を書いておきます。

購入したBluetooth – USBドングルはリテールコム社の BIT-BTH11H です。期間限定で890円でした。私が検索した時点ではamazon内最安値でした。
Linux-PC はもちろん、4桁機 Zaurus でも問題なく使用できました。

Bluetoothドングル
Bluetoothドングル

まずいきなりつまづいたのが、bluez の入手でした。bluez のパッケージは何種類かあるようで、どれを入れれば良いのかさっぱりわからず・・・。

とりあえず、4桁機でBluetoothを使う記事の多くで使われていた、bluez_zaurus_2.12_2.4.20_alpha4_arm.ipk を探してみましたが、リンク先のOESF の掲示板からは登録ユーザ以外はダウンロードできないようになっているようで。

結局、archive.org で過去のOESFの掲示板からなんとかダウンロードすることができました。
苦労したのでリンクを貼っておきます。

時系列一覧の中の、Nov 07,2006 のリンク以前であればダウンロードできるようです。

その後は、tera様の実用工房の「BluetoothからGPS接続」が参考になりました。

hcitool scan でデバイスを検索し、発見した M-241のデバイスIDを /etc/bluetooth/rfcomm.conf に記述することで、/dev/rfcomm0 にアクセスすると自動で接続するようになりました。

プロンプトから

# cat /dev/rfcomm0 

と実行すると、接続するまで2,3秒かかったのち、無事に NMEA データ(GPSレシーバとしてのNMEAデータ)が取得できることが確認できました。
/dev/rfcomm0 を close すると、しばらくしたのちに Bluetooth 接続が切断されます。

はじめて Bluetooth 機器を使ってみたのですが、設定さえしてしまえば後は自動接続・自動切断なので非常に便利ですね。
これなら、qgmap で手軽に現在位置を確認できそうです。M-241 を近づけて「G」キー(予定)を押すだけで現在位置が取得できる、といった使い方もできそうです。

qgmapのGPS対応はゆっくりながらも一応進んでます。 設定系のダイアログを実装したので、最低限の機能ながら、もうすぐ公開できそうです。お楽しみに。