将构建环境升级到Ubuntu 14.4后,主可执行文件拒绝在具有较旧Linux版本的主机上启动,并显示以下消息:
/lib/i386-linux-gnu/libc.so.6:版本`GLIBC_2.16'未找到 (由./executable_name要求)
为了安全地将我的包分发给具有较旧 Glibc 的主机,我应该尝试:
注意: - 我不需要使用旧版Ubuntu中的任何二进制文件 - 我不需要在较旧的Ubuntu版本上构建
答案 0 :(得分:6)
经过长时间的调查,我终于找到了解决问题的方法。
首先,我查看了可执行文件依赖项:
ldd -v <executable_name>
我的构建是使用Cmake构建的,只有它的Release版本具有以下依赖项:
Version information:
./build/Release/products/<executable_name>:
libc.so.6 (GLIBC_2.16) => /lib/i386-linux-gnu/libc.so.6
在使用objdump分析此文件时,我检索到它需要 __ poll_chk 符号:
00000000 F *UND* 00000000 __poll_chk@@GLIBC_2.16
虽然Glibc使用所谓的 _symbol版本_ ,但这个特殊功能仅在Glibc 2.16中添加。
因此,我尝试调查导致Debug和Release版本之间差异的原因。
设置CMAKE_BUILD_TYPE时,Cmake定义了确定编译器标志的国内变量。对于GCC 4.8.4,它们是:
Glibc poll.h 包含 poll2.h ,其中包含棘手的 _poll_chk ,但在GLibc 2.16中仍然无法使用。这包括 _USE_FORTIFY_LEVEL 定义。
根据发布版本中的Linux手册页(请参阅下面的引文),由于 -O3 级别,我有 -D_FORTIFY_SOURCE = 2 。
man gcc
注意:在Ubuntu 8.10及更高版本中,-D_FORTIFY_SOURCE = 2由 默认,并在-O设置为2或更高时激活。这个 为多个libc启用额外的编译时和运行时检查 功能。要禁用,请指定-U_FORTIFY_SOURCE或 -D_FORTIFY_SOURCE = 0。
man feature_test_macros
_FORTIFY_SOURCE(自glibc 2.3.4)定义此宏会导致执行一些轻量级检查以检测某些缓冲区溢出 采用各种字符串和内存操作时出错 功能。并非所有缓冲区溢出都被检测到,只有一些 常见病例。在当前实现中,添加了检查 调用memcpy(3),mempcpy(3),memmove(3),memset(3),stpcpy(3), strcpy(3),strncpy(3),strcat(3),strncat(3),sprintf(3),snprintf(3), vsprintf(3),vsnprintf(3),得到(3)。如果_FORTIFY_SOURCE设置为 1,用编译器优化级别1(gcc -O1)及以上,检查一下 不应该改变符合程序的行为。 将_FORTIFY_SOURCE设置为2时,会添加更多检查,但有些检查 合规程序可能会失败。可以执行一些检查 在编译时,导致编译器警告;其他检查 在运行时放置,如果检查失败,则会导致运行时错误。 使用此宏需要编译器支持,可与gcc(1)一起使用 从版本4.0开始。
或只是使用
man -K _FORTIFY_SOURCE
我检查了我的可执行文件包含的每个静态库,其代码使用了 poll 函数,最终找到了它:
objdump -t lib.a | grep poll
00000000 *UND* 00000000 Curl_poll
00000000 l d .text.Curl_poll 00000000 .text.Curl_poll
00000000 *UND* 00000000 poll
00000000 *UND* 00000000 __poll_chk
00000000 g F .text.Curl_poll 0000025c Curl_poll
可以通过将 -U_FORTIFY_SOURCE 添加到目标CmakeLists.txt中的编译器标志来禁用此优化。这消除了任何最近检测到的GLIBC2.16依赖性:
Version information:
products/<executable>:
ld-linux.so.2 (GLIBC_2.3) => /lib/ld-linux.so.2
librt.so.1 (GLIBC_2.2) => /lib/i386-linux-gnu/librt.so.1
libdl.so.2 (GLIBC_2.0) => /lib/i386-linux-gnu/libdl.so.2
libdl.so.2 (GLIBC_2.1) => /lib/i386-linux-gnu/libdl.so.2
libpthread.so.0 (GLIBC_2.2) => /lib/i386-linux-gnu/libpthread.so.0
libpthread.so.0 (GLIBC_2.3.2) => /lib/i386-linux-gnu/libpthread.so.0
libpthread.so.0 (GLIBC_2.1) => /lib/i386-linux-gnu/libpthread.so.0
libpthread.so.0 (GLIBC_2.0) => /lib/i386-linux-gnu/libpthread.so.0
libpulse.so.0 (PULSE_0) => /usr/lib/i386-linux-gnu/libpulse.so.0
libsndfile.so.1 (libsndfile.so.1.0) => /usr/lib/i386-linux-gnu/libsndfile.so.1
libc.so.6 (GLIBC_2.15) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.11) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.1.3) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.2.4) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.4) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.1) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.3) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.2) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.7) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.0) => /lib/i386-linux-gnu/libc.so.6
libc.so.6 (GLIBC_2.3.4) => /lib/i386-linux-gnu/libc.so.6
我唯一不能理解的是为什么我在其他调用GLibc民意调查的静态库中也没有这样的问题?
为了使情况清楚,我使用了特殊的GCC标志来显示预处理器输出:
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -E")
.c.o 包括 poll.h 和 poll2.h ,其内容如下:
# 1 "/usr/include/i386-linux-gnu/bits/poll2.h" 1 3 4
# 24 "/usr/include/i386-linux-gnu/bits/poll2.h" 3 4
extern int __poll_alias (struct pollfd *__fds, nfds_t __nfds, int __timeout) __asm__ ("" "poll")
;
extern int __poll_chk (struct pollfd *__fds, nfds_t __nfds, int __timeout,
unsigned int __fdslen);
extern int __poll_chk_warn (struct pollfd *__fds, nfds_t __nfds, int __timeout, unsigned int __fdslen) __asm__ ("" "__poll_chk")
__attribute__((__warning__ ("poll called with fds buffer too small file nfds entries")));
extern __inline __attribute__ ((__always_inline__)) __attribute__ ((__gnu_inline__)) __attribute__ ((__artificial__)) int
poll (struct pollfd *__fds, nfds_t __nfds, int __timeout)
{
if (__builtin_object_size (__fds, 2 > 1) != (unsigned int) -1)
{
if (! __builtin_constant_p (__nfds))
return __poll_chk (__fds, __nfds, __timeout, __builtin_object_size (__fds, 2 > 1));
else if (__builtin_object_size (__fds, 2 > 1) / sizeof (*__fds) < __nfds)
return __poll_chk_warn (__fds, __nfds, __timeout, __builtin_object_size (__fds, 2 > 1));
}
return __poll_alias (__fds, __nfds, __timeout);
}
但库对象文件仍然只有上层 poll 符号:
objdump -t lib.a | grep poll
00000000 UND 00000000 poll
到目前为止,我无法解释为什么棘手的 __ poll_chk 符号未添加到其他库中。但现在我的二进制文件在任何目标Linux主机上运行。