"将构建环境升级到Ubuntu 14.4后,未找到"版本`GLIBC_2.16'目标主机错误

kem*_*ono 3 glibc build

将构建环境升级到Ubuntu 14.4后,主可执行文件拒绝在具有较旧Linux版本的主机上启动,并显示以下消息:

/lib/i386-linux-gnu/libc.so.6:找不到版本`GLIBC_2.16'(./executable_name要求)

为了安全地将我的包分发给具有旧Glibc的主机,我应该尝试:

  • 静态链接libc?
  • 降级构建机器上的libc?
  • 安装旧的glibc和一个Ubuntu分配去?用?

注意: - 我不需要使用旧版Ubuntu中的任何二进制文件 - 我不需要在较旧的Ubuntu版本上构建

kem*_*ono 6

经过长时间的调查,我终于找到了解决问题的办法

首先,我查看了可执行文件依赖项:

ldd -v <executable_name>
Run Code Online (Sandbox Code Playgroud)

我的构建是使用Cmake构建的,只有它的Release版本具有以下依赖关系:

Version information: 
    ./build/Release/products/<executable_name>: 
    libc.so.6 (GLIBC_2.16) => /lib/i386-linux-gnu/libc.so.6
Run Code Online (Sandbox Code Playgroud)

在使用objdump分析此文件时,我检索到它需要__poll_chk符号:

00000000       F *UND*  00000000              __poll_chk@@GLIBC_2.16
Run Code Online (Sandbox Code Playgroud)

虽然Glibc使用所谓的_symbol版本_,但这个特殊功能仅在Glibc 2.16中添加.

因此,我尝试调查导致Debug和Release版本之间差异的原因.

CMAKE_BUILD_TYPE设置,CMake的定义国内变量,它决定编译器标志.对于GCC 4.8.4,它们是:

  • 调试:-g
  • 发布:-NDEBUG -O3

glibc poll.h包含poll2.h,其中包含棘手的_poll_chk但在GLibc 2.16中不可用.这包括在_USE_FORTIFY_LEVEL定义下.

根据发布版本中的Linux手册页(请参阅下面的引用),由于-O3级别,我有-D_FORTIFY_SOURCE = 2.

man gcc
Run Code Online (Sandbox Code Playgroud)

注意:在Ubuntu 8.10及更高版本中,默认设置-D_FORTIFY_SOURCE = 2,并在-O设置为2或更高时激活.这样可以对多个libc函数进行额外的编译时和运行时检查.要禁用,请指定-U_FORTIFY_SOURCE或-D_FORTIFY_SOURCE = 0.

man feature_test_macros
Run Code Online (Sandbox Code Playgroud)

_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)和gets(3).如果_FORTIFY_SOURCE设置为1,并且编译器优化级别1(gcc -O1)及更高级别,则执行不应更改符合程序行为的检查.将_FORTIFY_SOURCE设置为2时,会添加更多检查,但某些符合规定的程序可能会失败.某些检查可以在编译时执行,并导致编译器警告; 其他检查在运行时进行,如果检查失败则导致运行时错误.使用此宏需要编译器支持,自4.0版以来可以使用gcc(1).

或者只是使用

man -K _FORTIFY_SOURCE
Run Code Online (Sandbox Code Playgroud)

我检查了我的可执行文件包含的每个静态库,其代码使用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
Run Code Online (Sandbox Code Playgroud)

可以通过将-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
Run Code Online (Sandbox Code Playgroud)

我唯一不能得到的是为什么我在其他调用GLibc轮询的静态库中也没有这样的问题?

为了使情况清楚,我使用特殊的GCC标志来显示预处理器输出:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -E")
Run Code Online (Sandbox Code Playgroud)

.co包括poll.hpoll2.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); 
}
Run Code Online (Sandbox Code Playgroud)

但库对象文件仍然只有上轮询符号:

objdump -t  lib.a | grep poll 
00000000 UND    00000000 poll
Run Code Online (Sandbox Code Playgroud)

到目前为止,我无法解释为什么棘手的__poll_chk符号没有添加到其他库中.但现在我的二进制文件在任何目标Linux主机上运行.