【问题标题】:Getting executables to run on multiple Linux OS's:让可执行文件在多个 Linux 操作系统上运行:
【发布时间】:2015-06-14 23:56:10
【问题描述】:

我正在尝试编译一个非常基本的程序并在多个操作系统上运行。该程序只是尝试使用boost::filesystem 打印它的文件名以进行流式传输,以便我可以验证加载.so 是否按预期工作。

我在 Ubuntu 机器上编译它:

$ uname -a
Linux ubuntu 3.13.0-48-generic #80-Ubuntu SMP Thu Mar 12 11:16:15 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

我有一个 CentOS 机器,我尝试在其上运行它:

$ uname -a
Linux localhost.localdomain 3.10.0-123.20.1.el7.x86_64 #1 SMP Thu Jan 29 18:05:33 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

我使用$ORIGIN 编译可执行文件,以便从我的目录中获取链接的boost 库,然后我将ldd boost 库和cp 放入相同的库中。因此,lib 目录如下所示:

deliverable/
deliverable/hello
deliverable/.lib/
deliverable/.lib/libc.so.6
deliverable/.lib/libboost_system.so.1.58.0
deliverable/.lib/libpthread.so.0
deliverable/.lib/libm.so.6
deliverable/.lib/libstdc++.so.6
deliverable/.lib/libboost_filesystem.so.1.58.0
deliverable/.lib/libboost_filesystem.so
deliverable/.lib/libfoo.so
deliverable/.lib/libboost_system.so
deliverable/.lib/libgcc_s.so.1

hello 是我要运行的可执行文件。但是,在 CentOs 盒子上,我收到以下错误:

$ ./hello
$ ./hello: relocation error: ~/deliverable/.lib/libc.so.6: symbol _dl_find_dso_for_object, version GLIBC_PRIVATE not defined in file ld-linux-x86-64.so.2 with link time reference

如何解决这个问题?我还想知道这种模式是否违反了在 Linux 机器之间传送编译代码的最佳实践。 . .

更多相关信息:

$ cat Makefile 
CXX = g++
CPPFLAGS := -Wall -g -Wfatal-errors -std=c++11 -I./inc -fPIC
DELIVERABLE = $(CURDIR)/deliverable
LIB = $(DELIVERABLE)/.lib

all: $(DELIVERABLE)/hello

$(DELIVERABLE)/hello: main.o $(LIB)/libfoo.so
    $(CXX) -L./deliverable/.lib -Wl,--allow-shlib-undefined -Wl,-rpath='$$ORIGIN/.lib' -o $@ $< -lfoo

main.o: main.cc
    $(CXX) $(CPPFLAGS) -c $< -o $@

$(LIB)/libfoo.so: foo.o
    $(CXX) -L./deliverable/.lib -Wl,--allow-shlib-undefined -Wl,-rpath='$$ORIGIN/.lib' -shared -o $@ $^ -lboost_system -lboost_filesystem

foo.o: foo.cc
    $(CXX) $(CPPFLAGS) -c $< -o $@

clean:
    rm -f *.o $(LIB)/libfoo.so $(DELIVERABLE)/hello

$ cat main.cc 
#include "foo.hh"

int main()
{
    hello();
}
$ cat foo.hh 
#ifndef FOO_HH
#define FOO_HH

void hello();

#endif
$ cat foo.cc
#include "foo.hh"
#include <boost/filesystem.hpp>
#include <iostream>

void hello()
{
    boost::filesystem::path p{__FILE__};
    std::cout << "p.parent_path() " << p.parent_path() << '\n';
    std::cout << "p.string()      " << p.string() << '\n';
    std::cout << "__FILE__        " << __FILE__ << '\n';
}

我也在一个 RHEL 盒子上试过这个,结果更糟:

$ uname -a
Linux localhost.localdomain 2.6.18-164.6.1.el5 #1 SMP Tue Nov 3 ... EXT 2009 x86_64 x86_64 GNU/Linux

在这台机器上运行它崩溃了:

$./hello
./hello: error while loading shared libraries: ~/deliverable/.lib/libm.so.6: unexpected PLT reloc type 0x25

【问题讨论】:

  • 是否有理由将 libc、libstdc++、libm、libgcc 等添加到可交付成果目录中?据我所知,您的应用程序只需要libfoolibboost_system。请参阅此thread about how to deploy a custom libc。在我看来,您的问题是您的链接器和动态加载器无法正确理解彼此(重定位错误、PLT 错误等)。
  • 我只部署了libfoolibboost_system,然后在另一台机器上没有找到libstdc++等。 (他们在那里,但版本错误。)
  • 啊,好吧。如果您没有部署额外的库,您是否也可以发布您收到的错误消息
  • @Nick 如果您决定真正部署包括 libc 在内的所有共享库依赖项,您还必须指定一个自定义加载程序。见this thread。为了让应用程序在史前遗留系统上运行,我尝试过一次......

标签: c++ linux ubuntu centos


【解决方案1】:

我想,在系统安装了额外的.so 文件后,您必须“告诉”它们在那里?

ldconfig 是我想到的命令:

ldconfig creates the necessary links and cache (for use by the run-time linker, ld.so) to the most recent shared libraries found in the directories specified on the command line, in the file /etc/ld.so.conf, and in the trusted directories (/usr/lib and /lib). ldconfig checks the header and file names of the libraries it encounters when determining which versions should have their links updated. ldconfig ignores symbolic links when scanning for libraries.

http://linux.about.com/od/commands/l/blcmdl8_ldconfi.htm

【讨论】:

  • 使用 '$ORIGIN' 变量可以解决这个问题。此外,strace ./hello 表明正在加载正确的库。
【解决方案2】:

我猜这是 Linux 发行版之间细微差别的典型案例:

众所周知,基于 Debian 的 Ubuntu 和基于 RedHat 的 CentOS 使用不同的位置来存储系统库,而且 - 更糟糕的是 - 还使用不同的命名方案。

如果发行版的年龄差异很大(新的 Ubuntu 版本与旧的 CentOS 版本,反之亦然),则可能会出现更多复杂情况。

您在 Ubuntu 机器上编译了所有库。运行 ldd ~/deliverable/.lib/libc.so.6 将显示完整的依赖项列表,即找到所有依赖项。

我几乎可以肯定,在您的 CentOS 机器上运行相同的命令会显示一个不完整的列表(找不到一个或多个依赖项)。这很可能是由于上述 Linux 发行版之间的差异。

从我的脑海中,我看到了以下处理这种情况的方法:

  1. 在您的 CentOS 机器上,使用与您的 Ubuntu 机器上的 ldd 显示的相同绝对路径,识别缺失依赖项的特定于发行版的位置和名称,并 sym-link 它们。这是一个快速而肮脏的解决方案,它可能会或(相当)可能不起作用。我会建议不要这样做

  2. 为特定的目标平台编译deliverable/.lib/* 中的所有库并相应地链接它们。这也将消除发送libstd++ 的必要性,因为它将链接到正确的。

【讨论】:

  • 刚刚在 CentOS 上运行了ldd。找到所有依赖项。
  • @nils 它始终是同一个平台,即 x64。您如何/为什么要为 CentOS 或 Ubuntu 进行不同的编译?
  • @djf 我隐含地假设所有 Linux 操作系统的硬件都相同,所以 x64-or-not 不是问题。当前部署假定 Linux 发行版之间的 ABI 兼容性,这是无法保证的。为每个 Linux 发行版单独构建应用程序/库可确保该特定发行版的符号正确链接和名称修改。
  • @NickThompson 好的,这排除了这个问题。你能检查你的g++ 的版本吗?如果它们差异太大,它可以解释 CentOS 上缺少的符号。
  • @nils 你说得对。我天真地认为发行版是兼容的:)
猜你喜欢
  • 1970-01-01
  • 2016-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-10
  • 2019-12-05
  • 1970-01-01
相关资源
最近更新 更多