« [Mac OS X]LeopardではSafari3にてタブがらみのスクリプティングが可能に | Main | [iPod touch]iPhone由来のメモ(MobileNotes.app)からsqlite3でデータを出し入れする »

2007.10.29

[iPod touch]iPod touch (iPhone)用にGNUツールをクロスコンパイルする

iPod touchを、よりUNIXな感じで使いたいので頑張ってみました。

現時点ではLeopardでは問題が出るとのことで、Mac OS X Tigerで作業しました。
なお、iPhoneToolchainv05.dmgがintel向けバイナリで構成されているので、これを使う限りはPPCなMacでは試せません。悪しからず。

手順1: iPod touch / iPhone用のネイティブアプリの開発環境の構築

基本的に、「Native IPhone development using XCode, at last!」に書いてあります。iPhone toolchain のインストールとTipsも参考にしましょう。

前提として、Xcodeのインストールが必須です。

1)iPhoneToolchainv05.dmgをダウンロードしてきてインストール。同梱の「Ooo Shiny!!」は、「/usr/local/arm-apple-darwin/bin/」にPATHを通すために.profileに記述を追加するシェルスクリプトです。それを実行してもいいのですが、僕は手動で~/.bash_profileに追加しました。

1.5)arm-apple-darwin-gcc-4.0.1のシンボリックリンクをarm-apple-darwin-gccとして張っておきます。

sudo ln -s /usr/local/arm-apple-darwin/bin/arm-apple-darwin-gcc-4.0.1 /usr/local/arm-apple-darwin/bin/arm-apple-darwin-gcc

2)iPhoneのシステムソフトウェア1.1.1をダウンロードします。

3)iPhone1,1_1.1.1_3A109a_Restore.ipswの実体はzipなので、拡張子を変えるなどで解凍します。(Safariの場合は、最初から拡張子zipが付いてダウンロードされてきました)

4)vfdecryptをダウンロードし、解凍します。

5)iPhone1,1_1.1.1_3A109a_Restore.zipを解凍して出来たフォルダにvfdecryptをコピーします。

6)ターミナルから上記フォルダにcdした後、次のコマンドを実行します。

./vfdecrypt -i 022-3602-17.dmg -o iPhone_1.1.1.dmg \
-k f45de7637a62b200950e550f4144696d7ff3dc5f0b19c8efdf194c88f3bc2fa808fea3b3

7)iPhone_1.1.1.dmgをダブルクリックしてマウント。以下を実行してイメージ上のファイルを/usr/local/arm-apple-darwin/heavenly以下にコピーします。

sudo mkdir /usr/local/arm-apple-darwin/heavenly
sudo cp -Rn /Volume/Snowbird3A109a.UserBundle /usr/local/arm-apple-darwin/heavenly

8)Xcodeのテンプレートをダウンロードします。

9)解凍し、「iPhone UIKit Application/iPhoneApp.xcodeproj」をXcodeで開きます。

10)Foundation.frameworkとUIKit.frameworkが赤字で表示されているはずです。パスが違っているので、それぞれを選択した後、Command + Iで情報ウインドウを開き、「選択...」ボタンを押して「/usr/local/arm-darwin-apple/heavenly/System/Library/Frameworks」とたどり、それぞれ選択します。「/usr」は普通ファイルオープンダイアログからは見えないので、Command+Shift+Gを押して「フォルダへ移動」のダイアログを出し、「/usr」と指定して確定するとその先がたどれます。

11)「iPhone UIKit Application/Info.plist」を開き、項目"CFBundleExecutable”を“«PROJECTNAME»”に書き換えます。

12)「iPhone UIKit Application」フォルダを「/Library/Application Support/Apple/Developer Tools/Project Templates/Application/」にコピーします。これでXcodeで「新規プロジェクト」を作成する際のテンプレートの中のApplicationセクションに「iPhone UIKit Application」が現れるようになります。

13)以下のように実行します。subversion(svn)をインストールしておく必要があります。

cd /usr/local/arm-apple-darwin/arm-apple-darwin
sudo mv include include-old
sudo svn co http://svn.berlios.de/svnroot/repos/iphone-binutils/trunk/include/ include
sudo ln -s /System/Library/Frameworks/ApplicationServices.framework/Frameworks/CoreGraphics.framework/Headers \
/usr/local/arm-apple-darwin/arm-apple-darwin/include/CoreGraphics
sudo ln -s /System/Library/Frameworks/CoreServices.framework/Frameworks/CFNetwork.framework/Headers \
/usr/local/arm-apple-darwin/arm-apple-darwin/include/CFNetwork

ここまでで、iPhone UIKit Applicationはビルドできるようになっているはずです。
Xcodeで「iPhone UIKit Application」のプロジェクトを作り、そのままビルドして通ればまずは安心。

