goのアセンブリでHello, World!

情報科学苦手の会にちょっとだけ顔を出してきました。で、本エントリは@ucqさんの「goのアセンブリHello World」にインスパイアされて書いた。goのアセンブラはKen Thompson作で、ベースはPlan 9のものを流用している。つまり、Plan 9アセンブリコードはそのまま通る。例えば、「アセンブリでHello, world!」で書いたこんなコードとか。

	TEXT	main+0(SB),$0
	PUSHL	$14
	PUSHL	$.string+0(SB)
	PUSHL	$1
	PUSHL	$0
	MOVL	$20,AX
	INT	$64
	PUSHL	$0
	PUSHL	$0
	MOVL	$8,AX
	INT	$64

	GLOBL	.string+0(SB),$14
	DATA	.string+0(SB)/8,$"Hello, w"
	DATA	.string+8(SB)/6,$"orld!\n"
	END	,

ただ、ローダ(リンカ)でエラーがでる。エントリ関数名はmainではだめで「main·main」になるようだ。「main·init」は何か初期化関係なのだろうが、今回は無視してOK。シンボルにパッケージ名がプレフィックスされるようだ。苦手の会でも話題になったが、パッケージ名と関数名の区切りに中点(·)を使っている*1

$ 8a hello.S
$ 8l -o hello hello.S.8
mainstart: undefined: main·init
mainstart: undefined: main·main

あと、当然システムコールの呼出し規則はPlan 9LinuxMac OS Xでは異なる。今回はMac OS X上で試したので、最終的に次のようなコードになった。8lに成功し、Mach-Oバイナリが生成された。

// -*- coding: utf-8 -*-
	TEXT	main·main+0(SB),$0
	PUSHL	$14
	PUSHL	$.string+0(SB)
	PUSHL	$1
	MOVL	$4,AX
	PUSHL	AX	// alignment?
	INT	$0x80
	ADDL	$16,SP

	PUSHL	$0
	MOVL	$1,AX
	INT	$0x80

	TEXT	main·init+0(SB),$0
	RET

	GLOBL	.string+0(SB),$14
	DATA	.string+0(SB)/8,$"Hello, w"
	DATA	.string+8(SB)/6,$"orld!\n"
	END	,

システムコール番号*2はeaxレジスタで、引数はスタック渡しになる。あとスタックは16バイトアライメント取る必要がある?ここではソフトウェア割込み使っているけど、goのsyscall、runtimeパッケージではsysenter命令を使っている。嘘で、386はソフトウェア割込み、amd64はsyscall命令を使っている。

苦手の会でもコマンド名が気持ち悪いという話が出ていたが、Plan 9コンパイラの論文には次のようなリストが載っている。8が386なのはPlan 9もgoも同じだが、6はgoではamd64Plan 9ではIntel i960だったりする。

SPARC kc kl ka
Power PC qc ql
MIPS vc vl va
Motorola 68000 1c 1l
Motorola 68020 2c 2l
ARM 7500 5c 5l
Intel 960 6c 6l
DEC Alpha 7c 7l
Intel 386 8c 8l
AMD 29000 9c 9l

余談だが、Intel i960はシングルチップでスーパスカラを実装した最初のRISCプロセッサだった。商業的には失敗したが、組み込み市場ではそれなりに使われていたようだ。AMD Am29000も聞き慣れないが、i960と似たようなプロセッサだったのかな。Pentium PROやK5以降のIA32プロセッサに影響を残したという意味で、この寄り道も必要だったんだろう。多様性重要というやつだ。

(追記:2009-12-13)goのドキュメントにはちゃんと8aはPlan 9アセンブラだって書いてあるな。

8a 8a is a version of the Plan 9 assembler.
8c 8c is a version of the Plan 9 C compiler.
8g 8g is the version of the gc compiler for the x86.
8l 8l is a modified version of the Plan 9 linker.

*1:superpre記法だと文字参照で表示されちゃうのね。Emacsでもうまく文字コード認識してくれなくて手動でCtrl-c RET rとか面倒なので、magicコメント入れている。

*2:/usr/include/sys/syscall.hを見ればわかる。