问题
I want to selectively link libm.a
statically, all the other libraries (libc.so
included) dinamically. But if I use the math functions from math.h
, it almost always fails to link correctly. Why?
And why does it work sometimes? (For example if I only use sqrt
, fabs
or, strangely, tanh
, it seems to link correctly)
myscript.sh:
#!/bin/bash
for i in sqrt tanh sin tan
do
echo "-----$i----------"
sed "s/ciao/$i/" prova.c >provat.c
gcc provat.c -fno-builtin -l:libm.a
[[ $? -eq 0 ]] && { echo -n "$i(2.0)="; ./a.out; echo " OK!"; }
echo
done
prova.c:
#include <stdio.h>
#include <math.h>
int main()
{
printf("%f", ciao(2.0));
return 0;
}
If I run myscript.sh
, I can see that sqrt
and tanh
give no problems. sin
and tan
, instead, fail to link:
$./myscript.sh
-----sqrt----------
sqrt(2.0)=1.414214 OK!
-----tanh----------
tanh(2.0)=0.964028 OK!
-----sin----------
/usr/lib/x86_64-linux-gnu/libm-2.27.a(s_sin.o): In function `__sin_ifunc':
(.text+0x4d42): undefined reference to `_dl_x86_cpu_features'
/usr/lib/x86_64-linux-gnu/libm-2.27.a(s_sin.o): In function `__cos_ifunc':
(.text+0x4da2): undefined reference to `_dl_x86_cpu_features'
collect2: error: ld returned 1 exit status
-----tan----------
/usr/lib/x86_64-linux-gnu/libm-2.27.a(s_tan.o): In function `__tan_ifunc':
(.text+0x5782): undefined reference to `_dl_x86_cpu_features'
collect2: error: ld returned 1 exit status
I don't understand these error messages. Can somebody explain what happens?
Why can't I link libm.a
statically (and the rest dinamically)? And why does it work sometimes?
Note: I use the -fno-builtin
flag to GCC, so that GCC doesn't use any of its builtin functions. So the problem is not there.
回答1:
It's not very clear (to me) why the restriction (libm.a + libc.so) is necessary, so it smells like an XY Problem.
According to [RedHat.Bugzilla]: Bug 1433347 - glibc: Selective static linking of libm.a fails due to unresolved _dl_x86_cpu_features symbol (pointed out by @KamilCuk):
This is not supported.
You would be mixing a static libm.a with future libc.so.6 and ld.so and that breaks the interdependencies between the core libraries which form "the implemetnation of the C runtime."
Either the entire implementation of the runtime is statically linked or none of it is statically linked. You can't choose to link parts of it statically and not others because each part depends on the other to form a complete implementation. The math library is not a thin you can link against statically, it happens we have a libm.a, but that's an implementation detail.
Please use '-static' for the entire application.
it seems like it's an unsupported configuration. That makes sense, but it's also a little bit confusing: even if libc and libm are 2 separate files on disk (for each configuration: static, shared), they are part of the same library (glibc), so:
- It's not OK to use half of a library statically built, and the other half as a shared object (some things that come into my mind are: gcc's -fPIC flag, and also library's initialization)
- Of course, most of the times, a library consists of a single file, so the above bullet wouldn't apply (there's where the confusion comes from)
From the beginning, I suspected that it's some code that (detects and) uses (if present) some CPU capabilities (most likely to improve speed or accuracy), that:
- Is only used by some of the (trigonometric) functions (like sin, cos, but not tanh) - I'm just guessing here: based on how the function values are computed (e.g. Taylor series)
- It only happens when libc (libm) are in sync
I used this simple program.
main.c:
#include <stdio.h>
#include <math.h>
#if !defined(FUNC)
# define FUNC sqrt
#endif
int main() {
double val = 3.141592;
printf("func(%.06lf): %.06lf\n", val, FUNC(val));
return 0;
}
Below it's a series of steps that I followed when looking into the problem:
Environment:
[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q056415996]> ~/sopr.sh *** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages *** [prompt]> uname -a Linux cfati-ubtu16x64-0 4.15.0-51-generic #55~16.04.1-Ubuntu SMP Thu May 16 09:24:37 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux [prompt]> gcc --version gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. [prompt]> ldd --version ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23 Copyright (C) 2016 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Written by Roland McGrath and Ulrich Drepper. [prompt]> ls main.c
The case when the 2 library parts (libc and libm) are in sync:
[prompt]> gcc -fPIC main.c -DFUNC=sin -o sin_static.out -static -lm [prompt]> ll sin_static.out -rwxrwxr-x 1 cfati cfati 1007744 Jun 13 20:08 sin_static.out [prompt]> ldd sin_static.out not a dynamic executable [prompt]> ./sin_static.out func(3.141592): 0.000001 [prompt]> [prompt]> gcc -fPIC main.c -DFUNC=sin -o sin_mso.out -l:libm.so [prompt]> ll sin_mso.out -rwxrwxr-x 1 cfati cfati 8656 Jun 13 20:09 sin_mso.out [prompt]> ldd sin_mso.out linux-vdso.so.1 => (0x00007ffc80ddd000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f999636b000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9995fa1000) /lib64/ld-linux-x86-64.so.2 (0x00007f9996674000) [prompt]> ./sin_mso.out func(3.141592): 0.000001
Everything works fine in both cases.
Switch to libm.a (for sin and tanh):
[prompt]> gcc -fPIC main.c -DFUNC=sin -o sin_ma.out -l:libm.a /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/libm.a(s_sin.o): In function `__cos': (.text+0x3542): undefined reference to `_dl_x86_cpu_features' /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/libm.a(s_sin.o): In function `__sin': (.text+0x3572): undefined reference to `_dl_x86_cpu_features' collect2: error: ld returned 1 exit status [prompt]> ll sin_ma.out ls: cannot access 'sin_ma.out': No such file or directory [prompt]> [prompt]> gcc -fPIC main.c -DFUNC=tanh -o tanh_ma.out -l:libm.a [prompt]> ll tanh_ma.out -rwxrwxr-x 1 cfati cfati 12856 Jun 13 20:10 tanh_ma.out [prompt]> ldd tanh_ma.out linux-vdso.so.1 => (0x00007ffcfa531000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f124625c000) /lib64/ld-linux-x86-64.so.2 (0x00007f1246626000) [prompt]> ./tanh_ma.out func(3.141592): 0.996272
As seen:
- For sin, the linker complained (even without -fno-builtin)
- For tanh, things went fine
From now on, I'm going to focus on the case that doesn't work.
Play a bit with the linker flags (
man ld
([die.linux]: ld(1) - Linux man page)):[prompt]> gcc -fPIC main.c -DFUNC=sin -o sin_ma_undefined.out -l:libm.a -Wl,--warn-unresolved-symbols /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/libm.a(s_sin.o): In function `__cos': (.text+0x3542): warning: undefined reference to `_dl_x86_cpu_features' /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/libm.a(s_sin.o): In function `__sin': (.text+0x3572): warning: undefined reference to `_dl_x86_cpu_features' [prompt]> ll sin_ma_undefined.out -rwxrwxr-x 1 cfati cfati 104088 Jun 13 20:10 sin_ma_undefined.out [prompt]> ldd sin_ma_undefined.out linux-vdso.so.1 => (0x00007fff984b0000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f274ad00000) /lib64/ld-linux-x86-64.so.2 (0x00007f274b0ca000) [prompt]> ./sin_ma_undefined.out Segmentation fault (core dumped)
It passed the link phase, but segfaulted at runtime (which was kind of expected).
Came across [Amper.Git]: open-source/glibc - Add _dl_x86_cpu_features to rtld_global (note that none of that is in the official glibc: [GNU]: Index of /gnu/libc). It's some pretty heavy stuff there. I "borrowed" some and saved it.
_dl_x86_cpu_features.c:
#if defined(_DL_X86_CPU_FEATURES__WORKAROUND) # define FEATURE_INDEX_MAX 1 enum { COMMON_CPUID_INDEX_1 = 0, COMMON_CPUID_INDEX_7, COMMON_CPUID_INDEX_80000001, // for AMD // Keep the following line at the end. COMMON_CPUID_INDEX_MAX }; struct cpu_features { enum cpu_features_kind { arch_kind_unknown = 0, arch_kind_intel, arch_kind_amd, arch_kind_other } kind; int max_cpuid; struct cpuid_registers { unsigned int eax; unsigned int ebx; unsigned int ecx; unsigned int edx; } cpuid[COMMON_CPUID_INDEX_MAX]; unsigned int family; unsigned int model; unsigned int feature[FEATURE_INDEX_MAX]; }; struct cpu_features _dl_x86_cpu_features; #endif
#include "_dl_x86_cpu_features.c"
also needs to be added in main.c:[prompt]> gcc -fPIC main.c -DFUNC=sin -D_DL_X86_CPU_FEATURES__WORKAROUND -o sin_ma_workaround.out -l:libm.a [prompt]> ll sin_ma_workaround.out -rwxrwxr-x 1 cfati cfati 104088 Jun 13 20:13 sin_ma_workaround.out [prompt]> ldd sin_ma_workaround.out linux-vdso.so.1 => (0x00007fff17b6c000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5a992e5000) /lib64/ld-linux-x86-64.so.2 (0x00007f5a996af000) [prompt]> ./sin_ma_workaround.out func(3.141592): 0.000001
Apparently, it works (at least in my environment)!!!
Although it did the trick for me (and probably it will be the same in your case), I still consider it a workaround (gainarie), and I'm not aware of the full implications.
So, my advice is to go with (either one of) the recommended options (from step #2.).
But, it would be interesting to see how other compilers behave.
来源:https://stackoverflow.com/questions/56415996/linking-error-selective-static-linking-of-libm-a-in-gcc