Go 语言的编译链接过程控制,以及相应的子命令的使用

Tags: golang 

本篇目录

说明

Go 自带了很多不同用途的子命令,这里关注和编译相关的子命令用法,其它子命令见 Golang 的子命令

Go 编译链接过程控制

go build 串联了多个 go 内置工具完成编译链接过程,可以通过 go build 的参数控制编译链接过程:

# 查看 go build 支持的参数
go help build

其中 -asmflags 等参数的值被透传到 go build 使用的的对应工具:

-asmflags '[pattern=]arg list'
        arguments to pass on each go tool asm invocation.
-buildmode mode
        build mode to use. See 'go help buildmode' for more.
-compiler name
        name of compiler to use, as in runtime.Compiler (gccgo or gc).
-gccgoflags '[pattern=]arg list'
        arguments to pass on each gccgo compiler/linker invocation.
-gcflags '[pattern=]arg list'
        arguments to pass on each go tool compile invocation.
-installsuffix suffix
        a suffix to use in the name of the package installation directory,
        in order to keep output separate from default builds.
        If using the -race flag, the install suffix is automatically set to race
        or, if set explicitly, has _race appended to it. Likewise for the -msan
        flag. Using a -buildmode option that requires non-default compile flags
        has a similar effect.
-ldflags '[pattern=]arg list'
        arguments to pass on each go tool link invocation.

Go 支持的编译器

go build 的 -compiler 参数用来指定编译器,go 支持两个编译器: gccgo 和 gc,默认使用 gc。

gccgo

gccgo 是一个 gcc 的前端(将 go 文件翻译成 gcc 能处理的中间模式,然后由 gcc 后端编译成目标架构上二进制文件)。gccgo 作为 gcc 的一部分和 gcc 一起发布,需要通过安装 gcc 来安装,详细说明见 Go:Setting up and using gccgo

对应的编译链接参数和 gcc 相同。

gc

go tool compile 是 gc 的 go 代码编译器,通过 go build 的 -gcflogs 传入编译参数:

$ go tool compile
usage: compile [options] file.go...
  -% int
        debug non-static initializers
  -+    compiling runtime
  -B    disable bounds checking
  -C    disable printing of columns in error messages
  -D path
        set relative path for local imports
  -E    debug symbol export
...省略...

go tool asm 是 gc 的汇编编译器,通过 go build 的 -asmflags 传入编译参数:

$ go tool asm
usage: asm [options] file.s ...
Flags:
  -D value
        predefined symbol with optional simple value -D=identifier=value; can be set multiple times
  -I value
        include directory; can be set multiple times
  -S    print assembly and machine code
  -V    print version and exit
  -compiling-runtime
        source to be compiled is part of the Go runtime
...省略...

go tool link 是 gc 的链接器,通过 go build 的 -ldflags 传入链接参数:

$ go tool link
usage: link [options] main.o
  -B note
        add an ELF NT_GNU_BUILD_ID note when using ELF
  -E entry
        set entry symbol name
  -H type
        set header type
  -I linker
...省略...

gc 汇编代码编译

go tool asm 将汇编代码编译成可以被链接器链接的 object(.o)文件,它是一个基于 plan9 的汇编编译器,相应地采用了 plan9 的汇编语法。 go tool asm 支持的汇编指令不完全等同于目标架构上的指令,go tool asm 支持的是一个半抽象的指令集,和目标架构的指令集不完全相同。

A Manual for the Plan 9 assembler 介绍了 plan9 的汇编语法。

A Quick Guide to Go’s Assembler 介绍了 gc 汇编语法和 plan9 汇编语法的差异。

没找到将 .go 文件编译成 .s 汇编文件的方法(隐约记得以前是可以的,现在貌似不支持了),可以到 go 的源码里查看不同架构的汇编文件写法:

cd $GOROOT/src/runtime/
➜  runtime ls *.s
asm.s                     memclr_plan9_386.s        race_ppc64le.s            rt0_openbsd_amd64.s       sys_netbsd_arm64.s
asm_386.s                 memclr_plan9_amd64.s      rt0_aix_ppc64.s           rt0_openbsd_arm.s         sys_openbsd_386.s
asm_amd64.s               memclr_ppc64x.s           rt0_android_386.s         rt0_openbsd_arm64.s       sys_openbsd_amd64.s
asm_arm.s                 memclr_riscv64.s          rt0_android_amd64.s       rt0_openbsd_mips64.s      sys_openbsd_arm.s
asm_arm64.s               memclr_s390x.s            rt0_android_arm.s         rt0_plan9_386.s           sys_openbsd_arm64.s
...

gc 反汇编

go tool objdump 可以将编译后的 object 文件以及链接后的可执行文件反汇编,例如:

