The Mysteries of GLIBC...
As a Java developer I occasionally have to mess around with Linux C and C++ code. This post is aimed at helping fellow Java developers overcome some Linux backward compatibility issues and navigate the almighty power of GLIBC.
GLIBC is a library of functions used by many Linux packages. Each new release contains bug fixes and new features. While code compiled with an older GLIBC will work with a new GLIBC the inverse is not true. This is where the trouble starts....
GLIBC cannot be upgraded (or at least its not recommended) and asking customers who receive your code to upgrade GLIBC or the OS is a real hassle. So let's do a dive in to the version numbers...
To check your GLIBC version run one of the following commands. Notice they are from different OS version since 2.5 represents RHEL 5 and 2.12 represents RHEL 6. For a full history of versions click this link.
$ ldd --version ldd (GNU libc) **2.12** Copyright (C) 2010 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Written by Roland McGrath and Ulrich Drepper.
$ /lib/libc.so.6 GNU C Library stable release version **2.5**, by Roland McGrath et al. Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Compiled by GNU CC version 4.1.2 20080704 (Red Hat 4.1.2-50). Compiled on a Linux 2.6.9 system on 2012-01-19. Available extensions: The C stubs add-on version 2.1.2. crypt add-on version 2.1 by Michael Glad GNU Libidn by Simon Josefsson GNU libio by Per Bothner NIS(YP)/NIS+ NSS modules 0.19 by Thorsten Kukuk Native POSIX Threads Library by Ulrich Drepper et al BIND-8.2.3-T5B RT using linux kernel aio Thread-local storage support included. For bug reporting instructions, please see: http://www.gnu.org/software/libc/bugs.html.
Viewing your dependencies...
When building a C/C++ application on a RHEL6 box you are automatically binding to the GLIBC 2.12 library. However... this doesn't mean you app will fail to work on RHEL5. It very much depends on the functions you use. If you choose a function that's only in GLIBC 2.12 then you have automatically tied yourself to newer OS versions.
BUT WAIT... What if I didn't use a new function! I just have a plain old C program but it won't run. This is likely due to libraries you may link with that are also compiled on RHEL6.
To view your dependencies run the program
objdump. Objdump will show your overall dependencies and nm will show specific function dependencies. Both programs allow you to quickly home in on the function that will not run on older Linux servers (compare the GLIBC versions).
Once you find the offending function you can
grep -R for it in your libraries and binaries to figure out what third party code polluted your build!
$ objdump -p mybin mybin: file format elf64-x86-64 Program Header: : : Dynamic Section: NEEDED libocci.so.11.1 NEEDED libclntsh.so.11.1 NEEDED libnnz11.so NEEDED libstdc++.so.6 NEEDED libm.so.6 NEEDED libgcc_s.so.1 NEEDED libc.so.6 NEEDED libpthread.so.0 : : Version References: required from libgcc_s.so.1: 0x0b792650 0x00 07 GCC_3.0 required from libpthread.so.0: 0x09691a75 0x00 06 GLIBC_2.2.5 **HERE** required from libc.so.6: 0x0d696913 0x00 05 GLIBC_2.3 **HERE** 0x09691a75 0x00 04 GLIBC_2.2.5 **HERE** required from libstdc++.so.6: 0x08922974 0x00 03 GLIBCXX_3.4 **HERE** 0x056bafd3 0x00 02 CXXABI_1.3
$ nm mybin 00000000006124f0 V DW.ref._ZTIN6oracle4occi12SQLExceptionE 00000000006124f8 V DW.ref.__gxx_personality_v0 000000000040e575 t I_comp 0000000000612038 d _DYNAMIC 0000000000612290 d _GLOBAL_OFFSET_TABLE_ 00000000004032d0 t _GLOBAL__I_occi_db.cpp 0000000000401f90 t _GLOBAL__I_occi_port.cpp U _IO_putc@@GLIBC_2.2.5 **HERE** 000000000040f280 R _IO_stdin_used w _Jv_RegisterClasses0000000000405dfd t pvariable U read@@GLIBC_2.2.5 U realloc@@GLIBC_2.2.5 0000000000405d95 t release_chain U sprintf@@GLIBC_2.2.5 **HERE** U sscanf@@GLIBC_2.2.5 **HERE** U stderr@@GLIBC_2.2.5 **HERE** 0000000000405bae t strsave 000000000040d135 t to_float U tolower@@GLIBC_2.2.5 **HERE** 0000000000405d46 t undo_bindings 0000000000612754 b use_fallback.4930 U vfprintf@@GLIBC_2.2.5 **HERE** U write@@GLIBC_2.2.5 **HERE**
Solution for __isoc99_sscanf
__isoc99_sscanf caused me a lot of trouble. A library I was linking to was compiled with RHEL6 and GLIBC 2.12. I used objdump and nm (see above) to locate the offending function...
The GCC/G++ compiler I was using had a change in one of the header files regarding
scanf such that any builds now required
__isoc99_sscanf and GLIBC 2.12... locking me in to RHEL6 and breaking RHEL5 compatibility.
The solution is simple... define the following #define...
#define _GNU_SOURCE 1
or adjust your makefile CFLAGS/CPPFLAGS/CXXFLAGS...
This alters the behavior of the sscanf code block
stdio.h by setting
TRUE (an internal macro that is enabled when
_GNU_SOURCE is defined). The macro prevents the
__isoc99_sscanf redefinition and saves us from compatibility hell! :-)
#if defined __USE_ISOC99 && !defined __USE_GNU \ && (!defined __LDBL_COMPAT || !defined __REDIRECT) \ && (defined __STRICT_ANSI__ || defined __USE_XOPEN2K) : : /* For strict ISO C99 or POSIX compliance disallow %as, %aS and %a[ GNU extension which conflicts with valid %a followed by letter s, S or [. */ : : extern int __isoc99_sscanf (__const char *__restrict __s, __const char *__restrict __format, ...) __THROW; : : #define sscanf __isoc99_sscanf #endif