5

我试图编译一个开源项目的二进制文件,这样我们的用户就不必自己编译它。

我注意到在一台 32 位 ubuntu 机器“A”上创建的一些二进制文件在 32 位机器“B”上不起作用,并且报告了有关丢失 .so 文件的错误。

但是,如果我在机器“B”上从头开始编译,那么所有错误都消失了。

为什么在目标机器上编译代码会使这些错误消失?我只运行了“./configure”和“make”——而不是“make-install”,所以它不像我让这些 .so 文件在全球范围内可用。

是不是编译器检测到系统库中缺少 .so 文件,在这种情况下将静态库链接到可执行文件?

Ubuntu 如何编译它的包以便 i386 包在所有 x86 机器上运行?

4

4 回答 4

6

我猜这个问题被称为“二进制兼容性”(堆栈溢出上有一个专门针对这些问题的标签)。当您在机器上链接二进制文件时,周围的环境会影响二进制文件,并且在另一台机器上运行后,它仍然会尝试找到与编译它的环境相似的环境。

在这种情况下对不同环境的容忍称为二进制兼容性

它是如何工作的?

这里的关键点是,即使您在不同的机器上为链接器指定相同的选项,您仍然可能会得到不同的二进制文件。例如,如果您将二进制文件与使用 的共享库链接,则在您构建的机器上拥有-lfoo的确切版本foo(例如,libfoo.so.5)将硬编码到二进制文件中。当它在 machine 上运行时B,它可能只包含libfoo.so.4,并且二进制文件将拒绝运行,因为它需要丢失的libfoo.so.5so 文件。这就是为什么重新编译会有所帮助,没有它就行不通。

Ubuntu 包——以及任何其他发行版的包——都相同的环境中编译(彼此的)。这就是为什么他们安装得很好。分发供应商注意每个下一个版本都向后兼容以前的版本。

我该怎么办?

如果您想让您的软件与不同的发行版兼容,这比您想象的要容易。首先,尝试在最旧的发行版中编译您的应用程序。因为,正如我之前提到的,现代发行版通常是向后兼容的,所以你的软意志很可能在较新的发行版上运行而不会出现问题。

要更彻底地检查生成的软件包并获得有关兼容性的更多建议,您可以使用我们的免费Linux 应用程序检查器工具。您可能还对打包 Linux 软件的一般技巧感兴趣。

于 2011-02-10T22:04:19.550 回答
3

如果你在一台机器上编译代码,如果你在这台机器上执行程序,你很可能不会收到任何关于缺少库的错误。在配置运行期间,检测到所有需要的库(这是存在配置、自动工具等的主要原因)并且适当的标志,如 -lsomelib 和 -I/some/include/patch 被写入生成文件并传递给编译器和链接器.

我将该可执行文件复制到另一台机器上,该机器的库版本可能错误或根本丢失,因此它可能无法运行。

配置脚本通常不会构建静态二进制文件,除非您明确告诉它这样做。

Ubuntu 软件包不会在所有 x86 机器上运行。但是包管理器会进行依赖解析以确保没有错误的库或缺少库,否则会拒绝安装包。如果您强制安装而不考虑缺少依赖项,您可能会再次遇到缺少库的相同问题。

如果您想确保您的包能够在任何机器上运行,只需将其静态链接。

如果让它在特定发行版(例如 Ubuntu)上运行就足够了,您可以自己创建一个包。这需要更多的努力。

于 2011-02-10T21:42:49.257 回答
2

您可以使用像Ermine这样的项目来创建包含共享库的动态链接本机二进制文件的分发。

除此之外,您可以静态编译代码。这将要求您获取整个依赖关系树的源代码,编译它们,并在构建代码时引用那些已编译的二进制文件。您不能针对其他共享库(.so 文件)编译静态二进制文件。这可能是一个真正的痛苦,特别是如果您的依赖项有自己的依赖项。

于 2011-02-10T21:14:57.160 回答
0

除了容易出错的LD_LIBRARY_PATH方法(也需要用户交互)或静态链接(GPL 并不总是可能或禁止),您可以尝试使用相对路径作为库搜索路径(自 DOS 时代以来在 windows 中按标准提供)。将$ORIGIN标志与链接器一起使用。

这个博客存档版本)很好地描述了完整的方法。

于 2012-01-10T12:41:39.470 回答