🐒

arm64のUbuntuでC/C++のソースコードをx86_64用にクロスコンパイルしてQEMUで実行する方法

2022/04/20に公開

試した環境

前回の記事 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