Skip to content
Advertisement

Fedora 28 / GLIBC 2.27 libm.so.6 logf() and powf() c++

As I am sure other Fedora 28 users will know, the OS’s glibc was recently updated to glibc 2.27. Amongst many other things, 2.27 has added new implementations of logf() and powf(). This has caused my application to fail to run on distributions with an older glibc (Debian, for example). When the application is invoked on Debian, the following error is produced:

  • … libm.so.6 version GLIBC-2.27 not found (required by ./app_name)

I tracked the symbols down to logf and powf by using the following procedure:

objdump -T ./app_name | grep GLIBC_2.27

Which gave the following output:

0000000000000000      DF *UND*  0000000000000000  GLIBC_2.27  powf
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.27  logf

And then…

objdump -T /lib/libm.so.6 | grep -w logf
objdump -T /lib/libm.so.6 | grep -w powf

Which gave the following output:

000397a0 g    DF .text  00000135  GLIBC_2.27  logf
00010430 g    DF .text  0000009e (GLIBC_2.0)  logf

and…

000397a0 g    DF .text  00000135  GLIBC_2.27  powf
00010430 g    DF .text  0000009e (GLIBC_2.0)  powf

So, armed with the information that powf() and logf() are also implemented in GLIBC-2.0, I added the following to my project (above main()) and recompiled.

__asm__(".symver logf,logf@GLIBC_2.0");
__asm__(".symver powf,powf@GLIBC_2.0"); 

Unfortunately, my project is still using powf and logf from GLIBC-2.27. It’s actually quite important that I distributed binaries for Debian and I would prefer not to have to compile on that distribution if I can avoid it.

Historically, I have successfully used this procedure for symbols in libc.so.6 but not libm.so.6. Should I be doing things differently for libm.so.6?

Clearly, I am missing something here so I would be grateful for any help offered.

Many thanks

Amanda

Advertisement

Answer

I think the problem is that you used objdump to find the symbol versions of the 32-bit libm, and I assume you’re actually building a 64-bit application. In a Fedora 28 container if I look at the 64-bit library then I see these versions instead:

objdump -T /lib64/libm.so.6 | egrep -w 'logf|powf'
0000000000011ea0 g    DF .text  0000000000000138 (GLIBC_2.2.5) powf
000000000004cad0 g   iD  .text  000000000000002a  GLIBC_2.27  powf
000000000004c610 g   iD  .text  000000000000002a  GLIBC_2.27  logf
0000000000011e40 g    DF .text  0000000000000051 (GLIBC_2.2.5) logf

And this works as expected:

#include <math.h>

__asm__(".symver logf,logf@GLIBC_2.2.5");
__asm__(".symver powf,powf@GLIBC_2.2.5");

int main(int argc, char**)
{
  return powf(argc, 2.0f) * logf(argc);
}

It uses the versions from the 64-bit library:

$ g++ m.cc 
$ nm  --undefined-only a.out
                 w __gmon_start__
                 U __libc_start_main@@GLIBC_2.2.5
                 U logf@GLIBC_2.2.5
                 U powf@GLIBC_2.2.5

So I think the problem was that you were trying to link to symbols that simply aren’t in the 64-bit library (because glibc didn’t have 64-bit versions of those symbols until version 2.2.5, so they don’t exist with the GLIBC_2.0 version).

To make it work for 32-bit or 64-bit you could do:

#include <math.h>

#if __LP64__
# define SYMVER "GLIBC_2.2.5"
#else
# define SYMVER "GLIBC_2.0"
#endif
#define USE_OLD_SYM(F,V) __asm__(".symver " #F "," #F "@" V)
USE_OLD_SYM(logf,SYMVER);
USE_OLD_SYM(powf,SYMVER);

int main(int argc, char**)
{
  return powf(argc, 2.0f) * logf(argc);
}

That uses the right version for the wordsize:

$ g++ m.cc  
$ nm  --undefined-only a.out
                 w __gmon_start__
                 U __libc_start_main@@GLIBC_2.2.5
                 U logf@GLIBC_2.2.5
                 U powf@GLIBC_2.2.5
$ g++ m.cc  -m32
$ nm  --undefined-only a.out
         w __gmon_start__
         U __libc_start_main@@GLIBC_2.0
         U logf@GLIBC_2.0
         U powf@GLIBC_2.0
Advertisement