Форум программистов, компьютерный форум CyberForum.ru
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 19, средняя оценка - 4.79
kravam
быдлокодер
1695 / 882 / 45
Регистрация: 04.06.2008
Сообщений: 5,470
#1

Создание *.dll: для чего нужен компилятору параметр -DBUILD_DLL? (использую MinGW) - C++

07.02.2014, 17:22. Просмотров 2820. Ответов 46
Метки нет (Все метки)

Не, реально, зачем он?

...Друзья! Много где в инете вы найдёте как кропать dll-ки, например тут. обратите внимание на командную строку:

Bash
1
gcc -c -DBUILD_DLL dllfct.c
Да, так вот. Много где приводят примеры с использованием этго параметра, но зачем он никто толком не знает.

А вот я не поленился и скомандовал --help для каждого из экзешников лежащих в папке mingw\bin- ни для одного из экзешников такого параметра нет. То есть он просто-напросто ни одним из экзешников не используется. Вот результаты, кому интересно, в архиве.

Так зачем же нужен параметр -DBUILD_DLL? Спасибо, кто откликнется.
0
Вложения
Тип файла: rar rez.rar (46.3 Кб, 9 просмотров)
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
07.02.2014, 17:22
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Создание *.dll: для чего нужен компилятору параметр -DBUILD_DLL? (использую MinGW) (C++):

Создание и использование dll- Для чего dllimport? - C++
Всем доброго времени суток Вот пытаюсь разобраться в создании и использовании dll. По данной теме у меня возникли два вопроса. 1....

Параметр rhs, что это и для чего (конструктор-копировщик) - C++
Вот код: #include <iostream> class jurnal { public: jurnal() { }

Для чего используется DLL? - C++
исходники кода написанного на C++ хранятся в DLL верно?? Для чего используется DLL?? Добавлено через 2 минуты вернее не исходники а...

Может ли MinGW x64 компилить приложения x32? Или нужно для этого отдельно ещё MinGW х32 качать? - C++
Просто решил поиграться с CodeLite.

Для чего используются DLL-библиотеки? - C++
Для чего используются DLL-библиотеки Неужели заголовочного файла мало.

Создание программы, которая будет обращаться к компилятору - C++
Хочу написать программу, которая будет обращаться к компилятору, в нём же она будет проверять работоспособность кода, который напишут в...

46
DrOffset
7310 / 4406 / 998
Регистрация: 30.01.2014
Сообщений: 7,242
19.02.2014, 18:37 #31
Цитата Сообщение от kravam Посмотреть сообщение
Так, а теперь что мешает компилятору заменить bad_kod на good_kod? Ничего! Пусть заменяет и раз и навсегда избавимся от ненужного дополнительного промежуточного прыжка. Не заменяет. Почему?
Сначала много всего написал, но по ссылке, которую ты почему-то упорно не хочешь изучить полностью, содержится ответ на твой вопрос.

call 0x4000000 ; The address of 'func1'.
Если func1 хранится в другой библиотеке DLL, компоновщик не может ее распознать, поскольку отсутствует информация об адресе func1. В 16-битных средах компоновщик добавляет адрес этого кода в список в EXE-файле, который во время выполнения будет заполнен загрузчиком верными адресами. В 32-битных и 64-битных средах компоновщик производит преобразователь, адрес которого неизвестен.
Однако если func1 - это функция не из dll, а определенная в нашей программе, то код останется неизменным.
0
kravam
быдлокодер
1695 / 882 / 45
Регистрация: 04.06.2008
Сообщений: 5,470
19.02.2014, 18:51  [ТС] #32
Видите ли, в чём дело, некоторые вещи там непонятны. Но я просто не хотел на них акцентировать внимание, предполагая, что можно понять и без них. Ну, давайте всё подряд разбирать. Вот первая непонятная вещь:
"Если компилятор получает данные о том, что функция размещается в библиотеке DLL, он может создать непрямой вызов."
Что значит "может создать"? То есть может и не создать что ли? Это важно для ответа на мой вопрос?

Добавлено через 6 минут
И вообще, это пом-моему ерунда. Если непрямой вызов это bad_kod, то следует, что мы пишем
_declspec(dllimport)