「プロジェクトのフォルダ/build/Release」にappが出来ているはず。これをiPod touch / iPhoneにコピーして実行してみます。その際は、例の「chmod -Rf +x /Applications」で実行権を付けておくことを忘れずに。

ここにあるサンプルのプロジェクトがビルドできれば間違い無しです。

手順2: GNUツールをコンパイルするために必要なarm-apple-darwin-binutilsをインストール

ここまでの手順ではarm-apple-darwin-ldなどのbinutilsが無いため、RubyなどのGNUなツールがコンパイルできません。
MacPortsからarm-apple-darwin-binutilsをインストールします。MacPortsをインストールしていなければ、まずはそれをインストールします。

1)MacPorts-1.5.0-10.4.dmgをMacPortsのページから落としてきてインストールします。

2)「/opt/local/bin/」にPATHを通します。

3)MacPortsの方のディレクトリに、さきほどiPhoneのファイルをコピーしたheavenlyディレクトリのシンボリックリンクを作成します。arm-apple-darwin-binutilsのインストールプロセスで必要になるためです。

ln -s /usr/local/arm-apple-darwin/heavenly /opt/local/arm-apple-darwin/heavenly

3)portコマンドでarm-apple-darwin-binutilsをインストールします。

sudo port install arm-apple-darwin-binutils

4)「/opt/local/arm-apple-darwin/bin」にPATHを通します。ここまでで僕の.bash_profileのPATHのところはこんな感じです。

export PATH=/opt/local/arm-apple-darwin/bin:/usr/local/arm-apple-darwin/bin:/usr/local/bin:/opt/local/bin:$PATH

ただ、このままではいささか問題アリです。なぜなら、「/opt/local/arm-apple-darwin/bin」「/usr/local/arm-apple-darwin/bin」を見れば分かりますが、「gcc、ld」といった感じでアーキテクチャのPREFIX無しでネイティブコンパイラやbinutilsと同名のコマンドが入っているからです。だからこそ、iPod touch向けのコンパイルを失敗したくない今は、パスの順序をこのようにしました。「gcc、ld」みたいな素の名前のやつは、PREFIXを付けてネイティブなツールとかぶらないようにするか、作業時にパスの優先度を切り替えるか。ここは後で整理の予定です。

実践: nkfのコンパイル

これでGNUなツールのコンパイルができるようになっているはずなので試してみます。
試行錯誤しながらだったので、手順が抜けていたらご指摘願います。
さて、日常よく使っているnkfをコンパイルしてみます。iPod touch上でどう使うかはこれから考えます。 :-)

UTF-8 対応 nkf (nkf_utf8)からnkf205.tar.gzをダウンロードしてきます。「tar xvzf nkf205.tar.gz」で解凍し、Makefileを編集。以下の当該項目をこのように書き換えます。

CC = arm-apple-darwin-gcc
CFLAGS = -fsigned-char -v -Wl,-syslibroot,/usr/local/arm-apple-darwin/Heavenly -O

終わったらmakeします。

make
strip nkf

ちゃんと動作しているようです。

実践: wgetのコンパイルと名前解決の問題

今度はGNU wgetをコンパイルしてみます。

tar xvzf wget-1.10.2.tar.gz
cd wget-1.10.2
CC=arm-apple-darwin-gcc CFLAGS="-fsigned-char -v -Wl,-syslibroot,/usr/local/arm-apple-darwin/Heavenly" ./configure --host=arm-apple-darwin --prefix=/opt/iphone
make
strip src/wget
make install DESTDIR=.

ただし、この手順で出来たwgetは名前解決ができません。wickedpsyched.netDNSのページに書いてありますが、iPod touch / iPhoneのusr/lib/libc.dylibに含まれるgethostbyname()がおかしく、普通にコンパイルすると、これが使われることになって名前解決できないようです。つまり、wgetにとどまらずgethostbynameを使うものに共通の問題ということになります。

SafariやMobileMail.appではもちろん名前解決できるので、ネイティブアプリでは別のライブラリを使っているようです。

前述のDNSのページにあるlibresolv.a(こちらにも関数gethostbyname()が含まれる)を使ってコンパイルすると名前解決できるようになるようで、実際それを使ったと思われるworking ping (new resolver)は、ちゃんと名前解決してくれます。
libresolv.aのgethostbyname()を優先的に使わせる方法がありましたら教えてください。CFLAGSに「-lresolv」を付けたりDNS の謎に書いてあるLD_PRELOAD方式もダメでした。ちなみに、libresolv.aからlibresolv.soを作る場合は、以下のようなオプションになりました。Darwinのgccに「-shared」というオプションは無いので「-bundle」に置換。「-read-only-relocs」の代わりに「-read_only_relocs」(アンダーバー)。

