各種アーキテクチャのクロスコンパイラ環境を構築する

Docker上に作成したDebian 8環境にてbinutilsgccglibcソースコードからビルドし、さまざまなアーキテクチャのクロスコンパイラ環境を構築してみる。 なお、構築した環境は次のDocker imageとして公開してある(イメージサイズが2.8GBと大きいことに注意)。

環境

Ubuntu 14.04.3 LTS 64bit版、Docker 1.9.1

$ uname -a
Linux vm-ubuntu64 3.19.0-25-generic #26~14.04.1-Ubuntu SMP Fri Jul 24 21:16:20 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.3 LTS
Release:        14.04
Codename:       trusty

$ sudo docker version
Client:
 Version:      1.9.1
 API version:  1.21
 Go version:   go1.4.2
 Git commit:   a34a1d5
 Built:        Fri Nov 20 13:12:04 UTC 2015
 OS/Arch:      linux/amd64

Server:
 Version:      1.9.1
 API version:  1.21
 Go version:   go1.4.2
 Git commit:   a34a1d5
 Built:        Fri Nov 20 13:12:04 UTC 2015
 OS/Arch:      linux/amd64

ロスコンパイラをビルドしてみる

DockerでDebian 8のコンテナを起動し、ビルドに必要なパッケージをインストールする。

$ sudo docker run --name debian-crossbuild -i -t debian:jessie /bin/bash
root@8ad669f3b939:/# apt-get update
root@8ad669f3b939:/# apt-get upgrade
root@8ad669f3b939:/# apt-get install build-essential texinfo gawk
root@8ad669f3b939:/# apt-get install vim-tiny less wget

次のページを参考に、各ソースコードをダウンロードするシェルスクリプトを作成する。

ここでは、gccコンパイルに必要なライブラリ群のソースコード./contrib/download_prerequisitesを実行することで準備している。

#!/bin/bash
# download.sh

set -e

BINUTILS_VERSION=binutils-2.25
GCC_VERSION=gcc-5.2.0
LINUX_KERNEL_VERSION=linux-3.16
GLIBC_VERSION=glibc-2.22

wget -nc http://ftpmirror.gnu.org/binutils/$BINUTILS_VERSION.tar.gz
wget -nc http://ftpmirror.gnu.org/gcc/gcc-5.2.0/$GCC_VERSION.tar.gz
wget -nc https://www.kernel.org/pub/linux/kernel/v3.x/$LINUX_KERNEL_VERSION.tar.xz
wget -nc http://ftpmirror.gnu.org/glibc/$GLIBC_VERSION.tar.xz

for f in *.tar*; do
    tar xfk $f
done

cd $GCC_VERSION
./contrib/download_prerequisites

続けて、ビルドスクリプトを作成する。 PARALLEL_MAKEはビルドするマシンのCPU数に合わせて調整する。 なお、コメントアウトしているアーキテクチャは途中でエラーが発生しビルドできなかった。

#!/bin/bash
# build.sh

set -e

PREFIX=/usr/local
PARALLEL_MAKE=-j4
CONFIGURATION_OPTIONS="--disable-multilib --disable-nls"

BINUTILS_VERSION=binutils-2.25
GCC_VERSION=gcc-5.2.0
LINUX_KERNEL_VERSION=linux-3.16
GLIBC_VERSION=glibc-2.22

