По вашим тегам вы спрашиваете о связывании статических библиотек. Компоновщик
не знает или не волнует, на каком языке (ах) исходные файлы были скомпилированы
от. Язык C не имеет значения для вопроса, но я буду использовать C для иллюстрации.
Читать Stackoverflow
пометьте вики о статических библиотеках и это объяснит, что связывание
ваша программа со статической библиотекой - это то же самое, что связывание вашей программы
с 0 или более объектными файлами, которые заархивированы в статической библиотеке, а именно
0 или более объектных файлов, которые необходимы компоновщику для определения других
неразрешенные символьные ссылки в программе.
Как только компоновщик обнаружил объектный файл p.o
файл в статической библиотеке libx.a
, которая его предоставляет
с определением некоторого символа foo
, на который ссылается программа, она свяжется
объектный файл libx.a(p.o)
в программу для разрешения foo
. Я не буду
попробуйте найти любое другое определение foo
в любом другом объектном файле q.o
file в любом другом
статическая библиотека liby.a
, которая идет после libx.a
в связке.
Так что, если равен , то любой другой объектный файл q.o
находится в любой другой статической библиотеке liby.a
что позже в связи, чем libx.a
, который также содержит определение
foo
, этот объектный файл liby.a(q.o)
даже не будет связан с программой
, если компоновщик не нуждается в определении некоторого другого символа bar
что программа относится к. Если предположить, что это не так, liby.a(q.o)
может
а также не существует, для целей связи.
компоновщик не будет связывать несколько определений одного и того же символа из libx.a(p.o)
и liby.a(q.o)
, если он
не нужно. Он свяжет первый объектный файл libx.a(p.o)
, который определяет foo
в программу, а затем это делается с определением foo
.
Вот иллюстрация этого:
main.c
extern void foo(void);
extern void bar(void);
int main(void)
{
foo();
bar();
return 0;
}
p.c
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
q.c
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
r.c
#include <stdio.h>
void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
Функция foo
определена в p.c
, а также в q.c
.
Скомпилируйте все файлы .c
в .o
файлы:
$ gcc -c main.c p.c q.c r.c
Создание трех статических библиотек, по одной из p.o
, q.o
, r.o
:
$ ar rcs libx.a p.o
$ ar rcs liby.a q.o
$ ar rcs libz.a r.o
Затем свяжите программу, введя libx.a
перед liby.a
:
$ gcc -o prog main.o libz.a libx.a liby.a -Wl,-trace-symbol=foo
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: libx.a(p.o): definition of foo
Опция диагностической связи -Wl,-trace-symbol=foo
просит компоновщика сообщить
нам имена файлов, которые связаны с prog
, где он находит неразрешенные ссылки на foo
и
также имя файла, где определено foo
. Вы видите, что на foo
есть ссылка в main.o
и определение, предоставленное libx.a(p.o)
, связано. Другое определение foo
в
liby.a(q.o)
не связано. Эта связь точно такая же, как
gcc -o prog main.o r.o p.o
, который содержит только определение foo
из p.o
, как программа
показывает:
$ ./prog
foo from p.c
bar from r.c
Теперь перенесите prog
, на этот раз с liby.a
до libx.a
:
$ gcc -o prog main.o libz.a liby.a libx.a -Wl,-trace-symbol=foo
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: liby.a(q.o): definition of foo
На этот раз определение foo
было связано с liby.a(q.o)
. Эта связь точно такая же как:
gcc -o prog main.o r.o q.o
, который содержит только определение foo
из q.o
как программы
показывает:
$ ./prog
foo from q.c
bar from r.c
Компоновщику все равно, сколько определений foo
вы предлагаете в
разные объектные файлы в разных статических библиотеках. Все равно что
если в программе есть ссылка на foo
, то foo
определяется ровно один раз,
по файлам, которые он связывает в программе .
Если вы заставляете компоновщика связывать файлы в программе, которые содержат больше
чем одно определение foo
, то по умолчанию и обычно компоновщик будет
даст вам множественную ошибку определения и связь не будет выполнена, потому что
в программе не может быть более одного определения foo
. Вот
иллюстрация того:
qr.c
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
Скомпилируйте этот файл:
$ gcc -c qr.c
Архив qr.o
в новой статической библиотеке:
$ ar rcs libyz.a qr.o
Файл объекта libyz.a(qr.o)
определяет как foo
, так и bar
. Таким образом, мы можем связать
наша программа, как:
$ gcc -o prog main.o libyz.a -Wl,-trace-symbol=foo,-trace-symbol=bar
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: main.o: reference to bar
/usr/bin/ld: libyz.a(qr.o): definition of foo
/usr/bin/ld: libyz.a(qr.o): definition of bar
И это работает так:
$ ./prog
foo from qr.c
bar from qr.c
Но если мы попытаемся связать это как:
$ gcc -o prog main.o libx.a libyz.a -Wl,-trace-symbol=foo,-trace-symbol=bar
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: main.o: reference to bar
/usr/bin/ld: libx.a(p.o): definition of foo
/usr/bin/ld: libyz.a(qr.o): in function `foo':
qr.c:(.text+0x0): multiple definition of `foo'; libx.a(p.o):p.c:(.text+0x0): first defined here
/usr/bin/ld: libyz.a(qr.o): definition of bar
collect2: error: ld returned 1 exit status
существует множественное определение foo
. Это потому что:
- Компоновщику требовалось определение
foo
, и он нашел первое в libx.a(p.o)
;
поэтому он связал этот файл с программой. Он не будет искать другой.
- Компоновщику требовалось определение
bar
, и он нашел первое в libyz.a(qr.o)
;
поэтому он связал этот файл с программой. Он не будет искать другой.
- Но
libyz.a(qr.o)
содержит другое определение foo
, а также определение
bar
. Итак, теперь два определения foo
были связаны, и это ошибка.
Я сказал, что вы получите ошибку множественного определения по умолчанию , если вы сделаете
компоновщик пытается связать в программе более одного файла, который определяет символ.
Но вы можете избежать этого, сообщив компоновщику, что символ слабо определен , при условии, что ваш компоновщик понимает эту концепцию (как это делают компоновщики GNU и Apple).
Компиляторы GCC поддерживают нестандартное расширение языка, синтаксис __attribute__
что вы можете использовать, чтобы сообщить компоновщику, что определение символа является слабым .
Вот иллюстрация этого:
qr.c (2)
#include <stdio.h>
void __attribute__((weak)) foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
Рекомпилированные:
$ gcc -c qr.c
Удалить libyz.a
и воссоздать его:
$ rm libyz.a
$ ar rcs libyz.a qr.o
Повторите попытку, которая только что потерпела неудачу:
$ gcc -o prog main.o libx.a libyz.a -Wl,-trace-symbol=foo,-trace-symbol=bar
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: main.o: reference to bar
/usr/bin/ld: libx.a(p.o): definition of foo
/usr/bin/ld: libyz.a(qr.o): definition of bar
На этот раз без ошибок. Определение foo
из libx.a(p.o)
является
связаны между собой. Слабое определение из libyz.a(qr.o)
игнорируется. Программа
работает как:
$ ./prog
foo from p.c
bar from qr.c
Если определение символа не является слабым, оно будет сильным . Правила компоновщика:
- Можно связать самое большее одно сильное определение символа.
- Если вводится одно или несколько слабых определений, а также строгое определение,
тогда одно сильное определение связывается, а все слабые игнорируются.
- Если вводятся только слабые определения, то компоновщик может выбрать любое из них
произвольно. (На практике он выбирает тот, который найдет первым).
Не использовать слабые определения символов просто для того, чтобы убежать от множественного определения
ошибки, которые застают вас врасплох. Такие сюрпризы означают, что вы не понимаете
ваша связь. Проанализируйте и исправьте, чтобы вы больше не пытались
программа, содержащая несколько определений одной и той же вещи.
Слабые определения символов обычно генерируются вашим компилятором за
сцен, чтобы реализовать функции исходного языка, которые требуют их (например,
глобальные определения встроенных функций или экземпляры шаблонов в C ++).
Используйте их самостоятельно, только если вы точно понимаете, почему хотите компоновщик
введите несколько определений одного и того же символа.