libcを使わずシステムコール呼出し
Plan9でHello, world!に書いたプログラムを普通にコンパイルすると,サイズは36204バイト.Plan9に動的リンクは存在しないので,静的リンクした大きさである.で,strip(1)したサイズは21639バイト.
次にprintを使わず,直接write(2)を使って書いてみる.この場合,サイズは3657バイト.stripして1346バイト.ずいぶん小さくなったな.
#include <u.h> #include <libc.h> void main(int argc, char **argv) { write(1, "Hello, world!\n", 14); exits(nil); }
ここからは,Binary Hacks #25の真似で,libcのwrite(2)を使わずに,直接INT命令を実行して,システムコール呼出ししてみる.Hacks #25ではLinuxカーネルのインラインアセンブリのマクロを使っているけど,Plan9のインラインアセンブリの書き方を知らないので,次のようなアセンブリファイルを用意した.
cpu% cat asm.s TEXT xwrite(SB), 1, $0 MOVL $20, AX INT $64 RET TEXT xexits(SB), 1, $0 MOVL $8, AX INT $64 RET
呼出し側のCプログラムはこんな感じ.
cpu% cat hello2.c #include <u.h> #include <libc.h> xwrite(int fd, void *buf, long n); xexits(char *msg); void main(int argc, char **argv) { xwrite(1, "Hello, world!\n", 14); xexits(nil); }
コンパイルしてみるが,1303バイトとあまりサイズは変わらない.nmでシンボルを見ると,libcの関数がいろいろ残っている.8lのmanページを眺めていたら,-lオプションを発見.これを使えば,libc.aはリンクされなそうだ.-Emainはmainをエントリポイントにするためのおまじない.デフォルトはlibc.aの_mainがエントリポイントになる.
cpu% 8c hello2.c cpu% 8a asm.s cpu% 8l -l -Emain hello2.8 asm.8 cpu% ls -l 8.out --rwxrwxr-x M 1390850 oraccha oraccha 481 Nov 23 21:25 8.out cpu% strip 8.out cpu% ls -l 8.out --rwxrwxr-x M 1390850 oraccha oraccha 114 Nov 23 21:27 8.out cpu% 8.out Hello, world!
ということで,114バイトまで小さくなった.
さて,8.outをヘキサダンプしてみるとわかるけど,Plan9の実行ファイル形式はELFじゃないね.Hacks #25は488バイト,頑張れば58バイトまで小さくなるらしいが,正攻法でここまでになるのは,Plan9 a.outフォーマットがELFより,ずいぶんシンプルだからか.
ファイルの先頭32ビットのマジックナンバはアーキテクチャごとに異なっていて,IA32の場合は0x1ebになるようだ.詳細は"man a.out"を参照.
cpu% file 8.out hello: 386 plan 9 executable cpu% xd 8.out 0000000 000001eb 00000042 00000010 00000000 0000010 00000000 00001020 00000000 00000000 0000020 83ec10b8 01000000 890424b8 00200000 0000030 89442404 b80e0000 00894424 08e81000 0000040 0000c704 24000000 00e80c00 000083c4 0000050 10c3b814 000000cd 40c3b808 000000cd 0000060 40c34865 6c6c6f2c 20776f72 6c64210a 0000070 00000000 0000072
Plan9には,nm(1)やstrings(1)はあるけど,objdumpやreadelf相当のコマンドはないかな.やっぱりacid(1)か.