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.
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 nm
or 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**
The function __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... __isoc99_sscanf
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...
-D_GNU_SOURCE
This alters the behavior of the sscanf code block stdio.h
by setting __USE_GNU
to 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