ttt

getttyent

FreeBSDに玄箱のクロス開発環境を構築

2006-01-22 21:54:30 | デジタル・インターネット

クロス開発環境というのは、プログラムの開発をするときに、

  • プログラムのソースコードのコンパイルやデバッグをするコンピュータ
  • 作成されたプログラムを実行するコンピュータ

の2台のコンピュータに分けて開発する、開発環境のことです。これに対して、普通は、プログラムをコンパイルするコンピュータと、プログラムを実行するコンピュータは、同じコンピュータの場合が多いです。

めんどくさそうな方法ですが、クロス開発というのは、携帯用の小型端末とか、電化製品などに組み込まれている小さなコンピュータ用にプログラムを開発する方法です。そういうコンピュータには、メモリが少ししかのっていないし、ハードディスクもないし、そもそもキーボードもディスプレイもつながらないので、そのコンピュータ上で、Cコンパイラとかデバッガとかを動かすことはできません。そのために、開発用に別のコンピュータがもう1台必要になります。

玄箱も、まさに、このクロス開発環境の適用対象にぴったりあいます。

一応、玄箱には、CD-ROMの中に、Cコンパイラやデバッガが入っているので、それらをインストールすれば、玄箱を使って、玄箱用のプログラムを開発することができます。しかし、玄箱は、CPUの処理速度はそんなに速くないしメモリも少ないので、開発効率はなかなか上がりません。

一方、そこらで5万円以下で安売りされているパソコンでさえも、玄箱よりは1桁くらい処理性能は上ですので、これを利用しない手はありません。

なお、クロスコンパイラのビルド方法に関して、以下をちょこっと参考にしました。

■ はじめに

クロス開発では、プログラムを実行する側のコンピュータをターゲット(target)、プログラムのコンパイルなどの行う側のコンピュータをホスト(host)と呼んで区別しています。

今回は、FreeBSDを動かしているパソコン側がhost、玄箱がtargetです。

クロス開発では、host側のOSとtarget側のCPUやOSが違っていてもかまいません。そのへんは開発ツールががんばってくれています(というか、それがクロス開発ツールの仕事)。

玄箱には、GNUの開発ツールのbinutils、GCC、GDBが用意されています。これらのツールは、もともとクロス開発にも使えるように作られています。今回は、FreeBSD上で、玄箱用のbinutilsとGCCをコンパイルします。デバッガのGDBは、まあ後回しでいいかな、と思ったのでまたいつか。必要なら、玄箱のGDBを使えばなんとかなるでしょう。

以下の作業は、NFSとamdの設定をすませたあとに、行っています。

■ binutilsをインストール

まず最初に、binutilsをコンパイル、インストールします。binutilsは、アセンブラ(as,gas)やリンカ(ローダ、ld)などから構成された、プログラム開発用ツール集です。

(1)ソースファイルの入手

binutilsのソースファイルは、たとえば、こんなところにあります。

ftp://ftp2.jp.freebsd.org/pub/FreeBSD/ports/distfiles/binutils-2.13.2.1.tar.bz2

portsがある場合は、以下のようなかんじで、make fetchでダウンロードすることもできます。

% cd /usr/ports/devel/i386-rtems-binutils/
% make fetch

ファイルがどこからダウンロードできるのかわからない場合に、この方法は、私もよく使っています。

この場合、ダウンロードしたファイルは、とくに何もports関係の設定を変更していないなら、
/usr/ports/distfiles/binutils-2.13.2.1.tar.bz2
におかれています。

(2)ソースファイルを展開

ファイルbinutils-2.13.2.1.tar.bz2を展開します。

% cd どこか作業用ディレクトリ
% tar jxf /usr/ports/distfiles/binutils-2.13.2.1.tar.bz2
% ls
binutils-2.13.2.1/
% cd binutils-2.13.2.1

(3)configureスクリプトを実行する

こんな感じでconfigureを実行します。

% ./configure --prefix=/usr/local/kuro-box
--target=powerpc-hardhat-linux

(見やすくなるように途中で改行していますが、実行するときは改行せずに1行です)

「--prefix=ディレクトリ」で、インストール先ディレクトリを指定します。とりあえず「/usr/local/kuro-box」にします。

「--target」で、ターゲットマシンのアーキテクチャを指定します。powerpc-hardhat-linuxというのを指定していますが、これは玄箱のbinutilsがこれを指定してコンパイルされていたからです。

(4) コンパイルする

% gmake

(5)インストールする

% su
Password:
# gmake install

 (略)

# ls /usr/local/kuro-box/
bin                     lib                     powerpc-hardhat-linux
info                    man                     share

■ 玄箱からファイルをもらってくる

クロス開発を行う際に(FreeBSD上で、玄箱用のクロスコンパイラを実行する際に)、ターゲットマシンのヘッダファイル(*.h)とライブラリ(*.soとか*.a)が必要になります。

