2014年9月28日日曜日

Ubuntuに H8/300H用の gccをインストールしてみる。

Ubuntu 14.04に H8/300H用の gccをインストールしてみた。

H8/300Hは Softiesプロジェクトとは直接の関係は無い。
わざわざ直接の関係は無いと言う事は、間接の関係があると察していただけたと思う。
そもそも、Softiesプロジェクトは組み込み開発のテスト環境を提供するのが始まりだった。特に、単体テストフレームワークの CUnitがそれだ。そしてその CUnitは自社製の "ra7taj"という仮想マシンをテストするためのものであった。ra7tajの開発を 2年間一時停止して Softiesプロジェクトを進めてきた。CUnitの開発が今年の盆休みに実用域に達してひと段落したので、今月から ra7tajの開発を再開した。
それがなんで H8/300Hと関係があるのか??

ra7tajは不安定ながらも Solarisや Linux上で動作しているので、いよいよ次のステップではターゲットに組み込んで使えるようにすることになるが、そのターゲットが H8/300Hなのである。
今時なんで ARMとかじゃなくてH8/300Hかというと、…長くなるので止めとくが、CPUパワーもメモリも「そこそこ」しか無い環境で動かしてみたかったからである。
欲を言えば、8051くらいでも動作させたいが、ちょっとメモリが足りなさすぎるので見送りとなった。実を言うと、もう少し速くターゲット環境で動かしたかったが、2011年に欲を出してコンパクション可能なコレクタにしたら、それまで動作していたところがあちこちと調子悪くなってしまい、現在デバッグ中である。実際にターゲットを動かすのはいつになるやら。 ビルド環境を作るなんて気が早いかもしれない。
それに CUnitで単体テストやるのが楽しすぎて…。

CUnit作る前はどうやって ra7tajテストしていたかというと、Javaのプログラムから JNI経由で関数を呼び出していた。 Heapの状況を Swingで視覚化したりして大変便利だったのだが、ra7taj自体に JNI互換の関数を提供しはじめたら、テスト対象の ra7tajの JNIと テストドライバである JDKの JNIのシンボルが衝突して破綻してしまった。

なお、Softiesはオープンソースにしたいが、使用している一部のライブラリのライセンスの都合で未だ公開できる状況ではない。
ライセンス問題をクリアするまでもう少し掛かりそうである。

インストール

話が脱線してしまったので、元に戻そう。

インストールといっても、apt-getコマンドを使うだけである。
$sudo apt-get update

した後、
$ sudo apt-get install gcc-h8300-hms
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
以下の特別パッケージがインストールされます:
  binutils-h8300-hms
提案パッケージ:
  binutils-doc gcc-doc
以下のパッケージが新たにインストールされます:
  binutils-h8300-hms gcc-h8300-hms
アップグレード: 0 個、新規インストール: 2 個、削除: 0 個、保留: 5 個。
5,554 kB のアーカイブを取得する必要があります。
この操作後に追加で 14.9 MB のディスク容量が消費されます。
続行しますか? [Y/n] y
取得:1 http://jp.archive.ubuntu.com/ubuntu/ trusty/universe binutils-h8300-hms
 amd64 2.16.1-9ubuntu1 [2,125 kB]
取得:2 http://jp.archive.ubuntu.com/ubuntu/ trusty/universe gcc-h8300-hms amd64
 1:3.4.6+dfsg2-1ubuntu1 [3,429 kB]
5,554 kB を 14秒 で取得しました (381 kB/s)                                     
以前に未選択のパッケージ binutils-h8300-hms を選択しています。
(データベースを読み込んでいます ... 現在 328030 個のファイルとディレクトリが
インストールされています。)
Preparing to unpack .../binutils-h8300-hms_2.16.1-9ubuntu1_amd64.deb ...
Unpacking binutils-h8300-hms (2.16.1-9ubuntu1) ...
以前に未選択のパッケージ gcc-h8300-hms を選択しています。
Preparing to unpack .../gcc-h8300-hms_1%3a3.4.6+dfsg2-1ubuntu1_amd64.deb ...
Unpacking gcc-h8300-hms (1:3.4.6+dfsg2-1ubuntu1) ...
Processing triggers for man-db (2.6.7.1-1) ...
binutils-h8300-hms (2.16.1-9ubuntu1) を設定しています ...
gcc-h8300-hms (1:3.4.6+dfsg2-1ubuntu1) を設定しています ...
$