# 编译过程
main_empty.o  main_fmt.o:
	go tool compile main_empty.go
	go tool compile main_fmt.go

# 链接过程
main_empty.out main_fmt.out: main_empty.o  main_fmt.o
	go tool link -o main_empty.out main_empty.o
	go tool link -o main_fmt.out main_fmt.o

# 反汇编 object 文件
objdump_obj: main_fmt.o
	go tool objdump -S main_fmt.o

# 反汇编链接后的可执行文件
objdump_exe: main_fmt.out
	go tool objdump -S -s main.main main_fmt.out  # -s 只显示 main 函数部分
	#go tool objdump -S main_fmt.out              # 整个可执行文件反汇编会看到链接器引入了大量文件

go tool nm 可以列出编译后的 object 文件以及链接后的可执行文件中定义或使用的 Symbols。

# 查看使用的 symbols
nm_obj: main_fmt.o
	go tool nm -sort address -size -type main_fmt.o

# 查看使用的 symbols
nm_exe: main_fmt.out
	go tool nm -sort address -size -type main_fmt.out

cgo 引用 C 语言代码

cgo

待学习..

问题案例

升级 macOS 后,编译不通过:iostat_darwin.c:28:2: warning: ‘IOMasterPort’ is deprecated

升级 macOS 到 12.0 以后,原本可以编译的项目无法编译了:

# github.com/shirou/gopsutil/disk
iostat_darwin.c:28:2: warning: 'IOMasterPort' is deprecated: first deprecated in macOS 12.0 [-Wdeprecated-declarations]
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/IOKit.framework/Headers/IOKitLib.h:132:1: note: 'IOMasterPort' has been explicitly marked deprecated here

根据上述日志,iostat_darwin.c 使用了 macOS 的 IOMasterPort 接口,这个接口在 macOS 12.0 中不建议被使用:

//iostat_darwin.c:28:
IOMasterPort(bootstrap_port, &port);
match = IOServiceMatching("IOMedia");
CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue);
status = IOServiceGetMatchingServices(port, match, &drives);
if(status != KERN_SUCCESS)
    return -1;

macOS 12.0 将这个接口标记为不建议使用:

/*! @function IOMasterPort
    @abstract Deprecated name for IOMainPort(). */

kern_return_t
IOMasterPort( mach_port_t    bootstrapPort,
          mach_port_t * mainPort )
__API_DEPRECATED_WITH_REPLACEMENT("IOMainPort", macos(10.0, 12.0), ios(1.0, 15.0), watchos(1.0, 8.0), tvos(1.0, 15.0));

go mod why 查看为什么会依赖引发这个问题的 github.com/shirou/gopsutil:

 go mod why github.com/shirou/gopsutil 
XXX.XXX.XXX/x/xx/dal/db imports
        XXX.XXX.XXX/aweme-go/ktest/mock imports
        XXX.XXX.XXX/edu/memongo imports
        XXX.XXX.XXX/edu/memongo/mongobin imports
        github.com/spf13/afero imports
        io/fs: package io/fs is not in GOROOT (/Users/xx/Work/Bin/go-1.14.2/go/src/io/fs)
XXX.XXX.XXX/x/xx/dal/db imports
        XXX.XXX.XXX/aweme-go/ktest/mock imports
        XXX.XXX.XXX/edu/memongo imports
        XXX.XXX.XXX/edu/memongo/mongobin imports
        github.com/spf13/afero tested by
        github.com/spf13/afero.test imports
        testing/fstest: package testing/fstest is not in GOROOT (/Users/xx/Work/Bin/go-1.14.2/go/src/testing/fstest)

没法去掉这个依赖,目标服务要部署在 linux 上所以不想将 macOS 降级,考虑让 go 在编译的时候忽略这个 warning。

找来找去发现 go 的 gc 编译器没有忽略的错误的方法,而且导致问题的 github.com/shirou/gopsutil 引用 macOS 的 C 代码库时,使用的是 cgo:

// +build darwin
// +build cgo

package disk

/*
#cgo LDFLAGS: -framework CoreFoundation -framework IOKit
#include <stdint.h>
#include <CoreFoundation/CoreFoundation.h>
#include "iostat_darwin.h"
*/
import "C"
...

cgo 的编译参数要在目标代码的注释里添加,暂时没有什么好办法。。。

参考

  1. 李佶澳的博客
  2. Go:Setting up and using gccgo
  3. A Quick Guide to Go’s Assembler
  4. A Manual for the Plan 9 assembler
  5. cgo
  6. Golang 的子命令

推荐阅读

Copyright @2011-2019 All rights reserved. 转载请添加原文连接,合作请加微信lijiaocn或者发送邮件: [email protected],备注网站合作

友情链接:  系统软件  程序语言  运营经验  水库文集  网络课程  微信网文  发现知识星球