ARM 用クロス環境の構築

(v1.21, Apr. 2, 2000)
Author: 神木一也

Linux7k on Psion5 は (その数字に意味があるとして) 17 BogoMips 程度です。 近い将来に 5mx で動いたとしても 34 BogoMips 程度でしょう。 数百 BogoMips は軽く出る linux/i386 とはくらべものになりません。 そこで、実用上は linux/i386 の上で linux7k のバイナリを作成することになります。

以下は、このためのクロスコンパイラの作成手順です。 glibc2 が事前に用意できるか否かで手間の差は大きく、Debian woody に置かれている lib6_2.1 は利用すべきですが、以下では glibc2 を含めて build します。

おおまかな手順

  1. arm 用 binutils の build.
  2. i386, arm 共用 の binutils を構築。
  3. linux7k kernel ソースツリーの展開
  4. cross gcc の構築
  5. glibc の構築
  6. cross gcc の構築、再び。
  7. linux7k kernel, modules の構築。
  8. アプリケーションの make, make, make ...
gcc を 2 度 build するのは、gcc の build には glibc が必要だからです。 一度目は glibc なしのオプションで build し、arm 用 glibc をそれで build してから、本来の gcc を build します。

arm 用 binutils-2.9.1.0.25 の構築

The ARM Linux Project から 2.9.1.0.19 用パッチ binutils-2.9.1.0.19a-arm-diff-981230.gz が出ています。2.9.1.0.19a (Latest は 2.9.1.0.25. 今はこちらを使います) を展開し、 パッチをあてておきます。
linux7k project で公開している binutils-2.9.1.0.15 の arm 用パッチは 2.9.1.0.19 にはすでに含まれており、あてる必要はありません。

binutils の 2.9.5 系列 (Latest は 2.9.5.0.24) を build することもできますし、 arm linux の kernel 2.2.14 のあたりでは 2.9.5 が必須になりましたが、 linux7k の kernel 2.2.1 は逆に binutils 2.9.1 系列でなければ make できません。 また 2.9.1 系列と 2.9.5 系列の binutils は組み合わせる gcc に微妙に互換性がありません。... つまり 2.9.1.x 専用 gcc, 2.9.5.x 専用 gcc というものがそれぞれ必要です。

2.9.1.0.25 のソースを展開し、パッチをあてて make します。

% bzip2 -d < binutils-2.9.1.0.25.tar.bz2 | tar -xvp
% cd binutils-2.9.1.0.25
% zcat ../binutils-2.9.1.0.19a-arm-diff-981230.gz | patch -p1
{bfd, gas}/Changelog.linux に細かい rej が出ますが、これは仕方ないでしょう。 コードには影響ないようです。 デフォルトターゲットの --target は arm-linux (arm 用 linux, ELF format) を指定します。 as 以外は i386 と共有できるので、この段階で必要なのは実は as だけですが、 ぜんぶ作っておきます:
% ./configure --target=arm-linux
% make
% su root
# make install
aout 形式 (linux7k のもともとの標準) を build する場合は --target=arm-linuxaout としますが、 特に必要とする場面もないと思います。

また、/usr/local/lib/libbfd.*.so/usr/local/lib/libopecodes.*.so が次の共有用のとで衝突するので --enable-shared してはいけません。

i386, arm 共用 binutils の構築

通常 binutils は linux/i386 では linux/i386(aout,glibc1,glibc2) 専用に作成されているはずですが、ここで
% ./configure --enable-targets=arm-linuxaout,arm-linux,i586-linux --enable-shared
とすれば、標準の binutils が arm-linux, arm-linuxaout のものも扱うことができるようになります。 このあと make all ; make install はいつもどおり。

ただ、多くの makefile で arm-linux 用に立ち上げるバイナリは /usr/local/bin/arm-linux-* の形をしているので、このままでは共用できません。 共用する分について

# cd /usr/local/bin
# rm arm-linux-strip
# ln strip arm-linux-strip
などとする必要があります。

Linux7k カーネルソースツリーの展開

