Некоторые первые принципы
Для привязки программы можно ввести три вида файлов:
- объектные файлы
- статические библиотеки
- общие библиотеки
Но только два таких файлов могут быть связаны в программе:
объектные файлы и общие библиотеки. 1
Если вы введете любой объект файл p.o
в связывание программы компоновщик
связывает его в программу безоговорочно .
Если вы введете статическую библиотеку libx.a
для вашей ссылки, компоновщик извлечет из libx.a
каждого члена архива
libx.a(q.o)
что для необходимо для продолжения связи и ссылок it в
программа. Элемент архива libx.a(q.o)
необходим , если он определяет хотя бы один
символ, на который есть ссылка, но еще не определенный в каком-либо файле (объектном файле или
общая библиотека), которая уже была включена в программу.
Если вы введете общую библиотеку liby.so
, история разветвляется: -
Если опция компоновщика (ld
) --as-needed
действует при достижении liby.so
,
тогда liby.so
будет связан с программой, если она необходима в том же смысле, что и элемент архива libx.a(q.o)
.
Если --as-needed
имеет значение , а не , тогда liby.so
безусловно связано, как
объектный файл.
Если вы вызываете компоновщик через один из интерфейсов GCC вашего дистрибутива (gcc
, g++
, gfortran
...) в обычном режиме
Кстати, тогда вы всегда получите поведение --as-needed
для разделяемых библиотек по умолчанию или всегда не получите его, пока ваш
Набор инструментов GCC остается тем же, потому что сборка GCC вашего дистрибутива настроит --as-needed
как вариант связывания по умолчанию, или нет, в соответствии с оценкой этого дистрибутива плюсов и минусов этого.
В последние годы дистрибутивы Debian (Ubuntu et al.) По умолчанию установили --as-needed
. В прошлый раз я смотрел,
Производные от Redhat (Fedora et al.) Этого не сделали. 2
Когда какой-либо файл file.{o|so}
связан с программой, он вводит 0 или более неразрешенных ссылок в программу. Эти неразрешенные ссылки
ссылки на символы в file.{o|so}
, которые не определены в file.{o|so}
или в любом другом файле, уже связанном с программой.
Компоновщик должен разрешить все ссылки к тому времени, как он завершил соединение
программа, или связь не удается с неразрешенными ошибками ссылки Файлы должны быть введены и связаны, которые обеспечивают определения для
все символы, на которые имеются ссылки в файлах, которые связаны .
вплоть до вашего дела
Когда вы говорите:
мое приложение ... использует только функцию1
Вы имеете в виду, что какой-то файл будет связан с ссылками function1
. Это просто
предположить, что этот файл является неким объектным файлом, который будет безоговорочно связан,
скажем тот, который определяет main
, например
main.c
#include <stdio.h>
extern void function1(void);
void call_function1(void) {
function1();
}
int main(void)
{
puts("Hello World!");
}
который мы компилируем в объектный файл:
$ gcc -Wall -c main.c
Обратите внимание, что main.o
ссылается на function1
несмотря на то, что мы не можем
запустить программу с этой функцией main
любым способом, который вызовет
function1
. main.o
определяет функцию,
call_function1
, что вызывает function1
; поэтому ссылка function1
указана в main.o
.
call_function1
это не ссылается на себя, но function1
есть. Если мы посмотрим на
таблица символов main.o
:
$ nm main.o
0000000000000000 T call_function1
U function1
U _GLOBAL_OFFSET_TABLE_
000000000000000c T main
U puts
обозначенные символы T
определены. На аннотированные U
имеются ссылки
но не определено. Поэтому, когда мы пытаемся связать main.o
в программу:
$ gcc -o prog main.o
/usr/bin/ld: main.o: in function `call_function1':
main.c:(.text+0x5): undefined reference to `function1'
collect2: error: ld returned 1 exit status
нам нужно ввести файл, который определяет function1
, даже если программа связана
с этим main.o
невозможно назвать function1
.
Ваш function1
определен каким-то образом в библиотеке libutils
, которая также
определяет function2
. Ваша программа не ссылается на function2
, но
определение function2
ссылается на один или несколько символов, скажем, function3
, которые определены,как-то внутри другой библиотеки libanother
.
Вам нужно добавить libanother
, а также libutils
к связи вашего
Программа
Исходя из первых принципов, мы знаем, что вы должны добавить libanother
к вашей ссылке, если вы ссылаетесь на файл, который ссылается на function3
.
function3
упоминается в определении function2
. Таким образом, вы должны связаться с libanother
если вы связываете файл, который содержит определение function2
.
Вам нужно связать такой файл?
Это зависит от реализации самого libutils
.
Дело 1
Предположим, вы реализовали libutils
как статическую библиотеку, в которой у вас есть
заархивировал объектный файл, который определяет function1
, и другой объектный файл, который
определяет function2
. Вот так:
function1.c
#include <stdio.h>
void function1(void)
{
puts(__func__);
}
function2.c
extern void function3();
void function2(void)
{
function3();
}
Скомпилируйте исходные файлы:
$ gcc -Wall -c function1.c function2.c
Архивирование объектных файлов:
$ ar rcs libutils.a function1.o function2.o
Ссылка prog
нравится:
$ gcc -o prog main.o libutils.a
Нет проблем. Run:
$ ./prog
Hello World!
Давайте сделаем связь снова, на этот раз с диагностикой, чтобы показать, что
Объектные файлы и общие библиотеки были фактически связаны:
$ gcc -o prog main.o libutils.a -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/8/crtbeginS.o
main.o
(libutils.a)function1.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/8/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6 (//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)
/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/8/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/8/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crtn.o
Большинство этих входных данных представляют собой типовые объектные файлы и общие библиотеки, которые
gcc
добавляет к программе связь по умолчанию. Файлы, которые мы построили, просто:
main.o
(libutils.a)function1.o
Член архива libutils.a(function2.o)
вообще не был связан, потому что
ни один файл не был связан с ссылками function2
. Так что не важно, что
libutils.a(function2.o)
делает неопределенную ссылку на function3
. libutils.a(function2.o)
может также не существовать. Связь:
$ gcc -o prog main.o libutils.a
точно такая же связь, как:
gcc -o prog main.o function1.o
Дело 2
Предположим, вы реализовали libutils
как статическую библиотеку, которая содержит
один объектный файл, который определяет function1
и function2
:
function12.c
#include <stdio.h>
void function1(void)
{
puts(__func__);
}
extern void function3(void);
void function2(void)
{
function3();
}
Скомпилируйте этот файл:
$ gcc -Wall -c function12.c
Воссоздать libutils.a
:
$ rm libutils.a
$ ar rcs libutils.a function12.o
Relink prog
:
$ gcc -o prog main.o libutils.a -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/8/crtbeginS.o
main.o
(libutils.a)function12.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/8/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6 (//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)
/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/8/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/8/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crtn.o
/usr/bin/ld: libutils.a(function12.o): in function `function2':
function12.c:(.text+0x32): undefined reference to `function3'
/usr/bin/ld: link errors found, deleting executable `prog'
collect2: error: ld returned 1 exit status
На этот раз, как показывает вывод -trace
, наши объектные файлы:
main.o
(libutils.a)function12.o
были связаны. main.o
ссылка function1
, поэтому компоновщик искал libutils.a
для объектного файла, который определяет function1
. Найдено function12.o
и связано
это, включая определение function2
, а также определение function1
.
Определение function2
ввело неразрешенную ссылку на function3
,
в программу. Ни один файл не был связан с этой ссылкой, поэтому
сбой связи:
function12.c:(.text+0x32): undefined reference to `function3'
Неважно, что function2
не может быть вызвано программой -
просто не имеет значения, что function1
нельзя назвать. 3
Дело 3
Вы реализовали libutils
как разделяемую библиотеку, связав независимую от позиции связь
объектные файлы function1.o
и function2.o
, скомпилированные из function1.c
и function2.c
соответственно:
$ gcc -Wall -fPIC -c function1.c function2.c
$ gcc -shared -o libutils.so function1.o function2.o
Обратите внимание, что хотя общая библиотека libutils.so
представляет собой файл ELF
linkage (не ar
архив объектных файлов), мы можем успешно связать его
несмотря на то, что function2.o
содержит неопределенную ссылку на function3
.
Можно связать общую библиотеку с неопределенными ссылками. Смотрите таблицу символов:
$ nm libutils.so
0000000000004030 b completed.7930
w __cxa_finalize@@GLIBC_2.2.5
0000000000001060 t deregister_tm_clones
00000000000010d0 t __do_global_dtors_aux
0000000000003e18 t __do_global_dtors_aux_fini_array_entry
0000000000004028 d __dso_handle
0000000000003e20 d _DYNAMIC
0000000000001150 t _fini
0000000000001110 t frame_dummy
0000000000003e10 t __frame_dummy_init_array_entry
00000000000020f0 r __FRAME_END__
0000000000002020 r __func__.2361
0000000000001115 T function1
0000000000001142 T function2
U function3
0000000000004000 d _GLOBAL_OFFSET_TABLE_
w __gmon_start__
000000000000202c r __GNU_EH_FRAME_HDR
0000000000001000 t _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
U printf@@GLIBC_2.2.5
0000000000001090 t register_tm_clones
Есть U function3
. Но вы не можете связать программу с неопределенными ссылками:
$ gcc -o prog main.o libutils.so -Wl,-trace
/usr/bin/ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/8/crtbeginS.o
main.o
libutils.so
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/8/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6 (//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)
/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/8/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/8/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/crtn.o
/usr/bin/ld: libutils.so: undefined reference to `function3'
/usr/bin/ld: link errors found, deleting executable `prog'
collect2: error: ld returned 1 exit status
Когда мы ссылаемся:
main.o
libutils.so
связь не работает так же, как с:
main.o
(libutils.a)function12.o
или действительно:
$ gcc -o prog main.o function1.o function2.o
/usr/bin/ld: function2.o: in function `function2':
function2.c:(.text+0x5): undefined reference to `function3'
collect2: error: ld returned 1 exit status
Итак:
Требуется ли связывание во всех зависимостях?
Да, когда вы связываете программу . Но поймите, что зависимости от
программа:
Зависимости программы: объектные файлы и / или общие библиотеки
которые определяют символы, на которые ссылаются в объектных файлах и общих библиотеках
которые на самом деле связаны.
Статическая библиотека никогда не бывает таковой. Это контейнер объектных файлов, из которогокомпоновщик извлекает и связывает те, которые являются зависимостями программы, и игнорирует
остальное.
[1] То, что компоновщик
делает , чтобы связать объектный файл с программой, принципиально
отличается от того, что он делает, чтобы связать общую библиотеку.
Грубо говоря, чтобы связать объектный файл, он физически объединяет части, из которых объект
Файл состоит из соответствующих частей программы вывода.
Грубо говоря, чтобы связать разделяемую библиотеку liby.so
, компоновщик просто записывает краткую информацию в
стандартный формат в программе, который будет проанализирован загрузчиком времени выполнения,
поручив ему найти и загрузить liby.so
и объединить части, из которых он состоит
составлено в процессе выполненной программы.
[2] Вы можете проверить, имеет ли ваша цепочка инструментов GCC значение по умолчанию --as-needed
или нет
путем поиска этой опции в подробном выводе связи GCC по умолчанию, например
$ gcc -v -o prog main.o function1.o 2>&1 | grep '\--as-needed'
[3] Мы можем скомпилировать объектные файлы с опцией компиляции не по умолчанию -ffunction-sections
и связать их в программах с параметром компоновщика не по умолчанию -gc-sections
, с
эффект, что компоновщик сможет отбросить определения функций, которые
не может вызвать, так что они не могут привести к каким-либо ссылкам, которые навязывают бесполезные
зависимости от программы.