もうちょっとだけ zxLinux

2000/07/19 (最終更新:2005/08/28 15:27)

◎ せっかくなので

前回ちょろっとだけ zxLinux の開発環境を作ってみました。 簡単なCのソースをコンパイルするだけで 思いっきり手間取ってしまいました。

せっかくなので、もう少しいろいろと試してみることにします。

◎ 何度もいいますが

今現在の zxLinux は、完全に開発者向けのしろものです。 それも、Zaurus というポケットサイズの非常に小さいマシンの上で Linux が動いてしまうこと、 そして Zaurus 用アプリケーション開発の選択肢として Linux を選択できること、 さらに既存の ZaurusOS と共存してしまうところが とてつもなく すごい わけです。

今現在は開発者向けなので、 開発とか Linux と無関係なユーザーにとっては、 動かしても非常につまらないものです。 だけど、それは当たり前。

どんなおもしろくてすごい表現のゲーム、どんなにGUIで固められたてかてかの アプリケーションでも、 その根本である開発者レベルまで下がってしまえば テキストで書かれたソースコードにすぎません。 それを見ただけで 「なんだただの文字だけのテキストか、じゃあわからないからつまらない」 なーんて決めつけてちゃだめですよね。

Linux てのは、ZaurusOS と同じように目に見えない根本部分です。 画面も入出力インターフェースも何もかも、全部その上で動く 「アプリケーション次第」。

開発の選択肢が、ZaurusOS の SZAB、CodeWarrior、 さらに Linux まで増えたんだから今後に大期待ということです。

ちなみに MI Zaurus には、GUI 環境まで含めた統合環境として 大島芳樹 さんの Squeak/Zaurus もあります。 これは Zaurus 上に独自の ウィンドウシステムまで載ってしまってる かなりのものなので、本来ならもっとニュースになっても おかしくありません。 Squeak/Zaurus のページにはぜひ行ってみてください。

◎ zxLinux カーネルのコンパイル

さて、せっかくカーネルソースとカーネル用コンパイラをダウンロードしたので、 そのコンパイルも試してみましょう。 まずはアプリケーション開発環境同様に /usr/local に zxksrc-000318.tgz と zxkerndev-000326.tgz を展開します。 ダウンロードはこちらから

注: コマンド入力表す場合赤の囲み、青の囲みはファイルの内容です。 コマンド入力の場合「#」は root 時のプロンプト、 「%」は通常ユーザー時のプロンプトを意味します。 プロンプトのあとの文字列はキー入力したコマンドです。

	# tar -zxvf zxksrc-000318.tgz
	# tar -zxvf zxkerndev-000326.tgz

カーネル開発キット /usr/local/zx と、カーネルソース /usr/local/zxksrc-000326 ができます。 カーネルのコンパイル手順は次の通りです。

	# cd /usr/local/zxkerndev-000326
	# make clean dep
	# make

カーネルファイル ZLNXKNL.BIN ができます。

	# ls -l ZLN*
	-rw-rw-r--   1 oga      oga        532864 Jul 19 01:39 ZLNXKNL.BIN

これをカードの __zaurus にそのまま入れれば 新しいカーネルで起動することになります。

◎ コマンドオプションの解

カーネルコンパイル用の Makefile Makefile を調べていて、 コンパイラ(sh-hitachi-elf-gcc)オプションの意味がいくつかわかりました。

	-ml	リトルエンディアン指定
	-m3	CPU SH3指定
	-m4	CPU SH4指定
「お待たせしました zxLinux 解説」の最後で、 プログラムが動かなかった原因は -ml オプションにあることを書きました。 -ml がないと、ビッグエンディアンでコンパイルされてしまうので、 リトルエンディアンの Zaurus では、そりゃ動かないわけです。

さらにもう一つ、「お待たせしました zxLinux 解説」の Makefile 中

ZGCCLIB= $(ZTOP)/lib/gcc-lib/sh-hitachi-elf/2.95.2

と書いてあるところがありますが、これは

