2006年1月23日月曜日

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

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



  • プログラムのソースコードのコンパイルやデバッグをするコンピュータ


  • 作成されたプログラムを実行するコンピュータ


の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をクロスコンパイラでビルドしてみました。



1 件のコメント:

  1. ネットで古いソースファイルとかターゲットマシンを開発しなかったの?

    返信削除