Help us understand the problem. What is going on with this article?

最低限のクロスコンパイラの作り方

More than 5 years have passed since last update.

興味を持ったアーキテクチャの機械語を眺めるための、最低限のクロスコンパイラの作り方を説明します。全部入りbinutilsも紹介します。

※ 実用的な開発は目的としていません。指定するオプションも最小限に抑えています。

熱血!アセンブラ入門の最初に登場するPowerPCを例に説明しますが、gccでサポートされているアーキテクチャであれば同じ方法が使えます。

※ 同書よりも新しいgccを扱うため、いくつか新しいアーキテクチャが使えます。差分は最後の方にまとめています。

ビルド対象

  1. binutils: アセンブラ、逆アセンブラ、リンカなど
  2. gcc: コンパイラ

準備

gccが使える環境が必要です。

WindowsではMSYS2を使う前提で説明します。

依存ライブラリ

gcc-4.3以降は次のライブラリに依存しています。

使用しているパッケージ管理システムなどからインストールしてください。

MSYS2での例を示します。

$ pacman -S mpc-devel

※ mpc-develだけインストールすれば他も依存関係で入ります。

自分でビルドするときは次のようにすれば良いでしょう。

環境変数
$ export CPPFLAGS=-I/opt/cross/include LDFLAGS=-L/opt/cross/lib
オプション
$ ./configure --prefix=/opt/cross

ビルド

ビルド手順を示します。速いマシンだと10分程度で終わります。

ダウンロード

binutilsは執筆時の最新版、gccは4.6.4を使います。

$ wget http://ftp.gnu.org/gnu/binutils/binutils-2.25.tar.bz2
$ wget http://ftp.gnu.org/gnu/gcc/gcc-4.6.4/gcc-core-4.6.4.tar.bz2

gcc-4.6を使っているのは以下の理由によります。

  • gcc-4.5以前は新しいmakeinfoでエラーが出る
  • gcc-4.7以降は配布物が全部入りとなり巨大

展開

適当なディレクトリでソースを展開します。

$ tar xvf binutils-2.25.tar.bz2
$ tar xvf gcc-core-4.6.4.tar.bz2

パッチ (MSYS2)

MSYS2では環境を認識するように修正が必要です。

$ cp /usr/share/automake-1.15/config.* gcc-4.6.4

※ この手の修正は新しい環境では良くあることなので、知っていれば役立つかもしれません。

ビルドディレクトリ

ビルドはソースの外で行うのが流儀です。ビルド用のディレクトリを作成します。ディレクトリ名は任意ですが、ここではgccで指定するターゲット名に合わせます。

※ 先ほどのコマンドに続けて入力することを想定しています。以降も同様です。

$ mkdir powerpc-elf
$ cd powerpc-elf
  • powerpc: CPU名
  • elf: バイナリ形式(ELFファイル)

binutils

ビルドディレクトリを作って入り、相対パスでconfigureを呼びます。

$ mkdir binutils
$ cd binutils
$ ../../binutils-2.25/configure --prefix=/opt/cross --target=powerpc-elf

configureのオプションは次の通りです。

  • --prefix: インストール先
  • --target: 対象とするアーキテクチャ

ビルドします。-j22はCPUのコア数に合わせて調整すると(例:4コアなら-j4)、ビルド時間が短縮できます。

$ make -j2

ビルドが完了したらインストールします。

$ sudo make install

※ MSYS2ではsudoは不要です。

ディレクトリを抜けます。

$ cd ..

エラー対策

MSYS2でmake中に次のようなエラーで止まることがあります。

39 [main] make 7628 child_info_fork::abort:(略): Loaded to different address: parent(0x440000) != child(0x630000)
make: fork: Resource temporarily unavailable

その場合は次の方法で対処してください。

  1. MSYS2のターミナルをすべて閉じます。
  2. MSYS2がインストールされたディレクトリにあるautorebase.batを実行します。
  3. コマンドプロンプトのウィンドウが出て、数分後に自動的に閉じます。文字は何も表示されません。
  4. MSYS2のターミナルを開いて先ほどのディレクトリに移動して、もう一度makeを試してください。

次の情報を参考にしました。

gcc

binutilsと同様の手順ですが、makeのターゲットに-gccが付いているのに注意してください。

※ これを付けないとライブラリがビルドされてエラーになります。

