【お知らせ】プログラミング記事の投稿はQiitaに移行しました。

Linux互換用クロス開発環境構築

FreeBSD上のLinux互換環境で動かすバイナリをビルドするため、クロス開発環境を構築しました。手順は以下の通りです。gccglibcが相互依存しているため行ったり来たりします。途中でエラーを無視して進めますが、最終的にはすべて解消します。

  1. binutils-2.20
  2. gcc-4.4.2 (1回目): ランタイムなし (glibc用)
  3. カーネルヘッダ
  4. glibc-2.11 (1回目): ヘッダ, スタティック(libc.aなど), crt
  5. gcc (2回目): libgcc.a
  6. glibc (2回目): ダイナミック (libc.soなど)
  7. gcc (3回目): ランタイム (libstdc++等)

準備

以下のportsをインストールします。pkg_add -rでリモートインストールすると簡単です。

  • gmake
  • bison
  • texinfo
  • libgmp
  • mpfr
  • rpm2cpio

ヘッダやライブラリが見えるように環境変数を設定します。

setenv CPPFLAGS -I/usr/local/include
setenv LDFLAGS -L/usr/local/lib

環境指定

クロス環境の構築ではビルド環境と実行環境が異なります。configureで環境を指定します。指定する環境は以下の三種類があります。

  1. build: ビルドを行う環境。configureで自動判定されるため指定不要。
  2. target: binutilsgccで、出力するバイナリの環境を指定する。binutilsgcc自体はビルド環境(build)で実行される。
  3. host: クロス開発の指定。実行する環境を指定する。

今回はi686-linuxをターゲットにします。i386を指定するとglibcがビルドできなくてハマりました。

1. binutils-2.20

アセンブラやリンカなどコンパイラの下請けをするツール群です。

ソースをダウンロードして展開します。

fetch ftp://ftp.gnu.org/gnu/binutils/binutils-2.20.tar.bz2
tar xvjf binutils-2.20.tar.bz2

binutilsgccはサブディレクトリで作業するのが流儀のようです。強制ではないようですが、ソースが膨大なため、色々な環境向けにビルドする際に使い回すのが目的のようです。

cd binutils-2.20
mkdir build
cd build
../configure --target=i686-linux
gmake
gmake install

2. gcc-4.4.2 (1回目): ランタイムなし (glibc用)

最初はglibcのビルドに必要なコンパイラ本体だけをビルドします。ランタイムはglibcに依存するため後回しとなります。

ソースをダウンロードして展開します。

fetch ftp://ftp.gnu.org/gnu/gcc/gcc-4.4.2/gcc-core-4.4.2.tar.bz2
fetch ftp://ftp.gnu.org/gnu/gcc/gcc-4.4.2/gcc-g++-4.4.2.tar.bz2
tar xvjf gcc-core-4.4.2.tar.bz2
tar xvjf gcc-g++-4.4.2.tar.bz2

binutilsと同じようにサブディレクトリで作業します。

cd gcc-4.4.2
mkdir build
cd build
../configure --target=i686-linux
gmake all-gcc
gmake install-gcc

3. カーネルヘッダ

FreeBSDLinux互換環境はFedora Core 4相当らしいので、Fedora Core 4からカーネルヘッダを取って来ます。

fetch http://ftp.riken.jp/Linux/fedora/core/4/i386/os/Fedora/RPMS/glibc-kernheaders-2.4-9.1.94.i386.rpm

ファイルが想定外の場所に展開されないように、念のためディレクトリを作って展開します。

mkdir kernheaders
cd kernheaders/
rpm2cpio.pl ../glibc-kernheaders-2.4-9.1.94.i386.rpm | cpio -ivd
mkdir -p /usr/local/i686-linux/include
cp -r usr/include/* /usr/local/i686-linux/include

4. glibc-2.11 (1回目): ヘッダ, スタティック(libc.aなど), crt

ソースをダウンロードして展開します。

fetch ftp://ftp.gnu.org/gnu/glibc/glibc-2.11.tar.bz2
tar xvjf glibc-2.11.tar.bz2

古いカーネルヘッダではエラーが出るため、gentooのパッチを当てます。

fetch -o glibc-gentoo.diff "http://bugs.gentoo.org/attachment.cgi?id=209781"
cd glibc-2.11
patch -p1 < ../glibc-gentoo.diff

サブディレクトリを作って作業します。glibcでは強制です。configureでエラーにならないように色々と細工します。libc.soはlibgcc(まだ存在しない)依存でエラーになるため-kで無視します。

mkdir build
cd build
env CFLAGS="-O2 -march=i686" libc_cv_forced_unwind=yes libc_cv_c_cleanup=yes \
../configure --prefix=/usr/local/i686-linux --host=i686-linux --disable-sanity-checks
gmake -k
gmake -k install

エラー回避のため、空のスタブヘッダを追加します。

touch /usr/local/i686-linux/include/gnu/stubs-32.h

手順6で正規のstubs-32.hがインストールされるまでの一時的な措置です。

5. gcc (2回目): libgcc.a

環境を構築し直さずに続きからビルドします。libgcc.soを作ろうとしてエラーになるため-kで無視します。

cd gcc-4.4.2/build
gmake -k all-target-libgcc
gmake -k install-target-libgcc

libgcc.soはglibcの完成後に作ります。もしlibgcc.aでエラーになる場合は前の手順のスタブヘッダを確認してください。

6. glibc (2回目): ダイナミック (libc.soなど)

環境を構築し直さずに続きからビルドします。

cd glibc-2.11/build
gmake
gmake install

エラーなく最後まで行くはずです。これでglibcは完成です。

7. gcc (3回目): ランタイム (libstdc++等)

環境を構築し直さずに続きからビルドします。

cd gcc-4.4.2/build
gmake
gmake install

エラーなく最後まで行くはずです。これでgcc/g++は完成です。

テスト

a.c

#include <stdio.h>

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

実行結果

% i686-linux-gcc a.c
% ./a.out
Hello, World!
% file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2
.0.0, dynamically linked (uses shared libs), not stripped

Linuxバイナリを生成して、互換環境で実行しています。

謝辞

以下のサイトを参考にさせていただきました。

クロス開発用のglibcgccの構築方法について解説されています。

glibcのビルドエラーについて解説されています。

有益な情報を提供していただきありがとうございました。