Last active
April 3, 2022 19:44
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.