Go build したバイナリの中身を知りたい
Go は go build すると実行可能なシングルバイナリが生成される。これは、各OS別にビルドしていて、サイズは小さくとも数MBになる(Go 1.13)。
What is included in a Golang binary? - Quora にてすでに簡単な答えがある。
Go のバイナリには
1. Go の runtime
2. goroutine scheduler
3. kernel とのインターフェース
4. reflection で利用する runtime の型情報
5. ファイルや行などスタックトレースに使われる情報
では、実際にその割合は見れるのだろうか。
go tool nm でバイナリを解析する
nm - The Go Programming Language という tool を使う(name の略だろうか)。
Nm lists the symbols defined or used by an object file, archive, or executable.
T text (code) segment symbol
t static text segment symbol
R read-only data segment symbol
r static read-only data segment symbol
D data segment symbol
d static data segment symbol
B bss segment symbol
b static bss segment symbol
C constant address
U referenced but undefined symbol
hello world のみのバイナリの内訳を見てみた。
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello Go!")
}
$ ls
2.0M 1 20 00:13 simple*
$ go tool nm -size -sort size simple | head -n 100
16781312 U _mach_absolute_time
16781312 U _usleep
16781312 U _sysctl
16781312 U _stat64
16781312 U _sigaltstack
16781312 U _sigaction
16781312 U _setitimer
16781312 U _readdir_r$INODE64
16781312 U _read
16781312 U _raise
16781312 U _pthread_sigmask
16781312 U _pthread_mutex_unlock
16781312 U _pthread_mutex_lock
...
16781312 U _mach_timebase_info
16781312 U _exit
16781312 U _pthread_attr_init
10ee080 481753 R runtime.pclntab
1099560 223232 R runtime.types
1099560 223232 R type.*
1099560 223232 R runtime.rodata
10d7123 68357 R go.func.*
1183840 65744 B runtime.trace
10cfd60 29635 R go.string.*
1169380 25920 D runtime.firstmoduledata
1066600 20080 T unicode.init
117f980 16064 B runtime.semtable
1171dc0 14720 D fmt.ppFree
1096890 10224 T fmt.(*pp).printValue
117d280 9984 B runtime.mheap_
1083f60 8336 T time.Time.AppendFormat
117b280 8192 B runtime.timers
1179300 8064 B runtime.cpuprof
1047940 6720 T runtime.gentraceback
116f8c0 6712 D strconv.powersOfTen
11947a0 5992 B runtime.memstats
107deb0 5264 T internal/fmtsort.compare
1087070 4640 T time.LoadLocationFromTZData
10823d0 4464 T time.nextStdChunk
1176fa0 4112 D runtime.itabTableInit
104ca30 3936 T runtime.typesEqual
10e7c28 3835 R runtime.gcbits.*
103ee30 3344 T runtime.newstack
10ed300 3328 R runtime.typelink
1088440 3312 T time.loadTzinfoFromZip
1070fd0 3088 T reflect.FuncOf
105b6a0 3056 T internal/reflectlite.haveIdenticalUnderlyingType
106ffb0 3056 T reflect.haveIdenticalUnderlyingType
10ec5e0 3050 R runtime.findfunctab
102fba0 3024 T runtime.findrunnable
1072450 2928 T reflect.funcLayout
1017b80 2896 T runtime.gcMarkTermination
10620c0 2880 T strconv.appendEscapedRune
108db30 2848 T os.Getwd
pclntab とはなにか
src/debug/gosym/pclntab.go - The Go Programming Language
このデータは go のプログラムがクラッシュしたときに stack trace を生成するためのもの。
Program Counter Line Table の略語のようだ。関数がふえるとこのシンボルは増加するため、大規模な Go プログラムは pclntab が大きくなる。
Go1.2 からもともと圧縮していた文字列を展開した状態でバイナリに同梱されるというのだ。
なぜ変えたのかというと、Go 1.1 ではバイナリを起動するときに、圧縮した文字列を展開するのに時間がかかっていた。 そこで、圧縮を止めることで、バイナリが即座に起動できるようにしたということだそうだ。