build() {
    local TARGET="$1"
    local LINUX_ARCH="$2"

    # Step 1. Binutils
    mkdir -p build-binutils-$TARGET
    cd build-binutils-$TARGET
    ../$BINUTILS_VERSION/configure --prefix=$PREFIX --target=$TARGET $CONFIGURATION_OPTIONS
    make $PARALLEL_MAKE
    make install
    cd ..

    # Step 2. Linux Kernel Headers
    cd $LINUX_KERNEL_VERSION
    make ARCH=$LINUX_ARCH INSTALL_HDR_PATH=$PREFIX/$TARGET headers_install
    cd ..

    # Step 3. C/C++ Compilers
    mkdir -p build-gcc-$TARGET
    cd build-gcc-$TARGET
    ../$GCC_VERSION/configure --prefix=$PREFIX --target=$TARGET --enable-languages=c,c++ $CONFIGURATION_OPTIONS
    make $PARALLEL_MAKE gcc_cv_libc_provides_ssp=yes all-gcc
    make install-gcc
    cd ..

    # Step 4. Standard C Library Headers and Startup Files
    mkdir -p build-glibc-$TARGET
    cd build-glibc-$TARGET
    ../$GLIBC_VERSION/configure --prefix=$PREFIX/$TARGET --build=$MACHTYPE --host=$TARGET --target=$TARGET --with-headers=$PREFIX/$TARGET/include $CONFIGURATION_OPTIONS libc_cv_forced_unwind=yes
    make install-bootstrap-headers=yes install-headers
    make $PARALLEL_MAKE csu/subdir_lib
    install csu/crt1.o csu/crti.o csu/crtn.o $PREFIX/$TARGET/lib
    $TARGET-gcc -nostdlib -nostartfiles -shared -x c /dev/null -o $PREFIX/$TARGET/lib/libc.so
    touch $PREFIX/$TARGET/include/gnu/stubs.h
    cd ..

    # Step 5. Compiler Support Library
    cd build-gcc-$TARGET
    make $PARALLEL_MAKE all-target-libgcc
    make install-target-libgcc
    cd ..

    # Step 6. Standard C Library & the rest of Glibc
    cd build-glibc-$TARGET
    make $PARALLEL_MAKE
    make install
    cd ..

    # Step 7. Standard C++ Library & the rest of GCC
    cd build-gcc-$TARGET
    make $PARALLEL_MAKE all
    make install
    cd ..

    rm -rf build-binutils-$TARGET build-gcc-$TARGET build-glibc-$TARGET
}

build i686-linux-gnu x86
build arm-linux-gnueabi arm
build arm-linux-gnueabihf arm
build aarch64-linux-gnu arm64
build powerpc-linux-gnu powerpc
build powerpc64le-linux-gnu powerpc
build mips-linux-gnu mips
build mipsel-linux-gnu mips
build mips64el-linux-gnuabi64 mips
build alpha-linux-gnu alpha
build s390x-linux-gnu s390
build m68k-linux-gnu m68k

#build x86_64-linux-gnu x86
#build sparc-linux-gnu sparc
#build sparc64-linux-gnu sparc
#build cris-linux-gnu cris
#build sh4-linux-gnu sh

ついでに、次のページで紹介されている、全アーキテクチャに対応したbinutilsのビルドスクリプトも作成する。

#!/bin/bash
# build-binutils-all.sh

set -e

PREFIX=/usr/local
PARALLEL_MAKE=-j4
CONFIGURATION_OPTIONS="--disable-multilib --disable-nls"

BINUTILS_VERSION=binutils-2.25

mkdir -p build-binutils-all
cd build-binutils-all
../$BINUTILS_VERSION/configure --prefix=$PREFIX --enable-targets=all --enable-64-bit-bfd --program-prefix=all- $CONFIGURATION_OPTIONS
make $PARALLEL_MAKE
make install
cd ..

rm -rf build-binutils-all

/tmpに移動し、それぞれのスクリプトを実行する。 かなりの時間とディスク容量を要するので注意。

root@8ad669f3b939:/# cd /tmp
root@8ad669f3b939:/tmp# bash download.sh
root@8ad669f3b939:/tmp# nohup bash build.sh &
root@8ad669f3b939:/tmp# nohup bash build-binutils-all.sh &

ビルドが完了すると、以下のコンパイラ群が生成されていることが確認できる。

