Search code examples
c++linuxglibc

Problems discovering glibc version in C++


My application is distributed with two binaries. One which is linked to an old glibc (for example, 2.17) and another binary that targets a newer glibc (for example, 2.34). I also distribute a launcher binary (linked against glibc 2.17) which interrogates the user's libc version and then runs the correct binary.

My code to establish the user's libc looks something like the following:

std::string gnu(gnu_get_libc_version());
double g_ver = std::stod(gnu);
if(g_ver >= 2.34)
{
     return GLIBC_MAIN;
}
else
{
     return GLIBC_COMPAT;
}

This works perfectly for the best part, however, some of my users report that despite having a new glibc, the old glibc binary is actually run. I have investigated this and have discovered that double g_ver is equal to 2, not 2.34 as it should be. That is, the decimal part is missing. gnu_get_libc_version() always has the correct value so it must be a problem when converting the string to a double.

I have also tried boost::lexical_cast but this has the same effect.

std::string gnu(gnu_get_libc_version());
//double g_ver = std::stod(gnu);
double glibc = boost::lexical_cast<double>(gnu);
if(g_ver >= 2.34)
{
  return GLIBC_MAIN;
}
else
{
   return GLIBC_COMPAT;
}

Needless to say, I am unable to reproduce this behaviour on any of my computers even when running the exact same distribution / version of Linux as affected users.

Does anybody know why boost::lexical_cast or std::stod sometimes misses the decimal part of the version number? Is there an alternative approach to this?

UPDATE

Upon further tests, this problem is introduced when using a different locale. I set my locale to fr_FR.UTF-8 on a test machine and was able to reproduce this problem. However, the output of gnu_get_libc_version() seems to be correct but std::stod is unable to parse the decimal point part of the version.

Does anybody know how to correct this problem?


Solution

  • The fundamental problem is that the glibc version is a string and not a decimal number. So for a "proper" solution you need to parse it manually and implement your own logic to decide which version is bigger or smaller.

    However, as a quick and dirty hack, try inserting the line

    setlocale(LC_NUMERIC, "C");
    

    before the strtod call. That will set the numeric locale back to the default C locale where the decimal separator is .. If you're doing something that needs correct locales later in the program you need to set it back again. Depending on how your program initialized locales earlier, something like

    setlocale(LC_NUMERIC, "");
    

    should reset it back to what the environment says the locale should be.