arm-apple-darwin-gcc -read_only_relocs suppress -bundle -Wl,-syslibroot,/usr/local/arm-apple-darwin/heavenly/ -o libresolv.so *.o

Installer.appからダウンロードできるDNS Tools(gethostbyname()問題対策済み)をインストールし、それに含まれるhostコマンド等を使うとホスト名からIPが引けるので、当座のしのぎ方としては以下のようなことをやっています。

SITE=www.google.co.jp
wget `host -t A $SITE | sed -n '/[^0-9][^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)/{s//\1/;p;q;}'`

iPod touch (iPhone)上でバッククオートは入力できないようなので、気合いでどうにかします。
ただ、こうしても再帰取得、リンクをたどってダウンロードする際に絶対リンクが出てくると名前解決しなければならなくなるので、そこで止まります。「-L」オプションなどで相対リンクのみをたどる動作にするといいでしょう、というか、どなたかlibresolv.aのgethostbyname()を使うようにコンパイルする方法を教えてください。

実践: Rubyのコンパイル

rubyもコンパイルできました。

tar xvzf ruby-1.8.6-p111.tar.gz
cd ruby-1.8.6-p111
cross_compiling=yes ac_cv_c_bigendian=no ac_cv_func_getpgrp_void=yes ac_cv_func_setpgrp_void=yes CC=arm-apple-darwin-gcc CFLAGS="-fsigned-char -v -Wl,-syslibroot,/usr/local/arm-apple-darwin/Heavenly" ./configure --host=arm-apple-darwin --prefix=/opt/iphone
make
make install DESTDIR=.

しかし、Installer.appからインストールできるRuby同様、動作が不安定です。

# echo "#!/opt/iphone/bin/ruby" >test.rb
# echo "require 'time'" >>test.rb
# echo "puts Time.now" >>test.rb
# chmod +x test.rb
# ./test.rb
/opt/iphone/lib/ruby/1.8/date/format.rb:963: [BUG] Segmentation fault
ruby 1.8.6 (2007-09-24) [arm-darwin]

zsh: abort ./test.rb

参考ナマケログ: iPod touchでRubyを使うのはちょっと厳しいかも

ちなみに、topするとこんな感じ。

MemRegions: num =  2068, resident = 23.2M +    0B private, 27.2M shared
PhysMem:  23.6M wired, 29.6M active, 11.2M inactive, 64.4M used, 17.9M free
VM:  181P +    0B   7694(0) pageins, 0(0) pageouts

PID COMMAND %CPU TIME #TH #PRTS #MREGS RPRVT RSHRD RSIZE VSIZE
269 top 8.2% 0:17.92 1 17 47 552K 528K 0B 3.38P
222 sh 0.0% 0:01.26 1 13 31 316K 1.38M 0B 4.92P
221 dropbear 0.0% 0:02.79 1 9 25 124K 576K 0B 1.50P
183 sftp-serve 0.0% 0:03.10 1 14 30 244K 400K 0B 1.73P
182 dropbear 0.0% 0:04.56 1 9 25 156K 576K 0B 1.63P
164 notificati 0.0% 0:01.02 2 21 50 260K 500K 0B 1.77P
163 afcd 1.8% 0:09.47 1 15 51 736K 508K 0B 3.36P
143 MobileSafa 0.0% 0:09.55 6 95 227 7.65M 18.6M 0B 49P
72 MobileMail 0.0% 0:03.68 4 119 212 2.42M 14.3M 1.11P 18P
28 notifyd 0.0% 0:03.15 2 156 25 216K 316K 0B 1.64P
26 mediaserve 0.0% 0:14.41 14 145 172 1.23M 1.88M 1.68P 6.64P
25 ptpd 0.0% 0:02.71 2 55 71 484K 1.74M 0B 3.94P
24 update 0.0% 0:16.38 1 13 20 108K 284K 0B 1.08P
23 syslogd 0.0% 0:11.90 3 35 28 212K 340K 0B 1.69P
22 lockdownd 0.0% 0:05.24 4 53 79 648K 1.14M 117T 5.19P
21 mDNSRespon 0.0% 0:02.00 2 45 47 368K 668K 0B 3.08P

搭載メモリ64MBのLinux Zaurusでは、Emacs動かした上に、さらにRubyを動かしても何とかなるんですけどね。Pythonはそんなことなさそうなんだけど。仮想メモリが動いてないんでしょうか?

|

« [Mac OS X]LeopardではSafari3にてタブがらみのスクリプティングが可能に | Main | [iPod touch]iPhone由来のメモ(MobileNotes.app)からsqlite3でデータを出し入れする »