root@8ad669f3b939:/tmp# ls /usr/local/bin
aarch64-linux-gnu-addr2line   alpha-linux-gnu-g++            arm-linux-gnueabihf-gcc-ranlib  m68k-linux-gnu-ld.bfd               mips64el-linux-gnuabi64-readelf  powerpc64le-linux-gnu-addr2line
aarch64-linux-gnu-ar          alpha-linux-gnu-gcc            arm-linux-gnueabihf-gcov        m68k-linux-gnu-nm                   mips64el-linux-gnuabi64-size     powerpc64le-linux-gnu-ar
aarch64-linux-gnu-as          alpha-linux-gnu-gcc-5.2.0      arm-linux-gnueabihf-gcov-tool   m68k-linux-gnu-objcopy              mips64el-linux-gnuabi64-strings  powerpc64le-linux-gnu-as
aarch64-linux-gnu-c++         alpha-linux-gnu-gcc-ar         arm-linux-gnueabihf-gprof       m68k-linux-gnu-objdump              mips64el-linux-gnuabi64-strip    powerpc64le-linux-gnu-c++
aarch64-linux-gnu-c++filt     alpha-linux-gnu-gcc-nm         arm-linux-gnueabihf-ld          m68k-linux-gnu-ranlib               mipsel-linux-gnu-addr2line       powerpc64le-linux-gnu-c++filt
aarch64-linux-gnu-cpp         alpha-linux-gnu-gcc-ranlib     arm-linux-gnueabihf-ld.bfd      m68k-linux-gnu-readelf              mipsel-linux-gnu-ar              powerpc64le-linux-gnu-cpp
aarch64-linux-gnu-elfedit     alpha-linux-gnu-gcov           arm-linux-gnueabihf-nm          m68k-linux-gnu-size                 mipsel-linux-gnu-as              powerpc64le-linux-gnu-elfedit
aarch64-linux-gnu-g++         alpha-linux-gnu-gcov-tool      arm-linux-gnueabihf-objcopy     m68k-linux-gnu-strings              mipsel-linux-gnu-c++             powerpc64le-linux-gnu-embedspu
aarch64-linux-gnu-gcc         alpha-linux-gnu-gprof          arm-linux-gnueabihf-objdump     m68k-linux-gnu-strip                mipsel-linux-gnu-c++filt         powerpc64le-linux-gnu-g++
aarch64-linux-gnu-gcc-5.2.0   alpha-linux-gnu-ld             arm-linux-gnueabihf-ranlib      mips-linux-gnu-addr2line            mipsel-linux-gnu-cpp             powerpc64le-linux-gnu-gcc
aarch64-linux-gnu-gcc-ar      alpha-linux-gnu-ld.bfd         arm-linux-gnueabihf-readelf     mips-linux-gnu-ar                   mipsel-linux-gnu-elfedit         powerpc64le-linux-gnu-gcc-5.2.0
aarch64-linux-gnu-gcc-nm      alpha-linux-gnu-nm             arm-linux-gnueabihf-size        mips-linux-gnu-as                   mipsel-linux-gnu-g++             powerpc64le-linux-gnu-gcc-ar
aarch64-linux-gnu-gcc-ranlib  alpha-linux-gnu-objcopy        arm-linux-gnueabihf-strings     mips-linux-gnu-c++                  mipsel-linux-gnu-gcc             powerpc64le-linux-gnu-gcc-nm
aarch64-linux-gnu-gcov        alpha-linux-gnu-objdump        arm-linux-gnueabihf-strip       mips-linux-gnu-c++filt              mipsel-linux-gnu-gcc-5.2.0       powerpc64le-linux-gnu-gcc-ranlib
aarch64-linux-gnu-gcov-tool   alpha-linux-gnu-ranlib         i686-linux-gnu-addr2line        mips-linux-gnu-cpp                  mipsel-linux-gnu-gcc-ar          powerpc64le-linux-gnu-gcov
aarch64-linux-gnu-gprof       alpha-linux-gnu-readelf        i686-linux-gnu-ar               mips-linux-gnu-elfedit              mipsel-linux-gnu-gcc-nm          powerpc64le-linux-gnu-gcov-tool
aarch64-linux-gnu-ld          alpha-linux-gnu-size           i686-linux-gnu-as               mips-linux-gnu-g++                  mipsel-linux-gnu-gcc-ranlib      powerpc64le-linux-gnu-gprof
aarch64-linux-gnu-ld.bfd      alpha-linux-gnu-strings        i686-linux-gnu-c++              mips-linux-gnu-gcc                  mipsel-linux-gnu-gcov            powerpc64le-linux-gnu-ld
aarch64-linux-gnu-nm          alpha-linux-gnu-strip          i686-linux-gnu-c++filt          mips-linux-gnu-gcc-5.2.0            mipsel-linux-gnu-gcov-tool       powerpc64le-linux-gnu-ld.bfd
aarch64-linux-gnu-objcopy     arm-linux-gnueabi-addr2line    i686-linux-gnu-cpp              mips-linux-gnu-gcc-ar               mipsel-linux-gnu-gprof           powerpc64le-linux-gnu-nm
aarch64-linux-gnu-objdump     arm-linux-gnueabi-ar           i686-linux-gnu-elfedit          mips-linux-gnu-gcc-nm               mipsel-linux-gnu-ld              powerpc64le-linux-gnu-objcopy
aarch64-linux-gnu-ranlib      arm-linux-gnueabi-as           i686-linux-gnu-g++              mips-linux-gnu-gcc-ranlib           mipsel-linux-gnu-ld.bfd          powerpc64le-linux-gnu-objdump
aarch64-linux-gnu-readelf     arm-linux-gnueabi-c++          i686-linux-gnu-gcc              mips-linux-gnu-gcov                 mipsel-linux-gnu-nm              powerpc64le-linux-gnu-ranlib
aarch64-linux-gnu-size        arm-linux-gnueabi-c++filt      i686-linux-gnu-gcc-5.2.0        mips-linux-gnu-gcov-tool            mipsel-linux-gnu-objcopy         powerpc64le-linux-gnu-readelf
aarch64-linux-gnu-strings     arm-linux-gnueabi-cpp          i686-linux-gnu-gcc-ar           mips-linux-gnu-gprof                mipsel-linux-gnu-objdump         powerpc64le-linux-gnu-size
aarch64-linux-gnu-strip       arm-linux-gnueabi-elfedit      i686-linux-gnu-gcc-nm           mips-linux-gnu-ld                   mipsel-linux-gnu-ranlib          powerpc64le-linux-gnu-strings
all-addr2line                 arm-linux-gnueabi-g++          i686-linux-gnu-gcc-ranlib       mips-linux-gnu-ld.bfd               mipsel-linux-gnu-readelf         powerpc64le-linux-gnu-strip
all-ar                        arm-linux-gnueabi-gcc          i686-linux-gnu-gcov             mips-linux-gnu-nm                   mipsel-linux-gnu-size            s390x-linux-gnu-addr2line
all-as                        arm-linux-gnueabi-gcc-5.2.0    i686-linux-gnu-gcov-tool        mips-linux-gnu-objcopy              mipsel-linux-gnu-strings         s390x-linux-gnu-ar
all-c++filt                   arm-linux-gnueabi-gcc-ar       i686-linux-gnu-gprof            mips-linux-gnu-objdump              mipsel-linux-gnu-strip           s390x-linux-gnu-as
all-coffdump                  arm-linux-gnueabi-gcc-nm       i686-linux-gnu-ld               mips-linux-gnu-ranlib               powerpc-linux-gnu-addr2line      s390x-linux-gnu-c++
all-dlltool                   arm-linux-gnueabi-gcc-ranlib   i686-linux-gnu-ld.bfd           mips-linux-gnu-readelf              powerpc-linux-gnu-ar             s390x-linux-gnu-c++filt
all-dllwrap                   arm-linux-gnueabi-gcov         i686-linux-gnu-nm               mips-linux-gnu-size                 powerpc-linux-gnu-as             s390x-linux-gnu-cpp
all-elfedit                   arm-linux-gnueabi-gcov-tool    i686-linux-gnu-objcopy          mips-linux-gnu-strings              powerpc-linux-gnu-c++            s390x-linux-gnu-elfedit
all-gprof                     arm-linux-gnueabi-gprof        i686-linux-gnu-objdump          mips-linux-gnu-strip                powerpc-linux-gnu-c++filt        s390x-linux-gnu-g++
all-ld                        arm-linux-gnueabi-ld           i686-linux-gnu-ranlib           mips64el-linux-gnuabi64-addr2line   powerpc-linux-gnu-cpp            s390x-linux-gnu-gcc
all-ld.bfd                    arm-linux-gnueabi-ld.bfd       i686-linux-gnu-readelf          mips64el-linux-gnuabi64-ar          powerpc-linux-gnu-elfedit        s390x-linux-gnu-gcc-5.2.0
all-nlmconv                   arm-linux-gnueabi-nm           i686-linux-gnu-size             mips64el-linux-gnuabi64-as          powerpc-linux-gnu-embedspu       s390x-linux-gnu-gcc-ar
all-nm                        arm-linux-gnueabi-objcopy      i686-linux-gnu-strings          mips64el-linux-gnuabi64-c++         powerpc-linux-gnu-g++            s390x-linux-gnu-gcc-nm
all-objcopy                   arm-linux-gnueabi-objdump      i686-linux-gnu-strip            mips64el-linux-gnuabi64-c++filt     powerpc-linux-gnu-gcc            s390x-linux-gnu-gcc-ranlib
all-objdump                   arm-linux-gnueabi-ranlib       m68k-linux-gnu-addr2line        mips64el-linux-gnuabi64-cpp         powerpc-linux-gnu-gcc-5.2.0      s390x-linux-gnu-gcov
all-ranlib                    arm-linux-gnueabi-readelf      m68k-linux-gnu-ar               mips64el-linux-gnuabi64-elfedit     powerpc-linux-gnu-gcc-ar         s390x-linux-gnu-gcov-tool
all-readelf                   arm-linux-gnueabi-size         m68k-linux-gnu-as               mips64el-linux-gnuabi64-g++         powerpc-linux-gnu-gcc-nm         s390x-linux-gnu-gprof
all-size                      arm-linux-gnueabi-strings      m68k-linux-gnu-c++              mips64el-linux-gnuabi64-gcc         powerpc-linux-gnu-gcc-ranlib     s390x-linux-gnu-ld
all-srconv                    arm-linux-gnueabi-strip        m68k-linux-gnu-c++filt          mips64el-linux-gnuabi64-gcc-5.2.0   powerpc-linux-gnu-gcov           s390x-linux-gnu-ld.bfd
all-strings                   arm-linux-gnueabihf-addr2line  m68k-linux-gnu-cpp              mips64el-linux-gnuabi64-gcc-ar      powerpc-linux-gnu-gcov-tool      s390x-linux-gnu-nm
all-strip                     arm-linux-gnueabihf-ar         m68k-linux-gnu-elfedit          mips64el-linux-gnuabi64-gcc-nm      powerpc-linux-gnu-gprof          s390x-linux-gnu-objcopy
all-sysdump                   arm-linux-gnueabihf-as         m68k-linux-gnu-g++              mips64el-linux-gnuabi64-gcc-ranlib  powerpc-linux-gnu-ld             s390x-linux-gnu-objdump
all-windmc                    arm-linux-gnueabihf-c++        m68k-linux-gnu-gcc              mips64el-linux-gnuabi64-gcov        powerpc-linux-gnu-ld.bfd         s390x-linux-gnu-ranlib
all-windres                   arm-linux-gnueabihf-c++filt    m68k-linux-gnu-gcc-5.2.0        mips64el-linux-gnuabi64-gcov-tool   powerpc-linux-gnu-nm             s390x-linux-gnu-readelf
alpha-linux-gnu-addr2line     arm-linux-gnueabihf-cpp        m68k-linux-gnu-gcc-ar           mips64el-linux-gnuabi64-gprof       powerpc-linux-gnu-objcopy        s390x-linux-gnu-size
alpha-linux-gnu-ar            arm-linux-gnueabihf-elfedit    m68k-linux-gnu-gcc-nm           mips64el-linux-gnuabi64-ld          powerpc-linux-gnu-objdump        s390x-linux-gnu-strings
alpha-linux-gnu-as            arm-linux-gnueabihf-g++        m68k-linux-gnu-gcc-ranlib       mips64el-linux-gnuabi64-ld.bfd      powerpc-linux-gnu-ranlib         s390x-linux-gnu-strip
alpha-linux-gnu-c++           arm-linux-gnueabihf-gcc        m68k-linux-gnu-gcov             mips64el-linux-gnuabi64-nm          powerpc-linux-gnu-readelf
alpha-linux-gnu-c++filt       arm-linux-gnueabihf-gcc-5.2.0  m68k-linux-gnu-gcov-tool        mips64el-linux-gnuabi64-objcopy     powerpc-linux-gnu-size
alpha-linux-gnu-cpp           arm-linux-gnueabihf-gcc-ar     m68k-linux-gnu-gprof            mips64el-linux-gnuabi64-objdump     powerpc-linux-gnu-strings
alpha-linux-gnu-elfedit       arm-linux-gnueabihf-gcc-nm     m68k-linux-gnu-ld               mips64el-linux-gnuabi64-ranlib      powerpc-linux-gnu-strip