ZGCCLIB = $(ZTOP)/lib/gcc-lib/sh-hitachi-elf/2.95.2/ml/m3e

にしないといけないようです。 つまり間違いでした。 たいへん申し訳ありませんでした。

ml フォルダの下がリトルエンディアン用の gcc ライブラリで、 そのままではビッグエンディアン用のライブラリをリンクしてしまいます。 表示出力 1行のような単純な(複雑な数値演算を使わない)コードは libgcc に 依存しないので動きますが、 複雑なプログラムになるとさすがに動きません。 この指摘も 溝手 さんにいただきました。ありがとうございました。

◎ 感動の瞬間

手持ちのプログラムを試しにコンパイルしてみました。 昔作った UNIX 用漢字コードコンバータ ack v1.39 を展開してみます。 このプログラムは、もともと Linux 上でそのままコンパイルが通ります。

コンパイラの指定を変えるために Makefile のみ修正しました。
	: 略

#### -- zxLinux
ZTOP	= /usr/local/zxlinux
ZLIB	= $(ZTOP)/sh-hitachi-elf/lib
ZINC	= $(ZTOP)/sh-hitachi-elf/include
ZGCCLIB	= $(ZTOP)/lib/gcc-lib/sh-hitachi-elf/2.95.2/ml/m3e
CC	= $(ZTOP)/bin/sh-hitachi-elf-gcc -ml -m3 -I$(ZINC)
CFLAGS	= -O -DDEFCODE=2 -DDEFJIS=2 -DENVACK=1
CFLAGS	= -O -DDEFCODE=1 -DDEFJIS=2 -DENVACK=1 -DLANGCHK=1 -DJCONVSW=1
LK	= $(ZTOP)/bin/sh-hitachi-elf-ld -L$(ZLIB) -L$(ZGCCLIB) $(ZLIB)/prochead_main.o $(ZLIB)/crt0.o
ZSYSLIB	= -lzxl -lc -lm -lgcc -T $(ZLIB)/ldscripts/shlelf.x
####

	: 略

$(PROG): $(OBJ)
	$(LK) $(LDFLAGS) $(OBJ) -o $@ $(ZSYSLIB)

	: 以下略

コンパイルは、一切エラーも無くすんなり通ってしまいます。 できた ack 実行バイナリを、 ZLNXIMG.DAT にコピーして zxLinux を起動したら、 なんとそのまんま動いてしまいました! zxLinux に感謝!

Linux のプログラムが、無修正で Zaurus で動く! こっからが zxLinux の真の実力本領発揮。 わかっていた、当たり前といえば当たり前のこととはいえ、 いざ実際に試して目の当たりにすると格別です。 この ack は、 その昔 Sun の UNIX WorkStation (SunOS4.1) 上で開発したプログラムなのです。

◎ あれ?・・書けない

ファイル読み込みは OK 。でも書き込めない。 ack の動作テストで、なぜかファイルに書き込むことができません。 フォルダ作ってパーミッション開けてもだめ、フルパス指定してもだめ。

調べたところ、fopen( , "w" ) では新規のファイルを作ることができず、エラーになるようです。 creat() ではファイルを作ることができました。 また creat() で作ったファイルに対しては fopen( , "w" ) することができます。 今度はライブラリのソースをダウンロードしてくる必要があるようです。

◎ 抜き差し

zxLinux は MI-C1 でテストしています。 ファイルの転送でカードの抜き差しを頻繁に行うので、 本体裏側にくるっと回すふたがじゃまになります。

カードの抜き差しを頻繁に行うときは、 MI-C1 のふたを外しておくと便利です。

zxLinux の終了は、必ず [戻る] ボタンで行わなければなりません。 なので、zxLinux の弱点はオートパワーオフです。 AC アダプタを使っているときは、 パワーオフまでの時間を最大に設定しているので気が付きませんでした。 バッテリー駆動で開発してたらしょっちゅうオートパワーオフがかかってしまい、 そのたびに Zaurus がうまく起動しなくなってしまいます。 注意が必要です。 もちろん電池蓋ロックのリセットで直ります。

