【问题标题】:Static and dynamic linking w.r.t. portability, in the context of Go [closed]静态和动态链接 w.r.t.可移植性,在 Go 的上下文中 [关闭]
【发布时间】:2019-06-12 14:46:21
【问题描述】:

让我首先将一些事实放在桌面上进行事实检查,以免造成混淆:

  • 具有动态部分的 ELF 二进制文件将在编译时包含一些未解析的符号。解析将在二进制文件执行期间的某个时间由链接器执行。
  • 动态链接有利有弊。但是,如果系统上不存在二进制文件所需的目标库(在所需版本中),则二进制文件将不会运行。
  • 静态链接缓解了这个问题,但在较低层引入了一个新问题。通过静态链接二进制文件,库的可执行代码嵌入到您的二进制文件中,因此二进制库接口不再存在问题。但是,现在库-OS 界面可能会出现问题。那是对的吗?这里可能会出现什么问题?

现在让我们在 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.
  • 这是否意味着库是静态链接的?我猜是这样,但在我的机器上大小差异可以忽略不计(大约 72 kB),这让我很吃惊。
  • 关于跨 Linux 系统的可移植性,哪一个更可取?为什么?
  • Go 的标准库是如何开展业务的?实际上是在调用libc(在我的情况下为glibc)提供的C函数吗?我假设有一个本机系统调用接口。另一方面,我认为在原生 Go 中重新实现整个 stdlib 会很困难。
  • 最后,我听说“不能保证不同的发行版,甚至同一个发行版的不同版本,都兼容 ABI”。这是真的?我假设 ABI 主要是二进制可执行格式(Linux 上的 ELF 很长一段时间),所以我认为这里没有问题。这意味着什么?

谢谢!

【问题讨论】:

  • 1.是的。 2.没关系。 3.查看来源。 4号。???
  • 对于 1,当您选择不使用某些 libc 函数时,为什么大小会有所不同?是的,如果您完全链接到不同的 libc,可能会存在兼容性问题,但这是一个足够具体的情况,您应该知道自己在做什么(或者只是不使用 CGO)。
  • 请编辑问题以将其限制为具有足够详细信息的特定问题,以确定适当的答案。避免一次问多个不同的问题。
  • @Adrian 恐怕没有其他方法可以问这个问题,因为我不知道要问的确切问题。这是由于问题的性质,它涉及多个不同的主题(Go、Linux 上的 ELF 二进制文件、一般的动态链接)。但是,我已经从 Florian 那里得到了一个高质量的答案,这正是我所希望的,所以我想它可以理解:)。

标签: linux go linker glibc elf


【解决方案1】:

在 GNU/Linux 上,几乎所有 Go 可执行文件都属于以下类别:

  1. 包括应用程序、Go 运行时和 glibc(部分)的静态链接副本。
  2. 仅包含应用程序和 Go 运行时、静态链接且不包含 glibc 的那些。
  3. 仅包括应用程序和 Go 运行时、静态链接和动态链接到 glibc 的链接。

不幸的是,Go 相关工具经常将这些链接模式混为一谈。 glibc 依赖的主要原因是应用程序使用主机名和用户查找(函数如getaddrinfogetpwuid_r)。 CGO_ENABLED=0src/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 运行时已经静态链接,因此静态链接减少的开销甚至可能导致可执行文件大小的净减少。

这里要考虑的最重要的问题是 NSS、线程和静态链接在 glibc 中并不是那么好。所有 Go 程序都是多线程的,将 glibc (静态)链接到 Go 程序的原因正是对 NSS 函数的访问。因此,静态链接 Go 程序与 glibc 始终是错误的做法。基本上是alwaysbuggy。即使 Go 程序不是多线程的,使用 NSS 函数的静态链接程序在运行时也需要与构建时使用的 glibc完全相同的版本,因此此类应用程序的静态链接降低便携性。

所有这些都是为什么第一类 Go 应用程序如此糟糕的原因。使用CGO_ENABLED=0 生成静态链接的应用程序不存在这些问题,因为这些应用程序(第二类)不包含任何 glibc 代码(以减少用户/主机查找函数的功能为代价)。

如果您想创建一个需要 glibc 的可移植二进制文件,您需要在您希望支持的最旧 glibc 的系统上动态地(第三种)链接您的应用程序。然后应用程序将在该 glibc 版本和所有更高版本上运行(目前,Go does not link libc correctly,因此即使 glibc 也没有强大的兼容性保证)。发行版通常与 ABI 兼容,但它们具有不同版本的 glibc。 glibc 竭尽全力确保与旧版本的 glibc 动态链接的应用程序将继续在新版本的 glibc 上运行,但反之则不然:一旦您在某个 glibc 版本上链接应用程序,它可能会获得特性(符号)在旧版本上不可用,因此该应用程序不适用于这些旧版本。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-25
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    相关资源
    最近更新 更多