一个32位版本的应用程序,建立在CentOS 6 x64上,当在较新的Linux上启动时,在"dl_itera

A 32bit version of an application, built on CentOS 6 x64, crashes during exceptions in `dl_iterate_phdr`, when started on newer Linux

本文关键字:启动 Linux quot itera dl 32位 建立 版本 CentOS 一个 x64      更新时间:2023-10-16

TL;DR我的研究表明,如果我在较旧的Linux上构建一个应用程序(及其依赖项(,比如CentOS 6(带有GLIBC 2.12(,那么它应该在任何其他具有较新GLIBC的Linux发行版上都能很好地工作。这个假设是正确的吗?


对不起,这将是一篇很长的帖子,但这不是一个微不足道的问题。

这是构建机器:

$ rpm -q centos-release
centos-release-6-10.el6.centos.12.3.x86_64
$ ldd --version
ldd (GNU libc) 2.12
$ ld -v
GNU ld version 2.30-54.el6
$ gcc --version
gcc (GCC) 8.3.1 20190311 (Red Hat 8.3.1-3)
$ g++ --version
g++ (GCC) 8.3.1 20190311 (Red Hat 8.3.1-3)

应用程序几乎与所有静态链接,因此:

$ ldd ./app 
linux-gate.so.1 (0xf7f73000)
libdl.so.2 => /lib32/libdl.so.2 (0xf7f4c000)
librt.so.1 => /lib32/librt.so.1 (0xf7f41000)
libstdc++.so.6 => ./libstdc++.so.6 (0xf7e22000)
libm.so.6 => /lib32/libm.so.6 (0xf7d53000)
libgcc_s.so.1 => ./libgcc_s.so.1 (0xf7d35000)
libpthread.so.0 => /lib32/libpthread.so.0 (0xf7d14000)
libc.so.6 => /lib32/libc.so.6 (0xf7b36000)
/lib/ld-linux.so.2 (0xf7f75000)

因此:

  • linux-gateld-linux并不完全是库,因此它们无法部署
  • libdllibrtlibmlibpthreadlibcGLIBC的一部分,因此不应该同时部署它们(特别是考虑到GNU C库的向后兼容性(
  • libstdc++libgcc_s与二进制文件一起"部署"(取自构建机器(

测试机器:

$ lsb_release -a
Description:  Debian GNU/Linux 10 (buster)
$ ldd --version
ldd (Debian GLIBC 2.28-10) 2.28

64位构建(带有libstdc++libgcc_s(似乎运行得非常好。

问题在于32位构建-当抛出异常时(不是所有异常,但似乎是一致的(,它会崩溃。以下是堆栈跟踪的有趣之处:

SIGSEGV at  0# __kernel_sigreturn in linux-gate.so.1
1# dl_iterate_phdr in /lib32/libc.so.6
2# _Unwind_Find_FDE in ./libgcc_s.so.1
3# 0xF7D88AAE in ./libgcc_s.so.1
4# 0xF7D89227 in ./libgcc_s.so.1
5# _Unwind_RaiseException in ./libgcc_s.so.1
6# __cxa_throw in ./libstdc++.so.6
7# <some funct> in ./app
7# <some funct> in ./app
7# <some funct> in ./app
7# <some funct> in ./app
7# <some funct> in ./app
7# <some funct> in ./app
13# make_fcontext in ./app

我可以在这里看到两种可能的选择:

  1. 32位构建有一些问题(尽管它毫无例外地工作(
  2. 我对这件事确实有很大的误解

有什么想法吗?

我怀疑,如果您将GCC库(g++/GCC(从构建机器"部署"到具有不同操作系统或操作系统版本的目标机器,那么您的应用程序(或其依赖项之一(实际上可以使用目标系统中的GCC库,而其他依赖项则使用您部署的库,从而导致潜在的不兼容。

在目标框上构建应用程序,或者使用交叉编译来精确指定目标框的操作系统和版本,会安全得多。

此外,使用系统库的动态链接更安全,因为否则您可能会在一个应用程序中混合静态和动态库,这可能会导致问题(例如,gcc-lib中某个全局var的两个实例(。

您需要检查整个库依赖关系树,以确保每个库只有一个实例从同一位置加载。例如,您的应用程序依赖于./libgcc_s.so.1./libstdc++.so.6,但./libstdc++.so.6可能依赖于系统的libgcc_s.so

你可以在互联网上搜索这些和其他在非目标框上构建应用程序的陷阱。例如:

因此,仅仅在较旧的发行版上编译应用程序是不够的。