c - GCC 中的链接错误 : selective static linking of libm. a

标签 c linux gcc compilation static-linking

我想有选择地静态链接 libm.a,动态链接所有其他库(包括 libc.so)。但是,如果我使用 math.h 中的数学函数,它几乎总是 无法正确链接。为什么? 为什么它有时会起作用? (例如,如果我只使用 sqrtfabs 或者奇怪的是 tanh,它似乎链接正确)

我的脚本.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 就不会使用它的任何内置函数。所以问题不存在。

最佳答案

(对我而言)不是很清楚为什么限制 (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 指出):

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.

这似乎是一个不受支持的配置。这是有道理的,但也有点令人困惑:即使 libclibm 是磁盘上的 2 个独立文件(对于每个配置:< em>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. 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)
    

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

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

关于c - GCC 中的链接错误 : selective static linking of libm. a,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56415996/

相关文章:

gcc - LLVM 中的内联 NOP 未优化

windows - 如何更改 MSYS2 上的 HOME 目录和启动目录?

c - 获取 GCC 链接器错误 : Linker input file unused because linking is not done

c - 使 `too many arguments in call to function` 在 Clang 中成为错误

c - 表达式语法错误

c - 如何在Linux中添加延迟而不放弃CPU周期

linux - dokku 保留应用程序运行时生成的文件

c - p.a 和 p->a 有什么区别,其中 p 是指针?

c - 取消引用指向不完整类型的指针

c++ - 请解释此链接错误 : referenced in section . rodata