gcc本体だけでなく、binutilsも一緒にインストールされた。

gccのバージョンは最近の 4.8.xとかでなくて少し古い。

コマンドが認識されるかいくつか試してみる。
$ h8300-hms-gcc --version
h8300-hms-gcc (GCC) 3.4.6
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$

$ h8300-hms-objdump --version
GNU objdump 2.16.1 Debian GNU/Linux
Copyright 2005 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License.  This program has absolutely no warranty.
$

スタートアップルーチンは準備してないので、Hello, world.とかを試すのは今度にしよう。

2014年9月10日水曜日

言語Cで synchronizedを実現する(その1)

Javaの synchronized構文は便利だ。
オブジェクトをロックし、クリチカルセクションを実行したあと、確実にロックを解除するすることが言語でサポートされている。

そのような機構がない言語の場合、つぎのように記述するだろう。
lock();
// critical section
unlock();


クリチカルセクションが 1~2行程度ならよいが、長くなると何が起こるか分からない。
うっかり、この中で
if (hasError)
 return errorCode;


などとやってしまうとロックが解除されず、処理がとまってしまうか、再びロックしようとして多重ロックしてしまうかも知れない。

今回のチャレンジは、次のように関数を記述することだ。

void synchronized function(Something this) {
 // critical section
}


Javaでは synchronizedなメソッドのほかに、synchronizedブロックが記述できるが、ここでは関数だけを対象とする。

synchronizedな関数に入るときオブジェクトをロックし、出るときにアンロックする。
これを実現するための課題は、
  1. 関数に入るときと出る時に処理を行なえること。
  2. 関数に synchronized属性を持たせること。
  3. 関数が synchronizedを持って意いるか判断可能なこと。
  4. ロックするオブジェクトを認識できること。

1.関数に入るときと出る時に処理を行なう

これは以前の記事で取り上げたことのある、gcc拡張の __cyg_profile_func_enter, __cyg_profile_func_exitを使えそうである。
実験ではオブジェクトをロックせず、下記のようにトレース出力をする。
void NO_INSTRUMENT __cyg_profile_func_enter(void *function, void *caller) {
        if (isSynchronized(function))
                printf("enter synchronized function %p\n", function);
        else
                printf("enter %p\n", function);
}

void NO_INSTRUMENT __cyg_profile_func_exit(void *function, void *caller) {
        if (isSynchronized(function))
                printf("exit synchronized function %p\n", function);
        else
                printf("exit %p\n", function);
}


なお、NO_INSTRUMENTはコードを見やすくするための下記のようなマクロである。
#define NO_INSTRUMENT __attribute__((no_instrument_function))

2.関数に synchronized属性を持たせる

これはちょっと難しい。

上記の例にある、 isSynchronized(function)をどうやって実現するか。

関数に任意の属性を持たせたい。
関数とは別管理のルックアップテーブルを持たせれば簡単そうだが、別管理が面倒くさい。
関数が synchronizedかどうか人間が知りたいとき、いちいち管理テーブルを見ないとならない。
void synchronized function...のような記述できるほうが管理しやすい。
そのためには Javaのアノテーションのような機能が必要になる。
しかし、言語Cにはアノテーションは無い。 gcc拡張を探したがそれに近いものは無い。

諦め掛けていたが、トンでもないアイデアを思いついた。
synchronized用のメモリセクションを作り、そこに配置した関数を synchronizedとみなすことにしよう。

synchronizedセクションを作って関数を配置するには、gcc拡張の __attribute__構文が使える。
次のようなマクロを定義すれば、あとは関数に synchronizedを付けるだけ。
#define synchronized __attribute__((section("synchronized")))
gcc以外にもソースコード上にセクションを明記できるものがあるが、#pragma構文を使うものは残念ながら #defineすることはできない。

早速書いてみよう。 使い分けできるのが分かるよう synchronizedでない普通の関数も作っておく。

Sub.h(主要部分だけ)
extern void sub1();
extern synchronized void sub2();


Sub.c(主要部分だけ)
void sub1() {
 printf("sub1 called.\n");
}

void synchronized sub2() {
 printf("sub2 called.\n");
}