gcc の build には arm linux のヘッダが必要です。 現在、linux7k のカーネルは crash+burn-26 (uname 表記は 2.2.1-cb23 になっている) です:
% bzip2 -d < linux-2.2.1.tar.bz2 | tar -xvp
% cd linux
% bzip2 -d < ../patch-2.2.1-rmk2.bz2 | patch -p1
% bzip2 -d < ../linux-2.2.1-philb-990208.bz2 | patch -p1
% zcat ../crash+burn-26.patch.gz | patch -p1
作者の標準 config は myconfig の形で置かれています:
% cp myconfig .config
% make xconfig
% make depend
コンパイラがありませんから make は出来ませんが、これで gcc を make するためのヘッダが出来ました。
# ln -sf include/asm-arm /usr/local/arm-linux/include/asm
# ln -sf include/linux /usr/local/arm-linux/include/linux

余談:
ARM linux の latest は linux-2.2.14 + rmk1 です。 linux7k も早く 2.2.14 + rmk1 に対するパッチにならないかなぁ... sigh.

arm 用 gcc 2.95.1 の構築、1 回目

gcc 2.95.1 の build に入ります(egcs の latest は gcc 2.95.2 です)。 gcc-core-2.95.1 を展開します。 egcs は本来 C++, Objective-C, Pascal, Fortran 等を含みますが、今はまだ core にある C だけ build します。残りについては glibc の構築後に make すれば十分です。
まず i386 native compiler が作れるかどうかを確認:
% bzip2 -d < gcc-core-2.95.1.tar.bz2 | tar -xpv
% mkdir egcs-i386
% cd egcs-i386
% ../gcc-2.95.1/configure --enable-shared
% make all
% cd ..
ディレクトリ gcc-2.95.1/ でなく、 その外に置かれた egcs-i386/ で作業していますが、 egcs はソースツリーの中で構築することを推奨していません。 外で構築するよう勧めているので、ここでもそれに従いました。

arm 用コンパイラの make に入る前に Phil Blundell さんのサイトから

つづいて Chris Rutter さんのページ から をあてておきます。後者は 2.95 以前 (egcs-1.1.2 など) の場合は必要ないようです:
% cd gcc-2.95.1
% zcat ../gcc-2.95-diff-990730.gz | patch -p0
% cd gcc
% patch -p0 < ../../gcc-fold-const.patch
% cd ..

余談:
現在 gcc-2.95-diff-990730.gz のかわりに gcc-2.95.2-diff-991022.gzgcc-2.95.3-diff-000102.gz が公開されています。... が、snapshot 系列専用で 2.95.2 にはうまくあたりません。

このままだと gcc の build に glibc を使おうとするので、 gcc/config/arm/t-linux を 1 行編集しておきます:

- TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer -fPIC
+ TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer -fPIC -Dinhibit_libc -D__gthr_posix_h
% cd ..
% mkdir egcs-arm
% cd egcs-arm
% ../gcc-2.95.1/configure --enable-shared --target=arm-linux --disable-threads
% make cross
% make install-gcc-cross
--target=arm-linuxaout の場合も同様なのですが、 なぜか ld がエラーを吐いていまだ arm-linuxaout の gcc を構築できていません。 もっとも Debian で elf 系が充実しはじめた今 arm-linuxaout が要ることもないでしょう。

glibc の構築

glibc が arm をサポートしたのは 2.1 に入ってからのようです。 libc-5.x では make できません。ここでは latest の 2.1.2 を build します。
必要な tar ball は 3 つあります: glibc-2.1.2.tar.gz なら RING にもあるんですが、bz2 なものはないようですね。 でも一次サイトの cygnus よりは RING のほうがファイルサイズを勘案してもなお速いでしょうから、RING から get すべきです。
glibc-linuxthreads-2.1.2 は RING にはなく、cygnus か funet から get しなければなりません。
glibc-crypt も 2.1 用は funet だけのようです。 crypt 関連が集まるノルウェーにもない ...
% zcat glibc-2.1.2.tar.gz | tar -xvp
% cd glibc-2.1.2
% bzip2 -d < ../glibc-linuxthreads-2.1.2.tar.bz2 | tar -xvp
% zcat ../glibc-crypt-2.1.tar.gz | tar -xvp
さて、 configure:
% ./configure --host=arm-linux --build=i586-linux --prefix=/usr/local/arm-linux  --enable-add-ons=crypt,linuxthreads
binutils, gcc は --prefix 無指定なら /usr/local/${TARGET} に install されます。 arm-linux 用クロス関連なら /usr/local/arm-linux//usr/local/lib/gcc-lib/arm-linux/ です。