$ mkdir gcc
$ cd gcc
$ ../../gcc-4.6.4/configure --prefix=/opt/cross --target=powerpc-elf
$ make -j2 all-gcc
$ sudo make install-gcc

※ MSYS2ではsudoは不要です。

ビルド時間

参考としてbinutilsとgccのビルドにどれくらい時間が掛かるかを示します。

OS CPU クロック ジョブ 時間 ウィルス対策
Windows 8.1 (MSYS) Athlon 64 X2 4600+ 2.40GHz -j2 57m14.069s Windows Defender
Windows 8.1 (MSYS2) Athlon 64 X2 4600+ 2.40GHz -j2 27m13.587s (オフ)
Windows 8.1 (MSYS2) Athlon 64 X2 4600+ 2.40GHz -j2 30m08.217s Windows Defender
Windows 8.1 (MSYS2) Celeron N2830 2.16GHz -j2 38m26.803s Windows Defender
Windows 7 (MSYS2) Core i5-3320M 2.60GHz -j2 15m37.006s (オフ)
Windows 7 (MSYS2) Core i5-3320M 2.60GHz -j4 12m32.296s (オフ)
Windows 7 (MSYS2) Core i5-3320M 2.60GHz -j4 21m37.818s Virus Buster
  • 同じマシンでも無印MSYSよりMSYS2が高速
  • HTの効果は約1.25倍
  • ウィルス対策はかなり足を引っ張る

使用方法

ビルドしたPowerPC用クロスコンパイラを使ってみます。

パス

標準でパスの通っていない /opt/cross にインストールしたため、使用前にパスを通します。

$ export PATH="/opt/cross/bin:$PATH"

毎回やると面倒なので ~/.bashrc などに記載しておくと良いでしょう。

テスト

どんなコードが生成されるのかテストします。

※ ビルドディレクトリから出て、適当なディレクトリを作ってそこで作業してください。

add.c
int add(int a, int b) {
    return a + b;
}

コンパイルします。

  • -nostdlib: Cのライブラリ(libc)などをリンクしません。存在しないため指定しないとエラーとなります。
  • -g: 逆アセンブル時にC言語の該当箇所を表示するためデバッグ情報を有効化します。
  • -O: 最適化します。出力される機械語を単純にするために利用しています。外したものと対比してみると良いでしょう。
$ powerpc-elf-gcc -nostdlib -g -O add.c
/opt/cross/lib/gcc/powerpc-elf/4.6.4/../../../../powerpc-elf/bin/ld: 警告: エントリシンボル _start が見つかりません。デフォルトとして 0000000001800054 を使用します

※ 警告は無視します。

出力されたバイナリ(a.out)を逆アセンブルして、どんな機械語が出力されているかを確認します。

  • -S: C言語のソースと対比しながら逆アセンブルします。対象は-g付きでコンパイルされている必要があります。
$ powerpc-elf-objdump -S a.out

a.out:     ファイル形式 elf32-powerpc


セクション .text の逆アセンブル:

01800054 <add>:
int add(int a, int b) {
    return a + b;
}
 1800054:       7c 63 22 14     add     r3,r3,r4
 1800058:       4e 80 00 20     blr

引数がr3r4で渡されて、戻り値はr3で返される様子が読み取れます。

他のアーキテクチャ

targetを変えれば色々なアーキテクチャがビルドできます。

どんなアーキテクチャがあるかは次の記事を参考にしてください。

IA-64

リンクを分けないとエラーになります。

NG
$ ia64-elf-gcc -nostdlib add.c
/tmp/ccYFwywB.s: Assembler messages:
/tmp/ccYFwywB.s:14: Warning: Explicit stops are ignored in auto mode
/opt/cross/lib/gcc/ia64-elf/4.6.4/../../../../ia64-elf/bin/ld: so が見つかりません: No such file or directory
collect2: ld はステータス 1 で終了しました
OK
$ ia64-elf-gcc -c add.c
$ ia64-elf-ld add.o
ia64-elf-ld: 警告: エントリシンボル _start が見つかりません。デフォルトとして 40000000000000b0 を使用します

SH64

リンクを分けるとエラーになります。

