何だかんだでシリーズ第4弾 (^^;
結果を先に言うと、修正に成功しました!!
Haniwa様がコメントされていた SL-C3200 では2GB超えメディアへのインストールができる件は、SHARPのサイトに書いてありました。
SL-C3100 までは、4GB以上の CF では「メモリが足りません」と表示されてインストールできないと書かれていますが、SL-C3200では修正されているようですね。
それでは続きです。
コード置き場探し
原因も対処法もおおよそ目処がたったけど、まだ問題が1つだけ残っていた。それは修正コードを置く場所。
1命令で修正できればいいが、いろいろ考えてみたものの、今回の修正には数命令が必要そうだ。当然ながら普通にコンパイルされたコードには、わざわざ無駄なスペースなどない。
どこにコードを置こうか。どっかいいところないかな? 置けそうな場所は・・・
$ LANG=C arm-linux-readelf -l qinstall.so
Elf file type is DYN (Shared object file)
Entry point 0x1214c
There are 3 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x00000000 0x00000000 0x362c4 0x362c4 R E 0x8000
LOAD 0x0362c4 0x0003e2c4 0x0003e2c4 0x02e04 0x02e04 RW 0x8000
DYNAMIC 0x038ff8 0x00040ff8 0x00040ff8 0x000d0 0x000d0 RW 0x4
Section to Segment mapping:
Segment Sections...
00 .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.rodata .rel.data .rel.got .rel.plt .init .plt .text .fini .rodata
01 .data .ctors .dtors .got .dynamic
02 .dynamic
.text と同じセグメントにロードされるセクションである必要がある。そうでないと、アドレス解決しない限りロードされるアドレスが分からないので。
ELFについてそんなに詳しくないので分からないが、.gnu.version とかって要らないんじゃないのか?
と思って、おもむろに .gnu.version を strip したら動かなくなってしまった
必要なセクションだったのね。
うーん。他は必要そうなセクションばかりだな。。.hash, .dynsym, .dynstr, .init, .plt, .text, .fini と全然無駄がないじゃないか (当たり前だ ^^;)
ふと、.rodata が目に止まる。あれ? .rodata って .data とかと同じセグメントじゃないんだ。ReadOnlyだからか。
.rodata に無駄な文字列とかないかな・・・と思って .rodata セクションを見てみると・・・
$ LANG=C arm-linux-readelf -x 14 qinstall.so | less
Hex dump of section '.rodata':
0x00032f04 726f6c6f 43746573 00000072 6f6c6f63 color...setColor
0x00032f14 00000000 656e694c 6e616373 00000000 ....scanLine....
:
0x00032f84 6d6f682f 00000000 303d3d69 00296425 %d).i==0..../hom
0x00032f94 616d2d74 712f316e 2f797474 616d2f65 e/matty/n1/qt-ma
0x00032fa4 6e692f32 2e332e32 2d74712f 72657473 ster/qt-2.3.2/in
0x00032fb4 682e786f 62747369 6c712f65 64756c63 clude/qlistbox.h
0x00032fc4 0000002f 6e69622f 00007772 00000000 ....rw../bin/...
お! 程よく長くてデバッグ用とおぼしき文字列を発見。開発環境のファイル名だろうし、これは潰しても問題ないでしょう。
初めに \0 で終端したダミー文字列を用意しても、52Byteあるので 13 命令も置ける。これはいけそうだ。
修正コードの作成
修正コードは以下の掛け算を置き換える形で作成する。
26e90: e0050293 mul r5, r3, r2
このコードを bl 0x?????? のコードに書き換え、コール先で r5 = r2 * r3 を計算する。その結果が 0x80000000 以上だったら 0x7fffffff を r5 に代入してリターンするようなコードを書けば、うまく修正ができそうだ。
ということで、 そんな感じのコードをインラインアセンブラで書いてちゃんと動くか確認。(確認用コード)
実行してみると
res:7fff0000 rlong=7fff0000
res:7fff2000 rlong=7fff2000
res:7fff4000 rlong=7fff4000
res:7fff6000 rlong=7fff6000
res:7fff8000 rlong=7fff8000
res:7fffa000 rlong=7fffa000
res:7fffc000 rlong=7fffc000
res:7fffe000 rlong=7fffe000
res:7fffffff rlong=80000000
res:7fffffff rlong=80002000
res:7fffffff rlong=80004000
res:7fffffff rlong=80006000
res:7fffffff rlong=80008000
res:7fffffff rlong=8000a000
res:7fffffff rlong=8000c000
res:7fffffff rlong=fffffffe
res:7fffffff rlong=100000000
res:7fffffff rlong=100000002
OK。rlong が 0x80000000 以上の場合は 0x7fffffff が変数に格納できるようになった。4GB越えでも大丈夫。
この確認コードをベースに作った埋め込み用コードは以下の通り。あまりARMアセンブラに精通してないので、本当はもっと短く書けるかも。
84ec: e0805392 umull r5, r0, r2, r3
84f0: e3500000 cmp r0, #0 ; 0x0
84f4: 159f5008 ldrne r5, [pc, #8] ; 8504
84f8: e3550000 cmp r5, #0 ; 0x0
84fc: 459f5000 ldrmi r5, [pc, #0] ; 8504
8500: e1a0f00e mov pc, lr
8504: 7fffffff swivc 0x00ffffff
mul 命令前後を読んで調べた結果、r0 が破壊可能なレジスタだったのでワーク用に使用した。
アドレスは適当だけどリロケータブルなコードなのでそのまま埋め込める。6命令+4Byteなので、前回見つけた領域にも余裕で収まる。
ということでバイナリエディタで上記コードを埋め込んでみた。
修正コード埋め込み前
修正コード埋め込み後
文字列の先頭はダミー文字列として "/ho" だけ残した。
その後は13ロングワード分が書き換え可能な領域なので、一度すべてFFで埋めた後、作成した修正コードをリトルエンディアンで埋め込んだ。 後で埋め込んだコードがわかりやすいように、先頭にFFFFFFFFを1つだけ入れておいた。
埋め込んだコードを objdump で逆アセンブルして確認。
$ LANG=C arm-linux-objdump -D --start-address=0x32f90 --stop-address=0x32fc0 qinstall.so
00032f90 <.rodata+0x8c>:
32f90: 006f682f rsbeq r6, pc, pc, lsr #16
32f94: ffffffff swinv 0x00ffffff
32f98: e0805392 umull r5, r0, r2, r3
32f9c: e3500000 cmp r0, #0 ; 0x0
32fa0: 159f5008 ldrne r5, [pc, #8] ; 0x32fb0
32fa4: e3550000 cmp r5, #0 ; 0x0
32fa8: 459f5000 ldrmi r5, [pc, #0] ; 0x32fb0
32fac: e1a0f00e mov pc, lr
32fb0: 7fffffff swivc 0x00ffffff
32fb4: ffffffff swinv 0x00ffffff
32fb8: ffffffff swinv 0x00ffffff
32fbc: ffffffff swinv 0x00ffffff
埋め込み完了。思惑どおりのコードになった。あとは、mul命令の代わりにこのコードをコールするだけ。
コール先のアドレスは上記 objdump の結果から、0x32f98 を呼べばよい。
修正コードのコール
前に書いたように、以下の mul 命令を bl 命令に置き換えて修正コードをコールするようにする。
26e90: e0050293 mul r5, r3, r2
bl は相対ジャンプ命令なので、オフセットを計算する。このページによると、この命令があるアドレス+8からジャンプ先までのオフセットを4で割った値を24bitオフセットとして指定するらしい。
ジャンプ先は 0x32f98 なので、オフセットは (0x32f98 – (0x26e90 + 8)) / 4 = 0x003040 となる。
無条件 bl は 0xeb?????? (??はオフセット) らしいので、0xeb003040 を 0x26e90 へ書き込めばいい。
バイナリエディタでコードを書き換えて objdump で確認すると・・・
修正前
26e88: e51b205c ldr r2, [fp, -#92]
26e8c: e51b3050 ldr r3, [fp, -#80]
26e90: e0050293 mul r5, r3, r2
26e94: e5942000 ldr r2, [r4]
26e98: e5923000 ldr r3, [r2]
修正後
26e88: e51b205c ldr r2, [fp, -#92]
26e8c: e51b3050 ldr r3, [fp, -#80]
26e90: eb003040 bl 0x32f98
26e94: e5942000 ldr r2, [r4]
26e98: e5923000 ldr r3, [r2]
OK。mul 命令と bl 命令の置き換え完了。コール先アドレスも正しく 0x32f98 を指しててオフセット計算も合っていたようだ。
これで修正は終了・・・。
動作確認
/opt/QtPalmtop/binlib/qinstall.so を上記の修正を施した qinstall.so へ置き換えて、再起動。
祈りながら4GB SDにインストールしてみると・・・
インストールできた!
一発で動くとは思わなかったけど、ちゃんと動いてくれたようだ。
長かっただけに感動もひとしお。
これで何も気にせずに 4GB SDを使えるよ。うん。
あとは、これを ipk 化するだけだ。
ただ、qinstall.so は SHARP 製のプログラムなので自由に配布することはできないので、バイナリパッチと言う形で、ipk をインストールした時にパッチを当てるような実装にしないといけないかな。
SL-C1000 と SL-C3000 は持ってるから qinstall.so を入手できるけど、SL-C3100 は。。。SL-C3000と同じかなぁ?
どなたか、SL-C3100で qinstall.so の md5sum を取ってもらえませんか?
以下 SL-C3000 での実行結果
$ md5sum /opt/QtPalmtop/binlib/qinstall.so
cd2629a9cd451bcd3e5198f933bb0c33 /opt/QtPalmtop/binlib/qinstall.so
これと同じ MD5 だったら同じバイナリパッチが使えるんだけど・・・。
(次回いよいよ最終回?)