让我首先从桌子上拿一些事实进行事实检查,以免造成混乱:
现在让我们在Go的上下文中进行讨论。我注意到,如果我使用CGO_ENABLED=1 go build ...
构建二进制文件,则会得到带有动态部分的二进制文件:
david@x1 /tmp (git)-[master] % readelf -d rtloggerd.cgo1
Dynamic section at offset 0x7a6140 contains 19 entries:
Tag Type Name/Value
0x0000000000000004 (HASH) 0x914e40
0x0000000000000006 (SYMTAB) 0x915340
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000005 (STRTAB) 0x915100
0x000000000000000a (STRSZ) 570 (bytes)
0x0000000000000007 (RELA) 0x914a38
0x0000000000000008 (RELASZ) 24 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x0000000000000003 (PLTGOT) 0xba6000
0x0000000000000015 (DEBUG) 0x0
0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000006ffffffe (VERNEED) 0x914de0
0x000000006fffffff (VERNEEDNUM) 2
0x000000006ffffff0 (VERSYM) 0x914d80
0x0000000000000014 (PLTREL) RELA
0x0000000000000002 (PLTRELSZ) 816 (bytes)
0x0000000000000017 (JMPREL) 0x914a50
0x0000000000000000 (NULL) 0x0
david@x1 /tmp (git)-[master] % ldd rtloggerd.cgo1
linux-vdso.so.1 (0x00007ffd9a972000)
libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007fcb2853c000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007fcb28378000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fcb2858a000)
另一方面,如果我CGO_ENABLED=0 go build ...
时没有动态部分:
130 david@x1 /tmp (git)-[master] % readelf -d rtloggerd.cgo0
There is no dynamic section in this file.
libc
(在我的情况下为glibc
)提供的C函数中?我假设有一个本机系统调用接口。另一方面,我想在本机Go中重新实现整个stdlib会很困难。谢谢!
答案 0 :(得分:1)
在GNU / Linux上,几乎所有Go可执行文件都属于以下类别:
不幸的是,与Go相关的工具常常会使两者混为一谈。 glibc依赖性的主要原因是应用程序使用主机名和用户查找(诸如getaddrinfo
和getpwuid_r
之类的功能)。 CGO_ENABLED=0
从src/os/user/cgo_lookup_unix.go
(使用glibc)之类的实现切换到src/os/user/lookup_unix.go
(不使用glibc)。非glibc实现不使用NSS,因此提供的功能有些有限(通常不会影响不在LDAP / Active Directory中存储用户信息的用户)。
根据您的情况,设置CGO_ENABLED=0
会将您的应用程序从第三类移动到第二类。 (还有其他与Go相关的工具可以构建第一种应用程序。)非NSS查找代码不是很大,因此二进制大小的增加并不明显。由于Go运行时已经是静态链接的,因此静态链接减少的开销甚至有可能导致可执行文件大小的净减少。
这里要考虑的最重要的问题是,在glibc中,NSS,线程和静态链接并不是很好。所有Go程序都是多线程的,将glibc(静态)链接到Go程序的原因正是访问NSS函数。因此,针对glibc静态链接Go程序始终是错误的选择。它基本上是always buggy。即使Go程序不是多线程的,使用NSS函数的静态链接程序也需要在运行时使用与构建时使用的glibc完全相同的版本,因此此类应用程序减少可移植性。
所有这些都是为什么第一种Go应用程序如此糟糕的原因。使用CGO_ENABLED=0
生成静态链接的应用程序不会出现这些问题,因为那些(第二类)应用程序不包含任何glibc代码(以减少用户/主机查找功能的功能为代价)。
如果要创建需要glibc的可移植二进制文件,则需要在具有要支持的最旧glibc的系统上动态地链接应用程序(emem)(第三种)。然后,该应用程序将在该glibc版本和所有更高版本上运行(目前为Go does not link libc correctly,因此即使对于glibc,也没有强大的兼容性保证)。发行版通常与ABI兼容,但是它们具有glibc的不同版本。 glibc竭尽全力确保与旧版本glibc动态链接的应用程序将继续在新版本的glibc上运行,但是反之则不成立:一旦在某个版本的glibc上链接了应用程序,它可能会使用某些功能(符号)在较早版本上不可用,因此该应用程序将无法在那些较早版本上使用。