А компилятор создаёт непрямой вызов вместо прямого, и это имея указание _declspec(dllimport)! Мда. Вот и договорились. Извините, я просто честно пытаюсь понять ссыль.
0
DrOffset
7310 / 4406 / 998
Регистрация: 30.01.2014
Сообщений: 7,242
19.02.2014, 18:59 #33
Цитата Сообщение от kravam Посмотреть сообщение
Видите ли, в чём дело, некоторые вещи там непонятны. Но я просто не хотел на них акцентировать внимание, предполагая, что можно понять и без них. Ну, давайте всё подряд разбирать. Вот первая непонятная вещь:

Что значит "может создать"? То есть может и не создать что ли? Это важно для ответа на мой вопрос?
Это как раз то, что называется implementation-defined.

Цитата Сообщение от kravam Посмотреть сообщение
А компилятор создаёт непрямой вызов вместо прямого, и это имея указание _declspec(dllimport)! Мда. Вот и договорились. Извините, я просто честно пытаюсь понять ссыль.
Компилятор не может создать прямой вызов из dll, т.к. адреса становятся известны только при старте приложения. Об этом же уже шла речь

Не по теме:


Совет: Учи английский. Быть нормальным программистом без знания английского невозможно. Очень многие ответы на все твои (даже будущие) вопросы находятся в англоязычных источниках. Намного более подробно, чем могут вместить формат форума и конкретно мои ответы. Т.к. расписывать все от запятой к запятой довольно утомительно и это, как ты видишь, никто кроме меня не решался в этой теме делать.

0
kravam
быдлокодер
1695 / 882 / 45
Регистрация: 04.06.2008
Сообщений: 5,470
19.02.2014, 19:09  [ТС] #34
Цитата Сообщение от DrOffset Посмотреть сообщение
Компилятор не может создать прямой вызов из dll, т.к. адреса становятся известны только при старте приложения. Об этом же уже шла речь
Э, нет, так не пойдёт. Почему тогда выше в этой же ссылке написано, что компилятор может создать непрямой вызов? Не "должен создать" или "создаёт", но "может создать".

Ну, хорошо implementation-defined. И вот, согласно implementation-defined компилятор не создаёт непрямой вызов... А какой же тогда он создаёт? А у нас их только два, прямой и непрямой. Непрямой мы откинули, остался прямой. А вы пишите что прямой он не может создать ни при каких условиях...

...Я сразу сказал, в этой статье нет ответа на мой вопрос. Там просто сказано, когда создаётся прямой, а когда непрямой. И чем они различаются (И термины до кучи небрежно использованы). А почему один нельзя другим заменить- не сказано, хотя и в том и другом случае адрес функции становится известно только при загрузке dll
0
DrOffset
7310 / 4406 / 998
Регистрация: 30.01.2014
Сообщений: 7,242
19.02.2014, 19:38 #35
Цитата Сообщение от kravam Посмотреть сообщение
А вы пишите что прямой он не может создать ни при каких условиях...
прямой он может создать только если известен адрес! Адрес известен если функция в том же приложении. Т.е. прямой вызов будет только если функция НЕ dll. Однако как я писал, компилятор не может определить сам где находится функция, поэтому он генерит прямой вызов, который потом делает прыжок на таблицу импорта (или не делает, если функция таки в нашем приложении). Если мы указываем import, то прямого вызова на jmp не происходит, а сразу идет непрямой из таблицы импорта.
Т.е., еще раз. Если функция (func1) в dll и не помечена import, то:
1) Генерится прямой вызов на адрес находящийся внутри приложения.
2) По этому адресу размещен прыжок на таблицу импорта.
3) Происходит непрямой вызов через отрезовленный адрес через таблицу импорта.

Если функция помечена import:
1) Сразу происходит непрямой вызов через таблицу импорта.

Теперь внимание! Это очень важно!
Я отключаю dll от проекта и создаю еще один cpp с реализацией функции funс1:
1) Генерится прямой вызов на адрес находящийся внутри приложения (func1).

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

