链接(link)
我们编写的程序可能会使用其他程序或程序库( library ) 正如我们在helloworld程序中使用的fmt package
我们编写的程序必须与这些程序或程序库一起才能够执行
链接是将我们编写的程序与我们需要的外部程序组合在一起的过程
链接器是系统软件,在系统开发中起着至关重要的作用,因为它可以进行单独的编译。您可以将它分解为更小,更易管理的块,然后分别进行修改和编译,而不是将一个大型应用程序组织为一个整体的源文件。当您更改其中一个模块时,只需重新编译它并重新链接应用程序,而无需重新编译其他源文件。
链接分为两种,静态链接与动态链接
静态链接的特点在于链接器会将程序中使用的所有库程序复制到最后的可执行文件中。而动态链接只会在最后的可执行文件中存储动态链接库的位置,并在运行时调用。
因此静态链接要更快,可移植,因为它不需要在运行它的系统上存在该库。但是在磁盘和内存上占用更多的空间
链接发生的过程会在两个地方,一种是静态链接会在编译时的最后一步发生,一种是动态链接在程序加载到内存时发生。
下面我们简单对比一下静态链接与动态链接
go语言是静态链接还是动态链接?
net/httplibpthreadlibcgo buildgo help buildmode
» go help buildmode jackson@192 -buildmode=archive Build the listed non-main packages into .a files. Packages named main are ignored. -buildmode=c-archive Build the listed main package, plus all packages it imports, into a C archive file. The only callable symbols will be those functions exported using a cgo //export comment. Requires exactly one main package to be listed. -buildmode=c-shared Build the listed main package, plus all packages it imports, into a C shared library. The only callable symbols will be those functions exported using a cgo //export comment. Requires exactly one main package to be listed. -buildmode=default Listed main packages are built into executables and listed non-main packages are built into .a files (the default behavior). -buildmode=shared Combine all the listed non-main packages into a single shared library that will be used when building with the -linkshared option. Packages named main are ignored. -buildmode=exe Build the listed main packages and everything they import into executables. Packages not named main are ignored. -buildmode=pie Build the listed main packages and everything they import into position independent executables (PIE). Packages not named main are ignored. -buildmode=plugin Build the listed main packages, plus all packages that they import, into a Go plugin. Packages not named main are ignored.
archive: 将非 main package构建为 .a 文件. main 包将被忽略。
c-archive: 将 main package构建为及其导入的所有package构建为构建到 C 归档文件中
c-shared: 将mainpackage构建为,以及它们导入的所有package构建到C 动态库中。
shared: 将所有非 main package合并到一个动态库中,当使用-linkshared参数后,能够使用此动态库
exe: 将main package和其导入的package构建为成为可执行文件
本文不再介绍go如何手动使用动态库这一高级功能,读者只需现在知道go可以实现这一功能即可
编译与链接的具体过程
go build-x
go build -x main.go
输出如下:
WORK=/var/folders/g2/0l4g444904vbn8wxnrw0j_980000gn/T/go-build757876739 mkdir -p $WORK/b001/ cat >$WORK/b001/importcfg << 'EOF' # internal # import config packagefile fmt=/usr/local/go/pkg/darwin_amd64/fmt.a packagefile runtime=/usr/local/go/pkg/darwin_amd64/runtime.a EOF cd /Users/jackson/go/src/viper/XXX /usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p main -complete -buildid JqleDuJlC1iLMVADicsQ/JqleDuJlC1iLMVADicsQ -goversion go1.13.6 -D _/Users/jackson/go/src/viper/args -importcfg $WORK/b001/importcfg -pack -c=4 ./main.go /usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/_pkg_.a # internal cp $WORK/b001/_pkg_.a /Users/jackson/Library/Caches/go-build/cf/cf0dc65f39f01c8494192fa8af14570b445f6a25b762edf0b7258c22d6e10dc8-d # internal cat >$WORK/b001/importcfg.link << 'EOF' # internal packagefile command-line-arguments=$WORK/b001/_pkg_.a packagefile fmt=/usr/local/go/pkg/darwin_amd64/fmt.a packagefile runtime=/usr/local/go/pkg/darwin_amd64/runtime.a packagefile errors=/usr/local/go/pkg/darwin_amd64/errors.a ... EOF mkdir -p $WORK/b001/exe/ cd . /usr/local/go/pkg/tool/darwin_amd64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode=exe -buildid=zCU3mCFNeUDzrRM33f4L/JqleDuJlC1iLMVADicsQ/r7xJ7p5GD5T9VONtmxob/zCU3mCFNeUDzrRM33f4L -extld=clang $WORK/b001/_pkg_.a /usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/exe/a.out # internal mv $WORK/b001/exe/a.out main rm -r $WORK/b001/
-work
WORK=/var/folders/g2/0l4g444904vbn8wxnrw0j_980000gn/T/go-build757876739 mkdir -p $WORK/b001/ cat >$WORK/b001/importcfg << 'EOF' # internal
生成编译配置文件,主要为编译过程需要的外部依赖(如:引用的其他包的函数定义)
# import config packagefile fmt=/usr/local/go/pkg/darwin_amd64/fmt.a packagefile runtime=/usr/local/go/pkg/darwin_amd64/runtime.a
$WORK/b001/pkg.a
/usr/local/go/pkg/tool/darwin_amd64/compile -o $WORK/b001/_pkg_.a -trimpath "$WORK/b001=>" -p main -complete -buildid JqleDuJlC1iLMVADicsQ/JqleDuJlC1iLMVADicsQ -goversion go1.13.6 -D _/Users/jackson/go/src/viper/args -importcfg $WORK/b001/importcfg -pack -c=4 ./main.go
_.PKGDEF`、`_go.o
$ file _pkg_.a # 检查文件格式 _pkg_.a: current ar archive # 说明是ar格式的打包文件 $ ar x _pkg_.a #解包文件 $ ls __.PKGDEF _go_.o
src/cmd/compile/internal/gc/obj.go"!\n"
func dumpobj1(outfile string, mode int) { bout, err := bio.Create(outfile) if err != nil { flusherrors() fmt.Printf("can't create %s: %v\n", outfile, err) errorexit() } defer bout.Close() bout.WriteString("!<arch>\n") if mode&modeCompilerObj != 0 { start := startArchiveEntry(bout) dumpCompilerObj(bout) finishArchiveEntry(bout, start, "__.PKGDEF") } if mode&modeLinkerObj != 0 { start := startArchiveEntry(bout) dumpLinkerObj(bout) finishArchiveEntry(bout, start, "_go_.o") } }
生成链接配置文件,主要为需要链接的其他依赖
cat >$WORK/b001/importcfg.link << 'EOF' # internal packagefile command-line-arguments=$WORK/b001/_pkg_.a packagefile fmt=/usr/local/go/pkg/darwin_amd64/fmt.a packagefile runtime=/usr/local/go/pkg/darwin_amd64/runtime.a packagefile errors=/usr/local/go/pkg/darwin_amd64/errors.a ... EOF
main
/usr/local/go/pkg/tool/darwin_amd64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode=exe -buildid=zCU3mCFNeUDzrRM33f4L/JqleDuJlC1iLMVADicsQ/r7xJ7p5GD5T9VONtmxob/zCU3mCFNeUDzrRM33f4L -extld=clang $WORK/b001/_pkg_.a /usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/exe/a.out # internal mv $WORK/b001/exe/a.out main rm -r $WORK/b001/