玄箱から、それら必要なファイルを、FreeBSDへコピーします。

(1)その前に、ついでに玄箱にbinutilsとgccをインストールする

本当に必要かどうかわかりませんが、ファイルをFreeBSDへコピーする前に、玄箱にbinutilsとgccをインストールしておきます。binutilsとgccでインストールされるファイルも必要になるかな?と思ったからですが、もしかするといらないかもしれません。

玄箱で以下を実行します。

root@KURO-BOX:/# cd /
root@KURO-BOX:/# tar zxf /mnt/share/binary/binutils-2.10.91.0.2.tar.gz
root@KURO-BOX:/# tar zxf /mnt/share/binary/gcc-2.95.3.tar.gz

(2)コピーするファイルをtarでアーカイブしておく

玄箱の/lib/以下のファイルと、/usr/include/以下のファイルがあれば十分だと思います。

root@KURO-BOX:/# tar cf /mnt/share/kuro-box-files.tar lib usr/include usr/lib

(3)tarアーカイブをFreeBSDで展開する

クロスコンパイル用のGCCは、ターゲットマシン用のファイルを、ディレクトリ(GCCのインストール先)/(--targetで指定したアーキテクチャ)/以下から持ってくるので、今回の場合、/usr/local/kuro-box/powerpc-hardhat-linux/以下へファイルを展開します。

# cd /usr/local/kuro-box/powerpc-hardhat-linux/
# ls
bin     lib
# tar xf /net/kuro-box/mnt/share/kuro-box-files.tar
# ls
bin     lib     usr

ファイルの置き場所を、少し変更します。

# mv lib/* ../lib/
# rmdir lib
# mv usr/* .
# rmdir usr
# ls
bin     include lib

/usr/local/kuro-box/powerpc-hardhat-linux/include/
には、玄箱の/usr/include以下にあったファイルをおいて、

/usr/local/kuro-box/powerpc-hardhat-linux/lib/
には、玄箱の/usr/lib以下にあったファイルをおきます。

/usr/local/kuro-box/lib/
には、玄箱の/lib以下にあったファイルをおきます。

それから、あとでGCCで(正確に言うとldで)エラーがでてしまうので、いくつかシンボリックリンクを作成します。

# cd /usr/local/kuro-box/powerpc-hardhat-linux/lib/
# ln -s ../../lib/libc.so.6 .
# ln -s ../../lib/libc-2.2.3.so .
# ln -s ../../lib/libc.so.6 .
# ln -s ../../lib/ld-2.2.3.so .
# ln -s ../../lib/ld.so.1 .

これは、ldがlic.so.6とld.so.1を探しにいくときに、/usr/local/kuro-box/lib/は見てくれないための措置です。これ以外に、もうすこしスマートな方法がありかもしれません。

■ GCCをインストール

玄箱用にコンパイルされたGCCがバージョン2.95なので、それにあわせてGCC 2.95.3をインストールします。

(1) gcc-2.95.3のソースファイルを展開

2.95.3は、かなーり古いバージョンなので、きっとたくさんパッチがでているはずです。パッチをあてるのがめんどくさいので、FreeBSDのportsを使ってみました。

# cd /usr/ports/lang/gcc295/
# make patch
===>  Extracting for gcc-2.95.3_2
=> MD5 Checksum OK for gcc-core-2.95.3.tar.bz2.
=> SHA256 Checksum OK for gcc-core-2.95.3.tar.bz2.
=> MD5 Checksum OK for gcc-g++-2.95.3.tar.bz2.
=> SHA256 Checksum OK for gcc-g++-2.95.3.tar.bz2.
===>  Patching for gcc-2.95.3_2
===>  Applying FreeBSD patches for gcc-2.95.3_2

これで、ソースファイルのダウンロードして、パッチをあてるところまでやってくれます。便利ですねぇ。

ソースファイルは、portsの設定をとくに変更していない場合は、/usr/ports/lang/gcc295/work/以下に展開されています。

portsのコンパイル作業ディレクトリは、環境変数WRKDIRPREFIXで変更できます。/etc/make.confでたとえば

WRKDIRPREFIX=/home2/ports.work

とか書いておくと、/home2/ports.work/なんとか/かんとか/・・・以下に展開されるようになります。

ファイルを展開したところで、こんなかんじです。

# cd /home2/ports.work/home2/ports/lang/gcc295/work/gcc-2.95.3/
# ls
.brik                   config.guess            install-sh
.cvsignore              config.guess.orig       libiberty
COPYING                 config.if               libio
COPYING.LIB             config.sub              libstdc++
ChangeLog               configure               ltconfig
FAQ                     configure.in            ltmain.sh
INSTALL                 contrib                 missing
MAINTAINERS             etc                     mkinstalldirs
Makefile.in             faq.html                move-if-change
README                  gcc                     symlink-tree
config                  include                 texinfo
config-ml.in            install                 ylwrap

