GCC связывает libc static и некоторую другую библиотеку динамически, пересматривается?

Следующие вопросы актуальны, но не отвечают на мой вопрос:

Связывание частично статичного и частично динамического в GCC

Связывание динамической библиотеки со статической библиотекой, которая ссылается на другие статические библиотеки

GCC: статическая привязка только к некоторым библиотекам

Статическая ссылка функции общей библиотеки в gcc

Я задал очень похожий вопрос раньше, но так как предыдущий вопрос, начатый мной, несколько захламлен в разделе комментариев и не полностью ответил (но я отметил его как ответ, так как он был хорошим усилием и хотя бы частично ответил на него ) Я задам новый вопрос. Вопрос в том, как связать libc как статический, а динамически связывать некоторую другую библиотеку (например, libm). Было высказано предположение, что это невозможно сделать в первом вопросе, верно ли это? Если так, было бы очень интересно знать, почему бы и нет.

Возможно ли это сделать? Кто-то сделал комментарий (который по какой-то причине был удален, может быть, он был неправильным?), Но это возможно, но там также должна существовать динамически связанная версия libc, так как она потребуется динамической библиотеке (например, динамическая libm будет требуют динамического libc (?)).

Это хорошо для меня, но для меня не очевидно, как сказать GCC, чтобы сделать это, т.е. ссылку в libc как статическую, так и динамическую. Как это сделать (я сделал пару попыток, некоторые из них показаны позже в вопросе)? Или есть какой-то другой способ сделать то, что я хочу?

Сначала мы видим, что простым запуском gcc test.c -lm все связано динамически следующим образом:

$ gcc test.c -lm
$ ldd a.out 
        linux-vdso.so.1 (0x00007fffb37d1000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f3b0eeb6000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f3b0eb10000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f3b0f1b0000)

Чтобы связать только libm как статический, позволяя libc оставаться динамическим, мы можем сделать (как указал Z бозон в одном из вышеупомянутых вопросов):

$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libm.a

$ ldd a.out 
        linux-vdso.so.1 (0x00007fff747ff000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f09aaa0c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f09aadb2000)

Однако попытка выполнить ту же самую процедуру для ссылки на libc static и libm dynamic не работает:

$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a -lm
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status

Что означает это сообщение об ошибке?

Некоторые другие попытки (большинство из них также были включены в мой первый вопрос):

$ gcc test.c /usr/lib64/libc.a
linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
urned 1 exit status
$ gcc test.c -Wl,-Bdynamic -lm -Wl,-Bstatic -lc
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
collect2: error: ld returned 1 exit status
$ gcc -Wl,-Bdynamic -lm -Wl,-Bstatic -lc test.c
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
collect2: error: ld returned 1 exit status
$ gcc -Wl,-Bstatic -lc -Wl,-Bdynamic -lm test.c
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status
$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.so -lm
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status
$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.so /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a -lm

Обратите внимание, что последний успешно скомпилирован/связан. Однако libc не был связан статически, только динамически, поэтому это еще одна неудачная попытка.

Программа тестирования проста:

$ cat test.c 
#include <stdio.h>
#include <math.h>

int main(int argc, char **argv)
{
        int i;
        int result;

        for(i = 0; i < 65535; i++) {
                result = sin(i);
        }

        return 0;
}

Edit:

Я также попробовал статику и горностай, как это было предложено в этом вопросе:

Статическая ссылка функции общей библиотеки в gcc

Ничего не работает.

Ответ 1

В принципе, ваш первый подход - правильный способ сделать это:

gcc test.c libc.a -lm

После того, как gcc добавит неявные библиотеки, он будет выглядеть (концептуально) следующим образом:

gcc crt1.o test.c libc.a -lm -lc -lgcc -lc

Таким образом, это означает, что любые функции libc, вызываемые либо crt1.o, либо test.c, будут втянуты из libc.a и связаны статически, тогда как любые функции, называемые исключительно из libm или libgcc, будут связаны динамически ( но он будет повторно использовать статические функции, если libm вызывает что-то уже втянутое).

Линкеровщик всегда начинается с самого левого файла/библиотеки и работает вправо; он никогда не возвращается. .c и .o файлы связаны безоговорочно, но .a files и -l используются только для поиска функций, которые уже ссылаются, но еще не определены. Поэтому библиотека слева бессмысленна (а -lc должен появляться дважды, потому что -lc зависит от -lgcc, а -lgcc зависит от -lc). Ссылка на ссылку важна!

К сожалению, вы, похоже, были сорваны тем, что может быть ошибкой в ​​ strcmp (или, скорее, в libc, который содержит strcmp): вещь STT_GNU_IFUNC - это умная функция, которая допускает несколько версий функции чтобы быть включенным, и наиболее оптимальным, который будет выбран во время выполнения, на основе того, какое оборудование доступно. Я не уверен, но похоже, что эта функция доступна только в PIE (независимая запись позиции) или сборка разделяемой библиотеки.

Почему это было бы в статическом libc.a, является для меня загадкой, но есть простой способ: реализовать собственный strcmp (базовая, медленная реализация - это всего лишь несколько строк C) и связать ее в до libc.a.

gcc test.c mystrcmp.c libc.a -lm

В качестве альтернативы вы можете извлечь функции из libc.a, которые вы действительно хотите, и связать только те, что статически:

ar x libc.a
gcc test.c somefile.o -lm

ar относится к файлам .a, так как tar относится к .tar файлам, хотя использование команд немного меняется, поэтому в этом примере извлекаются файлы .o из файла .a, а затем напрямую связывает их.

Ответ 2

Основываясь на ответе ams, я сделал следующее

mystrcmp.c

int strcmp(const char *s1, const char *s2) {
}

Скомпилировать

gcc -c test.c
gcc -c mystrcmp.c

Файлы настроек

ln -s `gcc -print-file-name=crt1.o`
ln -s `gcc -print-file-name=crti.o`
ln -s `gcc -print-file-name=crtn.o`
ln -s `gcc -print-file-name=libgcc_eh.a`
ln -s `gcc -print-file-name=libc.a`
ln -s `gcc -print-file-name=libm.so`

Ссылка

Л.Д. -m -o elf_x86_64 математике crt1.o crti.o test.o mystrcmp.o libc.a libgcc_eh.a libc.a libm.so -dynamic-линкер/lib64/ld-linux-x86-64.so.2 crtn.o

Это ссылки и работает правильно. Однако ldd показывает

linux-vdso.so.1 =>  (0x00007fff51911000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8182470000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f81820a9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8182793000)

Похоже, динамический libm требует динамического libc. Фактически, это легко показать

ldd libm.so reports

linux-vdso.so.1 =>  (0x00007fff20dfe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcaf74fe000)
/lib64/ld-linux-x86-64.so.2 (0x00007fcaf7bed000)

Таким образом, невозможно связать с libm.so, не связывая libc.so, если не удастся скомпилировать libm без зависимости от libc.