影响编译的环境变量

环境变量 说明
GOARCH 指定CPU架构
GOOS 制定编译程序的运行平台
CGO_ENABLED 设置golang编译期间是否支持调用cgo命令

GOARCH and GOOS

通常来说, GOARCH和GOOS成对出现

$GOOS $GOARCH
android arm
darwin 386, amd64, arm, arm64
dragonfly amd64
freebsd 386, amd64, arm
linux 386, amd64, arm, arm64, ppc64, ppc64le, mips, mipsle, mips64, mips64le, s390x
netbsd 386, amd64, arm
openbsd 386, amd64, arm
plan9 386, amd64
solaris amd64
windows 386, amd64

CGO_ENABLED

当CGO_ENABLED=1, 进行编译时, 会将文件中引用libc的库(比如常用的net包),以动态链接的方式生成目标文件。
当CGO_ENABLED=0, 进行编译时, 则会把在目标文件中未定义的符号(外部函数)一起链接到可执行文件中。

默认CGO_ENABLED=1, 在不同linux上编译可能会造成libc版本不兼容的问题, 比如arch系列linux下编译的使用了libc动态链接库的go程序无法直接在debian系linux使用, 这时就要关闭它, 进行静态链接, 就不会依赖libc版本了.

通过ldd obj-file 可以查看引用的动态链接库

1
2
3
4
╰─(ノ˚Д˚)ノ ldd server 
linux-vdso.so.1 (0x00007ffd8b3e6000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f752a663000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f752a89e000)

通过nm obj-file 可以查看符号表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
╰─(ノ˚Д˚)ノ nm server | rg "^\s+U"
U abort
U __errno_location
U fprintf
U fputc
U free
U freeaddrinfo
U fwrite
U gai_strerror
U getaddrinfo
U malloc
U mmap
U munmap
U nanosleep
U pthread_attr_destroy
U pthread_attr_getstacksize
U pthread_attr_init
U pthread_cond_broadcast
...

nm命令输出的符号类型说明如下表

符号
类型
说明
A该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。
B该符号的值出现在非初始化数据段(bss)中。例如,在一个文件中定义全局static int test。则该符号test的类型为b,位于bss section中。其值表示该符号在bss段中的偏移。一般而言,bss段分配于RAM
C该符号为common。common symbol是未初始话数据段。该符号没有包含于一个普通section中。只有在链接过程中才进行分配。符号的值表示该符号需要的字节数。例如在一个c文件中,定义int test,并且该符号在别的地方会被引用,则该符号类型即为C。否则其类型为B
D该符号位于初始话数据段中。一般来说,分配到data section中。例如定义全局int baud_table[5] = {9600, 19200, 38400, 57600, 115200},则会分配于初始化数据段中
G该符号也位于初始化数据段中。主要用于small object提高访问small data object的一种方式。
I该符号是对另一个符号的间接引用。
N该符号是一个debugging符号。
R该符号位于只读数据区。例如定义全局const int test[] = {123, 123};test就是一个只读数据区的符号。注意在cygwin下如果使用gcc直接编译成MZ格式时,源文件中的test对应_test,并且其符号类型为D,即初始化数据段中。但是如果使用m6812-elf-gcc这样的交叉编译工具,源文件中的test对应目标文件的test,即没有添加下划线,并且其符号类型为R。一般而言,位于rodata section。值得注意的是,如果在一个函数中定义const char *test = “abc”, const char test_int = 3。使用nm都不会得到符号信息,但是字符串“abc”分配于只读存储器中,testrodata section中,大小为4
S符号位于非初始化数据区,用于small object。
T该符号位于代码区text section。
U该符号在当前文件中是未定义的,即该符号的定义在别的文件中。例如,当前文件调用另一个文件中定义的函数,在这个被调用的函数在当前就是未定义的;但是在定义它的文件中类型是T。但是对于全局变量来说,在定义它的文件中,其符号类型为C,在使用它的文件中,其类型为U
V该符号是一个weak object。
WThe symbol is a weak symbol that has not been specifically tagged as a weak object symbol.
-该符号是a.out格式文件中的stabs symbol。
?该符号类型没有定义

一般来说, 希望保持跨平台的特性会在编译时将CGO_ENABLED关闭, 但是当你的程序使用了cgo的命令时, CGO_ENABLED 必须设置为1, 否则会报错go: no Go source files, 例如下面程序CGO_ENABLED为0的情况编译会报错

1
2
3
4
5
6
7
8
package main
import "C"
import "unsafe"
func main() {
cs := C.CString("Hello from stdio")
C.myprint(cs)
C.free(unsafe.Pointer(cs))
}

RERERENCES

[1] https://www.jianshu.com/p/bc78c32db030
[2] https://blog.haohtml.com/archives/31332
[3] https://juejin.cn/post/6844903817071296525