(2)configureを実行

さきほどインストールしたbinutilsへPATHが通しておきます。

# setenv PATH /usr/local/kuro-box/bin:$PATH

configureを実行します。

# cd /home2/ports.work/home2/ports/lang/gcc295/work/gcc-2.95.3/

# ./configure --prefix=/usr/local/kuro-box
--target=powerpc-hardhat-linux
--with-gnu-ld --with-gnu-as --disable-nls

(3)コンパイルする

# gmake

しばらくすると・・・えーと・・・エラーでとまってしまいました。

とりあえず、エラーを無視して、インストールしてしまいます。

# gmake install

そして、GCCのファイルをすべて削除し、ソースを展開する(make patch)ところから、もう一度、同じ事をくりかえします。すると、2回目のgmakeは成功しました。

エラーメッセージを調べたところ、includeファイルを探すときに、
/usr/local/kuro-box/powerpc-hardhat-linux/lib/gcc-lib/powerpc-hardhat-linux/2.95.3
というディレクトリが作成されていないと、うまく見つけられないようなかんじでした。一度gmake installを実行すると、このディレクトリが作成されるので、2回目以降は成功する、というわけです。

■ Hello Worldを実行してみる

クロスコンパイラが、正しく動くか確認するために、Hello Worldプログラムで試してみます。

ソースファイルは、こんなかんじです。

% cat hello.c
#include <stdio.h>

int
main( int argc, char* argv[] )
{
  printf("Hello world\n");
  return 0;
}

コンパイルします。

% /usr/local/kuro-box/bin/powerpc-hardhat-linux-gcc hello.c
-o hello

% ls -l hello
-rwxr-xr-x  1 nhh  wheel  5567 Jan 22 21:40 hello*

なにやら、できたっぽいです。

% file hello
hello: ELF 32-bit MSB executable, PowerPC or cisco 4500, version 1 (SYSV),
for GNU/Linux 2.0.0, dynamically linked (uses shared libs), not stripped

玄箱へコピーします。

% cp hello /net/kuro-box/mnt/share

玄箱にログインして、実行してみます。

root@KURO-BOX:/mnt/share# ./hello
Hello world

どうやら、無事に、実行できたようです。

■ C++コンパイラ(g++)

g++も一応コンパイルされているのですが、実は、ライブラリlibstdc++がビルドされていません・・・うーむ・・・

C++コンパイラも必要な場合は、2回目のGCCのコンパイルのときに、以下のように--enable-languages=c++をつけます。

# ./configure --prefix=/usr/local/kuro-box
  --target=powerpc-hardhat-linux
  --with-gnu-ld --with-gnu-as --disable-nls
  --enable-languages=c++

以下、C++版のHello Worldでテストした例です。

まずFreeBSDでコンパイル。

% cat hello.cc
#include <iostream.h>

int
main( int argc, char* argv[] )
{
  cout << "Hello world ++" << endl;
  return 0;

% /usr/local/kuro-box/bin/powerpc-hardhat-linux-g++ hello.c
c -o hello++

% cp hello++ /net/kuro-box/mnt/share

玄箱で実行。

root@KURO-BOX:/mnt/share# ./hello++
Hello world ++

動きました。ちなみに、「bash: ./hello++: Text file busy」というエラーメッセージが表示されたことがあったのですが、なんなんでしょう???

■ トラブルシューティング

クロスコンパイラでコンパイルエラーになる場合、gccに「-v」オプションをつけて実行すると、どのようなファイルを参照しているか、ログメッセージとして表示されるようになります。玄箱からファイルをコピーしわすれていたり、コピー先の場所が違っていたりしないか、これで確認できるようになります。

ldでエラーになったケースがあって、これがやっかいでした。しかたないので、ldのラッパーを作成しました。

/usr/local/kuro-box/powerpc-hardhat-linux/bin/ld

/usr/local/kuro-box/powerpc-hardhat-linux/bin/ld.org
に名前を変えて、
/usr/local/kuro-box/powerpc-hardhat-linux/bin/ld
を、以下のようなシェルスクリプトに置き換えて、どういう引数が渡されているかを表示させました。

% cat /usr/local/kuro-box/powerpc-hardhat-linux/bin/ld

#! /bin/sh

echo =====
echo $*
echo =====
exec /usr/local/kuro-box/powerpc-hardhat-linux/bin/ld.org $*

gccへの引数で、なんとかできそうな気がしますが、調べてもすぐにわからなかったので、こういう乱暴な手を使いました。

■ 次回予告

ためしにtcshをクロスコンパイラでビルドしてみました。