ld linker question: параметр --whole-archive

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

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

Спасибо


person Community    schedule 30.04.2009    source источник


Ответы (5)


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

handlers.h

typedef void (*handler)(const char *data);
void register_handler(const char *protocol, handler h);
handler get_handler(const char *protocol);

handlers.cc (часть libhandlers.a)

typedef map<const char*, handler> HandlerMap;
HandlerMap m;
void register_handler(const char *protocol, handler h) {
   m[protocol] = h;
}
handler get_handler(const char *protocol) {
   HandlerMap::iterator it = m.find(protocol);
   if (it == m.end()) return nullptr;
   return it->second;
}

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

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

main.cc

#include <handlers.h>
int main(int argc, char *argv[])
{
    for (int i = 1; i < argc-1; i+= 2) {
        handler h = get_handler(argv[i]);
        if (h != nullptr) h(argv[i+1]);
    }
}

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

g++ main.cc -lhttp -lhandlers

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

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

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

person Employed Russian    schedule 09.05.2009
comment
Russion Если libhttp.a может быть собран, это доказывает, что функция register_handler существовала в этом libhttp.a. Итак, как эта функция может ссылаться на register_handler в main.cc? Так что в этом случае мы должны использовать какой-то другой способ реализовать вашу идею. - person longbkit; 21.10.2011
comment
@longbkit Я обновил ответ, чтобы обработчики были вынесены в библиотеку нижнего уровня, как и должно быть. Я устоял перед соблазном изменить тип handler с указателя функции C на C ++ std::function - person Arthur Tacca; 10.09.2019
comment
Есть ли способ нацелить всю функциональность архива на определенные символы глобальной регистрации, а не на всю библиотеку? - person David; 28.07.2020
comment
@David Если вы хотите получить только определенный набор символов, а не весь архив, --whole-archive (очевидно) не подходит. Вместо этого используйте -Wl,-u,needed_symbol. - person Employed Russian; 28.07.2020

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

person Steve Brooks    schedule 07.02.2011
comment
статические библиотеки включают все без использования --whole-archive, это кажется довольно бессмысленным делом - person WinterMute; 23.01.2019
comment
Совершенно неверно. Когда вы создаете библиотеку, статическую или динамическую, она будет содержать ВСЕ именованные объектные файлы. - person Swiss Frank; 29.09.2020

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

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

person lothar    schedule 04.05.2009
comment
одна причина использовать —whole-archive: обычно это используется для превращения файла архива (.o / .a) в разделяемую библиотеку, заставляя каждый объект быть включенным в результирующую разделяемую библиотеку, чтобы объединить несколько динамических библиотек при компиляции статической библиотеки во-первых. ref: Использование LD, компоновщика GNU - Параметры - person Kevin Chou; 23.04.2021

Еще один хороший сценарий, в котором --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.

person ilpelle    schedule 10.01.2017

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

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

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

person Rob Swarbrick    schedule 27.10.2015