2012年04月10日
Ubuntu 12.04LTS Beta2でMultiarchを試す
Ubuntu 12.04LTSではMultiarchが拡充されていると聞いたので、12.04LTS Beta2をインストールしてarm-linux-gnueabihfのクロスビルドと実行を試してみました。
Rubyのクロスビルドとインストールを追記しました。
Ubuntu 12.04LTS正式版では変更がありました。以下も参照してください。
Multiarchについて
ELC2012での以下のセッションを参照してください。
"Multiarch and Why You Should Care: Running, Installing and Crossbuilding With Multiple Architectures"
Wookey, Linaro
https://events.linuxfoundation.org/images/stories/pdf/lf_elc12_wookey.pdf
Debianで導入されてUbuntuにも入ったmultiarchの話。/usr/lib/libfoo は /usr/lib/x64-linux-gnu/libfoo や /usr/lib/arm-linux-gnueabi/libfoo のようにアーキテクチャを含むディレクトリに置く。これによって複数のアーキテクチャが共存できるようになる。armelとarmhfも共存できる。クロスコンパイルも容易になり、ユーザーモードQEMUを使って実行もできる。
準備
以下のページから、Ubuntu 12.04 LTS (Precise Pangolin) Beta 2の"64-bit PC (AMD64) server install CD"をダウンロードし、VMWareの仮想マシンにインストールしました。
http://www.ftp.ne.jp/Linux/packages/ubuntu/releases-cd//precise/
ARMツールチェインのインストール
$ sudo apt-get install g++-4.6-multilib-arm-linux-gnueabihf
これが適切なものかはわかりませんが、とにかく芋づる式にarm用のlibc.soが以下のディレクトリに入りました。
$ find /usr -name libc.so /usr/lib/x86_64-linux-gnu/libc.so /usr/arm-linux-gnueabihf/lib/libc.so /usr/arm-linux-gnueabi/lib/libc.so
テストプログラムのコンパイルと実行
$ mkdir work $ cd work $ vi hello.c $ cat hello.c #include <stdio.h> int main() { printf("Hello, world!\n"); }
まずはネイティブ(x86_64)のgccでコンパイルして実行してみます。
$ gcc -o hello_native hello.c $ file hello_native hello_native: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x6bb53dd947d71fe6ce39671f32fa9dcd92fb141c, not stripped $ ./hello_native Hello, world! $
ARMクロスコンパイラでのコンパイルと実行(static link)
次に、ARMのクロスコンパイラでstatic linkでビルドします。
$ arm-linux-gnueabihf-gcc-4.6 -static -o hello_armhf_static hello.c $ file ./hello_armhf_static ./hello_armhf_static: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, for GNU/Linux 2.6.31, BuildID[sha1]=0xcd238992870a935277df6f8416c01bea80275c88, not stripped
ARM用の実行ファイルなので、このままでは実行できませんが、qemuをインストールすれば可能になります。
$ sudo apt-get install qemu-user-static
これをインストールすると、binfmtの設定もしてくれるので、ARM用バイナリを実行しようとすると自動的にqemu-arm-staticの上で実行されるようになります。
$ ./hello_armhf_static Hello, world!
ARMクロスコンパイラでのコンパイルと実行(dynamic link)
でもここまでは、目新しいものではありません。
Multiarchではダイナミックリンクした実行オブジェクトも実行できるはずです。
$ arm-linux-gnueabihf-gcc-4.6 -o hello_armhf hello.c $ file ./hello_armhf ./hello_armhf: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.31, BuildID[sha1]=0x157b0b92790bf3dda0ee12cecd725da754b2b5a5, not stripped $ ./hello_armhf /lib/arm-linux-gnueabihf/ld-linux.so.3: No such file or directory
あれ?確かに /lib/arm-linux-gnueabihf/ld-linux.so.3 がありません。このファイルはダイナミックリンカの実体です。
$ find /usr -name ld-linux.so.3 /usr/arm-linux-gnueabihf/lib/ld-linux.so.3 /usr/arm-linux-gnueabi/lib/ld-linux.so.3 $ file /usr/arm-linux-gnueabihf/lib/ld-linux.so.3 /usr/arm-linux-gnueabihf/lib/ld-linux.so.3: symbolic link to `ld-2.15.so' $ file /usr/arm-linux-gnueabihf/lib/ld-2.15.so /usr/arm-linux-gnueabihf/lib/ld-2.15.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, stripped
見つけました。これを/lib/arm-linux-gnueabihf に置いてみます。
$ sudo mkdir /lib/arm-linux-gnueabihf $ sudo cp -a /usr/arm-linux-gnueabihf/lib/ld-* /lib/arm-linux-gnueabihf/ $ ls -l /lib/arm-linux-gnueabihf/ total 96 -rwxr-xr-x 1 root root 97520 Apr 6 23:17 ld-2.15.so lrwxrwxrwx 1 root root 10 Apr 6 23:25 ld-linux.so.3 -> ld-2.15.so
今度はどうでしょう。
$ ./hello_armhf ./hello_armhf: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory
ld-linux.so.3はうまくいったようですが、今度はlibc.so.6が見つからないと言っています。
ダイナミックリンカのログを見てみましょう。
$ LD_DEBUG=help ./hello_armhf Valid options for the LD_DEBUG environment variable are: libs display library search paths reloc display relocation processing files display progress for input file symbols display symbol table processing bindings display information about symbol binding versions display version dependencies scopes display scope information all all previous options combined statistics display relocation statistics unused determined unused DSOs help display this help message and exit To direct the debugging output into a file instead of standard output a filename can be specified using the LD_DEBUG_OUTPUT environment variable. $
$ LD_DEBUG=libs ./hello_armhf 27194: find library=libc.so.6 [0]; searching 27194: search cache=/etc/ld.so.cache 27194: search path=/lib/arm-linux-gnueabihf/tls/neon/vfp:/lib/arm-linux-gnueabihf/tls/neon:/lib/arm-linux-gnueabihf/tls/vfp:/lib/arm-linux-gnueabihf/tls:/lib/arm-linux-gnueabihf/neon/vfp:/lib/arm-linux-gnueabihf/neon:/lib/arm-linux-gnueabihf/vfp:/lib/arm-linux-gnueabihf:/usr/lib/arm-linux-gnueabihf/tls/neon/vfp:/usr/lib/arm-linux-gnueabihf/tls/neon:/usr/lib/arm-linux-gnueabihf/tls/vfp:/usr/lib/arm-linux-gnueabihf/tls:/usr/lib/arm-linux-gnueabihf/neon/vfp:/usr/lib/arm-linux-gnueabihf/neon:/usr/lib/arm-linux-gnueabihf/vfp:/usr/lib/arm-linux-gnueabihf:/lib/tls/neon/vfp:/lib/tls/neon:/lib/tls/vfp:/lib/tls:/lib/neon/vfp:/lib/neon:/lib/vfp:/lib:/usr/lib/tls/neon/vfp:/usr/lib/tls/neon:/usr/lib/tls/vfp:/usr/lib/tls:/usr/lib/neon/vfp:/usr/lib/neon:/usr/lib/vfp:/usr/lib (system search path) 27194: trying file=/lib/arm-linux-gnueabihf/tls/neon/vfp/libc.so.6 27194: trying file=/lib/arm-linux-gnueabihf/tls/neon/libc.so.6 27194: trying file=/lib/arm-linux-gnueabihf/tls/vfp/libc.so.6 27194: trying file=/lib/arm-linux-gnueabihf/tls/libc.so.6 27194: trying file=/lib/arm-linux-gnueabihf/neon/vfp/libc.so.6 27194: trying file=/lib/arm-linux-gnueabihf/neon/libc.so.6 27194: trying file=/lib/arm-linux-gnueabihf/vfp/libc.so.6 27194: trying file=/lib/arm-linux-gnueabihf/libc.so.6 27194: trying file=/usr/lib/arm-linux-gnueabihf/tls/neon/vfp/libc.so.6 27194: trying file=/usr/lib/arm-linux-gnueabihf/tls/neon/libc.so.6 27194: trying file=/usr/lib/arm-linux-gnueabihf/tls/vfp/libc.so.6 27194: trying file=/usr/lib/arm-linux-gnueabihf/tls/libc.so.6 27194: trying file=/usr/lib/arm-linux-gnueabihf/neon/vfp/libc.so.6 27194: trying file=/usr/lib/arm-linux-gnueabihf/neon/libc.so.6 27194: trying file=/usr/lib/arm-linux-gnueabihf/vfp/libc.so.6 27194: trying file=/usr/lib/arm-linux-gnueabihf/libc.so.6 27194: trying file=/lib/tls/neon/vfp/libc.so.6 27194: trying file=/lib/tls/neon/libc.so.6 27194: trying file=/lib/tls/vfp/libc.so.6 27194: trying file=/lib/tls/libc.so.6 27194: trying file=/lib/neon/vfp/libc.so.6 27194: trying file=/lib/neon/libc.so.6 27194: trying file=/lib/vfp/libc.so.6 27194: trying file=/lib/libc.so.6 27194: trying file=/usr/lib/tls/neon/vfp/libc.so.6 27194: trying file=/usr/lib/tls/neon/libc.so.6 27194: trying file=/usr/lib/tls/vfp/libc.so.6 27194: trying file=/usr/lib/tls/libc.so.6 27194: trying file=/usr/lib/neon/vfp/libc.so.6 27194: trying file=/usr/lib/neon/libc.so.6 27194: trying file=/usr/lib/vfp/libc.so.6 27194: trying file=/usr/lib/libc.so.6 27194: ./hello_armhf: error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory $
/lib/arm-linux-gnueabihf/libc.so.6 なら見つけてくれるようです。
さっきのは止めて、/lib/arm-linux-gnueabihf はディレクトリごとシンボリックリンクにしてしまいましょう。
$ sudo rm -rf /lib/arm-linux-gnueabihf/ $ sudo ln -s /usr/arm-linux-gnueabihf/lib /lib/arm-linux-gnueabihf $ ls /lib/arm-linux-gnueabihf/libc.so.6 /lib/arm-linux-gnueabihf/libc.so.6 $ ls /lib/arm-linux-gnueabihf/ld-linux.so.3 /lib/arm-linux-gnueabihf/ld-linux.so.3
再度トライ。
$ ./hello_armhf Hello, world!
うまくいきました!
結論から言うと
$ sudo ln -s /usr/arm-linux-gnueabihf/lib /lib/arm-linux-gnueabihf
これが足りなかっただけでした。
これが正しい方法なのかわかりませんが、とにかくMultiarch環境だとダイナミックリンクでもクロスコンパイルと実行が簡単にできることがわかりました。
追記: Rubyのビルドとインストール
HelloWorldだけでは物足りないので、Rubyのソースアーカイブを持ってきてビルドしてみました。
以下のように、CC=/usr/bin/arm-linux-gnueabihf-gcc-4.6 としてコンパイラにARMのクロスコンパイラを指定しただけでできてしまいました。
Rubyはビルド中にrubyコマンドが必要ですが、このマシンにはまだrubyのインストールしていないので、今ビルドしたばかりのARM版rubyが使用されたようです。そのため、Rdocの生成はすごく時間がかかりました。
$ sudo apt-get build-dep ruby $ wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p125.tar.gz $ tar xvf ruby-1.9.3-p125.tar.gz $ mkdir obj $ cd obj $ ../ruby-1.9.3-p125/configure --help $ CC=/usr/bin/arm-linux-gnueabihf-gcc-4.6 ../ruby-1.9.3-p125/configure --prefix=/usr/local/arm-linux-gnueabihf $ time make 2>&1 |tee make.log real 32m45.679s user 31m41.059s sys 0m52.527s $ time make test (途中で^Cで止めました。) $ sudo make install
テストはI/Oの部分で
qemu: Unsupported syscall: 336
が表示されて、Failした他、スレッドを使っているらしいところで長時間応答がなくなったので^Cで止めました。それ以外のテストはPassしていました。336番のシステムコールはppollです。
(2012.4.13追記。Linaro qemuの2012.04リリースではppollシステムコールがサポートされたようです。http://www.linaro.org/linaro-blog/2012/04/12/linaro-qemu-2012-04-released/ )
$ file /usr/local/arm-linux-gnueabihf/bin/ruby /usr/local/arm-linux-gnueabihf/bin/ruby: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.31, BuildID[sha1]=0x2bdd2f9d57e0f50cf8a0419b225771aedb9cd32f, not stripped
インストールされたrubyコマンドは確かにARMの実行ファイルです。
$ /usr/local/arm-linux-gnueabihf/bin/irb irb(main):001:0> p RUBY_VERSION "1.9.3" => "1.9.3" irb(main):002:0>
この状態で別のターミナルからpsコマンドでプロセスを見ると、qemu-arm-static経由でrubyが動いているのがわかります。
$ ps u USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND koba 1098 0.0 0.7 26284 7552 tty1 S+ 11:09 0:00 -bash koba 1347 0.0 0.7 26416 7740 pts/0 Ss+ 11:10 0:03 -bash koba 11708 0.0 0.7 26392 7728 pts/2 Ss 16:40 0:00 -bash koba 11822 0.1 1.1 4136292 11704 pts/2 Sl+ 16:45 0:01 /usr/bin/qemu-arm-static /usr/local/arm-linux-gnueabihf/bin/ruby /usr/local/arm-linux-gnueabihf/bin/irb koba 11828 0.0 0.1 18152 1272 pts/1 R+ 17:01 0:00 ps u koba 26947 0.0 0.7 26400 7740 pts/1 Ss 14:19 0:01 -bash
/usr/local/arm-linux-gnueabihf をまるごとARM Ubuntu 12.04 の実機にコピーすればそのまま動かせるはずです。(これは後で試します。)
おまけ
ダイナミックリンクされている実行ファイルがどのライブラリを使用するかは以下のように lddコマンドで調べることができます。
$ ldd /usr/bin/ruby linux-vdso.so.1 => (0x00007fff505ff000) libruby1.8.so.1.8 => /usr/lib/libruby1.8.so.1.8 (0x00007ff491959000) libpthread.so.0 => /lib/libpthread.so.0 (0x00007ff49173c000) librt.so.1 => /lib/librt.so.1 (0x00007ff491533000) libdl.so.2 => /lib/libdl.so.2 (0x00007ff49132f000) libcrypt.so.1 => /lib/libcrypt.so.1 (0x00007ff4910f6000) libm.so.6 => /lib/libm.so.6 (0x00007ff490e72000) libc.so.6 => /lib/libc.so.6 (0x00007ff490aef000) /lib64/ld-linux-x86-64.so.2 (0x00007ff491c7b000)
同じことをクロスビルドした実行ファイルで調べるには、以下のようにしたらできました。
$ /lib/arm-linux-gnueabihf/ld-linux.so.3 --list /usr/local/arm-linux-gnueabihf/bin/ruby libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0xf67bd000) librt.so.1 => /lib/arm-linux-gnueabihf/librt.so.1 (0xf67af000) libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0xf67a4000) libcrypt.so.1 => /lib/arm-linux-gnueabihf/libcrypt.so.1 (0xf676e000) libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0xf6704000) libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0xf66f2000) libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xf6610000) /lib/arm-linux-gnueabihf/ld-linux.so.3 (0xf6fe0000)
引数無しで実行するとヘルプメッセージが出ます。
$ /lib/arm-linux-gnueabihf/ld-linux.so.3 Usage: ld.so [OPTION]... EXECUTABLE-FILE [ARGS-FOR-PROGRAM...] You have invoked `ld.so', the helper program for shared library executables. This program usually lives in the file `/lib/ld.so', and special directives in executable files using ELF shared libraries tell the system's program loader to load the helper program from this file. This helper program loads the shared libraries needed by the program executable, prepares the program to run, and runs it. You may invoke this helper program directly from the command line to load and run an ELF executable file; this is like executing that file itself, but always uses this helper program from the file you specified, instead of the helper program file specified in the executable file you run. This is mostly of use for maintainers to test new versions of this helper program; chances are you did not intend to run this program. --list list all dependencies and how they are resolved --verify verify that given object really is a dynamically linked object we can handle --library-path PATH use given PATH instead of content of the environment variable LD_LIBRARY_PATH --inhibit-rpath LIST ignore RUNPATH and RPATH information in object names in LIST --audit LIST use objects named in LIST as auditors
参考
Kernel/VM探検隊でのmaltiarchのスライド
http://www.slideshare.net/uwabami/debian-next-release-goal-multiarch