2011-01-27 3 views
4

Я задал себе следующий вопрос, когда я обсуждал тему this.Мусор из других связующих единиц

Существуют ли случаи, когда какой-либо неиспользуемый код из единиц перевода связывается с окончательным исполняемым кодом (в режиме разблокировки, конечно) для популярных компиляторов, таких как GCC и VC++?

Например предположим, у нас есть 2 единицы компиляции:

//A.hpp 
//Here are declarations of some classes, functions, extern variables etc. 

и исходный файл

//A.cpp 
//defination of A.hpp declarations 

И, наконец, основные

//main.cpp 
//including A.hpp library 
#include "A.hpp" 
//here we will use some stuff from A.hpp library, but not everything 

Мой вопрос. Что делать, если в main.cpp используется не весь материал из A.hpp? Удаляет ли компоновщик весь неиспользуемый код или есть некоторые случаи, когда какой-то неиспользуемый код может связываться с исполняемым файлом?

Редактировать: Меня интересуют линкеры G ++ и VC++.

Редактировать: Конечно, я имею в виду в режиме выпуска.

Редактировать: Я начинаю благодарность за этот вопрос, чтобы получить хороший и полный ответ. Я ожидаю ответа, который объяснит, в каких случаях ссылки g ++ и VC++ связывают мусор и какой код они могут удалить из исполняемого файла (ненужные функции, ненужные глобальные переменные, ненужные определения классов и т. Д.) И почему они не могут удалить какой-то ненужный материал.

+1

Укажите, пожалуйста, компилятор. – leppie

+0

Для GCC выполните поиск SO для '-gc-sections'. Я уже ответил на этот вопрос. Я думаю, что это называется привязкой на уровне функций для VC++. – leppie

+0

@leppie Я искал в Интернете ссылку на функциональный уровень. Хорошо, это частично ответ, но как насчет классов, внешних переменных и т. Д.? Удалят ли они из исполняемого файла или нет? А также есть что-то подобное для gcc? – UmmaGumma

ответ

4

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

Для НКУ, это осуществляется в два этапа:

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

    -fdata сечения -ffunction сечения

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

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

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

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

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

Я также прочитал где-то, что связь Однако статические библиотеки разные. В этом случае GCC автоматически пропускает неиспользуемые символы. Возможно, другой плакат может подтвердить/опровергнуть это.

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

/Gy 

И тогда флаг линкер (отбросить неиспользуемые секции):

/OPT:REF 

EDIT: После дальнейших исследований, Я думаю, что бит о GCC автоматически делает это для статических библиотек ложным.

1

Компонент не удаляет код.

Вы по-прежнему можете получить к нему доступ через dlsym динамически в своем коде.

+2

Ну, конечно, вы можете сказать компоновщику, чтобы отменить неиспользуемые разделы кода. – leppie

+0

@Benoit Thiery Я пытаюсь выяснить, как работает dlsym. Я написал паровую программу http://pastebin.com/YpfFTkkn, а затем попытаюсь вызвать ее метод из другого http://pastebin.com/en8NjsQF.Но я получаю смену. Так что я делаю неправильно? – UmmaGumma

+0

dlopen позволяет загружать динамическую библиотеку (.so-файл), но не другой исполняемый файл. Пожалуйста, проверьте ошибки dlopen перед вызовом dlsym с помощью дескриптора. –

1

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

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

В коде C++, если исходный файл явно скомпилирован и связан с вашим приложением, я бы ожидал, что объекты со статическим хранением, в которых есть конструкторы и/или деструкторы, будут включены, а их конструкторы/деструкторы будут выполняться в соответствующие времена. Следовательно, любой код, вызванный этими конструкторами или деструкторами, должен быть в конечном исполняемом файле. Однако, если код не вызывается из любого места, вы не можете написать программу, чтобы сообщить, включен или нет код без использования таких вещей, как dlsym, поэтому компоновщик может опустить его, чтобы включить его в окончательный исполняемый файл.

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

0

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

Смежные вопросы