調査を進めるにつれ、SDだけでなく、CFでも同じ問題があることが判明したため、タイトルを変更した。
qinstall.so の InstallUtil::getAvailableSize() で statfs() が呼ばれていることが判明した続き。
statfs前後の処理
statfs をコールする前を確認してみる。
26e4c: e1a00004 mov r0, r4
26e50: ebffa6c1 bl 0x1095c ; call QString::latin1(void) const
26e54: e24b1060 sub r1, fp, #96 ; 0x60
26e58: ebffa617 bl 0x106bc ; call statfs()
statfs を呼び出す前を見ると、第1引数(r0) に QString::latin1() の結果(おそらくパス名が入っていると思われる)が、第2引数(r1) には、fp – 96 (スタック上のローカル変数と思われる)がセットされている。
雰囲気的には以下みたいな感じかな?
unsigned long InstallUtil::getAvailableSize(QString)
{
:
struct statfs stat;
int ret;
ret = statfs(path.latin1(), &stat);
:
}
ふむふむ。
その後を追いかけてみる。(簡単なコメント付き)
26e58: ebffa617 bl 0x106bc ; call statfs()
26e5c: e3500000 cmp r0, #0 ; 0x0 // 戻り値確認
26e60: 0a000008 beq 0x26e88 // 戻り値=0 (正常) なら 0x26e88 へジャンプ
26e64: e3a05000 mov r5, #0 ; 0x0
26e68: ea000009 b 0x26e94
:
:
26e88: e51b205c ldr r2, [fp, -#92] // struct statfs構造体の何かを r2 へ読み出し
26e8c: e51b3050 ldr r3, [fp, -#80] // struct statfs構造体の何かを r3 へ読み出し
26e90: e0050293 mul r5, r3, r2 // r5 = r3*r2
:
26ecc: e1a00005 mov r0, r5 // 上の掛け算結果を r0 へ
26ed0: ea000000 b 0x26ed8
26ed4: 00000800 andeq r0, r0, r0, lsl #16
26ed8: e91bac70 ldmdb fp, {r4, r5, r6, sl, fp, sp, pc} // リターン
struct statfs の何かのメンバ同士を掛け合わせたものが getAvailableSize() 関数の戻り値(r0)に入るようだ。では、statfs の何のメンバなのか。
statfs の第2引数は fp-96 のアドレスだったので、fp-96 から struct statfs 構造体が始まっている。その構造体にアドレスを当てはめると・・・
struct statfs {
long f_type; /* fp-96 */
long f_bsize; /* fp-92 */
long f_blocks; /* fp-88 */
long f_bfree; /* fp-84 */
long f_bavail; /* fp-80 */
long f_files; /* fp-76 */
long f_ffree; /* fp-72 */
fsid_t f_fsid; /* fp-68 */
long f_namelen; /* fp-64 */
};
fp-92 は f_bsize、fp-80 は f_bavail だと分かる。
man page によると、f_bsizeが「ブロックサイズ」、f_bavail が「非スーパーユーザが使用可能な空きブロック数」らしい。これらを掛け合わせているということは、非スーパーユーザが使用可能な空きバイト数を計算しているということになる。
掛け算で使用している mul という命令は、このページによると 32bit = 32bit * 32bit の符号なし掛け算のようだ。この時点で 4GB (32bit) を越える空き容量は正しく判断できないことが分かる。さらに、4GBSDで問題が起こると言うことは、この結果を singed long として扱っていると推測できる。
また、getAvailableSize() 関数は名前の通り、空き容量をバイト数で返す関数だということも確証が得られた。
さて、これをどうしようか。
空き容量2GBの壁の検証
前々回のブログに対して、Haniwa様から 4GB SD でもインストール可能でしたとのコメントを頂いた。
今までの解析結果と推測から、空き容量が 2GB 前後に壁があるのではないかと考えられる。
ということで、4GB SDにダミーファイルを作成して、空き容量を2GB前後にしてインストール可能/不可能が変わるのかを検証してみた。
その結果・・・
- 空き容量 2,147,450,880Byte (0x7FFF8000 Bytes)
→ インストールOK - 空き容量 2,147,483,648Byte (0x80000000Bytes)
→ インストールNG
やはり、空き容量が2GBの所に境界があるようだ。(SL-C1000, SL-C3000 で確認)
ついでに、4GBのCFでも確認してみたところ、やはり 2GB 以上の空き容量の場合はインストールできないようだ。(SL-C1000で確認)
ただし、Haniwa様がお持ちなのは SL-C3200 なので、SL-C3200 では元々 4GB メディアに対応している可能性もある。内蔵MicroDrive は6GBなので、空き容量が2GB越えに対応する必要もありそうだし。(そう考えると SL-C3000で対応済みでもよさそうだが・・・SD/CFへのインストールと本体へのインストールは容量判定処理が別なのだろうか・・・)
修正方針
qinstall.so に手を入れるとすると、修正する方法は2通りある。InstallUtil::getAvailableSize() 関数に手を入れるか、この関数を呼び出した後の判定に手を入れるか。
getAvailableSize() を呼んでいる場所を調べるため、逆アセンブルした結果を bl 0x26d20 で grep したら 13 個も見つかった。これを追うのは気が滅入りそうなので、getAvailableSize()を修正することにした。
また、空き容量が2GB以下であれば、getAvailableSize() の呼出側は正常に動作できるようなので、空き容量が2GB以上の場合は 2GB-1Byteとして返却することにしよう。そうすれば、何GBのメディアでも対応可能だし。
(もう少し続く・・・)