kemono
kemono

Reputation: 103

“Version `GLIBC_2.16' not found” target host error after upgrading build environment to Ubuntu 14.4

After upgrading build environment to Ubuntu 14.4, main executable refuses to start on hosts with older Linux version with the following message:

/lib/i386-linux-gnu/libc.so.6: version `GLIBC_2.16' not found (required by ./executable_name)

In order to safely distribute my package to hosts, which have older Glibc, should I try:

Note: - I don't need to use any binaries from older Ubuntu versions - I don't need to build on older Ubuntu version

Upvotes: 3

Views: 3072

Answers (1)

kemono
kemono

Reputation: 103

After long investigation I finally found solution for the issue.

First of all I looked at the executable file dependencies:

ldd -v <executable_name>

My build is built with Cmake and only its Release version has the following dependency:

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

On analyzing this file with objdump, I retrieved that it needs __poll_chk symbol:

00000000       F *UND*  00000000              __poll_chk@@GLIBC_2.16

Though Glibc uses so called _symbol versioning _, this particular function was added only in Glibc 2.16.

Therefore I tried investigating what caused the difference between Debug and Release builds.

When CMAKE_BUILD_TYPE is set, Cmake defines domestic variables, which determine compiler flags. For GCC 4.8.4 they are:

  • Debug: -g
  • Release: -NDEBUG -O3

Glibc poll.h includes poll2.h, which contains tricky _poll_chk yet unavailable in GLibc 2.16. This include goes under _USE_FORTIFY_LEVEL define.

And according to Linux man pages (see quotes below) in Release build I have -D_FORTIFY_SOURCE=2 due to -O3 level.

man gcc

NOTE: In Ubuntu 8.10 and later versions, -D_FORTIFY_SOURCE=2 is set by default, and is activated when -O is set to 2 or higher. This enables additional compile-time and run-time checks for several libc functions. To disable, specify either -U_FORTIFY_SOURCE or -D_FORTIFY_SOURCE=0.

man feature_test_macros

_FORTIFY_SOURCE (since glibc 2.3.4) Defining this macro causes some lightweight checks to be performed to detect some buffer overflow errors when employing various string and memory manipulation functions. Not all buffer overflows are detected, just some common cases. In the current implementation, checks are added for calls to 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), and gets(3). If _FORTIFY_SOURCE is set to 1, with compiler optimization level 1 (gcc -O1) and above, checks that shouldn't change the behavior of conforming programs are performed. With _FORTIFY_SOURCE set to 2 some more checking is added, but some conforming programs might fail. Some of the checks can be performed at compile time, and result in compiler warnings; other checks take place at run time, and result in a run-time error if the check fails. Use of this macro requires compiler support, available with gcc(1) since version 4.0.

or just use

man -K _FORTIFY_SOURCE

I checked every static library my executable includes, whose code uses poll function and ultimately had found it:

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

This optimization may be disabled by adding -U_FORTIFY_SOURCE to the compiler flags within target CmakeLists.txt. This eliminate any lately detected GLIBC2.16 dependencies:

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

The only thing I can't get is why I donn't have such problems in other static libraries which invokes GLibc poll as well?

To make the situation clear I used special GCC flag to display preprocessor output:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -E")

.c.o includes poll.h and poll2.h and its content goes as following:

# 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); 
}

But library object file still has only upper poll symbol:

objdump -t  lib.a | grep poll 
00000000 UND    00000000 poll

So far I can't explain why tricky __poll_chk symbol is not added to the other libraries. But now my binary runs on any of target Linux hosts.

Upvotes: 6

Related Questions