Ubuntu ARMクロスコンパイル — aarch64向けバイナリをx86でビルド

開発環境

「Raspberry Pi 用のバイナリを手元の Ubuntu マシンでビルドしたい」「ARM64向けのDockerイメージを x86 環境で作りたい」——そんな場面で役立つのがクロスコンパイルです。Ubuntu には gcc-aarch64-linux-gnu というクロスコンパイラが apt で1コマンド入ります。私の環境(Ubuntu 24.04 LTS / Docker上)で実際に aarch64 向けバイナリを生成して確認しました。

この記事のポイント

  • apt-get install gcc-aarch64-linux-gnu で x86_64 ホストに ARM64クロスコンパイラが入る
  • コンパイル後は file コマンドで「ARM aarch64」と表示されるか確認する
  • -static オプションで依存なし単体バイナリを作れる(ただしサイズは約9倍)
  • Ubuntu 24.04 では GCC 13.3.0 / Binutils 2.42 が入る(22.04 は GCC 11.4.0 / Binutils 2.38)
  • Makefile に CC = aarch64-linux-gnu-gcc を書けばプロジェクトで使い回せる

クロスコンパイルとは

通常のコンパイルは「ビルドするマシン」と「実行するマシン」が同じアーキテクチャです。クロスコンパイルはこの2つが異なる場合に使います。たとえば x86_64 の Ubuntu マシンで、ARM64(aarch64)向けのバイナリを作ることがこれにあたります。

クロスコンパイルの処理フロー(概念図)
クロスコンパイルの処理フロー(概念図)

ARM64(aarch64)は ARMv8 アーキテクチャの64bit版で、Raspberry Pi 4/5、Apple M1/M2、AWS Graviton、Ampere Altra などが採用しています。x86_64 マシンしか手元にない場合でも、クロスコンパイラがあれば実機なしに ARM64 バイナリを生成できます。

前提環境

動作確認環境

Ubuntu 24.04 LTS(Docker公式イメージ ubuntu:24.04)で検証しています。Ubuntu 22.04 でも同じ手順で動きますが、インストールされる GCC バージョンが異なります(後述)。

クロスコンパイラをインストールする

手順1:パッケージリストを更新する




ubuntu@linuxlab: ~
$ sudo apt-get update
Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease
Fetched 30.9 MB in 2s (19.4 MB/s)
Reading package lists… Done

手順2:クロスコンパイラとリンカをインストールする




ubuntu@linuxlab: ~
$ sudo apt-get install -y gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
Setting up gcc-13-aarch64-linux-gnu (13.3.0-6ubuntu2~24.04.1cross1) …
Setting up binutils-aarch64-linux-gnu (2.42-4ubuntu2.10) …
Setting up gcc-aarch64-linux-gnu (4:13.2.0-7ubuntu1) …
クロスコンパイラ インストール ターミナルスクリーンショット(実測)
クロスコンパイラ インストール ターミナルスクリーンショット(実測)

インストールが終わったらバージョンを確認します。




ubuntu@linuxlab: ~
$ aarch64-linux-gnu-gcc –version
aarch64-linux-gnu-gcc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
Copyright (C) 2023 Free Software Foundation, Inc.
$ aarch64-linux-gnu-ld –version | head -1
GNU ld (GNU Binutils for Ubuntu) 2.42
gcc-aarch64-linux-gnu インストール実ログ(Ubuntu 24.04・実測)
gcc-aarch64-linux-gnu インストール実ログ(Ubuntu 24.04・実測)

Ubuntu 24.04 では GCC 13.3.0 と GNU Binutils 2.42 が入りました。これで ARM64 向けの C/C++ プログラムをビルドする準備が整っています。

実際にクロスコンパイルしてみる

手順3:サンプルの C プログラムを作る




ubuntu@linuxlab: ~
$ cat hello.c
#include <stdio.h>
int main(void) {
printf(“Hello from aarch64!\n”);
return 0;
}

手順4:aarch64向けにコンパイルする

コンパイラ名の先頭に aarch64-linux-gnu- をつけることがポイントです。ここを省略すると x86_64 バイナリが生成されてしまいます。




ubuntu@linuxlab: ~
$ aarch64-linux-gnu-gcc -o hello_arm64 hello.c
(エラーなし・正常終了)

手順5:file コマンドで ARM64 バイナリか確認する




ubuntu@linuxlab: ~
$ file hello_arm64
hello_arm64: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV),
dynamically linked, interpreter /lib/ld-linux-aarch64.so.1,
for GNU/Linux 3.7.0, not stripped
$ ls -lh hello_arm64
-rwxr-xr-x 1 root root 69K Jun 22 04:05 hello_arm64
クロスコンパイル実行と file コマンド確認(実測)
クロスコンパイル実行と file コマンド確認(実測)

「ARM aarch64」という表記が出れば成功です。 x86_64 のホスト上で、ARM64向けの ELF バイナリが正しく生成できています。バイナリサイズは 69KB でした。

クロスコンパイル実行ターミナルスクリーンショット(実測)
クロスコンパイル実行ターミナルスクリーンショット(実測)
著者アイコン
著者アイコン

file コマンドの出力を確認せずに「コンパイルできた」と思ってはまりがちです。実際 aarch64-linux-gnu- プレフィックスをうっかり省略したことがあって、x86_64 バイナリを ARM デバイスに転送して「実行できない」と30分悩みました。

動的リンクと静的リンクの違い

クロスコンパイルで生成したバイナリには「動的リンク」と「静的リンク」の2種類があります。用途で使い分けが必要です。

動的リンクバイナリ(デフォルト)