これをビルドしてできた Sub.oを逆アセンブルしてみると、ちゃんと synchronizedセクションに対応づけられている。
$ objdump -d Sub.o

Sub.o:     file format elf32-i386


Disassembly of section .text:

00000000 :
   0: 55                    push   %ebp
   1: 89 e5                 mov    %esp,%ebp
   3: 83 ec 18              sub    $0x18,%esp
   6: 8b 45 04              mov    0x4(%ebp),%eax
   9: 89 44 24 04           mov    %eax,0x4(%esp)
   d: c7 04 24 00 00 00 00  movl   $0x0,(%esp)
  14: e8 fc ff ff ff        call   15 
  19: c7 04 24 00 00 00 00  movl   $0x0,(%esp)
  20: e8 fc ff ff ff        call   21 
  25: 8b 45 04              mov    0x4(%ebp),%eax
  28: 89 44 24 04           mov    %eax,0x4(%esp)
  2c: c7 04 24 00 00 00 00  movl   $0x0,(%esp)
  33: e8 fc ff ff ff        call   34 
  38: c9                    leave  
  39: c3                    ret    

Disassembly of section synchronized:

00000000 :
   0: 55                    push   %ebp
   1: 89 e5                 mov    %esp,%ebp
   3: 83 ec 18              sub    $0x18,%esp
   6: 8b 45 04              mov    0x4(%ebp),%eax
   9: 89 44 24 04           mov    %eax,0x4(%esp)
   d: c7 04 24 00 00 00 00  movl   $0x0,(%esp)
  14: e8 fc ff ff ff        call   15 
  19: c7 04 24 0d 00 00 00  movl   $0xd,(%esp)
  20: e8 fc ff ff ff        call   21 
  25: 8b 45 04              mov    0x4(%ebp),%eax
  28: 89 44 24 04           mov    %eax,0x4(%esp)
  2c: c7 04 24 00 00 00 00  movl   $0x0,(%esp)
  33: e8 fc ff ff ff        call   34 
  38: c9                    leave  
  39: c3                    ret 

因みに synchronizedでない普通の関数は .textセクション。

3.関数が synchronizedを持って意いるか判断

そして、isSynchronized関数はつぎの通り。
int NO_INSTRUMENT isSynchronized(void* function) {
 extern char __start_synchronized;
 extern char __stop_synchronized;

 return (function >= (void*)&__start_synchronized)
  && (function < (void*)&__stop_synchronized);
}

gccでは、__start_, __stop_の後にセクション名をつけたものが、それぞれセクションの先頭と後端のアドレスを示している。 これを実行時に関数アドレスと比較することで、そのセクションの中の関数かどうか判断できるというわけである。

あとは次のようなプログラムで sub1()と sub2()を実際に呼び出してみる。 動作が確認しやすいよう、synchronizedセクションの先頭、後端アドレスと、sub1(), sub2()のアドレスも出力しておき、つづいてsub1()と sub2()を実際に呼び出す。

int NO_INSTRUMENT main(int argc, char** argv) {
 extern char __start_synchronized;
 extern char __stop_synchronized;

 printf("[synchronized section]\n");
 printf("start: %p\n", &__start_synchronized);
 printf("end: %p\n", &__stop_synchronized);
 printf("\n");

 printf("[function address]\n");
 printf("sub1: %p\n", sub1);
 printf("sub2: %p\n", sub2);
 printf("\n");

 printf("[call functions]\n");
 sub1();
 printf("\n");
 sub2();
 return 0;
}
結果は次のように、synchronizedとそうでないものがちゃんと区別できることを確認できた。
$ ./Main
[synchronized section]
start: 0x80486ec
end: 0x8048726

[function address]
sub1: 0x8048600
sub2: 0x80486ec

[call functions]
enter 0x8048600
sub1 called.
exit 0x8048600

enter synchronized function 0x80486ec
sub2 called.
exit synchronized function 0x80486ec

なお、Sub.h, Sub.cの両方で sub2()に synchronizedを記述したが、Sub.h, Sub.cのいづれか一方だけに記述しても結果は同じになる。

この手法は、synchronized以外にも使えるかも知れない。 なんとも馬鹿げたやり方だが。
※本プロジェクトの名称 softiesの先頭の sは stupidの略ですから、何でもアリです。 お許しください。