быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
|
||||||
1 | ||||||
Создание *.dll: для чего нужен компилятору параметр -DBUILD_DLL? (использую MinGW)07.02.2014, 17:22. Показов 8326. Ответов 46
Метки нет (Все метки)
Не, реально, зачем он?
...Друзья! Много где в инете вы найдёте как кропать dll-ки, например тут. обратите внимание на командную строку:
А вот я не поленился и скомандовал --help для каждого из экзешников лежащих в папке mingw\bin- ни для одного из экзешников такого параметра нет. То есть он просто-напросто ни одним из экзешников не используется. Вот результаты, кому интересно, в архиве. Так зачем же нужен параметр -DBUILD_DLL? Спасибо, кто откликнется.
0
|
07.02.2014, 17:22 | |
Ответы с готовыми решениями:
46
AddItem и TObject. Для чего нужен второй параметр в приведенном коде Подскажите для чего нужен WdfCoInstallerXXXX.dll Создание и использование dll- Для чего dllimport? Нужно найти путь к компилятору MinGW в QT Creator |
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
|
|
18.02.2014, 14:12 | 21 |
Ну раз зачем нужен __declspec(dllimport) понятно, то ответ зачем нужна конструкция выше, уже не раз сюда постили. Первый ответ в теме ну и мой ответ, например.
Ну, могу еще раз. Ситуация: Я разработчик библиотеки (dll). Я собираю свою библиотеку из исходников и указываю макрос -DBUILD_DLL в мэйкфайле. Конструкция выше срабатывает и проставляет всем функциям __declspec(dllexport), перед которыми стоял EXPORT (на самом деле EXPORT некорректный пример и только запутывает. Лучше использовать нотацию из моего примера. Или посмотреть на WIN32 API (например в файле winbase.h функции помечены как WINBASEAPI). Простановка __declspec(dllexport) помечает функции как экспортируемые. Смотрим MSDN: Ситуация 2: Теперь я пользователь библиотеки. Я подключаю заголовочный файл вышеозначенной библиотеки, естественно никакой -DBUILD_DLL я не указываю в своем мэйкфайле. Автоматически начинает применяться вариант__declspec(dllimport). Зачем он нужен, ты уже понял. ЗЫ. В линуксе, например, можно обходиться без всего этого. Там правила игры немного другие. Но есть похожие по форме приемы (в моем примере как раз один из вариантов). Подробнее тут.
0
|
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
|
||||||
18.02.2014, 17:24 [ТС] | 22 | |||||
нет, первый ответ был не про то. Первый ответ был про то, почему хорошо определять BUILD_DLL командой компилятору, а явно
+++++++++++++++++++++++++++++++++++++++== Я же выше писал- ну зачем эти сложности? Ну написать перед каждой экспортируемой функцией EXPORT и ВСЁ, и не надо никаких ключей. А перед каждой импортируемой IMPORT и опять всё. Я же по условию сам пишу dll и сам определяю, какая пойдёт на экспорт, а какую я буду импортировать!
0
|
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
|
|
18.02.2014, 17:34 | 23 |
Именно это и делается, только препроцессором и на автомате. Все соответствующие функции для пользователя библиотеки будут помечены как import. Все функции, которые разработчик пометил будут export. Заголовочный файл один, а импортировать или экспортировать через него будут функции переключается одни флажком.
Ну да. И что? Нужно смотреть на библиотеку с двух сторон, с точки зрения пользователя и с точки зрения ее разработчика. Иначе при твоем подходе нужно будет иметь две версии заголовочного файла, один для экспорта, нужный при сборке либы, а другой для импорта, нужный при использовании либы. Это - сложнее.
0
|
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
|
|||||||||||||||||||||
18.02.2014, 18:36 [ТС] | 24 | ||||||||||||||||||||
Ничё не понял. Может, перейдём к конкретике? Я вот тут подумал- где может быть реальна полезна штука из первого поста. И написал такой код (хотя мне потом скажут, что примеры приводили, никто примеров полезности не приводил. Первый пример действительной полезности ниже)
Так с каким пор стало правильным усложнять себе жизнь?
0
|
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
|
||||||||||||||||
18.02.2014, 19:14 | 25 | |||||||||||||||
Сообщение было отмечено kravam как решение
Решение
Ох... Неверно полагать, что все вокруг идиоты.
А теперь к конкретике. Заголовочный файл где? Как использовать твою dll? Она же не сферическая в вакууме? Не надо эту конструкцию писать в cpp, ей там действительно не место. Эту конструкцию пишут в заголовочном файле, вместе с прототипом:
Теперь я пользователь библиотеки. Мой код:
1
|
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
|
||||||
18.02.2014, 20:11 [ТС] | 26 | |||||
А, ну вот, теперь вроде всё понятно. Оказывается, хидер работает на два фронта. Разработчик dll написал её и дал пользователю вместе с хидером и *.a. Пользователь, не думая о прототипах функций, подключает хидер и, не определяя BUILD_DLL вызывает функции (f()) и те оказываются import. Всё круто. Очень удобно для пользователя.
Небольшая тык скыть, догадка- получается, если мы имеем скомпилированные таким образом экзешники (к примеру, я пользуюсь билиотекой X.dll, невесть где взятой, и с её помощью что-то пишу), то если вдруг я заимею другую X.dll, то экзешники надо будет перекомпилировать, ибо, вызыванные в них функции вызываются по жёстко прописанным адресам, т. к.
Ну что ж теперь всё ясно. Большое спасибо, огромный пласт тык скыть знаний раскрыт. Если что, спрошу здесь.
0
|
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
|
|
18.02.2014, 20:42 | 27 |
Наконец-то Все-таки все тоже самое, что в последнем сообщении было написано в первом.
Вот смотри, я выделю все ключевые слова: Разве нет? Не так. Таблица импорта заполняется фактическими адресами при старте программы. При компиляции она только создается и заполняется "ссылками" на импортируемые функции. А вот таблица экспорта фиксирована. Вот тут все очень подробно. Рекомендую еще раз прочитать ссылку на мсдн. Для полноты картины.
0
|
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
|
|||||||||||||||||||||
18.02.2014, 21:57 [ТС] | 28 | ||||||||||||||||||||
Соглашусь, но разве не для того форум и существует, чтобы чисто и честно, по-товарищески объяснить? Это вообще философский вопрос. Всё написано в книгах/учебниках. И всё написано правильно. А вот поди ж ты- откуда берутся двоечники (лентяев не берём в расчёт)? Есть такой феномен как непонимание, и никуда от него не деться
Действительно, заполняется. И этот адрес используется при вызове функции, то есть у меня в отладчике так:
Не по теме: ...Откуда взялся "чётко прописанный адрес"? Ну, когда мы компильнули экзешник с модификатором __declspec(dllimport), вот компилятор его и прописал тогда чётко по месту, что называется. Но теперь, я вижу, это не так. Не по теме:
+++++++++++++++++++++++++++++++++++++++++++++ Тут вопросов нет. Вопрос возникает совершено не там, откуда ждали. Смотрите. Если мы компилим функцию с модификатором __declspec(dllimport), загрузчик во время загрузки ищет адрес этой функции и его использует. А если компилим БЕЗ __declspec(dllimport), загрузчик... делает то же самое, только использует адрес по-другому. Но по большому счёту, вся-то и сложность и разница в поведении по-разному скомпиленных экзешников должна заключаться в том, КОГДА В ЭКЗЕШНИКЕ СТАНЕТ ИЗВЕСТЕН АДРЕС ФУНКЦИИ И если адрес функции в ОБОИХ СЛУЧАЯХ становится известным только при загрузке программы в память, тогда мы В ОБОИХ случаях ("с" и "без" __declspec(dllimport)) можем использовать более эффективный код, а именно:
0
|
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
|
|
18.02.2014, 22:19 | 29 |
Функция без __declspec(dllimport) может находиться как внутри твоей программы, так и в dll. Понять это без подсказки компилятор не способен. Поэтому чтобы разрешить ситуацию он генерит наиболее общий код, который возможно, он подходит и для этого случая и для того. Помечая функцию __declspec(dllimport) мы как бы намекаем ему, что она точно не в нашем модуле, позволяя исключить лишний шаг.
0
|
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
|
|
19.02.2014, 18:13 [ТС] | 30 |
Честное слово, я не понял мысли. Во-первых, почему компилятор не способен это понять? Откуда такой вывод? Я в отладчике наблюдаю, что очень даже способен. Но это не главное. На этот вопрос, как и на возникающие далее вообще можно не отвечать. Мне кажется, они не имеют отношения к делу.
А вот это имеет. Псведокод: Вызов функции из dll Это назовём good_kod
0
|
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
|
|
19.02.2014, 18:37 | 31 |
Сначала много всего написал, но по ссылке, которую ты почему-то упорно не хочешь изучить полностью, содержится ответ на твой вопрос.
0
|
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
|
|
19.02.2014, 18:51 [ТС] | 32 |
Видите ли, в чём дело, некоторые вещи там непонятны. Но я просто не хотел на них акцентировать внимание, предполагая, что можно понять и без них. Ну, давайте всё подряд разбирать. Вот первая непонятная вещь:
Добавлено через 6 минут И вообще, это пом-моему ерунда. Если непрямой вызов это bad_kod, то следует, что мы пишем _declspec(dllimport) А компилятор создаёт непрямой вызов вместо прямого, и это имея указание _declspec(dllimport)! Мда. Вот и договорились. Извините, я просто честно пытаюсь понять ссыль.
0
|
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
|
|
19.02.2014, 18:59 | 33 |
Это как раз то, что называется implementation-defined.
Компилятор не может создать прямой вызов из dll, т.к. адреса становятся известны только при старте приложения. Об этом же уже шла речь Не по теме:
0
|
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
|
|
19.02.2014, 19:09 [ТС] | 34 |
Э, нет, так не пойдёт. Почему тогда выше в этой же ссылке написано, что компилятор может создать непрямой вызов? Не "должен создать" или "создаёт", но "может создать".
Ну, хорошо implementation-defined. И вот, согласно implementation-defined компилятор не создаёт непрямой вызов... А какой же тогда он создаёт? А у нас их только два, прямой и непрямой. Непрямой мы откинули, остался прямой. А вы пишите что прямой он не может создать ни при каких условиях... ...Я сразу сказал, в этой статье нет ответа на мой вопрос. Там просто сказано, когда создаётся прямой, а когда непрямой. И чем они различаются (И термины до кучи небрежно использованы). А почему один нельзя другим заменить- не сказано, хотя и в том и другом случае адрес функции становится известно только при загрузке dll
0
|
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
|
|
19.02.2014, 19:38 | 35 |
прямой он может создать только если известен адрес! Адрес известен если функция в том же приложении. Т.е. прямой вызов будет только если функция НЕ dll. Однако как я писал, компилятор не может определить сам где находится функция, поэтому он генерит прямой вызов, который потом делает прыжок на таблицу импорта (или не делает, если функция таки в нашем приложении). Если мы указываем import, то прямого вызова на jmp не происходит, а сразу идет непрямой из таблицы импорта.
Т.е., еще раз. Если функция (func1) в dll и не помечена import, то: 1) Генерится прямой вызов на адрес находящийся внутри приложения. 2) По этому адресу размещен прыжок на таблицу импорта. 3) Происходит непрямой вызов через отрезовленный адрес через таблицу импорта. Если функция помечена import: 1) Сразу происходит непрямой вызов через таблицу импорта. Теперь внимание! Это очень важно! Я отключаю dll от проекта и создаю еще один cpp с реализацией функции funс1: 1) Генерится прямой вызов на адрес находящийся внутри приложения (func1). Видишь, первые этапы одинаковы в случае когда функция не помечена как import. Добавлено через 12 минут Вот нашел качественную ссылку:
0
|
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
|
||||||||||||||||
19.02.2014, 20:24 [ТС] | 36 | |||||||||||||||
(Дальше не читал). Это НЕ ТАК. Я, кстати, и предложил с этого начать наш разговор: компилятор ОТЛИЧНО МОЖЕТ ОПРЕДЕЛИТЬ, где находится функция, в dll или в текущем модуле, и это, заметьте, безо всякого _declspec(dllimport).
Поехали:
Так вот. Первый вызов этот вызов my_func_exe (); и адрес его ИЗВЕСТЕН, он- 40138C и по этому адресу находится тело функции, и сейчас туда будет прыжок. А следующий вызов- это вызов my_func_dll (); через таблицу импорта и её адрес найден при загрузке программы. Да, вы написали: , то есть вроде как вы писали, так в отладчике и есть. Пока нормально всё. Но далее ваши же слова: Может. Он это сделал САМ, безо всяких моих указаний __declspec(dllexport). Он отлично определил, что одна из функций находится ТУТ, а другая ТАМ. Он это сделал сам. Я не говорил ему, вы не говорили, никто не говорил. Не по теме: Так оставь вызов my_func_exe () как есть, а вызов my_func_dll () замени на good_kod, всё равно загрузчик напишет адрес функции в таблице импорта. Вот хотя бы даже с этим разобраться, а потом двигаться дальше.
0
|
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
|
|
19.02.2014, 20:39 [ТС] | 37 |
0
|
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
|
|
19.02.2014, 20:47 | 38 |
Вот зря ты не читал..
Это сделал не компилятор, а линкер! И сделал он то, что писано в МСДН, добавил jmp на таблицу импорта. Компилятор генерит объектники, а линкер все остальное. Но на этапе работы линкера уже поздно менять объектники. Он может только скомпоновать получившийся код, а никак не перестраивать внутри инструкции! Замена меток на адреса, вот простейшая его работа + генерация системозависимого кода. А вот понимание компилятором что нужно, т.е. вместо одной инструкции сгенерировать другую было бы возможно только в случае повторного шага компиляции с использованием уже собранной информации о других модулях. Вот тогда и только тогда именно компилятор бы сделать описанную тобой работу, заменив инструкцию вызова прямого адреса, а переход на таблицу импорта. Читай, пожалуйста, до конца материал, прежде чем возражать...
0
|
быдлокодер
1724 / 911 / 106
Регистрация: 04.06.2008
Сообщений: 5,679
|
|
19.02.2014, 22:01 [ТС] | 39 |
Я сейчас прочитал, объяснений нет. Написано, как обстоят дела, а почему они так обстоят- непонятно.
...Это делает линковщик: А это компилятор Так?
0
|
18822 / 9826 / 2401
Регистрация: 30.01.2014
Сообщений: 17,260
|
|
19.02.2014, 22:26 | 40 |
Нет... Как-то ты странно читаешь. Я устал если честно.
Вопрос плевый на самом деле, разобраться в нем дело 30 минут... Тем более такое огромное количество инфы было сюда выложено. Вот скажи, ты знаешь, что компилятор собирает каждый модуль отдельно, независимо? Именно поэтому у нас получаются объектные файлы на выходе cpp->obj (или o). Понимаешь что такое компиляция? Понимаешь что такое связывание (линковка)? Еще раз, вся соль в том, что компилятор собирает модули независимо (это называется единицей трансляции (translation unit)). И вся соль в том, что линкер не меняет сгенерированные объектники (т.к. там уже машинный код). Что на самом деле вызывается, функция из dll или функция из другого obj становится известно только на этапе линковки, а линкер не может менять машинный код, он может только, грубо говоря, поменять метки на фактические адреса. Поэтому единственное что может линкер - вставить в результирующий exe редирект (jmp) на таблицу импорта в случае, если функция не найдена в доступных obj. И именно поэтому я говорю, что компилятор не МОЖЕТ узнать природу вызова, он может сгенерировать наиболее оптимальный код из доступной инфы. Когда мы говорим в коде import для функции, он получает больше инфы и генерит более оптимальный код.
0
|
19.02.2014, 22:26 | |
19.02.2014, 22:26 | |
Помогаю со студенческими работами здесь
40
Нужен glaux для mingw Подлючение библиотеки sfml 2.0 sjlj к дефолтному компилятору mingw sjlj code::blocks 13.12 x32 Для чего нужен Seed() и для чего его override? MinGW - скомпилированная программа требует наличия libstdc++-6.dll и libgcc_s_seh-1.dll Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |