使用-static-libgcc -static-libstdc++进行编译仍然会导致对libc.so的动态依赖性

30

我试图制作一个尽可能便携的可执行文件。在删除了一些依赖项后,当在另一个系统上运行二进制文件时,我遇到了以下情况:

/lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.15' not found (required by foob)
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.15' not found (required by foob)
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.14' not found (required by foob)

我希望我的二进制文件不需要用户升级他们的libc版本,因此我想要移除这个依赖关系。

产生上述二进制文件的链接器标志已经包括了-static-libgcc -static-libstdc++。为什么该二进制文件仍然依赖共享库libc.so.6

我尝试添加-static标志,但是当我尝试运行那个二进制文件时,结果非常奇怪:

$ ls -l foob
-rwxr-xr-x 1 claudiu claudiu 13278191 Oct 10 13:03 foob
$ ./foob
bash: ./foob: No such file or directory

该怎么办?

编辑:

$ file foob
foob: ELF 64-bit LSB  executable, x86-64, version 1 (GNU/Linux), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=5adee9a598b9261a29f1c7b0ffdadcfc72197cd7, not stripped
$ strace -f ./foob
execve("./foob", ["./foob"], [/* 64 vars */]) = -1 ENOENT (No such file or directory)
write(2, "strace: exec: No such file or di"..., 40strace: exec: No such file or directory
) = 40
exit_group(1)                           = ?
+++ exited with 1 +++

有趣的是,如果我运行ldd命令在没有使用-static参数的版本上,则条目比使用-static参数的版本要少两个,具体如下:

libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f4f420c1000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f4f41636000)

你能展示一下针对那个静态二进制文件的 strace -f -v 命令吗?关于 "file foob" 的信息呢?你可能需要包括你所使用的具体源和目标(Linux?)发行版以及架构。 - ggiroux
1
我刚刚使用 仅有的 -static 标志编译了一个相对简单的程序:"gcc -o ts --std=c99 --static test.c"。运行良好。 - Hack Saw
运行 strace -f ./foob 命令,这样我们就可以看到 "No such file or directory" 的具体信息了。 - nos
1
@ChrisStratton,我在多个Linux系统上使用“-static”从未遇到过问题。我可能需要安装一些静态库,但仅此而已。 - Z boson
你使用的操作系统和版本是什么?Ubuntu 14.04?Redhat?你使用的是哪种libc,它的版本是多少?我正在使用Ubuntu 14.04和Eglibc 2.19,静态链接对我来说很好用。 - Z boson
显示剩余6条评论
2个回答

38

GNU libc不适合静态链接。一些重要的函数,例如gethostbynameiconv在静态二进制文件中可能会出现故障或完全无法工作。更糟糕的是,在某些条件下,静态二进制文件将尝试动态打开并使用libc.so.6,即使静态链接的整个目的就是避免这样的依赖。

您应该选择对uClibcmusl libc进行编译。

(至少已经如此15年了。)


嗨@zwol,你能帮我理解为什么最好链接到uClibc或musl libc吗?如果一个人的目标是静态链接libc以实现自定义libc(例如内存分配行为)用于共享库但不是目标应用程序,那么他们是否仍然会遵循这个建议?请参见https://dev59.com/XJPfa4cB1Zd3GeqPEIXB。 - sholsapp
@sholsapp 这取决于所使用的共享库使用了哪些C库函数。如果不知道你的共享库做了什么,需要从C库中获取什么以及为什么要这样做,我就无法给出更具体的答案。 - zwol
我刚刚尝试了以下命令,其警告消息确认: gcc-4.8 --static ... netcat.c警告:在静态链接应用程序中使用“gethostbyaddr”需要在运行时从用于链接的glibc版本获取共享库。 - zhaorufei

1

首先要知道,libc的静态链接可能不会提高程序的可移植性,因为libc可能依赖于系统的其他部分,例如内核版本。

如果您想尝试完全静态链接,只需使用-static即可。前提是所有使用的库都有静态版本已安装。

您可以使用以下命令检查程序是否仅链接了静态库:

ldd binary_name

编辑:

提供有用信息以调试此问题的另一种方式是在链接器标志中添加--verbose。


那个最后的错误真的很奇怪。-static确实做了他想做的事情。而-static-libgcc -static-libstdc++可以省略,但这不应该改变任何东西。 - crash
静态链接在典型的现代桌面Linux上并不完全可行,这一点是众所周知的。 - Chris Stratton
4
只有在使用glibc时才会如此。 - Janus Troelsen
当然,那是海报正在使用的。在现代桌面Linux上,还有什么其他东西可以使用呢? - Chris Stratton
1
现在的Musl很流行,比如Chris Stratton。Voidlinux使用它。这很有趣 - 我认为它在功能方面不具备竞争力,但我认为更多的竞争是件好事。还要注意,“部分”静态链接是可以的;我保留了一个busybox的静态版本,但你可以静态编译make、sed、grep、coreutils等等,这些都可以工作。有些人甚至用GCC做到了;我还没有。如果你设法破坏了一个库(而且有一些意外的方法可以做到这一点),它可能会很有用。 - shevy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接