На момент написания статьи вики-тег Stackoverflow для статических библиотек сообщает нам:
Статическая библиотека - это архив объектных файлов.Используемый в качестве входных данных компоновщика, компоновщик извлекает объектные файлы, необходимые ему для связи.
Необходимые объектные файлы - это те, которые предоставляют компоновщику определения для символов, которые он находит, используются без определения вдругие входные файлы.Необходимые объектные файлы и никакие другие извлекаются из архива и вводятся в связывание точно так, как если бы они были отдельными входными файлами в команде связывания, а статическая библиотека вообще не упоминалась.
...
Обычно компоновщик поддерживает опцию (GNU ld: --whole-archive, MS link: / WHOLEARCHIVE), чтобы переопределить обработку статических библиотек по умолчанию и вместо этого связать все содержащиеся в нем объектные файлы, если они необходимыили нет.
Статическая библиотека ничего не вносит в связь, кроме объектных файлов, которые извлекаются из нее, которые могут различаться в разных связях.Он должен быть противопоставлен совместно используемой библиотеке, другому типу файла, с совершенно другой ролью в связывании.
Это должно прояснить, что делает --whole-archive
.Область действия --whole-archive
продолжается до конца командной строки компоновщика или до появления --no-whole-archive
1 .
По умолчанию компоновщик будет проверять статическую библиотеку только в каждой точке, где этоБиблиотека встречается в последовательности командной строки входов компоновщика.Он не пойдет назад, чтобы пересмотреть статическую библиотеку ради разрешения ссылок на символы, которые он обнаруживает в последующих входных данных.
Пара параметров --start-group ... --end-group
изменяет это поведение по умолчанию.Он предписывает компоновщику проверять статические библиотеки, упомянутые в ...
несколько раз , в указанном порядке, пока это приводит к любым новым разрешениям новых ссылок на символы.--start-group ... --end-group
не влияет на выбор по умолчанию компоновщиком объектных файлов из статических библиотек в ...
.Он будет извлекать и связывать только те объектные файлы, которые ему нужны , если только --whole-archive
не также в действии.
Подводя итог: -
--start-group lib0.a ... libN.a --end-group
сообщает компоновщику: Продолжайте искать в lib0.a ... libN.a
необходимые вам объектные файлы, пока не найдете больше .
--whole-archive lib0.a ... libN.a --no-whole-archive
подсказоккомпоновщик: Забудьте о том, что вам нужно.Просто свяжите все объектные файлы во всех lib0.a ... libN.a
.
Тогда вы можете видеть, что любая связка, которую вы можете установить, преуспеет с --start-group lib0.a ... libN.a --end-group
, также преуспеет с --whole-archive lib0.a ... libN.a --no-whole-archive
, потому что последний будет связыватьвсе необходимые объектные файлы и все ненужные, не удосужившись заметить разницу.
Но обратное неверно.Вот тривиальный пример:
xc
#include <stdio.h>
void x(void)
{
puts(__func__);
}
yc
#include <stdio.h>
void y(void)
{
puts(__func__);
}
main.c
extern void x(void);
int main(void)
{
x();
return 0;
}
Скомпилировать все исходные файлы:
$ gcc -Wall -c x.c y.c main.c
Создать статическую библиотеку, архивируя x.o
и y.o
:
ar rcs libxy.a x.o y.o
Попыткасвязать программу с main.o
и libxy.a
в неправильном порядке :
$ gcc -o prog libxy.a main.o
main.o: In function `main':
main.c:(.text+0x5): undefined reference to `x'
collect2: error: ld returned 1 exit status
Не удалось, поскольку ссылка на x
в main.o
была обнаружена толькокомпоновщик слишком поздно, чтобы найти определение x
в libxy.a(x.o)
.Он достиг libxy.a
first и не нашел объектных файлов, которые ему нужны.На тот момент он еще не связывал никакие объектные файлы с программой, поэтому было необходимо 0 ссылок на символы, которые нужно было разрешить.Рассмотрев libxy.a
и не найдя для него никакой пользы, он больше его не рассматривает.
Правильная связь, конечно, такова:
$ gcc -o prog main.o libxy.a
, но если вы не понимаете,что у вас просто есть порядок связи задом наперед, вы можете получить связь, чтобы преуспеть с помощью --whole-archive
:
$ gcc -o prog -Wl,--whole-archive libxy.a -Wl,--no-whole-archive main.o
$ ./prog
x
Очевидно, что вы не можете добиться успеха с помощью
$ gcc -o prog -Wl,--start-group libxy.a -Wl,--end-group main.o
main.o: In function `main':
main.c:(.text+0x5): undefined reference to `x'
collect2: error: ld returned 1 exit status
потому что это ничем не отличается от:
$ gcc -o prog libxy.a main.o
Теперь вот пример связи, которая не работает с поведением по умолчанию, но может бытьсделано, чтобы преуспеть с --start-group ... --end-group
.
ac
#include <stdio.h>
void a(void)
{
puts(__func__);
}
bc
#include <stdio.h>
void b(void)
{
puts(__func__);
}
ab.c
extern void b(void);
void ab(void)
{
b();
}
ba.c
extern void a(void);
void ba(void)
{
a();
}
abba.c
extern void ab(void);
extern void ba(void);
void abba(void)
{
ab();
ba();
}
main2.c
extern void abba(void);
int main(void)
{
abba();
return 0;
}
Скомпилируйте все источники: -
$ gcc -Wall a.c b.c ab.c ba.c abba.c main2.c
Затем создайте следующие статические библиотеки:
$ ar rcs libbab.a ba.o b.o x.o
$ ar rcs libaba.a ab.o a.o y.o
$ ar rcs libabba.a abba.o
(Примечаниечто эти старые объектные файлы x.o
и y.o
снова заархивированы).
Здесь libabba.a
зависит от libbab.a
и libaba.a
.В частности, libabba.a(abba.o)
делает ссылку на ab
, который определен в libaba.a(ab.o)
;и он также ссылается на ba
, который определен в libbab.a(ba.o)
.Таким образом, в порядке связи libabba.a
должно произойти до того, как libbab.a
и libaba.a
И libbab.a
зависит от libaba.a
.В частности, libbab.a(ba.o)
делает ссылку на a
, который определен в libaba(a.o)
.
Но libaba.a
также зависит от libbab.a
.libaba(ab.o)
делает ссылку на b
, который определен в libbab(b.o)
.Существует круговая зависимость между libbab.a
и libaba.a
.Поэтому, какой бы из них мы не разместили первым в связи по умолчанию, он потерпит неудачу с неопределенными ссылочными ошибками.Либо так:
$ gcc -o prog2 main2.o libabba.a libaba.a libbab.a
libbab.a(ba.o): In function `ba':
ba.c:(.text+0x5): undefined reference to `a'
collect2: error: ld returned 1 exit status
Или так:
$ gcc -o prog2 main2.o libabba.a libbab.a libaba.a
libaba.a(ab.o): In function `ab':
ab.c:(.text+0x5): undefined reference to `b'
collect2: error: ld returned 1 exit status
A круговая зависимость - это проблема, для которой --start-group ... --end-group
является решением:
$ gcc -o prog2 main2.o libabba.a -Wl,--start-group libbab.a libaba.a -Wl,--end-group
$ ./prog2
b
a
Следовательно, --whole-archive ... --no-whole-archive
также является решением:
$ gcc -o prog2 main2.o libabba.a -Wl,--whole-archive libbab.a libaba.a -Wl,--no-whole-archive
$ ./prog2
b
a
Но это решение плохо .Давайте проследим, какие из наших объектных файлов фактически связаны с программой в каждом конкретном случае.
С --start-group ... --end-group
:
$ gcc -o prog2 main2.o libabba.a -Wl,--start-group libbab.a libaba.a -Wl,--end-group -Wl,--trace
/usr/bin/x86_64-linux-gnu-ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
main2.o
(libabba.a)abba.o
(libbab.a)ba.o
(libaba.a)ab.o
(libaba.a)a.o
(libbab.a)b.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
они:
main2.o
(libabba.a)abba.o
(libbab.a)ba.o
(libaba.a)ab.o
(libaba.a)a.o
(libbab.a)b.o
, которыеименно те, которые необходимы в программе.
с --whole-archive ... --no-whole-archive
:
$ gcc -o prog2 main2.o libabba.a -Wl,--whole-archive libbab.a libaba.a -Wl,--no-whole-archive -Wl,-trace
/usr/bin/x86_64-linux-gnu-ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
main2.o
(libabba.a)abba.o
(libbab.a)ba.o
(libbab.a)b.o
(libbab.a)x.o
(libaba.a)ab.o
(libaba.a)a.o
(libaba.a)y.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
они:
main2.o
(libabba.a)abba.o
(libbab.a)ba.o
(libbab.a)b.o
(libbab.a)x.o
(libaba.a)ab.o
(libaba.a)a.o
(libaba.a)y.o
то же самое, что и раньше, плюс:
(libbab.a)x.o
(libaba.a)y.o
, которые являются мертвым кодом (и их могло быть больше, без ограничений).Избыточные функции x()
и y()
определены в образе:
$ nm prog2 | egrep 'T (x|y)'
000000000000067a T x
00000000000006ac T y
Еда на вынос
- Используйте связывание по умолчанию, переводя входные данные в зависимостьПорядок, если вы можете.
- Используйте
--start-group ... --end-group
для преодоления циклических зависимостей между библиотеками, когда вы не можете исправить библиотеки.Имейте в виду, что скорость связывания сработает. - Используйте
--whole-archive ... --no-whole-archive
только в том случае, если на самом деле нужно для связывания всех объектных файлов во всех статических библиотеках в ...
.В противном случае выполните одно из двух предыдущих действий.
[1] И помните, что командная строка компоновщика
, когда GCC вызывает компоновщик, на самом деле оченьдольше, чем параметры связывания, которые вы явно передаете в командной строке GCC, с опционально добавленными опциями.Поэтому всегда закрывайте --whole-archive
с помощью --no-whole-archive
и закрывайте --start-group
с помощью --end-group
.