Go猜想录
大道至简,悟者天成
Go语言程序的一生

理解可执行文件

编译过程

文本 -> 编译 -> 二进制可执行文件

// 文本
package main

func main() {
	println("hi")
}

// 编译
go build main.go

// 二进制可执行文件
main

编译过程详细版

$ go build -x main.go
WORK=/tmp/go-build3065476763
mkdir -p $WORK/b001/
cat >$WORK/b001/_gomod_.go << 'EOF' # internal
package main
import _ "unsafe"
//go:linkname __debug_modinfo__ runtime.modinfo
var __debug_modinfo__ = "0w\xaf\f\x92t\b\x02A\xe1\xc1\a\xe6\xd6\x18\xe6path\tcommand-line-arguments\nmod\tcommand-line-arguments\t(devel)\t\n\xf92C1\x86\x18 r\x00\x82B\x10A\x16\xd8\xf2"
EOF
cat >$WORK/b001/importcfg << 'EOF' # internal
# import config
packagefile runtime=/usr/lib/go-1.17/pkg/linux_amd64/runtime.a
EOF
cd /home/lucas/github/go-higher/lifecycle
# 编译: 文本代码 -> 目标文件(.o,.a)
/usr/lib/go-1.17/pkg/tool/linux_amd64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p main -lang=go1.17 -complete -buildid lnP5TiJOsIUwPmLiIfPV/lnP5TiJOsIUwPmLiIfPV -goversion go1.17.5 -D _/home/lucas/github/go-higher/lifecycle -importcfg $WORK/b001/importcfg -pack -c=2 ./main.go $WORK/b001/_gomod_.go
/usr/lib/go-1.17/pkg/tool/linux_amd64/buildid -w $WORK/b001/_pkg_.a # internal
cp $WORK/b001/_pkg_.a /home/lucas/.cache/go-build/22/2265575b71ed2f5a520a0c207374b79d322454b578824e8c3c39f6bf10770432-d # internal
cat >$WORK/b001/importcfg.link << 'EOF' # internal
packagefile command-line-arguments=$WORK/b001/_pkg_.a
packagefile runtime=/usr/lib/go-1.17/pkg/linux_amd64/runtime.a
packagefile internal/abi=/usr/lib/go-1.17/pkg/linux_amd64/internal/abi.a
packagefile internal/bytealg=/usr/lib/go-1.17/pkg/linux_amd64/internal/bytealg.a
packagefile internal/cpu=/usr/lib/go-1.17/pkg/linux_amd64/internal/cpu.a
packagefile internal/goexperiment=/usr/lib/go-1.17/pkg/linux_amd64/internal/goexperiment.a
packagefile runtime/internal/atomic=/usr/lib/go-1.17/pkg/linux_amd64/runtime/internal/atomic.a
packagefile runtime/internal/math=/usr/lib/go-1.17/pkg/linux_amd64/runtime/internal/math.a
packagefile runtime/internal/sys=/usr/lib/go-1.17/pkg/linux_amd64/runtime/internal/sys.a
EOF
mkdir -p $WORK/b001/exe/
cd .
# 连接: 将目标文件合并为可执行文件
/usr/lib/go-1.17/pkg/tool/linux_amd64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode=exe -buildid=G-g9D6X-2ZthM87RLc5o/lnP5TiJOsIUwPmLiIfPV/MZuyVms2pq5r1tYts6Az/G-g9D6X-2ZthM87RLc5o -extld=gcc $WORK/b001/_pkg_.a
/usr/lib/go-1.17/pkg/tool/linux_amd64/buildid -w $WORK/b001/exe/a.out # internal
mv $WORK/b001/exe/a.out main
rm -r $WORK/b001/

可执行文件

linux中的可执行文件 ELF(Executable and Linkable Format) 由几部分构成:

  • ELF header
  • Section header
  • Sections

操作系统执行可执行文件的步骤(以 linux 为例):

  1. 解析 ELF Header
  2. 加载文件内容至内存
  3. 从 entry point 开始执行代码

通过 entry point 找到 Go 进程的执行入口,使用 readelf

$ readelf -h ./main
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  # 执行入口
  Entry point address:               0x453b60
  Start of program headers:          64 (bytes into file)
  Start of section headers:          456 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         7
  Size of section headers:           64 (bytes)
  Number of section headers:         23
  Section header string table index: 3

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。