◎ newlib のコンパイル

他のソース同様 newlibsrc-000318.tgz をダウンロードして展開します。 make すると、 途中で /usr/home/mako/work/app-4/crt0.c が無いといわれて止まります。 なので、crt0 のソース rt-src-000318.tgz もダウンロードします。

	# cd /usr/local
	# tar -zxvf newlibsrc-000318.tgz
	# tar -zxvf rt-src-000318.tgz

/usr/local/rt-src-000318/config.mk を修正します。 KERNTOP=/usr/home/zxlinux の部分を カーネルソースを展開したディレクトリ (今回は KERNTOP=/usr/local/zxksrc-000318) に変更して、 rt-src-000318 の中で make します。

次に /usr/local/newlibsrc-000318/libc/syscalls/Makefile を書き換えます。 変更は次の3カ所。
	lib_a_SOURCES = \
		/usr/local/rt-src-000318/crt0.c

	DEFS =  -DPACKAGE=\"newlib\" -DVERSION=\"1.8.2\"  -I. -I$(srcdir)  \
			-I/usr/local/zxksrc-000318/include \
			-I/usr/local/rt-src-000318

	lib_a_OBJECTS = \
		/usr/local/rt-src-000318/crt0.o \
		/usr/local/rt-src-000318/xqread.obj \
		/usr/local/rt-src-000318/xqwrite.obj \
		/usr/local/rt-src-000318/xslfgnm.obj \
		/usr/local/rt-src-000318/xusrface.obj \
		/usr/local/rt-src-000318/xsbrk.obj \
		/usr/local/rt-src-000318/zx_syscall.o

これで newlibsrc-00318 の中で make が通るようになります。

◎ 書き込めない原因を探る

詳しく調べてみると、creat() は大丈夫で open( , O_CREAT|O_TRUNC|O_WRONLY ) を使うとエラーになるようです。

newlibsrc のソースコードを追いかけてみることにします。 open() や creat() の実体は libc/syscalls で見つけることができます。 ところがこれはリンクされておらず、 すべて rt-src-000318/zx_syscall.c で置き換えられています。 zxLinux のシステムコールエントリは zx_syscall.c にあるようです。

sys/sh/syscalls.c の方は _open() や _creat() を zx_syscall.c の open() , creat() 呼び出しに戻しているだけです。 シンボルを調べていると libc.a では _creat (creat()) が多重定義らしいことがわかりました。 ループしてしまうので、 sys/sh/creat.c はコメントアウトしておきます。

さて rt-src-000318 の zx_syscall.c を見ると、 システムコール呼び出しのメッセージは XTAL の queue に入れられていることがわかります。 なるほど、ここで zxLinux の肝ともいうべき システムコールとプロセス間通信の変換が行われているわけですね。

今度はメッセージを受信しているところを探さなければなりません。 カーネルソースで当たりをつけて、creat() システムコールの実体は sys_creat() らしきことがわかりました。

sys_creat をキーにしてソースリストの検索をかけ、 arch/sh/kernel/entry.c にメッセージ受信&カーネルコール変換があることを発見。 そこでは、システムコール __NR_open も __NR_creat も、 単純な関数テーブルで呼び出しているだけです。 なにも特殊なことをしていません。 そして呼び出している fs/open.c の sys_creat() の中では

	asmlinkage long sys_creat(const char * pathname, int mode)
	{
		return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
	}

となっています。 これでは結局いっしょです。 何の解決にもなっていませんでした。

◎ 妥協しようかな

ここで極端に後ろ向きな方法で妥協してしまうことにします。 zx_syscall.c の中でシステムコール open の定義を乗っ取って、 モード指定が O_CREAT|O_WRONLY|O_TRUNC の場合だけ creat() に置き換えてしまおうというものです。 根本的な解決にはなってませんが、とりあえず動けば OK (かな?)。