ヘッダなど関連ファイルの読み込みもこのディレクトリ下からおこなわれます。 つまり arm-linux-gcc がデフォルトで参照するヘッダ、ライブラリパスは /usr/local/arm-linux/ の下になるため、glibc でも --prefix=/usr/local/arm-linux を指定しなければなりません。 binutils, gcc の install 先が異なる場合はそれに応じて glibc の --prefix も換えておきます。

なお、ここで --enable-omitfp (フレームポインターの除去。最適化を強める) をつけてはいけません。 つけてコンパイルしても build は出来るんですが、適当にプログラムをコンパイル、実行しようとすると permission error でコケます。 ライブラリアセンブラソースのどれかが frame pointer の存在を仮定しているようですね。
どこかの文書に確かに host,build, prefix,enable-add-ons 以外の option をつけて configure してくれるな と書いてあったのですが、試しに build したところやっぱり動きませんでした。

% make
gcc の make もけっこう長い時間がかかったはずですが、glibc の make は さらに長くかかります。ディスクも 300MB ほど消費します。 mP6-266 で、
4139.138s real  2935.3s user  728.64s system
% du -s
282293  .
でした。install すると /usr/local/arm-linux/lib 下にライブラリを かれこれあわせて 72 MB ほど install しますので、その分の容量も必要です。
# make install
# cd ..
これでようやく gcc を build する準備がととのいました。

arm 用 gcc の構築、本番

先の egcs ディレクトリを綺麗にします:
% rm -rf egcs-arm
egcs の他のコンパイラ、C++, Fortran etc. が必要ならここで展開:
% cd gcc-2.95.1
% bzip2 -d < gcc-g++-2.95.1.tar.bz2 | tar -xvp
% bzip2 -d < gcc-g77-2.95.1.tar.bz2 | tar -xvp
先にいじった gcc/config/arm/t-linux の 1 行をもとにもどします:
- TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer -fPIC -Dinhibit_libc -D__gthr_posix_h
+ TARGET_LIBGCC2_CFLAGS = -fomit-frame-pointer -fPIC
% cd ..
% mkdir egcs-arm
% cd egcs-arm
% ../gcc-2.95.1/configure --enable-shared --target=arm-linux
% make
% make install
% cd ..

ディスクの整理

以上のインストールの結果 /usr/local/bin/ に arm-lunux 向けに build された(単独起動用の) cpp が /usr/local/bin/cpp の名前で入ってしまいます。 通常の cpp は i386 用 native のほうがいいので i386 用のコンパイラを make ; make install して塗りつぶしておきます:
% cd egcs-i386
% make install
% cd ..
ドキュメントについては、i386 と arm 用で同じものが名前を換えてコピーされているため、 ディスクが節約のため arm-linux- は消しておきます:
% pushd
% cd /usr/local/man/man1
% rm arm-linux-*
i386, arm 共用の binutils が導入されているなら、/usr/local/bin/strip は arm 用にも使えます。arm 用のバイナリ、i386 用のバイナリが混在する /usr/local/arm-linux/bin で力を発揮するでしょう:
% cd /usr/local/arm-linux/bin
% /usr/local/bin/strip --strip-all *
% popd

カーネルの構築

カーネルの再構築は以下の通り:
% cd linux
% make xconfig
% make depend 
% make Image
% make modules
make zImage, make bzImage などはまだサポートされていないそうです。 また、modules をランタイムにロードする際に必要になる insmod は modutils-2.1.121 以降 (できれば modutils-2.36) が必要です。linux7k project の ftp サイトにある module-2.0.0 は使えません。 Jim's arm binaries か Debian woody などから modutils-2.1.121バイナリか、modutils-2.36 を使うようにしてください。

余談:
Linux7k project に置かれている glue_kernel binary 自身は modules 機構が外されて make されています。modules-2.0.0 がメンテされてない訳です ...

Arlo 用グルーカーネル

Linux7k project の ftp サイトにある boot-13.tar.gz を展開、make:
% zcat boot-13.tar.gz | tar -xp
% cd boot
% make all
さらに
% ./glue.pl ../linux/arch/arm/boot/Image > glue_image
としてつくった glue_image が Arlo でロードすべきカーネルイメージになります。

フォーマットの管理

