C и C ++ статические ссылки: просто копия? - PullRequest
12 голосов
/ 17 марта 2011

Когда кто-то статически связывает .lib, будет ли компоновщик копировать все содержимое библиотеки lib в конечный исполняемый файл или только функции, используемые в объектных файлах?

Ответы [ 6 ]

18 голосов
/ 17 марта 2011
  • Вся библиотека? - Нет.
  • Только те функции, которые вы вызывали? - Нет.
  • Что-то еще? - Да.

Это, конечно, не бросает в целомбиблиотека.

Но она не обязательно включает только " функции, используемые в объектных файлах ".

Компоновщик создаст рекурсивно построенный список того, какой объектмодули в библиотеке удовлетворяют вашим неопределенным символам.

Затем он будет включать каждый из этих объектных модулей.

Как правило, данный объектный модуль будет включать в себя более одной функции, и если некоторые из нихне вызываются теми, кого вы делаете , вы получите некоторое количество функций (и объектов данных), которые вам не нужны.

4 голосов
/ 17 марта 2011

Компоновщик обычно не удаляет мертвый код перед сборкой окончательного исполняемого файла. То есть он (обычно) будет связывать во ВСЕХ символах, используются ли они в конечном исполняемом файле или нет. Однако компоновщики часто явно предоставляют параметры оптимизации, которые вы можете использовать, чтобы заставить компоновщика приложить дополнительные усилия.

Для GCC это выполняется в два этапа:

  1. Сначала скомпилируйте данные, но скажите компилятору разделить код на отдельные разделы в модуле перевода. Это будет сделано для функций, классов и внешних переменных с помощью следующих двух флагов компилятора:

    -fdata-секции -функция-секции

  2. Соедините блоки перевода вместе, используя флаг оптимизации компоновщика (это заставляет компоновщик отбрасывать несвязанные разделы):

    -Wl, - ГЦ-секции

Итак, если у вас был один файл с именем test.cpp, в котором были объявлены две функции, но одна из них не использовалась, вы можете опустить неиспользуемый файл с помощью следующей команды для gcc (g ++):

gcc -Os -fdata-sections -ffunction-sections test.cpp -o test.o -Wl,--gc-sections

(Обратите внимание, что -Os - это дополнительный флаг компилятора, который указывает GCC оптимизировать размер)

Что касается MSVC, связывание на уровне функций выполняет то же самое. Я считаю, что флаг компилятора для этого (для сортировки вещей по разделам):

/Gy

А затем флаг компоновщика (для удаления неиспользуемых разделов):

/OPT:REF
3 голосов
/ 17 марта 2011

Линкеры были изобретены в древние времена, когда память была особенно драгоценной. Одной из их основных функций было удаление модулей, которые вы не использовали. Эта способность была перенесена на сегодняшний день.

Впрочем, некоторые библиотечные функции довольно часто полагаются на другие, и все зависимости будут связаны.

0 голосов
/ 17 марта 2011

Зависит от компоновщика.Некоторые компоновщики ленивы и просто добавляют всю библиотеку. Другая крайность - компоновщики, которые добавляют только необходимый код в исполняемый файл.

Пример теста - написать программу, которая использует puts, и сравнить с программой, которая использует printf.Если исполняемые файлы одинакового размера, у вас больше ленивого компоновщика.

Пример:

put_test.cpp

#include <cstdio>
using namespace std;

int main(void)
{
  puts("Hello World\n");
  return 0;
}

printf_test.cpp

#include <cstdio>
using namespace std;

int main(void)
{
  printf("%s\n", "Hello World");
  return 0;
}

В приведенном выше примере функция puts не требует дополнительного кода для разбора строк формата или преобразования чисел в текст.Это базовая линия, потому что она требует минимальной библиотечной функции.

Пример использования printf требует большей функциональности.Функция printf требует анализа строки формата и вывода текста.

Ожидаемый результат заключается в том, что исполняемый файл printf должен быть больше исполняемого файла puts.Большинство компиляторов будут использовать весь код для функции printf для разрешения символов (например, для отображения float s), даже если эта часть кода не используется.Более интеллектуальные (и дорогостоящие) компиляторы нарушают функцию printf и включают только те части, которые используются или требуются.В приведенном выше примере компилятор должен включать только части для обработки текста и не включать код для форматирования целых чисел и значений с плавающей запятой.

Ленивый компилятор или в режиме отладки скопирует всю библиотеку для примера puts, таким образом выполняя исполняемые файлы одинакового размера.

Сравнение символов

Платформы * nix и Cygwin предоставляют инструменты для получения символов из исполняемых файлов.Одна из таких утилит - nm.Запустите nm для каждого исполняемого файла, направив вывод в текстовый файл.Сравните два текстовых файла.Ленивые компиляторы должны иметь одинаковые символы;кроме их местоположения могут отличаться (что не важно для вопроса).

0 голосов
/ 17 марта 2011

Он будет использовать только используемые функции и символы (если не указано иное, но это может быть сложно).

Дополнительная информация:

Это действительно может быть проблемой, если вы f.ex. есть классы, которые просто регистрируются на фабрике. Никто не вызывает эти классы напрямую, поэтому они не будут включены и, следовательно, не зарегистрированы на фабрике. Есть способы обойти это (обычно путем объявления некоторой анонимной переменной в заголовочном файле, которая ссылается на исходный файл).

0 голосов
/ 17 марта 2011

Вроде.Однако также необходимо исправить все указатели на вызовы функций.Особенно, если эти вызовы функций существуют вне статической библиотеки (то есть в другой статической библиотеке или исполняемом файле).

...