zx_syscall.o はあちこちでリンクされており、 libzxl.a にも libc.a にも含まれています。 かといって、ライブラリには他のファンクションも含まれているので、 やっぱり両方のライブラリをリンクしなければなりません。

zx_syscall.c を書き換えて、libzxl と libc をそれぞれ make し直してみます。 O_* のシンボルが未定義だと怒られたので、ヘッダファイルを探して #define されているシンボルの値を直接書き込むことにしました。 このとき参照したヘッダファイルは、どうせ同じ Linux だからと PC の /usr/include/fcntlbits.h です。

/usr/include/fcntlbits.h
#define O_ACCMODE         0003
#define O_RDONLY            00
#define O_WRONLY            01
#define O_RDWR              02
#define O_CREAT           0100  /* not fcntl */
#define O_EXCL            0200  /* not fcntl */
#define O_NOCTTY          0400  /* not fcntl */
#define O_TRUNC          01000  /* not fcntl */

O_CREAT|O_WRONLY|O_TRUNC の値を数値で埋め込んで、 この組み合わせの場合だけ creat() 呼び出しに置き換えます。 それでも結果はいっしょ、open() ではエラーになります。 いったいどこが悪いのでしょうか。

◎ 原因判明

参照したのが PC のフォルダ /usr/include のシンボル値だから悪いのかもしれません。 zxLinux アプリケーション開発キットのヘッダファイル /usr/local/zxlinux/sh-hitachi-elf/include/sys/fcntl.h を見ると、 思った通り O_* のシンボルの値が異なっています。

/usr/local/zxlinux/sh-hitachi-elf/include/sys/fcntl.h
#define _FCREAT         0x0200  /* open with file create */
#define _FTRUNC         0x0400  /* open with truncation */

#define O_RDONLY        0               /* +1 == FREAD */
#define O_WRONLY        1               /* +1 == FWRITE */
#define O_RDWR          2               /* +1 == FREAD|FWRITE */
#define O_APPEND        _FAPPEND
#define O_CREAT         _FCREAT
#define O_TRUNC         _FTRUNC
#define O_EXCL          _FEXCL
/*      O_SYNC          _FSYNC          not posix, defined below */
/*      O_NDELAY        _FNDELAY        set in include/fcntl.h */
/*      O_NDELAY        _FNBIO          set in 5include/fcntl.h */
#define O_NONBLOCK      _FNONBLOCK
#define O_NOCTTY        _FNOCTTY

ついでにカーネルが参照しているヘッダファイルを探します。 カーネルソースを O_CREAT で検索すると、 include/asm-sh/fcntl.h の中に定義部分がありました。 その値は次の通り。PC の Linux と同じ値です。

#define O_ACCMODE          0003
#define O_RDONLY             00
#define O_WRONLY             01
#define O_RDWR               02
#define O_CREAT            0100 /* not fcntl */
#define O_EXCL             0200 /* not fcntl */
#define O_NOCTTY           0400 /* not fcntl */
#define O_TRUNC           01000 /* not fcntl */

要するに、newlib のヘッダのシンボル定義が Linux カーネルと異なっていたわけです。 だから、カーネル内でコンパイラのシンボル参照が閉じていた creat() は成功し、 newlib のシンボル値を持ってカーネルを呼び出していた open( O_CREAT.. ) は失敗します。 原因が分かればテストも簡単。 open( , 0x100 | 0x400 | 0x01 ) で呼び出しを行うと、 open() システムコールでもきちんとファイル作成できました。

◎ 原因はわかったけど

この調子だと zxLinux の newlib(libc) は、まだまだ大幅な手直しが必要になりそうです。 (なるほどそれで、zxLinux のコマンド類は libc を使っていないのか・・) ある程度のライブラリは、自分で用意した方が早いのかもしれません。

[戻る]
[メニューに戻る] [ZAURUS総合] [DirectX] [Ko-Window] [Win32] [WinCE] [携帯電話] [その他]
フルパワー全開 Hyperでんち

Hiroyuki Ogasawara <ho>