crt*.oに頼らないHello, world!!プログラム

crt*.oに頼らないHello, world!!プログラムを書いてみました(ちょっとパディングの入れ方が自信ないですけど…)。
ただHello, world!!を出力するだけなんてたいしたことないじゃんと思うかもしれませんが、このプログラムをよくよく見ると、プログラム内で直にソフトウェア割込みを発生させてカーネルに制御を移しているのがわかると思います。普段はlibcの層に隠蔽されているのであまり意識しないと思いますが、システムコールの正体はソフトウェア割込みだというのを知っておいた方がいいと思います。
ちなみにNetBSDシステムコールを呼ぶ場合には、システムコールの引数をスタックに積んで、システムコールの番号をeaxレジスタにおいてソフトウェア割込みを発生させます。Linuxの場合は、確かシステムコールの引数も(数個までは)レジスタを使っていたと記憶しています。
そうそう、NetBSDでは.note.netbsd.identセクションがないとexec(3)がENOEXECで失敗します。このことを知らずにはまりました。
ファイル: tiny_hello.s

.text

.globl sys_exit
sys_exit:
	mov	$0x1,%eax
	int	$0x80

.globl sys_write
sys_write:
	mov	$0x4,%eax
	int	$0x80
	ret

.globl _start
_start:
        pushl   $15
        pushl   $.LC0
        pushl   $1
        call	sys_write
        pushl	$123
        call	sys_exit
	
.section ".rodata"
.LC0:
	.string	"Hello, world!!\n"

.section ".note.netbsd.ident", "a"
.p2align 2
	.long	7
	.long	4
	.long	1
	.ascii	"NetBSD\0\0"
	.long	200000200

コンパイルは、以下のように行います。-nostdlibを付けてcrt*.oなどをリンクしないように、-sでシンボルを付けないようにしています。

$ gcc -Wall -nostdlib -s -o tiny_hello tiny_hello.s

できたプログラムのサイズは、564バイトでした。これ以上ダイエットするには、リンカを使わずに自分でELFの構造を出力する必要があると思います。さすがにそこまでは…
参考: http://www.netbsd.org/ja/Documentation/kernel/elf-notes.html