クロスコンパイラの起動は -b で指定します:
% gcc -b arm-linux hogehoge.c
あるいは
% arm-linux-gcc hogehoge.c
この時、ターゲットバイナリのフォーマットを管理するのはインストールした /usr/local/ 直下にある /usr/local/arm-linux//usr/local/i586-linux-glibc1/ などです。 この管理ファイルを通じて gcc は cc1 や as の起動パスを定めるので、 binutils は gcc と同じ --prefix で configure しなければなりません。
もし gcc が -b で推測される位置に as を発見できなかった場合、 デフォルトの /usr/local/bin/as/usr/bin/as を起動してしまいますが、 多くの場合これはセルフ用のバイナリであるはずです。
クロスコンパイル時にアセンブラが「i386 にないオペコードだぞ」という類のエラーメッセージをはくようなら、 まず binutils のインストールミスと考えられます。

クロス環境を build する際にはインストールディレクトリを gcc, binutils で揃えることに留意して下さい。

ところで手元の環境では gcc -b arm-linux では ld がフォーマットを認識せず、 ld --help として調べたところ elf32-arm というフォーマットになっていました。 しかし -b elf32-arm にするとこんどは gcc 側が認識しません。 窮余の策として、

# cd /usr/local/lib/gcc-lib
# ln -sf arm-linux elf32-arm
# cd /usr/local/
# ln -sf arm-linux elf32-arm
してあります。これで gcc -b elf32-arm hogehoge.c という形でコンパイルできるようになります。 なお、arm-linux-gcc によるコンパイルの方は何の問題もなくコンパイルできました。

通常 configure でクロスコンパイルを指定(--target=arm-linux)すると arm-linux-gcc を使うように Makefile が作成されます。 gcc, arm-linux-gcc のどちらでもコンパイルできるからといって arm-linux-gcc を消すのはやめましょう。

余談:
binutils 2.9.5 で互換性がなくなるのはこの部分です。 elf32-arm が elf32-littlearm に変わっています。

アプリケーションの make

configure を持ち、かつクロスコンパイルのことが考慮されているパッケージなら、 今や linux/i386 の上でも
% ./configure --host=arm-linux --build=i586-linux
とすることで linux7k 用のバイナリが build できるようになりました。 .... もっともこのままでは install できないので、--prefix に適切な位置を指示してやる必要があるでしょうし、 --host, --build は受け付けるのに Makefile には CC = gcc とだけ定義されてしまい、 セルフ用のバイナリが make されてしまうケースも多いのですが。
CC, LD のオーバーライドだけでいいなら
% CC=arm-linux-gcc LD=arm-linux-ld ./configure --host=arm-linux --build=i586-linux
とするのが素直です。ただこれも Makefile.in 中に "gcc" を直書きしているものが タマにあります -_-;;

例として linux7k の上でうごく cat を make します。 cat が無いというのは不便とかいうレベルではないので:

% zcat textutils-1.22.tar.gz | tar -x
% ./configure --host=arm-linux --build=i586-linux --prefix=/mnt/flashcard
% for name in **/Makefile ; do
>  sed 's/CC = gcc/CC = arm-linux-gcc/g' < $name > /tmp/Makefile
>  mv /tmp/Makefile $name
> done
% make
% make install
textutils はそのままだと CC = gcc としますので、これを arm-linux-gcc で上書きしてから make に入ります。 Makefile を作成したのちに sed で上書きしているのは、リンクに gcc を Makefile.in 中に直書きしている部分があるからです。

この方法で大抵うまくいくんですが、 セルフとクロスと両方のコンパイラを同時に必要とするパッケージでたまにハマります。 たとえば多くの場合、document で .info や .tex などが使われますが、 これらのファイルをパッケージ独自のフィルタ経由で作成する場合、 そのフィルタはセルフのコンパイラで動かなければなりません。 あるいは emacs のように、build された自分自身(temacs) がその場で動く必要のあるものもあります。

textutils では何もしてないのでそのままでうまく make できます。

/usr/local/bin/strip が arm-linux format を受け付けるようにしてあるなら、

# cd /mnt/flashcard/bin
# for name in * ; do
>  if file $name | grep 'not stripped' > /dev/null ; then
>   /usr/local/bin/strip --strip-all $name
>  fi
> done
もしておくと、Psion5 のカードの容量の節約になるのは普段と同じです。

クロス環境のための有用な文書、サイト


[目次へ]