我试图编译一个开源项目的二进制文件,以便我们的用户不必自己编译它。
我注意到,在一台32位ubuntu机器“A”上创build的一些二进制文件在32位机器“B”上不起作用,并报告了丢失的.so文件的错误。
但是,如果我在机器“B”上从头开始编译,那么所有的错误都消失了。
为什么编译目标机器上的代码会使这些错误消失? 我只运行“./configure”和“make” – 而不是“make-install”,所以它不像我在全局提供这些.so文件。
难道是编译器检测到系统库中没有.so文件,在这种情况下,将静态库链接到可执行文件中?
Ubuntu如何编译它的包,以便在所有的x86机器上运行i386包?
我想这个问题被称为“二进制兼容性”(有一个堆栈溢出致力于这些问题的标签 )。 当你在一台机器上连接一个二进制文件时,周围的环境会影响二进制文件,并且在另一台机器上运行时,它仍然会尝试找到与编译的环境类似的环境。
在这种情况下对不同环境的容忍被称为二进制兼容性 。
它是如何工作的?
这里的关键是,即使你在不同的机器上为链接器指定了相同的选项,你仍然可能得到不同的二进制文件。 例如,如果您使用-lfoo
将您的二进制文件与共享库链接,则您在构建的计算机上使用的foo
的确切版本(例如libfoo.so.5
)将被硬编码到二进制文件中。 当它在机器B
上运行时,它可能只包含libfoo.so.4
,二进制将拒绝运行,因为它需要缺少libfoo.so.5
so文件。 这就是为什么重新编译有帮助,没有它,它不起作用。
Ubuntu软件包(以及其他任何发行版本)都是在相同的环境下编译的。 这就是为什么他们安装好。 分销商们注意到,每个下一个版本都与以前的版本向后兼容。
我该怎么办?
如果你想使你的软件与不同的发行版兼容,比你想象的要容易。 首先,尽可能以最老的分布来编译你的应用程序 。 正如我之前提到的那样,现代分布通常是向后兼容的,你的软意愿很可能会在没有问题的新发行版上运行。
为了更彻底地检查所得到的软件包并获得更多有关兼容性的建议,可以从我们的免费Linux应用程序检查工具中获得。 您可能也会对Linux软件打包的一般技巧感兴趣。
如果你在一台机器上编译代码,如果你在这台机器上执行这个程序,你很可能不会在丢失lib的时候出错。 在配置运行期间,检测到所有需要的库(这是配置,自动工具等存在的主要原因),并将合适的标志(如-lsomelib和-I / some / include / patch)写入makefile并传递给编译器和链接器。
我将该可执行文件复制到另一台机器,该机器可能具有错误版本的libs或根本不存在,因此可能无法运行。
配置脚本通常不会建立静态的二进制文件,除非你明确地告诉它这样做。
Ubuntu软件包不能在所有的x86机器上运行。 但是,软件包管理器会执行依赖性解决方案,以确保没有任何错误的库或库丢失,否则拒绝安装软件包。 如果你强制安装而不管缺少依赖关系,你可能会再次遇到缺少libs的问题。
如果你想确保你的软件包能够在任何机器上,只要把它连接到静态。
如果足够让它运行在特定的发行版(例如Ubuntu)上,则可以自己创建一个包。 这需要更多的努力。
您可以使用像Ermine这样的项目来创建包含共享库的动态链接本机二进制文件的分发。
除此之外,你可以静态编译你的代码。 这将需要您获取整个依赖关系树的源代码,编译它们,并在构建代码时引用这些已编译的二进制文件。 您无法针对其他共享库(.so文件)编译静态二进制文件。 这可能是一个真正的痛苦,尤其是如果你的依赖有自己的依赖。
您可以尝试使用相对路径作为库搜索路径(由DOS时间以标准方式提供),而不是容易出错的LD_LIBRARY_PATH
方法(这也需要用户交互)或静态链接(并非总是可行或被GPL禁止)。 在链接器中使用$ORIGIN
标志。
完整的方法在这个博客 ( 存档版本 )中很好地描述。