Добавлено через 12 минут
Вот нашел качественную ссылку:
However, the matching double compile is not performed when consuming
libraries. It is therefore not possible to reliably distinguish if the
consumer is importing from a DLL or if it is going to use a static
library.
И действительно, дизайн языков С и С++ построен так, чтобы исключить лишние этапы компиляции из процесса сборки. Более того (сейчас это не всегда так) раньше и С и С++ использовали совершенно одинаковый линкер.
0
kravam
быдлокодер
1695 / 882 / 45
Регистрация: 04.06.2008
Сообщений: 5,470
19.02.2014, 20:24  [ТС] #36
Цитата Сообщение от DrOffset Посмотреть сообщение
Однако как я писал, компилятор не может определить сам где находится функция, поэтому он генерит прямой вызов, который потом делает прыжок на таблицу импорта (или не делает, если функция таки в нашем приложении).
(Дальше не читал). Это НЕ ТАК. Я, кстати, и предложил с этого начать наш разговор: компилятор ОТЛИЧНО МОЖЕТ ОПРЕДЕЛИТЬ, где находится функция, в dll или в текущем модуле, и это, заметьте, безо всякого _declspec(dllimport).

Поехали:

C++
1
2
3
4
5
6
7
8
//DLL-ка
/*A.cpp*/
#include <stdio.h>
 
extern "C" __declspec(dllexport) void my_func_dll () {
 printf ("Я нахожусь в теле dll\n");
 getchar ();
}
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//экзешник
//B.cpp
#include <windows.h>
#include <stdio.h>
 
void my_func_exe () {
 printf ("Я нахожусь в теле экзешника\n");
 getchar ();
}
 
////////////////////////////////////////
 
extern "C" void my_func_dll ();
 
int main () {
 SetConsoleCP(1251);
 SetConsoleOutputCP(1251);
 
 printf ("%x", my_func_exe);
 printf ("%x", my_func_dll);
 
 getchar ();
 
 my_func_exe ();
 my_func_dll ();
 
 
 return 0;
}
компилим:
Bash
1
2
3
4
5
6
7
8
9
10
11
rem удаляем A.dll и B.exe для чистоты эксперимента
del A.dll B.exe
 
rem Кропаем libAdll.a и A.dll
gcc -shared -o A.dll -Wl,--out-implib,libAdll.a A.cpp
 
rem Кропаем B.exe
gcc -o B.exe B.cpp -L./ -lA
 
rem удаляем libAdll.a
del libAdll.a
Смотрим в отладчике:

Так вот. Первый вызов этот вызов my_func_exe (); и адрес его ИЗВЕСТЕН, он- 40138C и по этому адресу находится тело функции, и сейчас туда будет прыжок. А следующий вызов- это вызов my_func_dll (); через таблицу импорта и её адрес найден при загрузке программы.

Да, вы написали:
Цитата Сообщение от DrOffset Посмотреть сообщение
прямой он может создать только если известен адрес! Адрес известен если функция в том же приложении.
, то есть вроде как вы писали, так в отладчике и есть. Пока нормально всё. Но далее ваши же слова:
Цитата Сообщение от DrOffset Посмотреть сообщение
компилятор не может определить сам где находится функция,
Может. Он это сделал САМ, безо всяких моих указаний __declspec(dllexport). Он отлично определил, что одна из функций находится ТУТ, а другая ТАМ. Он это сделал сам. Я не говорил ему, вы не говорили, никто не говорил.

Не по теме:

Так оставь вызов my_func_exe () как есть, а вызов my_func_dll () замени на good_kod, всё равно загрузчик напишет адрес функции в таблице импорта.



