Ошибка компоновки: выборочная статическая компоновка libm.a в GCC - PullRequest
2 голосов
/ 02 июня 2019

Я хочу выборочно связать 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 статически (а остальные динамически)? И почему это иногда работает?

Примечание: Я использую флаг -fno-builtin для GCC, чтобы GCC не использовал ни одну из своих встроенных функций. Так что проблема не в этом.

1 Ответ

2 голосов
/ 13 июня 2019

Не очень понятно (для меня), почему ограничение ( libm.a + libc.so ) необходимо, поэтому оно пахнет как XY Проблема .

Согласно [RedHat.Bugzilla]: Ошибка 1433347 - glibc: Селективное статическое связывание libm.a завершается неудачно из-за неразрешенного символа _dl_x86_cpu_features (на который указывает @KamilCuk):

Это не поддерживается.

Вы бы смешали статический libm.a с будущими libc.so.6 и ld.so, и это нарушит взаимозависимости между базовыми библиотеками, которые образуют «реализацию среды выполнения C».

Либо вся реализация среды выполнения статически связана, либо ни одна из них не является статически связанной. Вы не можете связывать его части статически, а не другие, потому что каждая часть зависит от другой, чтобы сформировать полную реализацию. Математическая библиотека - не то, что можно статически связать, бывает, что у нас есть libm.a, но это деталь реализации.

Пожалуйста, используйте '-static' для всего приложения.

похоже, что это неподдерживаемая конфигурация. Это имеет смысл, но это также немного сбивает с толку: даже если libc и libm являются 2 отдельными файлами на диске (для каждой конфигурации: static , shared ), они являются частью одной библиотеки ( g libc ), поэтому:

  • Это не ОК использовать половину статически построенной библиотеки, а другую половину как общий объект (некоторые вещи, которые приходят мне в голову: gcc -fPIC флаг, а также инициализация библиотеки)
  • Конечно, в большинстве случаев библиотека состоит из одного файла , поэтому вышеупомянутый пункт не будет применяться (отсюда и путаница)

С самого начала я подозревал, что это некоторый код, который (обнаруживает и) использует (если присутствует) некоторые возможности CPU (скорее всего, для повышения скорости или точности), что:

  • Используется только некоторыми (тригонометрическими) функциями (например, sin , cos , но not tanh ) - I Я просто догадываюсь здесь: на основе того, как вычисляются значения функции (например, ряд Тейлора )
  • Это происходит только тогда, когда 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 библиотечные части ( 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
    

    Все отлично работает в обоих случаях.

  3. Переключиться на 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
    

    Как видно:

    • Для sin , компоновщик жаловался (даже без -fno-builtin )
    • Для Тан все прошло хорошо

    С этого момента я собираюсь сосредоточиться на случае, который не работает .

  4. Поиграть немного с флагами компоновщика (man ld ( [die.linux]: ld (1) - Страница man Linux )):

    [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 ed во время выполнения (что было отчасти ожидаемо).

  5. Обнаружено [Amper.Git]: с открытым исходным кодом / glibc - Добавить _dl_x86_cpu_features в rtld_global (обратите внимание, что нет из этого в официальный glibc : [GNU]: индекс / 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. ).
Но было бы интересно посмотреть, как ведут себя другие компиляторы.

...