Вопрос линкера ld: опция --whole-archive

Единственное реальное использование --whole-archive Опция линкера, которую я видел, заключается в создании разделяемых библиотек из статических. Недавно я наткнулся на Makefile (s), которые всегда используют эту опцию при связывании с домашними статическими библиотеками. Это, конечно, заставляет исполняемые файлы без необходимости извлекать объектный код без ссылок. Моей реакцией на это было то, что это совершенно неправильно, я что-то здесь упускаю?

Второй вопрос, который у меня есть, связан с чем-то, что я прочитал относительно опции "целый архив", но не смог разобрать. Что-то такое, что --whole-archive опция должна использоваться при связывании со статической библиотекой, если исполняемый файл также связывается с общей библиотекой, которая, в свою очередь, имеет (частично) тот же объектный код, что и статическая библиотека. То есть разделяемая библиотека и статическая библиотека перекрываются с точки зрения объектного кода. При использовании этой опции все символы (независимо от использования) будут разрешены в исполняемом файле. Это должно избежать дублирования объектного кода. Это сбивает с толку, если символ упоминается в программе, он должен быть однозначно разрешен во время соединения, что это за дублирование? (Простите, если этот пункт не совсем воплощение ясности)

Спасибо

5 ответов

Есть законное использование --whole-archive при связывании исполняемого файла со статическими библиотеками. Одним из примеров является создание кода C++, где глобальные экземпляры "регистрируются" в своих конструкторах (предупреждение: непроверенный код):

main.cc

typedef void (*handler)(const char *protocol);
typedef map<const char *, handler> M;
M m;

void register_handler(const char *protocol, handler) {
   m[protocol] = handler;
}
int main(int argc, char *argv[])
{
   for (int i = 1; i < argc-1; i+= 2) {
      M::iterator it = m.find(argv[i]);
      if (it != m.end()) it.second(argv[i+1]);
   }
}

http.cc (часть libhttp.a)

class HttpHandler {
  HttpHandler() { register_handler("http", &handle_http); }
  static void handle_http(const char *) { /* whatever */ }
};
HttpHandler h; // registers itself with main!

Обратите внимание, что в http.cc тот main.cc необходимо. Если вы связываете это как

g++ main.cc -lhttp

вы не получите http-обработчик, связанный с основным исполняемым файлом, и не сможете вызвать handle_http(), Сравните это с тем, что происходит, когда вы ссылаетесь как:

g++ main.cc -Wl,--whole-archive -lhttp -Wl,--no-whole-archive

Тот же стиль "самостоятельной регистрации" также возможен в обычном C, например, с __attribute__((constructor)) Расширение GNU.

Еще одно законное использование для --whole-archive предназначен для разработчиков инструментария для распространения библиотек, содержащих несколько функций, в одной статической библиотеке. В этом случае поставщик не знает, какие части библиотеки будут использоваться потребителем, и поэтому должен включать все.

Дополнительный хороший сценарий, в котором --whole-archive хорошо используется при работе со статическими библиотеками и инкрементными ссылками.

Давайте предположим, что:

  1. libA реализует a() а также b() функции.
  2. Некоторая часть программы должна быть связана с libA только, например, из-за использования функции --wrap (классический пример malloc)
  3. libC реализует c() функции и использует a()
  4. финальная программа использует a() а также c()

Дополнительные этапы связывания могут быть:

ld -r -o step1.o module1.o --wrap malloc --whole-archive -lA
ld -r -o step2.o step1.o module2.o --whole-archive -lC
cc step3.o module3.o -o program

В противном случае --whole-archive лишит функции c() который так или иначе используется program, мешая правильному процессу компиляции.

Конечно, это особый случай, в котором необходимо выполнить инкрементное связывание, чтобы избежать переноса всех вызовов на malloc во всех модулях, но это случай, который успешно поддерживается --whole-archive,

Я согласен, что использование -whole-archive для создания исполняемых файлов, вероятно, не то, что вам нужно (из-за ссылок в ненужном коде и создания раздутого программного обеспечения). Если у них была веская причина для этого, они должны были документировать это в системе сборки, как теперь вам остается только догадываться.

Что касается вашей второй части вопроса. Если исполняемый файл связывает как статическую библиотеку, так и динамическую библиотеку, которая имеет (частично) тот же объектный код, что и статическая библиотека, то -whole-archive будет гарантировать, что во время компоновки код из статической библиотеки предпочтителен. Обычно это то, что вы хотите, когда вы делаете статические ссылки.

Старый запрос, но по вашему первому вопросу ("Почему") я видел --whole-archive, который также используется для внутренних библиотек, в первую очередь для обхода циклических ссылок между этими библиотеками. Это имеет тенденцию скрывать плохую архитектуру библиотек, поэтому я бы не рекомендовал это. Однако это быстрый способ получить быструю пробную работу.

Для вашего второго запроса, если один и тот же символ присутствовал в общем объекте и статической библиотеке, компоновщик будет удовлетворять ссылке с той библиотекой, с которой он встречается первым.
Если общая библиотека и статическая библиотека имеют точный общий доступ к коду, это может сработать. Но если совместно используемая библиотека и статическая библиотека имеют разные реализации одних и тех же символов, ваша программа все равно будет компилироваться, но будет вести себя по-разному в зависимости от порядка библиотек.

Принудительная загрузка всех символов из статической библиотеки - это один из способов избежать путаницы относительно того, что загружается откуда. Но в целом это звучит как решение не той проблемы; в большинстве случаев вам не нужны одни и те же символы в разных библиотеках.

Другие вопросы по тегам