Вот хотя бы даже с этим разобраться, а потом двигаться дальше.
0
Миниатюры
Создание *.dll: для чего нужен компилятору параметр -DBUILD_DLL? (использую MinGW)  
kravam
быдлокодер
1695 / 882 / 45
Регистрация: 04.06.2008
Сообщений: 5,470
19.02.2014, 20:39  [ТС] #37
Исправление:
Цитата Сообщение от kravam Посмотреть сообщение
Он это сделал САМ, безо всяких моих указаний __declspec(dllimport).
0
DrOffset
7310 / 4406 / 998
Регистрация: 30.01.2014
Сообщений: 7,242
19.02.2014, 20:47 #38
Цитата Сообщение от kravam Посмотреть сообщение
(Дальше не читал)
....
Может. Он это сделал САМ, безо всяких моих указаний __declspec(dllexport). Он отлично определил, что одна из функций находится ТУТ, а другая ТАМ.
Вот зря ты не читал..
Это сделал не компилятор, а линкер! И сделал он то, что писано в МСДН, добавил jmp на таблицу импорта. Компилятор генерит объектники, а линкер все остальное. Но на этапе работы линкера уже поздно менять объектники. Он может только скомпоновать получившийся код, а никак не перестраивать внутри инструкции! Замена меток на адреса, вот простейшая его работа + генерация системозависимого кода. А вот понимание компилятором что нужно, т.е. вместо одной инструкции сгенерировать другую было бы возможно только в случае повторного шага компиляции с использованием уже собранной информации о других модулях. Вот тогда и только тогда именно компилятор бы сделать описанную тобой работу, заменив инструкцию вызова прямого адреса, а переход на таблицу импорта.

Читай, пожалуйста, до конца материал, прежде чем возражать...
0
kravam
быдлокодер
1695 / 882 / 45
Регистрация: 04.06.2008
Сообщений: 5,470
19.02.2014, 22:01  [ТС] #39
Цитата Сообщение от DrOffset Посмотреть сообщение
Вот зря ты не читал..
Я сейчас прочитал, объяснений нет. Написано, как обстоят дела, а почему они так обстоят- непонятно.

...Это делает линковщик:
Цитата Сообщение от DrOffset Посмотреть сообщение
Т.е., еще раз. Если функция (func1) в dll и не помечена import, то:
1) Генерится прямой вызов на адрес находящийся внутри приложения.
2) По этому адресу размещен прыжок на таблицу импорта.
3) Происходит непрямой вызов через отрезовленный адрес через таблицу импорта.
А это компилятор
Цитата Сообщение от DrOffset Посмотреть сообщение
Если функция помечена import:
1) Сразу происходит непрямой вызов через таблицу импорта.
Так?
0
DrOffset
7310 / 4406 / 998
Регистрация: 30.01.2014
Сообщений: 7,242
19.02.2014, 22:26 #40
Цитата Сообщение от kravam Посмотреть сообщение
...Это делает линковщик:
А это компилятор
Так?
Нет... Как-то ты странно читаешь. Я устал если честно.
Вопрос плевый на самом деле, разобраться в нем дело 30 минут... Тем более такое огромное количество инфы было сюда выложено.

Вот скажи, ты знаешь, что компилятор собирает каждый модуль отдельно, независимо? Именно поэтому у нас получаются объектные файлы на выходе cpp->obj (или o).
Понимаешь что такое компиляция?
Понимаешь что такое связывание (линковка)?

Еще раз, вся соль в том, что компилятор собирает модули независимо (это называется единицей трансляции (translation unit)).
И вся соль в том, что линкер не меняет сгенерированные объектники (т.к. там уже машинный код). Что на самом деле вызывается, функция из dll или функция из другого obj становится известно только на этапе линковки, а линкер не может менять машинный код, он может только, грубо говоря, поменять метки на фактические адреса. Поэтому единственное что может линкер - вставить в результирующий exe редирект (jmp) на таблицу импорта в случае, если функция не найдена в доступных obj.
И именно поэтому я говорю, что компилятор не МОЖЕТ узнать природу вызова, он может сгенерировать наиболее оптимальный код из доступной инфы. Когда мы говорим в коде import для функции, он получает больше инфы и генерит более оптимальный код.
0
kravam
быдлокодер
1695 / 882 / 45
Регистрация: 04.06.2008
Сообщений: 5,470
19.02.2014, 22:31  [ТС] #41
Цитата Сообщение от DrOffset Посмотреть сообщение
Вот скажи, ты знаешь, что компилятор собирает каждый модуль отдельно, независимо? Именно поэтому у нас получаются объектные файлы на выходе cpp->obj (или o).
да
Цитата Сообщение от DrOffset Посмотреть сообщение
Понимаешь что такое компиляция?
нет
Цитата Сообщение от DrOffset Посмотреть сообщение
Понимаешь что такое связывание (линковка)?
нет
Если бы все ответы были "да", тогда и вопрос был бы плёвым.
0
DrOffset
7310 / 4406 / 998
Регистрация: 30.01.2014
Сообщений: 7,242
19.02.2014, 23:16 #42
Цитата Сообщение от kravam Посмотреть сообщение
да
нет
нет
Если бы все ответы были "да", тогда и вопрос был бы плёвым.
Первого "да" хватит, чтобы понять почему компилятор не в состоянии узнать природу вызываемой функции.
В момент построения кода конкретного модуля у него нет информации об остальных модулях. В некоторых других языках - это не так и С++ часто за это ругают. Но эта архитектура уже существует. И мы идем от нее.

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

