Вот возник вопрос: 1. При сборке линкуются все функции или только те которые используются?
2. А если только те, которые используются, то как линкер умудряется оторвать их тело от всего остального контекста?
Меня даже больше интересуют ответ на второй вопрос...
Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>Всем доброго времени суток!
ATP>Вот возник вопрос: 1. При сборке линкуются все функции или только те которые используются? ATP>2. А если только те, которые используются, то как линкер умудряется оторвать их тело от всего остального контекста? ATP>Меня даже больше интересуют ответ на второй вопрос...
Здравствуйте, BlackHeretic, Вы писали:
ATP>>Всем доброго времени суток!
ATP>>Вот возник вопрос: 1. При сборке линкуются все функции или только те которые используются? ATP>>2. А если только те, которые используются, то как линкер умудряется оторвать их тело от всего остального контекста? ATP>>Меня даже больше интересуют ответ на второй вопрос...
BH>1. Все. BH>2. см. отв. 1
Интересно из каких соображений это выдумано ?
В действительности стандарт C++ это не оговаривает. Поэтому каждый производитель сам решает что и как в данной ситуации делать.
Майкрософтовский линкер (по крайней мере от VC 6.0 и далее) выбрасывает не использующиеся ф-ии. Более того (впрочем, возможно это делает компилятор) он может объединять одинаковые ф-ии. Например код vector<int> и vector<long> ничем не различается (32 бинтая x86 платформа), поэтому он объединяется в одну ф-ию (проверено).
Как выбрасывает ? Это доподлинно мне не извесно.
Однако никаких трудностей я не вижу...
Допустим: Каждый кусок кода помечен именем. Это может быть ф-ия, глобальная переменная. Также есть таблица использования — какой блок что использует. Линкер находит куски, которые не используются другими другими и не включает их в результирующий файл. Поскольку в разных call и jmp используется не абсолютный адрес, а относительный — их править не нужно. А те куски, что нужно прописаны в другой таблице — релокаций. В итоге получаем довольно худой файл.
Здравствуйте, Sergey J. A., Вы писали:
SJA>Допустим: Каждый кусок кода помечен именем. Это может быть ф-ия, глобальная переменная. Также есть таблица использования — какой блок что использует. Линкер находит куски, которые не используются другими другими и не включает их в результирующий файл. Поскольку в разных call и jmp используется не абсолютный адрес, а относительный — их править не нужно. А те куски, что нужно прописаны в другой таблице — релокаций. В итоге получаем довольно худой файл.
Здравствуйте, rus blood, Вы писали:
SJA>>Допустим: Каждый кусок кода помечен именем. Это может быть ф-ия, глобальная переменная. Также есть таблица использования — какой блок что использует. Линкер находит куски, которые не используются другими другими и не включает их в результирующий файл. Поскольку в разных call и jmp используется не абсолютный адрес, а относительный — их править не нужно. А те куски, что нужно прописаны в другой таблице — релокаций. В итоге получаем довольно худой файл.
RB>Сборка мусора по периметру. GarbageCollector.
22.
Здравствуйте, Sergey J. A., Вы писали:
SJA>Здравствуйте, BlackHeretic, Вы писали:
ATP>>>Всем доброго времени суток!
ATP>>>Вот возник вопрос: 1. При сборке линкуются все функции или только те которые используются? ATP>>>2. А если только те, которые используются, то как линкер умудряется оторвать их тело от всего остального контекста? ATP>>>Меня даже больше интересуют ответ на второй вопрос...
BH>>1. Все. BH>>2. см. отв. 1
SJA>Интересно из каких соображений это выдумано ?
Во первых, BlackHeretic имеет совершенно отличную точку зрения от вашей
SJA>В действительности стандарт C++ это не оговаривает. Поэтому каждый производитель сам решает что и как в данной ситуации делать. SJA>Майкрософтовский линкер (по крайней мере от VC 6.0 и далее) выбрасывает не использующиеся ф-ии.
Выбрасывает из исходного кода, во время компиляции — да, а из линкуемой либы — вопрос?
Более того (впрочем, возможно это делает компилятор) он может объединять одинаковые ф-ии. Например код vector<int> и vector<long> ничем не различается (32 бинтая x86 платформа), поэтому он объединяется в одну ф-ию (проверено).
Все, что выбрасывается на стадии компиляции, меня не интересовало, итак понятно.
SJA>Как выбрасывает ? Это доподлинно мне не извесно.
Вот это собственно и был мой вопрос.
SJA>Однако никаких трудностей я не вижу...
А я как раз вижу, причем на мой взгляд неразрешимые.
SJA>Допустим: Каждый кусок кода помечен именем. Это может быть ф-ия, глобальная переменная. Также есть таблица использования — какой блок что использует. Линкер находит куски, которые не используются другими другими и не включает их в результирующий файл.
Интересно как же он это делает, ведь функция может вызывать другие, те в свою очередь другие, и т.д. На мой взгляд разобраться в этой иерархии линкеру (не компилятору) будет очень сложно, если ввобще невозможно. И потом кто сказал, что либа вообще написана на С++, она вообще может быть написана на ассемблере, где вообще граница между функциями стирается.
SJA> Поскольку в разных call и jmp используется не абсолютный адрес, а относительный — их править не нужно. А те куски, что нужно прописаны в другой таблице — релокаций. В итоге получаем довольно худой файл.
Вот как раз с таким подходом, на их править как раз и нужно. Пример:
есть либа:
1. Глобальная таблица данных
2. Функция 1
3. Функция 2
4. Функция 3
Предположим все три фукции используют глобальные данные и что мы используем только функцию 2 из либы.
Получается, что мы должны выкинуть функции 1 и 3. А может и не должны, т.к как они могут вызываться из функции в функции которая вызывается в Функции 2? А если должны выкинуть, то смещения на глобальные данные изменится, и относительные адреса не помогут. Так как же линкер поступит в этом случае?
Есть ли действительно компетентные специалисты в данном вопросе, которые могли бы помоч разобраться мне в данном вопросе?
Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>Всем доброго времени суток!
ATP>Вот возник вопрос: 1. При сборке линкуются все функции или только те которые используются? ATP>2. А если только те, которые используются, то как линкер умудряется оторвать их тело от всего остального контекста? ATP>Меня даже больше интересуют ответ на второй вопрос...
1. Линкуется единица трансляции из библиотеки (во всяком случае gcc, в win32 скорее всего что-то похожее), т.е. имеем:
someunit.cpp
someunit2.cpp
Дык вот, если будет использоваться функция из someunit.cpp, то весь объектный someunit.o код попадет итогововый бинарь.
И если при этом из someunit2.cpp ничего использоваться не будет, то он никуда не попадет.
2. Не уверен 100%, руки еще не дошли проверить, но это возможно с флагом global optimization. Тогда
после сборки, стоится xref таблица, в которой оптимизатор ищет неиспользуемые функции и выкидывает их.
Правда если функция помечена как export, ее он точно никуда не выкенет.
Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>Во первых, BlackHeretic имеет совершенно отличную точку зрения от вашей
Вот именно. Причём ничем не подтвердмл своё высказывание.
ATP>Вот это собственно и был мой вопрос.
Ну я не пишу компиляторы. У меня есть только предположения, которые подтверждаются практикой. Больше ничем не могу помочь.
SJA>>Однако никаких трудностей я не вижу... ATP>А я как раз вижу, причем на мой взгляд неразрешимые.
А я всё же не вижу
ATP>Есть ли действительно компетентные специалисты в данном вопросе, которые могли бы помоч разобраться мне в данном вопросе?
Действительно компетентные работают над улучшением компиляторв и не лазают по форумам. Послушайте хотя бы просто компетентного.
Итак. Исследования в лабораторных условиях.
Дано: родной компилятор и линкер от MS Visual Studio 6.0, Far manager + Colorer и немного любопытства.
1. Выбрасывет ли линкер не нужные ф-ии из lib ?
Итак пишем небольшую либку с ф-ией:
void f_lib()
{
volatile int a = 0;
if(a)
{
__asm
{
int 3
int 2
}
}
};
Компилируем в релизе. Берём Far manager, ищем коды CC CD 02 — это коды нашей ассемблерной вставки. Находми. Значит ф-ия присутствует в либке.
Создаём проект, в нём импортируем ф-ию, добавляем в проект либку, добиваемся успешной компиляции (release). Код примерно такой:
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
__asm{int 3}
f_lib();
return 0;
}
Компилируем. Берём Far manager, ищем коды CC CD 02 — (коды нашей ассемблерной вставки). Находми. Значит ф-ия присутствует в екзешнике.
Замечательно. Коментируем вызов ф-ии f_lib(); теперь она не нужна и линкер может попытаться выбровить её.
Опять берём Far manager, ищем коды CC CD 02 — не находим. Вывод ?
Вывод: Линкер выбрасывает ненужные ф-ии из либок.
2. Выбрасывет ли линкер ненужные ф-ии из obj ?
Проект:
File2.cpp: здесь иы положим ф-ию с вставкой CC CD 02
void f2()
{
volatile int a = 0;
if(a)
{
__asm
{
int 3
int 2
}
}
}
File1.cpp: здесь иы положим ф-ию с вставкой CC CD 01 и используем обе.
void f2();
void f1()
{
volatile int a = 0;
if(a)
{
__asm
{
int 3
int 1
}
}
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
__asm{int 3}
f1();
f2();
return 0;
}
Компилируем, проверяем. Обе вставки на месте. Коментируем по очереди обе ф-ии, проверяем — вставки исчезают.
Вывод:
Линкер выбрасывает ненужные ф-ии из объектных файлов.
Пометка: функцию f1() не может выбросить компилятор, т.е. она не статическая и может использоваться в другом объектном файле. Компилятор не может делать предаоложения и засовывает f1 в obj в любом случае.
3. Объединяет ли линкер похожие ф-ии ?
Проект:
File2.cpp: здесь иы положим ф-ию с вставкой CC CD 02
Запускаем под отладчиком (release версия !), проверяем и видим, что адреса std::vector<int>::push_back и std::vector<long>::push_back совмадают.
Вывод:
Линкер может находить одинаковые куски кода и объединять их.
Теоретические изыскания:
Как это делает линкер ?
Шьёрт. Писал, писал. Не сохранил. А я(а)нус упал.....
Ладно. Буду краток.
Компилятор составляет таблица в которой прописано что где исиспользовано. Допустим WimMain использует f(), f() использует cool_func() ...
Линкер начинает с ф-ии указанной как entry. Ложит её. Смотрит, что она вызывает. Ложит их. И т.д. В результате все нужные ф-ии попадают в экзешник. (+ возможно есть какие-то сущности безусловно попадающие в результат). С иерархией использования думаю понятно.
Адреса.
При компиляции в объектный файл не ложаться. Вместо этого в таблице (вероятнее всего это та же таблица использования) помечается: ф-ия WinMain по смещению +10h использует отностельный адрес сущности f().
После того, как линкер соберёт все нужные процедуры он пробегается по таблице и прописывает по нужным смещениям нужные адреса. Т.е. пропишет в WinMain по мещению +10h реальный адрес f(). Причём линкеру в принципе не интересно что такое f() — ф-ия или это глобальная переменная: он знает адрес f() и знает куда его нужно вписать.
Насчёт ассемблера.
Формат объектного файла одинаков для ассемблера и С++. Если писать по определённым правилам, думаю можно добиться авбрасывания ненужных ф-ий и из асм-либки. Правила примерно такие (я только предпологаю — проверять лень):
Каждая сущность должна распологаться в отдельном сегменте. Каждая сущность должна быть PUBLIC.
Думаю в этом случае линкер будет иметь возможность покилять ненужные секции (на которые нет ссылок) и сделать релокацию и слияние оставшихся.
Вот допустим как это делает VC 6.0:
Из
#include"stdafx.h"volatile int a = 10;
void f()
{
a = 1;
};
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
f();
return 0;
}
Получаем:
PUBLIC ?a@@3HC ; Публичное имя переменной a
_DATA SEGMENT ; сегмент с переменной
?a@@3HC DD 0aH ; значние
_DATA ENDS
PUBLIC ?f@@YAXXZ ; Имя ф-ии f
; COMDAT ?f@@YAXXZ
_TEXT SEGMENT
?f@@YAXXZ PROC NEAR ; Тело ф-ии f, COMDAT
mov DWORD PTR ?a@@3HC, 1 ; a
ret 0
?f@@YAXXZ ENDP ; f
_TEXT ENDS
PUBLIC _WinMain@16
; COMDAT _WinMain@16
_TEXT SEGMENT
_WinMain@16 PROC NEAR ; COMDAT
call ?f@@YAXXZ ; f
xor eax, eax
ret 16 ; 00000010H
_WinMain@16 ENDP
_TEXT ENDS
Здравствуйте, aka50, Вы писали:
A>Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>>Всем доброго времени суток!
ATP>>Вот возник вопрос: 1. При сборке линкуются все функции или только те которые используются? ATP>>2. А если только те, которые используются, то как линкер умудряется оторвать их тело от всего остального контекста? ATP>>Меня даже больше интересуют ответ на второй вопрос...
A>1. Линкуется единица трансляции из библиотеки (во всяком случае gcc, в win32 скорее всего что-то похожее), т.е. имеем:
A>someunit.cpp A>someunit2.cpp
A>Дык вот, если будет использоваться функция из someunit.cpp, то весь объектный someunit.o код попадет итогововый бинарь. A>И если при этом из someunit2.cpp ничего использоваться не будет, то он никуда не попадет.
Вот это как-то больше похожена правду, т.к с отдельными объектниками линкеру будет гораздо проще разобраться.
И еще я для себя понял, что существует единица трансляции. Вот только интересно, ее определяет только отдельный файл, или она может включать в себя несколько файлов...
A>2. Не уверен 100%, руки еще не дошли проверить, но это возможно с флагом global optimization. Тогда A>после сборки, стоится xref таблица, в которой оптимизатор ищет неиспользуемые функции и выкидывает их. A>Правда если функция помечена как export, ее он точно никуда не выкенет.
Функции ли он выкидывает, вот в чем основной вопрос? На мой взгляд, разобраться в скомпилированном коде, непонятоно какокго языка, понять, какие функции вызываются, а какие нет, это просто нереальная задача. Совсем другое дело, выкинуть функции на этапе компиляции, по дереву разбора, это компилятору не составит труда.
Так всетаки вопрос: Викидывает ли линкер из уже откомпилированной библиотеки или oбъектника (.lib, .a, .obj, .o) отдельные функции или он может оперировать только единицами трансляции?
Здравствуйте, aka50, Вы писали:
A>Дык вот, если будет использоваться функция из someunit.cpp, то весь объектный someunit.o код попадет итогововый бинарь.
Как показали исследования VC6.0 не ложит весь объектник, а только то что нужно.
A>2. Не уверен 100%, руки еще не дошли проверить, но это возможно с флагом global optimization. Тогда A>после сборки, стоится xref таблица, в которой оптимизатор ищет неиспользуемые функции и выкидывает их. A>Правда если функция помечена как export, ее он точно никуда не выкенет.
Немного не так. Незнаю как по стандарту, но для VC6.0 любая не static ф-ия является внешней. Т.е. проще говоря если в одном файле написать ф-ию f():
void f()
{
};
то в другом файле можно написать:
void f();
void f2
{
f();
};
И всё сработает. Что говорит о том, что f по умолчанию внешняя. Поэтому компилятор может выбрасывать только static ф-ии, которые не видны извне.
Скорее вего по стандарту это правильно, ибо зачем иначе этот модификатор static для ф-ии ?
Да...... такого прогона я еще не встречал. Непойму, как человек, может объяснять другим людям, то что он сам не понимает, и над чем сам никогда всерьез не задумывался, а ставил какие-то непонятные опыты от балды...
Начинаем попорядку:
SJA>Итак. Исследования в лабораторных условиях. SJA>Дано: родной компилятор и линкер от MS Visual Studio 6.0, Far manager + Colorer и немного любопытства.
SJA>1. Выбрасывет ли линкер не нужные ф-ии из lib ? SJA>Итак пишем небольшую либку с ф-ией: SJA>
SJA>void f_lib()
SJA>{
SJA> volatile int a = 0;
SJA> if(a)
SJA> {
SJA> __asm
SJA> {
SJA> int 3
SJA> int 2
SJA> }
SJA> }
SJA>};
SJA>
SJA>Компилируем в релизе. Берём Far manager, ищем коды CC CD 02 — это коды нашей ассемблерной вставки. Находми. Значит ф-ия присутствует в либке.
Конечно — кудаж она денется
SJA>Создаём проект, в нём импортируем ф-ию, добавляем в проект либку, добиваемся успешной компиляции (release). Код примерно такой: SJA>
SJA>Компилируем. Берём Far manager, ищем коды CC CD 02 — (коды нашей ассемблерной вставки). Находми. Значит ф-ия присутствует в екзешнике.
А теперь внимание!
SJA>Замечательно. Коментируем вызов ф-ии f_lib(); теперь она не нужна и линкер может попытаться выбровить её. SJA>Опять берём Far manager, ищем коды CC CD 02 — не находим. Вывод ? SJA>Вывод: Линкер выбрасывает ненужные ф-ии из либок.
Непонимаю, как на очснове данного опыта можно смделать такой поспешный вывод?!
Да — линкер викинул либу, т.к неодна из ее функций не используется. И данный случай самый трививльный — он ниочем не говорит.
Вот если бы вы сделали 20-ток функций, и вызывали бы их все, а потом закоментировали одну, то увидели бы, что все функции в exeшнике на месте.
SJA>2. Выбрасывет ли линкер ненужные ф-ии из obj ? SJA>Проект: SJA>File2.cpp: здесь иы положим ф-ию с вставкой CC CD 02 SJA>
SJA>void f2()
SJA>{
SJA> volatile int a = 0;
SJA> if(a)
SJA> {
SJA> __asm
SJA> {
SJA> int 3
SJA> int 2
SJA> }
SJA> }
SJA>}
SJA>
SJA>File1.cpp: здесь иы положим ф-ию с вставкой CC CD 01 и используем обе. SJA>
SJA>Компилируем, проверяем. Обе вставки на месте. Коментируем по очереди обе ф-ии, проверяем — вставки исчезают. SJA>Вывод: SJA>Линкер выбрасывает ненужные ф-ии из объектных файлов.
А вот тут у человека полная каша в голове, т.к он путает ликер и компилятор. Компилятор, выкидывает функции — это понято и так, и естественно не пишет в объектник неиспользуемых функций. Теперь представте, что линкер отделная программа, и ей на вход приходит объектник, в котором есть функции составлящие либу. Предположим что вызывается из все только три. И как по вашему линкер (не компилятор) должен понять какие из них дейсвительно нужны, а какие нет?
Данный опыть совершенно не в тему....
SJA>Пометка: функцию f1() не может выбросить компилятор, т.е. она не статическая и может использоваться в другом объектном файле. Компилятор не может делать предаоложения и засовывает f1 в obj в любом случае.
SJA>3. Объединяет ли линкер похожие ф-ии ? SJA>Проект: SJA>File2.cpp: здесь иы положим ф-ию с вставкой CC CD 02 SJA>
SJA>Запускаем под отладчиком (release версия !), проверяем и видим, что адреса std::vector<int>::push_back и std::vector<long>::push_back совмадают.
SJA>Вывод: SJA>Линкер может находить одинаковые куски кода и объединять их.
Опять полная каша в голове, пулное непонимание вопроса. ЛИНКЕРРРРР — причем тут компилятор. То что вы описали делает компилятор....
SJA>Теоретические изыскания: SJA>Как это делает линкер ?
SJA>Шьёрт. Писал, писал. Не сохранил. А я(а)нус упал..... SJA>Ладно. Буду краток. SJA>Компилятор составляет таблица в которой прописано что где исиспользовано. Допустим WimMain использует f(), f() использует cool_func() ... SJA>Линкер начинает с ф-ии указанной как entry. Ложит её. Смотрит, что она вызывает. Ложит их. И т.д. В результате все нужные ф-ии попадают в экзешник. (+ возможно есть какие-то сущности безусловно попадающие в результат). С иерархией использования думаю понятно.
SJA>Адреса. SJA>При компиляции в объектный файл не ложаться. Вместо этого в таблице (вероятнее всего это та же таблица использования) помечается: ф-ия WinMain по смещению +10h использует отностельный адрес сущности f(). SJA>После того, как линкер соберёт все нужные процедуры он пробегается по таблице и прописывает по нужным смещениям нужные адреса. Т.е. пропишет в WinMain по мещению +10h реальный адрес f(). Причём линкеру в принципе не интересно что такое f() — ф-ия или это глобальная переменная: он знает адрес f() и знает куда его нужно вписать.
Тут далше просто какие-то рассуждения, как могло быть то, чего на самом деле нет.... или есть, но это совсем не потому, почему вы думаете. И делает это все не линкер.
SJA>Насчёт ассемблера. SJA>Формат объектного файла одинаков для ассемблера и С++. Если писать по определённым правилам, думаю можно добиться авбрасывания ненужных ф-ий и из асм-либки. Правила примерно такие (я только предпологаю — проверять лень): SJA>Каждая сущность должна распологаться в отдельном сегменте. Каждая сущность должна быть PUBLIC. SJA>Думаю в этом случае линкер будет иметь возможность покилять ненужные секции (на которые нет ссылок) и сделать релокацию и слияние оставшихся.
Вы хоть раз писали на ассемблере?
SJA>Вот допустим как это делает VC 6.0: SJA>Из SJA>
SJA>#include"stdafx.h"
SJA>volatile int a = 10;
SJA>void f()
SJA>{
SJA> a = 1;
SJA>};
SJA>int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
SJA>{
SJA> f();
SJA> return 0;
SJA>}
SJA>
SJA>Получаем: SJA>
SJA>PUBLIC ?a@@3HC ; Публичное имя переменной a
SJA>_DATA SEGMENT ; сегмент с переменной
SJA>?a@@3HC DD 0aH ; значние
SJA>_DATA ENDS
SJA>PUBLIC ?f@@YAXXZ ; Имя ф-ии f
SJA>; COMDAT ?f@@YAXXZ
SJA>_TEXT SEGMENT
SJA>?f@@YAXXZ PROC NEAR ; Тело ф-ии f, COMDAT
SJA> mov DWORD PTR ?a@@3HC, 1 ; a
SJA> ret 0
SJA>?f@@YAXXZ ENDP ; f
SJA>_TEXT ENDS
SJA>PUBLIC _WinMain@16
SJA>; COMDAT _WinMain@16
SJA>_TEXT SEGMENT
SJA>_WinMain@16 PROC NEAR ; COMDAT
SJA> call ?f@@YAXXZ ; f
SJA> xor eax, eax
SJA> ret 16 ; 00000010H
SJA>_WinMain@16 ENDP
SJA>_TEXT ENDS
SJA>
SJA>Как видим каждая сущность в своём сегменте.
Во пример который разбивает впух и прах ваши рассуждения насчет ассеблера:
Вот уж не думал что это вызовет такое обсуждение...
В общем правило линковки с либой такое:
Если имеется хотя бы что-то (имя функции, имя переменной) что юзается в проге — либа линкуется полностью. Если нет — выкидывается полностью (линкер обычно кидает предупреждение об этом) — хотя это как правило по умолчанию — можно сказать линкеру линковать либу в любом случае.
Полная линковка обусловлена тем, что практически невозможно быть увереным в вызове/невызове той или иной функции (кроме прямых вызовов есть еще и всякие косвенные, да такие косвенные — что без пол литры никак)
Здравствуйте, BlackHeretic, Вы писали:
BH>Здравствуйте, AcidTheProgrammer, Вы писали:
BH>Вот уж не думал что это вызовет такое обсуждение... BH>В общем правило линковки с либой такое: BH>Если имеется хотя бы что-то (имя функции, имя переменной) что юзается в проге — либа линкуется полностью. Если нет — выкидывается полностью (линкер обычно кидает предупреждение об этом) — хотя это как правило по умолчанию — можно сказать линкеру линковать либу в любом случае. BH>Полная линковка обусловлена тем, что практически невозможно быть увереным в вызове/невызове той или иной функции (кроме прямых вызовов есть еще и всякие косвенные, да такие косвенные — что без пол литры никак)
Вы правы, но только в лучае, если ваша либа реализована как один файл, т.е. линкуется в один объектник. Тут точно линкер никак не опредлит что нужно, а что нет, и вставит все.
Но сдается мне, что ака50 прав: этому есть косвенное подтверждение, т.к стандартные библиотеки С реализованы так: каждая функция в отдельном файле. Поэтому при линковке стандартых либ, линкуются только одтдельные функции, т.к каждая находится в отдельном объектнике.
Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>Здравствуйте, BlackHeretic, Вы писали:
BH>>Здравствуйте, AcidTheProgrammer, Вы писали:
BH>>Вот уж не думал что это вызовет такое обсуждение... BH>>В общем правило линковки с либой такое: BH>>Если имеется хотя бы что-то (имя функции, имя переменной) что юзается в проге — либа линкуется полностью. Если нет — выкидывается полностью (линкер обычно кидает предупреждение об этом) — хотя это как правило по умолчанию — можно сказать линкеру линковать либу в любом случае. BH>>Полная линковка обусловлена тем, что практически невозможно быть увереным в вызове/невызове той или иной функции (кроме прямых вызовов есть еще и всякие косвенные, да такие косвенные — что без пол литры никак)
ATP>Вы правы, но только в лучае, если ваша либа реализована как один файл, т.е. линкуется в один объектник. Тут точно линкер никак не опредлит что нужно, а что нет, и вставит все.
ATP>Но сдается мне, что ака50 прав: этому есть косвенное подтверждение, т.к стандартные библиотеки С реализованы так: каждая функция в отдельном файле. Поэтому при линковке стандартых либ, линкуются только одтдельные функции, т.к каждая находится в отдельном объектнике.
ATP>Только вот хочется узнать это точно — на все 100.
Про объектники честно говоря не знаю. Но логично предположить что так оно и есть (в случае если каждая функция реализована отдельным объектником).
Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>Непонимаю, как на очснове данного опыта можно смделать такой поспешный вывод?! ATP>Да — линкер викинул либу, т.к неодна из ее функций не используется. И данный случай самый трививльный — он ниочем не говорит. ATP>Вот если бы вы сделали 20-ток функций, и вызывали бы их все, а потом закоментировали одну, то увидели бы, что все функции в exeшнике на месте.
Согласен. Недоделал. Однако только что попробовал вставить 2 ф-ии в либку (в один файл). Затем использовал обе и каждую по очереди. Ответственно заявляю — ВЫБРАСЫВАЕТ ! Прежде чем ответить, потрудитесь хотя бы проверить это.
SJA>>Линкер выбрасывает ненужные ф-ии из объектных файлов.
ATP>А вот тут у человека полная каша в голове, т.к он путает ликер и компилятор. Компилятор, выкидывает функции — это понято и так, и естественно не пишет в объектник неиспользуемых функций.
Боюсь тут ты не правы. А если данная ф-ия будет использовться в другом объектнике, а компилятор её уже выбросил — что тогда ?
ATP> Теперь представте, что линкер отделная программа, и ей на вход приходит объектник, в котором есть функции составлящие либу. Предположим что вызывается из все только три. И как по вашему линкер (не компилятор) должен понять какие из них дейсвительно нужны, а какие нет?
Я же это кажется подробно описал.
ATP>Данный опыть совершенно не в тему....
Данный опыт бвл всего лиш не полон. Он показал что линкер по крайней мере может выбрасывать неиспользуемые объектники из либки. Я дополнил тест см. выше и выяснил, что и отдельные ф-ии он выбрасывет.
SJA>>Вывод: SJA>>Линкер может находить одинаковые куски кода и объединять их.
ATP>Опять полная каша в голове, пулное непонимание вопроса. ЛИНКЕРРРРР — причем тут компилятор. То что вы описали делает компилятор....
Не нужно волноваться. Объединяет объектники — линкер. Две приведённые ф-ии были в разных объектниках. Их объединил линкер.
ATP>Тут далше просто какие-то рассуждения, как могло быть то, чего на самом деле нет.... или есть, но это совсем не потому, почему вы думаете. И делает это все не линкер.
Ок. В таком случае расскажите, как они это делают ? Но прежде чем расказать какую-нибудь глупость советую почитать форматы: PE32, COFF
ATP>Вы хоть раз писали на ассемблере?
Ну.... Лет 5. Но давно.
ATP>Во пример который разбивает впух и прах ваши рассуждения насчет ассеблера:
ATP>
ATP>И какую я функцию интересно вызвал? Этого линкеру никогда не понять.
Это я протещу в ближайшее время.
ATP>Поэтому вывод такой, ввсе что вы описали — это антинаучно и вообще полное непонимание, того что просходит в процессе компиляции.
Конечно-конечно.
Здравствуйте, Sergey J. A., Вы писали:
SJA>Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>>Непонимаю, как на очснове данного опыта можно смделать такой поспешный вывод?! ATP>>Да — линкер викинул либу, т.к неодна из ее функций не используется. И данный случай самый трививльный — он ниочем не говорит. ATP>>Вот если бы вы сделали 20-ток функций, и вызывали бы их все, а потом закоментировали одну, то увидели бы, что все функции в exeшнике на месте.
SJA>Согласен. Недоделал. Однако только что попробовал вставить 2 ф-ии в либку (в один файл). Затем использовал обе и каждую по очереди. Ответственно заявляю — ВЫБРАСЫВАЕТ ! Прежде чем ответить, потрудитесь хотя бы проверить это.
Сейчас вмотрел исходники VCRT. И зачем это разработчики С++ библиотек, пихали каждую функцию в один объектник? Наверное поленились посмотреть, что их же линкер итак все выбрасывает...
SJA>>>Линкер выбрасывает ненужные ф-ии из объектных файлов.
ATP>>А вот тут у человека полная каша в голове, т.к он путает ликер и компилятор. Компилятор, выкидывает функции — это понято и так, и естественно не пишет в объектник неиспользуемых функций. SJA>Боюсь тут ты не правы. А если данная ф-ия будет использовться в другом объектнике, а компилятор её уже выбросил — что тогда ?
В чем я неправ — да компилятор выкидывае неиспользуемые функции. Но только, те за которые дейсвительно нигде не используются. Компилятор не может выкинуть используемые функции, иначе это нонсенс! А если функция используется в другом объектнике, то как минимум ее нужно описать как extern и естественно такиефункции компилятор не выкинет.
ATP>> Теперь представте, что линкер отделная программа, и ей на вход приходит объектник, в котором есть функции составлящие либу. Предположим что вызывается из все только три. И как по вашему линкер (не компилятор) должен понять какие из них дейсвительно нужны, а какие нет? SJA>Я же это кажется подробно описал.
Вы описали алгоритм разбора машинног кода, где определяется какой кусок кода нужен, а какой нет? Что то я незаметил..
SJA>>>Вывод: SJA>>>Линкер может находить одинаковые куски кода и объединять их.
ATP>>Опять полная каша в голове, пулное непонимание вопроса. ЛИНКЕРРРРР — причем тут компилятор. То что вы описали делает компилятор.... SJA>Не нужно волноваться. Объединяет объектники — линкер. Две приведённые ф-ии были в разных объектниках. Их объединил линкер.
В этом случае — согласен. Но этор примитивнейшая задача, если два объектника побайтно равны.
ATP>>Тут далше просто какие-то рассуждения, как могло быть то, чего на самом деле нет.... или есть, но это совсем не потому, почему вы думаете. И делает это все не линкер. SJA>Ок. В таком случае расскажите, как они это делают ? Но прежде чем расказать какую-нибудь глупость советую почитать форматы: PE32, COFF
А я пока и неуверен, что и как делается. Я собственно и задал поэтому вопрос. Но неожидал просто такого ответа. Я всетаки тоже не первый год прогриммирую. Конечноже форматы ехешников от Microsoft — это то что прольет свет на проблемму компиляции для людей во всем мире!!!
В общем ваши странные тесты не вносят никокой ясности. Это все равно что сказать, что я знаю что это так, но почему??
Собственно вопрос был, как он это делает, если делает вообще.
Здравствуйте, AcidTheProgrammer, Вы писали:
SJA>>В действительности стандарт C++ это не оговаривает. Поэтому каждый производитель сам решает что и как в данной ситуации делать. SJA>>Майкрософтовский линкер (по крайней мере от VC 6.0 и далее) выбрасывает не использующиеся ф-ии. ATP>Выбрасывает из исходного кода, во время компиляции — да, а из линкуемой либы — вопрос? ATP>есть либа: ATP>1. Глобальная таблица данных ATP>2. Функция 1 ATP>3. Функция 2 ATP>4. Функция 3
ATP>Предположим все три фукции используют глобальные данные и что мы используем только функцию 2 из либы. ATP>Получается, что мы должны выкинуть функции 1 и 3. А может и не должны, т.к как они могут вызываться из функции в функции которая вызывается в Функции 2? А если должны выкинуть, то смещения на глобальные данные изменится, и относительные адреса не помогут. Так как же линкер поступит в этом случае?
ATP>Есть ли действительно компетентные специалисты в данном вопросе, которые могли бы помоч разобраться мне в данном вопросе?
1. Не путайте объектный модуль и отдельную функцию. В либе хранятся объектные модули. Каждый объектный модуль обязательно содержит 2 словаря: импортируемых и экспортируемых имен. Имена функций туда входят по умолчанию.
2. Типичная организация либы — блоки переменной длины. В начале каждого блока — справочник со смещениями объектных модулей от начала блока (или файла). Поэтому вытащить отдельный объектный модуль линкеру труда не представляет.
3. Не знаю как в стандартных либах, но я сделал бы по отдельному объектному модулю на функцию. Тогда линкер вытаскивает отдельный модуль со своими словарями и по словарям узнает имена необходимых функций.
Читать структуру заголовка объектного модуля — там вся инфа про модуль расписана.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, LaptevVV, Вы писали:
LVV>Здравствуйте, AcidTheProgrammer, Вы писали:
SJA>>>В действительности стандарт C++ это не оговаривает. Поэтому каждый производитель сам решает что и как в данной ситуации делать. SJA>>>Майкрософтовский линкер (по крайней мере от VC 6.0 и далее) выбрасывает не использующиеся ф-ии. ATP>>Выбрасывает из исходного кода, во время компиляции — да, а из линкуемой либы — вопрос? ATP>>есть либа: ATP>>1. Глобальная таблица данных ATP>>2. Функция 1 ATP>>3. Функция 2 ATP>>4. Функция 3
ATP>>Предположим все три фукции используют глобальные данные и что мы используем только функцию 2 из либы. ATP>>Получается, что мы должны выкинуть функции 1 и 3. А может и не должны, т.к как они могут вызываться из функции в функции которая вызывается в Функции 2? А если должны выкинуть, то смещения на глобальные данные изменится, и относительные адреса не помогут. Так как же линкер поступит в этом случае?
ATP>>Есть ли действительно компетентные специалисты в данном вопросе, которые могли бы помоч разобраться мне в данном вопросе?
LVV>1. Не путайте объектный модуль и отдельную функцию. В либе хранятся объектные модули. Каждый объектный модуль обязательно содержит 2 словаря: импортируемых и экспортируемых имен. Имена функций туда входят по умолчанию. LVV>2. Типичная организация либы — блоки переменной длины. В начале каждого блока — справочник со смещениями объектных модулей от начала блока (или файла). Поэтому вытащить отдельный объектный модуль линкеру труда не представляет. LVV>3. Не знаю как в стандартных либах, но я сделал бы по отдельному объектному модулю на функцию. Тогда линкер вытаскивает отдельный модуль со своими словарями и по словарям узнает имена необходимых функций.
LVV>Читать структуру заголовка объектного модуля — там вся инфа про модуль расписана.
Если линкер оперирует модулями — то все становится на свои места. Тогда все прозрачно.
Но просто тут человек утверджает что у него линкер выкидывает неиспользуемые функции из одного объектника
SJA>>Линкер выбрасывает ненужные ф-ии из объектных файлов.
Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>Если линкер оперирует модулями — то все становится на свои места. Тогда все прозрачно. ATP>Но просто тут человек утверджает что у него линкер выкидывает неиспользуемые функции из одного объектника
SJA>>>Линкер выбрасывает ненужные ф-ии из объектных файлов.
ATP>и даже поясняет как он это делает
Не, единица трансляции превращается в объектный модуль и линкер работает с ним целиком. Другое дело, что стандартные модули, как я уже говорил, можно таким образом состряпать, что прицепляться будет минимум.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, LaptevVV, Вы писали:
LVV>Здравствуйте, AcidTheProgrammer, Вы писали:
ATP>>Если линкер оперирует модулями — то все становится на свои места. Тогда все прозрачно. ATP>>Но просто тут человек утверджает что у него линкер выкидывает неиспользуемые функции из одного объектника
SJA>>>>Линкер выбрасывает ненужные ф-ии из объектных файлов.
ATP>>и даже поясняет как он это делает LVV>Не, единица трансляции превращается в объектный модуль и линкер работает с ним целиком. Другое дело, что стандартные модули, как я уже говорил, можно таким образом состряпать, что прицепляться будет минимум.
Спасибо большое за ответ. Теперь все стало понятно и ясно. Теперб понятно, что ликер, как собственно и должно быть — тупая прога — смотрит что надо, смотрит в каких объектниках есть, то что надо, и включает их целиком, и не обладает искуственным интеллектом, как, я смотрю, думают многие.