【问题标题】:Linking error: selective static linking of libm.a in GCC链接错误:GCC 中 libm.a 的选择性静态链接
【发布时间】:2019-06-02 14:36:13
【问题描述】:

我想选择性地静态链接libm.a,动态链接所有其他库(包括libc.so)。但是,如果我使用来自 math.h 的数学函数,它几乎总是无法正确链接。为什么? 为什么它有时会起作用? (例如,如果我只使用sqrtfabs 或奇怪的是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,我可以看到sqrttanh 没有问题。 sintan 链接失败:

$./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 不使用它的任何内置函数。所以问题不存在。

【问题讨论】:

  • 来自bugzilla.redhat.com/show_bug.cgi?id=1433347Either the entire implementation of the runtime is statically linked or none of it is statically linked.
  • 你能解释一下这是为什么吗?是否在某些文档中进行了解释?为什么有时会起作用?谢谢
  • 我发布的链接中的答案中对此进行了解释。你还需要什么?如果您在没有静态 C 库的其他部分的情况下链接 libm.a,您将得到未解析的符号,因为 ABI 不完整。
  • 我看到了那个链接。它只是非常含糊地说明了这一事实。那么为什么它可以与sqrtfabstanh 一起使用呢?
  • @KamilCuk 你刚刚发布了一个指向第一个谷歌结果的链接。您还没有解释为什么 tanh 有效而 tan 无效。

标签: c linux gcc compilation static-linking


【解决方案1】:

(对我来说)不太清楚为什么限制 (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”。

这似乎是一个不受支持的配置。这是有道理的,但也有点令人困惑:即使 libclibm 是磁盘上的 2 个单独的文件(对于每个配置:static、shared),它们是同一个库的一部分glibc) ,所以:

  • OK使用静态构建的库的一半,而另一半作为共享对象(我想到的一些事情是:gcc-fPIC 标志,以及库的初始化)
  • 当然,大多数情况下,库由单个文件组成,因此上述项目符号不适用(这就是混淆的来源)

从一开始,我就怀疑是某些代码(检测并)使用(如果存在)某些 CPU 功能(最有可能提高速度或准确性),即:

  • 仅由某些(三角函数)函数(如 sincos 使用,但 不是 tanh) - 我只是在这里猜测:基于函数值的计算方式(例如 泰勒级数
  • 只有在 libc (libm) 同步时才会发生这种情况

我使用了这个简单的程序。

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;
}

下面是我在调查问题时遵循的一系列步骤:

  1. 环境:

    [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
    
  2. 两个库部分(libclibm)同步的情况:

    [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
    

    在这两种情况下一切正常。

  3. 切换到 libm.a(对于 sintanh):

    [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
    

    如所见:

    • 对于sin,链接器抱怨(即使没有-fno-builtin
    • 对于 tanh,一切顺利

    从现在开始,我将专注于不起作用的情况

  4. 玩一下链接器标志 (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)
    

    它通过了链接阶段,但在运行时 segfaulted(这是意料之中的)。

  5. 遇到 [Amper.Git]: open-source/glibc - Add _dl_x86_cpu_features to rtld_global(请注意,没有官方 glibc中:@987654324 @)。那里有一些很重的东西。我“借”了一些并保存了它。

    _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. 开始)(其中之一)。
但是,看看其他编译器的行为会很有趣。

【讨论】:

  • 感谢您非常详细的回答。我对这种行为很好奇,但我想这超出了我目前的能力。我努力遵循您的第 5 点(我仍然是初学者),但其余的我都得到了。顺便说一句,我向您保证,对于 GCC 版本 7.4.0,如果您不使用 -fno-builtin 标志,编译器会将其内置函数用于所有这些数学函数。
  • 我试过 gcc 7.4.0,但在 Cygwin 上,没有 -fno-builtin也需要,以便重现故障。最重要的是,最后一步是否适用于您的方案?无论如何,这只是 libc 启动期间填充的内存区域,所以我想它足以传递足够大的缓冲区。
  • @SE:这是否回答了您的问题? (或者它是否揭示了一些亮点)? [SO]: What should I do when someone answers my question?.
  • @CristiFati 非常感谢您的#5。这是整个互联网上唯一解决我链接到 libm.a 问题的解决方案对于那些 cmake 用户来说,诀窍是从 #5 中的源代码编译和存档 libfixm.a 和 link_libraries(libm.a , libfixm.a) 早期的 CMakeLists.txt
  • @Hackless:很高兴听到您的问题得到解决!那就传话吧! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-25
相关资源
最近更新 更多