ユーザモードQEMUを使って実行してみる

アーキテクチャをエミュレーション実行するために、qemu-user-staticパッケージをインストールする。 また、合わせてよく使うパッケージもインストールしておく。

root@8ad669f3b939:/tmp# apt-get install qemu-user-static
root@8ad669f3b939:/tmp# apt-get install man file

ここで、次のようなHello worldコードを用意する。

/* hello.c */
#include <stdio.h>

int main()
{
    printf("Hello, world!\n");
    return 0;
}

例として、MIPS用の実行ファイルをコンパイルし、ユーザモードQEMUで実行してみると次のようになる。 さらに、全アーキテクチャ対応のobjdumpでディスアセンブルした結果も確認してみる。

root@8ad669f3b939:/tmp# mips-linux-gnu-gcc hello.c -o hello-mips
root@8ad669f3b939:/tmp# qemu-mips-static -L /usr/local/mips-linux-gnu/ ./hello-mips
Hello, world!
root@8ad669f3b939:/tmp# all-objdump -d ./hello-mips | head

./hello-mips:     file format elf32-bigmips


Disassembly of section .init:

004004c8 <_init>:
  4004c8:       3c1c0002        lui     gp,0x2
  4004cc:       279c84e8        addiu   gp,gp,-31512
  4004d0:       0399e021        addu    gp,gp,t9

実行結果とディスアセンブル結果から、MIPS用の実行ファイルがコンパイルできていることがわかる。

関連リンク