🐒
arm64のUbuntuでC/C++のソースコードをx86_64用にクロスコンパイルしてQEMUで実行する方法
試した環境
前回の記事 Oracle Cloudでarm64のインスタンスを作ってみた の環境でやっています。
$ uname -a
Linux instance-20220420-1140 5.11.0-1028-oracle #31~20.04.1-Ubuntu SMP Wed Jan 26 14:20:52 UTC 2022 aarch64 aarch64 aarch64 GNU/Linux
題材のソースコード
hello.c
#include <stdio.h>
int main()
{
printf("Hello, world!\n");
}
ネイティブでコンパイルして実行
$ gcc -o hello_native hello.c
$ file hello_native
hello_native: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=982c4d8bebf992bf011731c4782b8ace99cd4387, for GNU/Linux 3.7.0, not stripped
$ ./hello_native
Hello, world!
想定通り実行できました。
ちなみに、このファイルを実行するには以下のファイルが必要です。
$ ldd ./hello_native
linux-vdso.so.1 (0x0000ffff91d92000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffff91bdd000)
/lib/ld-linux-aarch64.so.1 (0x0000ffff91d62000)
x86_64用のクロスコンパイラの準備
$ sudo apt install g++-x86-64-linux-gnu
gccしか必要なくてもg++をインストールするのがコツです。そうするとlibcなど必要なものが全て芋づる式にインストールされます。
クロスコンパイルして実行
$ x86_64-linux-gnu-gcc -o hello_x86_64 hello.c
$ file hello_x86_64
hello_x86_64: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=61598e48dc6550a995c3bc0a6ee539bf5263ea02, for GNU/Linux 3.2.0, not stripped
fileコマンドの結果の通り、x86_64の実行ファイルができました。
実行しても、もちろんエラーになります。
$ ./hello_x86_64
-bash: ./hello_x86_64: cannot execute binary file: Exec format error
QEMUのインストール
$ sudo apt install qemu-user-binfmt
これでスタティックリンクされたx86_64の実行ファイルならそのまま実行できるようになります。
$ x86_64-linux-gnu-gcc -static -o hello_x86_64_static hello.c
$ file hello_x86_64_static
hello_x86_64_static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=bb1fedfb8684a4eba6e9d0c1e02b1e54f744de8b, for GNU/Linux 3.2.0, not stripped
$ ./hello_x86_64_static
Hello, world!
x86_64用のライブラリのインストール(失敗)
x86_64(=amd64)用のライブラリをubuntu(Debian)のパッケージのmultiarchの機能を使ってインストールを試しましたが ...
$ sudo dpkg --add-architecture amd64
$ sudo apt update
$ sudo apt install libc6:amd64
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package libc6:amd64
うまくいきませんでした。
クロスコンパイル用のライブラリを流用
ちょっと無理やりですが、ライブラリとローダが所定の位置に見えるように調整します。
$ sudo mv /lib/x86_64-linux-gnu /lib/x86_64-linux-gnu.org
$ sudo ln -s /usr/x86_64-linux-gnu/lib /lib/x86_64-linux-gnu
$ sudo ln -s /lib/x86_64-linux-gnu.org/ldscripts /lib/x86_64-linux-gnu/
$ sudo ln -s /usr/x86_64-linux-gnu/lib64 /lib64
$ ./hello_x86_64
Hello, world!
なんとか実行できるようになりました。
qemuを通したエミュレーションでの実行なので、実行速度は遅いと思いますがマルチアーキテクチャ向けのライブラリのCIを回す時などには便利だと思います。
関連
arm64のubuntuでarm32bitのコードを実行する方法
x86_64のUbuntuでC/C++のソースコードをARM/ARM64用にクロスコンパイルしてQEMUで実行する方法のまとめ
Discussion