ubuntu@linuxlab: ~
$ aarch64-linux-gnu-gcc -o bench_dyn bench.c
$ file bench_dyn
bench_dyn: ELF 64-bit LSB pie executable, ARM aarch64 … dynamically linked
$ ls -lh bench_dyn
-rwxr-xr-x 1 root root 69K Jun 22 04:06 bench_dyn

静的リンクバイナリ(-static オプション)




ubuntu@linuxlab: ~
$ aarch64-linux-gnu-gcc -static -o bench_static bench.c
$ file bench_static
bench_static: ELF 64-bit LSB executable, ARM aarch64 … statically linked
$ ls -lh bench_static
-rwxr-xr-x 1 root root 619K Jun 22 04:06 bench_static
動的リンク vs 静的リンク バイナリ比較(実測)
動的リンク vs 静的リンク バイナリ比較(実測)

静的リンクにするとサイズが 69KB → 619KB と約9倍になりますが、ターゲット環境に libc.so が不要になります。Alpine Linux ベースのコンテナや最小構成の組み込みシステムに転送する場合は静的リンクが楽です。Raspberry Pi OS(Debian ベース)のように libc が入っているなら動的リンクで十分です。

Makefile でクロスコンパイルをまとめる

毎回 aarch64-linux-gnu-gcc と打つのは手間なので、Makefile に CC 変数を定義しておくと便利です。私は環境変数で切り替えられるようにしています。




ubuntu@linuxlab: ~/crossbuild
$ cat Makefile
CC = aarch64-linux-gnu-gcc
CFLAGS = -Wall -O2
TARGET = app_arm64

$(TARGET): main.c
$(CC) $(CFLAGS) -o $(TARGET) main.c

clean:
rm -f $(TARGET)
$ make
aarch64-linux-gnu-gcc -Wall -O2 -o app_arm64 main.c
$ ls -lh app_arm64
-rwxr-xr-x 1 root root 69K Jun 22 04:07 app_arm64

make を実行すると aarch64-linux-gnu-gcc -Wall -O2 -o app_arm64 main.c が走り、正常に ARM64 バイナリが生成されました。既存の Makefile を流用するなら CC 変数を上書きするだけで済みます。




ubuntu@linuxlab: ~
$ make CC=aarch64-linux-gnu-gcc
(既存Makefileの CC 変数を外から上書き)

Ubuntu 22.04 vs 24.04 — ツールチェーンのバージョン差

Docker コンテナで両バージョンを実際に確認しました。GCC のメジャーバージョンが変わっているので注意してください。

Ubuntu 22.04 vs 24.04 ツールチェーンバージョン比較(実測)
Ubuntu 22.04 vs 24.04 ツールチェーンバージョン比較(実測)
ツール Ubuntu 22.04 LTS Ubuntu 24.04 LTS
aarch64-linux-gnu-gcc 11.4.0 13.3.0
aarch64-linux-gnu-ld 2.38 2.42
gcc-aarch64-linux-gnu パッケージ 4:11.2.0-1ubuntu1 4:13.2.0-7ubuntu1

Ubuntu 24.04 では GCC 13.3.0 が入り、C17 や C++23 の機能が使えます。22.04 の GCC 11.4.0 でも ARM64 クロスコンパイル自体は問題なく動きますが、新しい言語機能を使う場合は 24.04 を選ぶのが安全です。実際に docker コンテナで確認した数値なので、インターネット上のスクリーンショットと異なる場合はパッケージのアップデートが入った可能性があります。

よくあるエラーと解決策

①「cannot execute binary file: Exec format error」が出る




ubuntu@linuxlab: ~
$ ./hello_arm64
bash: ./hello_arm64: cannot execute binary file: Exec format error

これは正常です。ARM64 バイナリを x86_64 ホストで直接実行しようとするとこのエラーになります。ARM64 マシンや Raspberry Pi に転送して実行するか、qemu-user-static をインストールして qemu-aarch64-static ./hello_arm64 とすれば x86_64 ホストでもエミュレーション実行できます。

②「/usr/bin/aarch64-linux-gnu-gcc: No such file or directory」が出る

インストールが完了していないか、パッケージ名を間違えている場合です。apt-get install gcc-aarch64-linux-gnu(ハイフン2本で分かれている)を再度実行してください。apt-cache show gcc-aarch64-linux-gnu でパッケージが存在するか確認できます。

③静的リンクで「cannot find -lgcc」が出る

注意

-static オプションを使うとき、libgcc-s1-arm64-crosslibc6-dev-arm64-cross が不足するとリンクエラーになります。その場合は apt-get install libc6-dev-arm64-cross を追加でインストールしてください。

④「aarch64-linux-gnu-gcc: command not found」が出る

インストール後に hash -r(bash のコマンドキャッシュをリセット)を実行してみてください。それでも見つからない場合は ls /usr/bin/aarch64-linux-gnu-* でバイナリの存在を確認します。

まとめ

  • Ubuntu 24.04 に apt-get install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu で ARM64 クロスコンパイル環境が整う
  • インストールされる GCC は 13.3.0、GNU Binutils は 2.42(Ubuntu 24.04 実測)
  • コンパイルは aarch64-linux-gnu-gcc -o 出力ファイル ソース.c で実行する
  • file コマンドで「ARM aarch64」と表示されたら ARM64 バイナリとして正しく生成されている
  • 静的リンク(-static)にするとバイナリサイズが約9倍になる代わりに、ターゲット環境の libc が不要になる
  • Makefile の CC 変数を差し替えるだけで既存プロジェクトにも適用できる

VPS でクロスコンパイル環境を構築すると、Raspberry Pi への自動デプロイパイプラインと組み合わせて使えます。学習用 VPS の選び方は以下の記事が参考になります。

コメント

タイトルとURLをコピーしました