2012年04月10日

Ubuntu 12.04LTS Beta2でMultiarchを試す

Ubuntu 12.04LTSではMultiarchが拡充されていると聞いたので、12.04LTS Beta2をインストールしてarm-linux-gnueabihfのクロスビルドと実行を試してみました。

Rubyのクロスビルドとインストールを追記しました。



Ubuntu 12.04LTS正式版では変更がありました。以下も参照してください。

Ubuntu 12.04LTS 正式版でMultiarchを試す

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



トラックバックURL

コメントする

名前
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード