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