OK
$ sh64-elf-gcc -nostdlib add.c
/opt/cross/lib/gcc/sh64-elf/4.6.4/../../../../sh64-elf/bin/ld: 警告: エントリシンボル start が見つかりません。デフォルトとして 0000000000001000 を使用します
NG
$ sh64-elf-gcc -c add.c
$ sh64-elf-ld add.o
sh64-elf-ld: sh5 アーキテクチャ (入力ファイル`add.o') は sh 出力と互換性がありません
sh64-elf-ld: 警告: エントリシンボル start が見つかりません。デフォルトとして 0000000000001000 を使用します
sh64-elf-ld: 最終リンクに失敗しました: 不正な値です

オプションを指定する必要があります。

OK
$ sh64-elf-gcc -c add.c
$ sh64-elf-ld -mshelf32 add.o
sh64-elf-ld: 警告: エントリシンボル start が見つかりません。デフォルトとして 0000000000001000 を使用します

テストスクリプト

参考までにコンパイル・リンク・逆アセンブルを一発でやるスクリプトの例を示します。

※ 前述の理由でSH64には使えません。

/opt/cross/bin/testgcc
#!/usr/bin/env bash
arch=$1
tmp=`mktemp`
shift
$arch-gcc -o $tmp -c -O -g -fomit-frame-pointer $@ &&
    $arch-ld $tmp &&
    $arch-objdump -S a.out
rm -f $tmp
使用例
$ testgcc ia64-elf add.c

全部入りbinutils

binutilsは全部入りにできます。これさえあれば様々なCPUのバイナリを逆アセンブルできます。

いくつか注意点があります。

  • gccは全部入りにできません。個別に指定してビルドするしかありません。
  • アセンブラがうまく機能しないため、ほぼ逆アセンブラ専用となります。gccとセットでビルドするbinutilsにはアセンブラが不可欠なため、全部入りがあっても依然としてbinutilsのビルドは必要です。

パッチ (MSYS2)

MSYS2ではこのパッチが必要です。

$ wget https://raw.githubusercontent.com/Alexpux/MSYS2-packages/1ec8aa42e0892f04400039ea1e5ede51ffc4d35e/binutils/binutils-trunk-msys2.patch
$ patch -p1 -d binutils-2.25 < binutils-trunk-msys2.patch

ビルド

$ mkdir all
$ cd all
$ ../binutils-2.25/configure --prefix=/opt/cross --enable-targets=all --enable-64-bit-bfd --program-prefix=all-
$ make -j2
$ sudo make install

※ MSYS2ではsudoは不要です。

使い方

コマンドにall-の接頭辞を付けます。

$ all-objdump -S a.out

自動ビルドスクリプト

参考として自動化したスクリプトを置いておきます。あくまで参考のため無保証です。

MSYS2用パッケージ

pacmanからインストールできるパッケージを用意しました。

熱血!アセンブラ入門のサンプル

次のサイトで公開されています。

Makefileから不要なアーキテクチャをコメントアウトして、PREFIXを修正すればとりあえず使えます。

パッチ

ここで紹介している方法で作った全アーキテクチャに対応するパッチです。

差分は次の通りです。

  • 除去: m6811-elf, strongarm-elf, xscale-elf
  • 追加: bfin-elf, m32c-elf, microblaze-elf, moxie-elf, rx-elf, score-elf, sparc64-elf, spu-elf

具体的には次を参照してください。

ARC

64ビット環境ではarc-elfがエラーになるため、コメントアウトした方が良いでしょう。

---- compile (arc-elf.c => arc-elf.s)
/usr/bin/arc-elf-gcc \
                -fno-builtin -nostdinc -nostdlib -static -O -Wall -g  \
                -fverbose-asm -fomit-frame-pointer  -o arc-elf.s -S arc-elf.c
arc-elf.c: 関数 ‘return_short_upper’ 内:
arc-elf.c:49:1: エラー: 認識できない命令:
(insn 5 4 6 3 (set (reg:HI 68 [ <retval> ])
        (const_int -18 [0xffffffffffffffee])) arc-elf.c:48 -1
     (nil))
arc-elf.c:49:1: コンパイラ内部エラー: extract_insn 内、位置 recog.c:2109
Please submit a full bug report,
with preprocessed source if appropriate.
See <http://gcc.gnu.org/bugs.html> for instructions.

gcc内の中間言語RTLのLISP風表記が見えています。

7shi
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away