Хотя существуют решения, которые потребуют ухода от текущей архитектуры, увеличения времени сборки.
Называется LTCG (Microsoft specific). Наверное твой вопрос разрешится, если задействовать LTCG. Но если разобраться, то это читерство, т.к. после включения LTCG obj-файл содержит уже не машинный код, а некоторое промежуточное представление, с которым может работать компилятор при последующих проходах. Это уже совсем не классический С++, хоть и может давать существенный прирост производительности.
1
Nick Alte
Эксперт С++
1639 / 1011 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
20.02.2014, 15:25 #43
Цитата Сообщение от DrOffset Посмотреть сообщение
Называется LTCG (Microsoft specific).
В GCC это называется LTO.
Цитата Сообщение от DrOffset Посмотреть сообщение
Это уже совсем не классический С++
Технические особенности работы компилятора и линковщика собственно к C++ не относятся. Так что никакой потери "классичности" нет, по этому поводу можно не беспокоиться. Как и прежде, мы имеем на входе исходные коды, а на выходе исполняемый файл или библиотеку.
0
DrOffset
7310 / 4406 / 998
Регистрация: 30.01.2014
Сообщений: 7,242
20.02.2014, 15:32 #44
Цитата Сообщение от Nick Alte Посмотреть сообщение
В GCC это называется LTO.
Да. Появилось относительно недавно.

Цитата Сообщение от Nick Alte Посмотреть сообщение
Технические особенности работы компилятора и линковщика собственно к C++ не относятся. Так что никакой потери "классичности" нет, по этому поводу можно не беспокоиться. Как и прежде, мы имеем на входе исходные коды, а на выходе исполняемый файл или библиотеку.
Чтобы было понятно, я в этом своем утверждении исходил не из стандарта языка, а из заявления Страуструпа (если мне не изменяет память - в книге The Design and Evolution of C++), что С++ проектировался так, чтобы исключить лишние проходы компилятора и иметь максимально возможную совместимость с С, если не на уровне кода, то на уровне линковки. То есть это уже практическая сторона (вне стандарта) и взгляд Страуструпа на этот вопрос.
0
Nick Alte
Эксперт С++
1639 / 1011 / 119
Регистрация: 27.09.2009
Сообщений: 1,945
Завершенные тесты: 1
20.02.2014, 15:44 #45
Цитата Сообщение от DrOffset Посмотреть сообщение
Да. Появилось относительно недавно.
Так ведь и у Микрософта оно не с начала времён.
Цитата Сообщение от DrOffset Посмотреть сообщение
То есть это уже практическая сторона (вне стандарта) и взгляд Страуструпа на этот вопрос.
Точнее говоря, это вопрос реализации. Но даже с учётом этого аспекта процесс всё равно не изменяется принципиально: компилятору достаётся меньше работы и он выдаёт более "сырой" промежуточный формат, линкеру приходится потрудиться больше, но по большому счёту обе фазы всё те же, всё там же.
0
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
20.02.2014, 15:44
Привет! Вот еще темы с ответами:

Для чего нужен с++? - C++
Я школьник, мне 16 лет, учусь в 10 классе, планирую поступить на факультет защиты информационных технологий. Сейчас начал изучать с++, не...

Для чего нужен Sizeof - C++
Вот строка memcpy(pMatrPr, pMatr, sizeof(double) * n * m); Это копирование исходной матрицы в преобразованную А что такое...

Для чего нужен define? - C++
Для чего нужен оператор define? например код #define N 20 /* сколько первых чисел посчитать */ void main(){ ...

Для чего нужен break? - C++
для чего нужен break?


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
45
Yandex
Объявления
20.02.2014, 15:44
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru