У меня есть два исходных кода, и я хочу связать их.
// test.c
#include <stdio.h>
void lib2();
void lib1(){
lib2();
return 0;
}
// main.c
#include <stdio.h>
int main() {
return 0;
}
Я использовал gcc -c main.c
и gcc -c test.c
для создания файлов объектов.
$ ls *.o
main.o test.o
и я использовал команду ar rcs test.a test.o
для создания статической библиотеки (test.a
) из объектного файла test.o
Затем я попытался создать исполняемый файл, связав main.o
с test.a
или test.o
. Насколько мне известно, файл статической библиотеки (расширение .a) представляет собой своего рода простую коллекцию объектных файлов (.o). поэтому я ожидал, что оба дадут одинаковый результат: ошибка или успех. но это не так.
Связывание с объектным файлом дает ошибку undefined reference
.
$ gcc -o main main.o test.o
/usr/bin/ld: test.o: in function `lib1':
test.c:(.text+0xe): undefined reference to `lib2'
collect2: error: ld returned 1 exit status
$
но связывание со статической библиотекой не дает никаких ошибок и успеха при компиляции.
$ gcc -o main main.o test.a
$
Почему это происходит? и как я могу получить ошибки undefined reference даже при линковке со статическими библиотеками?
Да, вызывая/ссылаясь на lib1()
из main.c
, я могу получить ошибку неопределенной ссылки либо для объектного файла, либо для статической библиотеки. но я хочу получить целые неопределенные ссылочные ошибки, даже если они на самом деле не указаны.
объекты и библиотеки обрабатываются компоновщиком по-разному.
Решение: соединение с --whole-archive
. например ld --whole-archive -o main main.o test.a
или gcc -o main -Wl,--whole-archive test.a main.o -Wl,--no-whole-archive
Если ваш код содержит выражение вызова функции, то стандарт языка требует наличия определения функции. (См. C11 6.9/3). Если вы не предоставите определение, то это неопределенное поведение, не требующее диагностики.
Правило было написано таким образом, чтобы поставщики реализации не были вынуждены выполнять анализ, чтобы определить, вызывается ли когда-либо функция или нет; например, в вашем библиотечном сценарии компилятор не вынужден копаться в библиотеке, если ни один из остального кода не содержит ничего, что ссылается на эту библиотеку.
Что делать, полностью зависит от реализации, и в вашем случае она решает выдать ошибку в одном случае, а не в другом. Чтобы избежать этого, вы можете предоставить определения для всех функций, которые вы вызываете.
Возможно, вы сможете изменить поведение в первом случае, используя параметры компоновщика, такие как удаление неиспользуемых разделов кода. Еще одна вещь, которую вы можете сделать, это вызвать lib1()
из main()
— это все еще не гарантирует, что приведет к ошибке, но более вероятно.
Обоснование другое. Представьте себе библиотеку, в которой некоторым функциям требуются символы из объектных файлов (например, функции обратного вызова). Если бы компоновщик анализировал любой объект в библиотеке, ваша программа должна была бы иметь все эти объекты и функции, определенные, даже если вы их не используете.
@0___________ Я не понимаю, чем это отличается от того, что я сказал
Понял. Как вы сказали, это зависит от реализации, и, как сказал @0___________, «объекты и библиотеки обрабатываются компоновщиком по-разному», кажется, в моем случае компоновщик решает вызвать ошибку неопределенной ссылки после того, как передан объектный файл, но не статическая библиотека. Чтобы найти все неопределенные ссылки, я должен извлечь все объектные файлы из статической библиотеки и связать их с ними или использовать другие методы, такие как команда nm
. Спасибо за Ваш ответ!
Этот ответ на самом деле не отвечает на вопрос «почему .o отличается от .a».
@EmployedRussian это «потому что реализация работает по-другому»
Заставьте компоновщик выполнить некоторую работу, используя опцию -flto
, и ошибка исчезнет.
ld
не ищет в библиотеках неиспользуемые объекты, а ищет только символы, используемые в объектных файлах. Представьте, что у вас есть библиотека, в которой некоторые функции требуют определенных обратных вызовов. Если у вас их нет в каждой программе, которую вы связываете с библиотекой, даже если вы не используете эти функции.
Я не понимаю, как применяются обратные вызовы. Обратные вызовы реализуются с помощью указателей функций, значение которых компоновщик обычно не может знать во время выполнения, но сам указатель функции всегда будет существовать.
Я ожидал, что оба дадут одинаковый результат: ошибка или успех. но это не так.
Ваше ожидание неверно. Хорошее объяснение разницы между .o
и .a
в отношении ссылок — здесь.
Попробуйте вызвать
lib1()
изmain()
(ошибка все равно не гарантирована, но с большей вероятностью)