Go言語はコンパイル言語なのでソースコードをコンパイルしてバイナリを実行するという形になるのですが、
コンパイル → 実行ファイルまでの流れをよく理解できていなかったのでサラッと学んでみました。
こちらの記事を要所要所で翻訳しつつ、噛み砕いたものを記事にしています。
間違い等あればご指摘いただければ幸いですm(_ _)m
コンパイル
ソースをコンパイルしてgoアセンブリを作成し、それをアセンブラが解釈してオブジェクトファイルを作成します。
最終的にはオブジェクトファイルやアーカイブライブラリをリンカーがまとめてリンクし、実行可能バイナリファイルを作成するという手順です。
アセンブリを覗いてみる
以下のようなgoファイルを用意します
package calculate
func add(m int, n int) int {
return m + n
}
func sum(m int, n int) int {
return m - n
}
以下サンプルでコンパイルしてオブジェクトファイルを作ります。
ソースコード → アセンブリ → オブジェクトファイルという作成の流れを踏むのですが、-S
オプションによって標準出力にアセンブリが出力されます。
go tool compile -S add.go
↓
"".add STEXT size=16 args=0x10 locals=0x0 funcid=0x0 align=0x0 leaf
0x0000 00000 (add.go:3) TEXT "".add(SB), LEAF|NOFRAME|ABIInternal, $0-16
0x0000 00000 (add.go:3) FUNCDATA ZR, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (add.go:3) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (add.go:3) FUNCDATA $5, "".add.arginfo1(SB)
0x0000 00000 (add.go:3) FUNCDATA $6, "".add.argliveinfo(SB)
0x0000 00000 (add.go:3) PCDATA $3, $1
0x0000 00000 (add.go:4) ADD R1, R0, R0
0x0004 00004 (add.go:4) RET (R30)
0x0000 00 00 01 8b c0 03 5f d6 00 00 00 00 00 00 00 00 ......_.........
"".sum STEXT size=16 args=0x10 locals=0x0 funcid=0x0 align=0x0 leaf
0x0000 00000 (add.go:7) TEXT "".sum(SB), LEAF|NOFRAME|ABIInternal, $0-16
0x0000 00000 (add.go:7) FUNCDATA ZR, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (add.go:7) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (add.go:7) FUNCDATA $5, "".sum.arginfo1(SB)
0x0000 00000 (add.go:7) FUNCDATA $6, "".sum.argliveinfo(SB)
0x0000 00000 (add.go:7) PCDATA $3, $1
0x0000 00000 (add.go:8) SUB R1, R0, R0
0x0004 00004 (add.go:8) RET (R30)
0x0000 00 00 01 cb c0 03 5f d6 00 00 00 00 00 00 00 00 ......_.........
go.cuinfo.packagename. SDWARFCUINFO dupok size=0
0x0000 63 61 6c 63 75 6c 61 74 65 calculate
gclocals·33cdeccccebe80329f1fdbee7f5874cb SRODATA dupok size=8
0x0000 01 00 00 00 00 00 00 00 ........
"".add.arginfo1 SRODATA static dupok size=5
0x0000 00 08 08 08 ff .....
"".add.argliveinfo SRODATA static dupok size=2
0x0000 00 00 ..
"".sum.arginfo1 SRODATA static dupok size=5
0x0000 00 08 08 08 ff .....
"".sum.argliveinfo SRODATA static dupok size=2
0x0000 00 00
以下のようにgo build
コマンドに-gcflags -S
オプションをつけても、同様にアセンブリが出力されます。
go build -gcflags -S add.go
goアセンブリには色々なディレクティブが存在するわけですが、以下の2つはガベージコレクタが使用するもので、コンパイラによってアセンブリに挿入されます。
- FUNCDATA
- PBDATA
またアーキテクチャによってアセンブリのシンボルは異なるのですが、以下の仮想レジスタを指すシンボルは全アーキテクチャで統一されています。
- FP: Frame pointer: arguments and locals.
- PC: Program counter: jumps and branches.
- SB: Static base pointer: global symbols.
- SP: Stack pointer: the highest address within the local stack frame.
goアセンブリは基本的に疑似命令で構成されています。
命令にはアセンブル後の機械語と1対1で対応しているものもあれば、直接対応していないものもあります。
関連コマンドetc
go tool compile
goパッケージをコンパイルしてオブジェクトファイルを生成します。-S
オプションで、生成過程のアセンブリを標準出力に出すことができます。
go tool link
オブジェクトファイルやアーカイブライブラリをリンカーに渡し、実行ファイルを生成(リンク)します。
go tool nm
オブジェクトファイル、アーカイブライブラリ、実行ファイルで定義、使用されているシンボルのリストを返します。
アドレス / 型 / シンボル名
という書式になっています。
未定義シンボル(U)のアドレスは省略されます。
go tool objdump
実行ファイルを逆アセンブルします。
出力されるのはリンク後の実行ファイルのアセンブリのため、オブジェクトファイルをgo tool compile -S
した時とは別の結果となります。
コメント