Skip to content

Instantly share code, notes, and snippets.

@DanielGibson
Last active April 3, 2022 19:44
Show Gist options
  • Save DanielGibson/be53dd2c6da6b4637b084b1998c12874 to your computer and use it in GitHub Desktop.
Save DanielGibson/be53dd2c6da6b4637b084b1998c12874 to your computer and use it in GitHub Desktop.
Find out which version of libstdc++.so.6, libgcc_s.so.1 and libSDL2-2.0.so.0 is installed on a (x86 or x86_64) Linux system
/*
* Try to find out the libstdc++.so.6 version on the (x86 or x86_64) Linux
* system this is executed on.
* (you could then use that information to decide whether to use LD_PRELOAD
* or LD_LIBRARY_PATH to make a C++ program launched from here use a newer
* version of libstdc++.so.6 that you provide)
*
* (C) 2017 Daniel Gibson
*
* LICENSE
* This software is dual-licensed to the public domain and under the following
* license: you are granted a perpetual, irrevocable license to copy, modify,
* publish, and distribute this file as you see fit.
* No warranty implied; use at your own risk.
*
* So you can do whatever you want with this code, including copying it
* (or parts of it) into your own source.
* No need to mention me or this "license" in your code or docs, even though
* it would be appreciated, of course.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
enum stdcpp_gcc_versions {
STDCPP_GCC_OLDER = -1,
// see https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html#abi.versioning
// Note that this does not list all released GCC versions, but only
// the ones that bumped the libstdc++ ABI version (GLIBCXX_3.4.x)
// BTW, the value of MY_GCC_* corresponds with x in GLIBCXX_3.4.x
STDCPP_GCC_3_4_0 = 0, // GLIBCXX_3.4, CXXABI_1.3
STDCPP_GCC_3_4_1, // GLIBCXX_3.4.1, CXXABI_1.3
STDCPP_GCC_3_4_2, // GLIBCXX_3.4.2
STDCPP_GCC_3_4_3, // GLIBCXX_3.4.3 - unfortunately there's no function with that version in libstdc++
STDCPP_GCC_4_0_0, // GLIBCXX_3.4.4, CXXABI_1.3.1
STDCPP_GCC_4_0_1, // GLIBCXX_3.4.5
STDCPP_GCC_4_0_2, // GLIBCXX_3.4.6
STDCPP_GCC_4_0_3, // GLIBCXX_3.4.7
STDCPP_GCC_4_1_1, // GLIBCXX_3.4.8 - no function for x86?!
STDCPP_GCC_4_2_0, // GLIBCXX_3.4.9,
STDCPP_GCC_4_3_0, // GLIBCXX_3.4.10, CXXABI_1.3.2
STDCPP_GCC_4_4_0, // GLIBCXX_3.4.11, CXXABI_1.3.3
STDCPP_GCC_4_4_1, // GLIBCXX_3.4.12, CXXABI_1.3.3 - no function with that version
STDCPP_GCC_4_4_2, // GLIBCXX_3.4.13, CXXABI_1.3.3
STDCPP_GCC_4_5_0, // GLIBCXX_3.4.14, CXXABI_1.3.4
STDCPP_GCC_4_6_0, // GLIBCXX_3.4.15, CXXABI_1.3.5
STDCPP_GCC_4_6_1, // GLIBCXX_3.4.16, CXXABI_1.3.5
STDCPP_GCC_4_7_0, // GLIBCXX_3.4.17, CXXABI_1.3.6
STDCPP_GCC_4_8_0, // GLIBCXX_3.4.18, CXXABI_1.3.7
STDCPP_GCC_4_8_3, // GLIBCXX_3.4.19, CXXABI_1.3.7
STDCPP_GCC_4_9_0, // GLIBCXX_3.4.20, CXXABI_1.3.8
STDCPP_GCC_5_1_0, // GLIBCXX_3.4.21, CXXABI_1.3.9
STDCPP_GCC_6_1_0, // GLIBCXX_3.4.22, CXXABI_1.3.10
// TODO: add newer versions, once available (also in gcc_version_checks below)
_NUM_STDCPP_GCC_VERSIONS
};
struct gcc_version_check
{
const char* gcc_ver_name;
const char* fn_version;
const char* fn_name;
};
enum { HAVE_64_BIT = (sizeof(void*) == 8) };
static struct gcc_version_check
libstdcpp_version_checks[_NUM_STDCPP_GCC_VERSIONS] =
{
{ "3.4.0", "GLIBCXX_3.4", "_ZNSt5ctypeIwED2Ev" },
{ "3.4.1", "GLIBCXX_3.4.1", "_ZNSt12__basic_fileIcE4fileEv" },
{ "3.4.2", "GLIBCXX_3.4.2", "_ZN9__gnu_cxx17__pool_alloc_base12_M_get_mutexEv" },
{ "3.4.3", "GLIBCXX_3.4.3", NULL }, // FIXME: couldn't find a function with that version in libstdc++.so.6
{ "4.0.0", "GLIBCXX_3.4.4", "_ZN9__gnu_cxx6__poolILb0EE10_M_destroyEv" },
{ "4.0.1", "GLIBCXX_3.4.5", "_ZNKSs11_M_disjunctEPKc" },
{ "4.0.2", "GLIBCXX_3.4.6", "_ZNSt6locale5facet13_S_get_c_nameEv" },
// the following has a different signature for 32bit vs 64bit
{ "4.0.3", "GLIBCXX_3.4.7", HAVE_64_BIT ? "_ZNSt6locale5_Impl16_M_install_cacheEPKNS_5facetEm"
: "_ZNSt6locale5_Impl16_M_install_cacheEPKNS_5facetEj" },
{ "4.1.1", "GLIBCXX_3.4.8", HAVE_64_BIT ? "_ZSt17__copy_streambufsIcSt11char_traitsIcEElPSt15basic_streambufIT_T0_ES6_"
: NULL }, // FIXME: couldn't find a function for i686 ?!
{ "4.2.0", "GLIBCXX_3.4.9", "_ZNSo9_M_insertIdEERSoT_" },
{ "4.3.0", "GLIBCXX_3.4.10", "_ZNKSt4hashISbIwSt11char_traitsIwESaIwEEEclES3_" },
{ "4.4.0", "GLIBCXX_3.4.11", "_ZSt16generic_categoryv" },
{ "4.4.1", "GLIBCXX_3.4.12", NULL }, // FIXME: couldn't find a function with that version in libstdc++.so.6
{ "4.4.2", "GLIBCXX_3.4.13", "_ZNSt14basic_ofstreamIcSt11char_traitsIcEEC2ERKSsSt13_Ios_Openmode" },
{ "4.5.0", "GLIBCXX_3.4.14", "_ZNSs13shrink_to_fitEv" },
{ "4.6.0", "GLIBCXX_3.4.15", "_ZNSt17bad_function_callD2Ev" },
{ "4.6.1", "GLIBCXX_3.4.16", HAVE_64_BIT ? "_ZNSs10_S_compareEmm" : "_ZNSs10_S_compareEjj" },
{ "4.7.0", "GLIBCXX_3.4.17", "_ZNSt6thread20hardware_concurrencyEv" },
{ "4.8.0", "GLIBCXX_3.4.18", "_ZNSt13random_device9_M_getvalEv" },
{ "4.8.3", "GLIBCXX_3.4.19", "_ZNSt6chrono3_V212steady_clock3nowEv" },
{ "4.9.0", "GLIBCXX_3.4.20", "_ZSt14get_unexpectedv" },
{ "5.1.0", "GLIBCXX_3.4.21", "_ZNSt13runtime_errorC1EPKc" },
{ "6.1.0", "GLIBCXX_3.4.22", "_ZNSt6thread6_StateD1Ev" }
// TODO: add newer versions, once available
};
static int get_libstdcpp_version(const char* path)
{
void* handle = dlopen(path, RTLD_LAZY);
int i, ret = STDCPP_GCC_OLDER;
if(handle == NULL)
{
eprintf("couldn't dlopen() %s : %s\n", path, dlerror());
return ret;
}
for(i=STDCPP_GCC_3_4_0; i < _NUM_STDCPP_GCC_VERSIONS; ++i)
{
const char* fn_name = libstdcpp_version_checks[i].fn_name;
if(fn_name != NULL)
{
void* fn = dlvsym(handle, fn_name, libstdcpp_version_checks[i].fn_version);
if(fn == NULL)
{
break;
}
ret = i;
}
}
dlclose(handle);
// if ret == _NUM_STDCPP_GCC_VERSIONS-1, the real version could potentially be even newer
return ret;
}
enum libgcc_versions {
LIBGCC_OLDER = -1,
LIBGCC_3_0_0=0, // GCC_3.0
LIBGCC_3_3_0, // GCC_3.3
LIBGCC_3_3_1, // GCC_3.3.1
LIBGCC_3_3_2, // GCC_3.3.2
LIBGCC_3_3_4, // GCC_3.3.4
LIBGCC_3_4_0, // GCC_3.4
LIBGCC_3_4_2, // GCC_3.4.2
LIBGCC_3_4_4, // GCC_3.4.4
LIBGCC_4_0_0, // GCC_4.0.0
LIBGCC_4_1_0, // GCC_4.1.0
LIBGCC_4_2_0, // GCC_4.2.0
LIBGCC_4_3_0, // GCC_4.3.0
LIBGCC_4_4_0, // GCC_4.4.0
LIBGCC_4_5_0, // GCC_4.5.0
LIBGCC_4_6_0, // GCC_4.6.0
LIBGCC_4_7_0, // GCC_4.7.0
LIBGCC_4_8_0, // GCC_4.8.0
// TODO: add new versions, if any (at least up to gcc 6.1.0 all seem to use libgcc 4.8.0)
_NUM_LIBGCC_VERSIONS
};
static struct gcc_version_check
libgcc_version_checks[_NUM_STDCPP_GCC_VERSIONS] =
{
{ "3.0.0", "GCC_3.0", "__mulvsi3" },
{ "3.3.0", "GCC_3.3", "_Unwind_Backtrace" },
{ "3.3.1", "GCC_3.3.1", "__gcc_personality_v0" },
{ "3.3.2", "GCC_3.3.2", NULL }, // no function with this version in libgcc
{ "3.3.4", "GCC_3.3.4", NULL }, // ditto
{ "3.4.0", "GCC_3.4", "__paritydi2" },
{ "3.4.2", "GCC_3.4.2", "__enable_execute_stack" },
{ "3.4.4", "GCC_3.4.4", HAVE_64_BIT ? "__addvti3" : NULL }, // no x86 function with that version
{ "4.0.0", "GCC_4.0.0", "__muldc3" },
{ "4.1.0", "GCC_4.1.0", NULL }, // no function for this version
{ "4.2.0", "GCC_4.2.0", "_Unwind_GetIPInfo" },
{ "4.3.0", "GCC_4.3.0", "__bswapdi2" },
{ "4.4.0", "GCC_4.4.0", HAVE_64_BIT ? NULL : "__letf2" }, // no 64bit function for this version
{ "4.5.0", "GCC_4.5.0", HAVE_64_BIT ? NULL : "__extendxftf2" }, // ditto
{ "4.6.0", "GCC_4.6.0", NULL }, // no function for that version at all
{ "4.7.0", "GCC_4.7.0", "__clrsbdi2" },
{ "4.8.0", "GCC_4.8.0", "__cpu_indicator_init" }
// TODO: add new versions
};
static int get_libgcc_version(const char* path)
{
void* handle = dlopen(path, RTLD_LAZY);
int i, ret = LIBGCC_OLDER;
if(handle == NULL)
{
eprintf("couldn't dlopen() %s : %s\n", path, dlerror());
return ret;
}
for(i=LIBGCC_3_0_0; i < _NUM_LIBGCC_VERSIONS; ++i)
{
const char* fn_name = libgcc_version_checks[i].fn_name;
if(fn_name != NULL)
{
void* fn = dlvsym(handle, fn_name, libgcc_version_checks[i].fn_version);
if(fn == NULL)
{
break;
}
ret = i;
}
}
dlclose(handle);
// if ret == _NUM_STDCPP_GCC_VERSIONS-1, the real version could potentially be even newer
return ret;
}
typedef struct My_SDL2_version
{
unsigned char major;
unsigned char minor;
unsigned char patch; // update version, e.g. 5 in 2.0.5
} My_SDL2_version;
My_SDL2_version get_libsdl2_version(const char* path)
{
My_SDL2_version ret = {0};
void* handle = dlopen(path, RTLD_LAZY);
if(handle == NULL)
{
eprintf("couldn't dlopen() %s : %s\n", path, dlerror());
return ret;
}
void (*sdl_getversion)(My_SDL2_version* ver);
sdl_getversion = dlsym(handle, "SDL_GetVersion");
if(sdl_getversion == NULL)
{
eprintf("Couldn't find SDL_GetVersion() in %s !\n", path);
}
else
{
sdl_getversion(&ret);
}
dlclose(handle);
return ret;
}
int main(int argc, char** argv)
{
int libstdcpp_ver = get_libstdcpp_version("libstdc++.so.6");
if(libstdcpp_ver >= 0)
printf("%s should be compatible with the one from GCC %s\n", "libstdc++.so.6", libstdcpp_version_checks[libstdcpp_ver].gcc_ver_name);
else
printf("%s probably is not even libstdc++ v6 really?\n", "libstdc++.so.6");
int libgcc_ver = get_libgcc_version("libgcc_s.so.1");
if(libgcc_ver >= 0)
printf("libgcc version: %s\n", libgcc_version_checks[libgcc_ver].gcc_ver_name);
else
printf("unknown libgcc version\n");
My_SDL2_version sdl2ver = get_libsdl2_version("libSDL2-2.0.so.0");
printf("SDL2 version: %d.%d.%d\n", (int)sdl2ver.major, (int)sdl2ver.minor, (int)sdl2ver.patch);
return 0;
}
@gqmelo
Copy link

gqmelo commented Jun 29, 2017

Cool!
But if you want to just compare two libstdc++.so versions and set LD_LIBRARY_PATH or LD_PRELOAD you can do something simpler. I use something like this: https://gist.github.com/gqmelo/5bf305ad4fd440ffa4b5834050a253db
That approach is also (supposedly) future proof, no need to update the script when new gcc versions as released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment