我想静态地选择性地链接libm.a
,而动态地链接所有其他库(包括libc.so
)。但是,如果我使用math.h
中的数学函数,则几乎总是无法正确链接。为什么?
为何有时起作用? (例如,如果我仅使用sqrt
,fabs
或奇怪的是tanh
,它似乎可以正确链接)
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;
}
如果我运行myscript.sh
,则可以看到sqrt
和tanh
没问题。 sin
和tan
却无法链接:
$./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
我不理解这些错误消息。有人可以解释会发生什么吗?
为什么不能静态链接libm.a
(其余链接则动态链接)?为何有时起作用?
注意::我对GCC使用了-fno-builtin
标志,因此GCC不使用其任何内置函数。因此问题不存在。
答案 0 :(得分:2)
(对我来说)不是很清楚为什么需要限制( libm.a + libc.so ),所以它闻起来像 XY问题。
根据[RedHat.Bugzilla]: Bug 1433347 - glibc: Selective static linking of libm.a fails due to unresolved _dl_x86_cpu_features symbol(由@KamilCuk指出):
不支持此操作。
您将静态libm.a与将来的libc.so.6和ld.so混合在一起,这将破坏核心库之间的相互依赖关系,这些核心库形成了“ C运行时的实现”。
要么整个运行时实现都是静态链接的,要么都不是静态链接的。您不能选择静态链接它的一部分,而不能选择静态链接,因为每个部分都依赖于另一个来形成完整的实现。数学库不是可以静态链接的瘦库,它恰好有一个libm.a,但这是一个实现细节。
请在整个应用程序中使用“ -static”。
似乎是不受支持的配置。这是有道理的,但也有些令人困惑:即使 libc 和 libm 是磁盘上的 2个独立文件(对于每种配置:< em> static , shared ),它们是同一库的一部分( g libc ) ,所以:
从一开始,我就怀疑这是一些(检测并)使用(如果存在)某些 CPU 功能(最有可能提高速度或准确性)的代码,
我用了这个简单的程序。
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;
}
下面是调查问题时遵循的一系列步骤:
环境:
[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
两个库部分( libc 和 libm )同步的情况:
[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
在这两种情况下,一切都正常。
切换到 libm.a (用于 sin 和 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
如图所示:
从现在开始,我将重点讨论无法解决的情况。
使用链接器标志(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)
它已经通过了链接阶段,但是在运行时进行了 segfault (这是预料之中的)。
出现在 [Amper.Git]: open-source/glibc - Add _dl_x86_cpu_features to rtld_global 中(请注意,其中没有都在官方 glibc中:[GNU]: Index of /gnu/libc)。那是一些很重的东西。我“借用”了一些并保存了。
_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"
也需要添加到 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
显然,它有效(至少在我的环境中有效)!
尽管它对我有用(可能与您的情况相同),但我仍然认为它是一种解决方法( gainarie ),并且我不知道全部含义。
所以,我的建议是使用推荐的选项之一(从步骤 #2。 )。
但是,看看其他编译器的行为会很有趣。