"Когда вы говорите Иван Васильевич, такое ощущение , что вы бредите..."
Именно так можно описать ситуацию, когда надо понять почему не идет отладка кода библиотек ssleay32 проекта openssl (cdt дебаггер) из под среды Qt Creator.
Как связана отладка и ключи сборки библиотек и самое главное как связана экспортируемость функций.
Ситуация под Windows 10-64 , Qt Creator 2.4.1, Qt 4.8.1 , openssl 1.0.2.
Оказывается библиотека ssleay32.dll собирается (у нас) под Qt Creator 2.4.1) без использования директивы __declspec(dllexport) (экспорт функций).
Почему?... Да потому ,что в исходниках opensssl есть файл ssleay32.def для указания экспортных функций в явном виде при сборки.
DEF_FILE - эта переменная используется под Windows для явного указания экспортируемых функций в создаваемой библиотеке.
DEF расшифровывается как Export Definition File.
DEF_FILE = libeay32.def НА САМОМ ДЕЛЕ нужен если у вас в исходниках нет директив (префиксов) для определения экспорта функций , а именоо : __declspec( dllexport ).
Разработчики openssl решили использовать вариант def файла, вместо того , чтобы прописать префиксы для экспортируемых функций как __declspec( dllexport ).
Тогда использование DEF_FILE = libeay32.def делает функции libeay32.dll экспортируемыми.
Может показаться , что экспортируемость функций по-видимому как-то связана с возможностью входить отладчиком из Qt creator в эти функции (примечание: и это не правда).
Может показаться , что QtNetwork.lib собирается "нормально" с экспортируемыми функциями. Вот кусок файла map сборки:
Address Publics by Value Rva+Base Lib:Object
0000:00000000 __except_list 00000000 <absolute>
0000:00000540 ___safe_se_handler_count 00000540 <absolute>
0000:00009876 __ldused 00009876 <absolute>
0000:00009876 __fltused 00009876 <absolute>
0000:00000000 ___ImageBase 64000000 <linker-defined>
0001:0000e1c0 ??9QUrlInfo@@QBE_NABV0@@Z 6400f1c0 f i qftp.obj
0001:0000e1f0 ?tr@QFtp@@SA?AVQString@@PBD0@Z 6400f1f0 f i qftp.obj
0001:0000e230 ?trUtf8@QFtp@@SA?AVQString@@PBD0@Z 6400f230 f i qftp.obj
0001:0000e270 ?tr@QFtp@@SA?AVQString@@PBD0H@Z 6400f270 f i qftp.obj
0001:0000e2c0 ?trUtf8@QFtp@@SA?AVQString@@PBD0H@Z 6400f2c0 f i qftp.obj
0001:0000e310 ?d_func@QFtp@@AAEPAVQFtpPrivate@@XZ 6400f310 f i qftp.obj
................
Обратите внимание здесь также есть
информация от прилинкованных библиотек openssl.
Это будет возможным если в network.pro
подключить def файл :
DEF_FILE = $$PWD/../qt_openssl1_0_2_libs/ssl/ssleay32.def # делает функции экпортируемыми.
Но так делать не надо!
Не надо включать openssl в
библиотеку QtNetWork.
Это нарушение прав и т.д.
Так делать нельзя, только для проверки :
как будет выглядеть иоговый dll файл.
111 _SSLv23_method
112 _SSLv23_server_method
113 _SSLv2_client_method
114 _SSLv2_method
115 _SSLv2_server_method
116 _SSLv3_client_method
117 _SSLv3_method
118 _SSLv3_server_method
314 _TLSv1_1_client_method
313 _TLSv1_1_method
315 _TLSv1_1_server_method
341 _TLSv1_2_client_method
350 _TLSv1_2_method
343 _TLSv1_2_server_method
172 _TLSv1_client_method
170 _TLSv1_method
171 _TLSv1_server_method
119 _d2i_SSL_SESSION
120 _i2d_SSL_SESSION
Но на самом деле кракозябры у имен функций QtNetWork связаны лишь с тем, что это с++ файлы, то есть имеют расширение cpp.
У файлов *.с кракозябр не будет. И это нормально и на экспортируемость не влияет.
А вот кусок файла map сборки libeay32.dll ( без DEF_FILE = libeay32.def ), когда собирается отдельно как самостоятельная библиотека, то есть как отдельный прнкт ssl.pro в составе src.pro:
Address Publics by Value Rva+Base Lib:Object
0000:00000000 __except_list 00000000 <absolute>
0000:00000001 ___safe_se_handler_count 00000001 <absolute>
0000:00009876 __ldused 00009876 <absolute>
0000:00009876 __fltused 00009876 <absolute>
0000:00000000 ___ImageBase 10000000 <linker-defined>
0001:00009ca0 _OPENSSL_Uplink 1000aca0 f uplink.obj
0001:0000a290 _AES_wrap_key 1000b290 f aes_wrap.obj
0001:0000a2c0 _AES_unwrap_key 1000b2c0 f aes_wrap.obj
0001:0000a300 _AES_ofb128_encrypt 1000b300 f aes_ofb.obj
0001:0000a340 _AES_ige_encrypt 1000b340 f aes_ige.obj
0001:0000a980 _AES_bi_ige_encrypt 1000b980 f aes_ige.obj
0001:0000b0f0 _AES_ecb_encrypt 1000c0f0 f aes_ecb.obj
0001:0000b140 _AES_ctr128_encrypt 1000c140 f aes_ctr.obj
В логе сборки openssl 1.0.2 штатно (из командной строки) видно, что есть присоединение def файла :
link /nologo /subsystem:console /opt:ref /debug /dll /out:out32dll\libeay32.dll /def:ms/LIBEAY32.def @C:\Users\p\AppData\Local\Temp\nm9634.tmp
....
link /nologo /subsystem:console /opt:ref /debug /dll /out:out32dll\ssleay32.dll /def:ms/SSLEAY32.def @C:\Users\p\AppData\Local\Temp\nmD31E.tmp
Или можно в Makefile сборки openssl увидеть то же :
$(O_SSL): $(SSLOBJ)
$(LINK_CMD) $(MLFLAGS) /out:$(O_SSL) /def:ms/SSLEAY32.def @<<
$(SHLIB_EX_OBJ) $(SSLOBJ) $(L_CRYPTO) $(EX_LIBS)
<<
IF EXIST $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;2
$(O_CRYPTO): $(CRYPTOOBJ)
$(LINK_CMD) $(MLFLAGS) /out:$(O_CRYPTO) /def:ms/LIBEAY32.def @<<
$(SHLIB_EX_OBJ) $(CRYPTOOBJ) $(EX_LIBS)
<<
IF EXIST $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;2
Все это означает, что надо подключать LIBEAY32.def и SSLEAY32.def в проект на Qt .
Может показаться, что библиотека libeay32.lib , создаваемая с libeay32.dll при сборке динамики, является такой же, что при сбрке статики. Но похоже это вообще разные вещи. Но название почему-то одно - lib.
Мы стандартно подключаем libeay32 и ssleay32 в файл проекта network.pro таким образом :
CONFIG(debug, debug|release){
INCLUDEPATH += $$PWD/../qt_openssl1_0_2_libs
LIBS += -L$$PWD/../../lib -llibeay32 -lssleay32
}
После сборки на Qt можно посмотреть содержание библиотек через dumpbin:
call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86
dumpbin.exe /exports libeay32.dll > dumpbin_libeay32_dll.txt
dumpbin.exe /exports libeay32.lib > dumpbin_libeay32_lib.txt
Для dll :
Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file libeay32.dll
File Type: DLL
Section contains the following exports for LIBEAY32.dll
00000000 characteristics
623B1CEF time date stamp Wed Mar 23 16:13:19 2022
0.00 version
1 ordinal base
4790 number of functions
3700 number of names
ordinal hint RVA name
1994 0 00001B09 ACCESS_DESCRIPTION_free = @ILT+2820(_ACCESS_DESCRIPTION_free)
2751 1 00003B39 ACCESS_DESCRIPTION_it = @ILT+11060(_ACCESS_DESCRIPTION_it)
1925 2 000041C9 ACCESS_DESCRIPTION_new = @ILT+12740(_ACCESS_DESCRIPTION_new)
3860 3 000046C4 AES_bi_ige_encrypt = @ILT+14015(_AES_bi_ige_encrypt)
3171 4 00002DFB AES_cbc_encrypt = @ILT+7670(_AES_cbc_encrypt)
3217 5 00002EAA AES_cfb128_encrypt = @ILT+7845(_AES_cfb128_encrypt)
3279 6 00005899 AES_cfb1_encrypt = @ILT+18580(_AES_cfb1_encrypt)
3261 7 00004AC5 AES_cfb8_encrypt = @ILT+15040(_AES_cfb8_encrypt)
Для lib:
Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file libeay32.lib
File Type: LIBRARY
Exports
ordinal name
1994 _ACCESS_DESCRIPTION_free
2751 _ACCESS_DESCRIPTION_it
1925 _ACCESS_DESCRIPTION_new
3860 _AES_bi_ige_encrypt
3171 _AES_cbc_encrypt
3217 _AES_cfb128_encrypt
3279 _AES_cfb1_encrypt
3261 _AES_cfb8_encrypt
3216 _AES_ctr128_encrypt
3040 _AES_decrypt
2801 _AES_ecb_encrypt
3033 _AES_encrypt
Может показаться , что здесь нет отладочной информации. Но на самом деле это не так. Отладочная информация здесь никак и не должна присутствовать. Это стандартная инфа с файла.
Можно поэкспирементировать с экспортом функций на примере одноц функции .
Сначала делаем префикс одной из функций проекта ssl.pro. Например для SSL_get_current_cipher, ставим префикс __declspec(dllexport). Кстати делаем это в заголовочном файле ssl.h , но он у нас в 2 местах в /openssl каталоге , который надо тащить с библиотеками типа lib , и в самом проекте ssl.pro. DEF_FILE убираем из проектов конечно же для чистоты эксперимента :
__declspec(dllexport) const SSL_CIPHER *SSL_get_current_cipher(const SSL *s);
Результат не замедляет себя ждать, dumpbin показывает изменения в ssleay32.dll и ssleay32.lib :
dll
ordinal hint RVA name
1 0 00001078 SSL_get_current_cipher = @ILT+115(_SSL_get_current_cipher)
lib
Exports
ordinal name
_SSL_get_current_cipher
If I had to guess, I'd say @ILT+270 is the synthesized name that would
allow to link to an exported function by ordinal
На следующем этапе мы попробуем объединить работу ssleay32.def файла , исключить из него одну функцию (например SSL_get_current_cipher) , в коде для SSL_get_current_cipher сделаем __declspec(dllexport) (как в примере ранее) и получим результат сборки в таком уже виде :
это кусок dumpbin export ssleay32.lib:
52 _SSL_get_cipher_list
55 _SSL_get_ciphers
56 _SSL_get_client_CA_list
_SSL_get_current_cipher
272 _SSL_get_current_compression
274 _SSL_get_current_expansion
это кусок dumpbin export ssleay32.dll:
55 B7 00001541 SSL_get_ciphers = @ILT+1340(_SSL_get_ciphers)
56 B8 000016AE SSL_get_client_CA_list = @ILT+1705(_SSL_get_client_CA_list)
14 B9 00001078 SSL_get_current_cipher = @ILT+115(_SSL_get_current_cipher)
272 BA 00001087 SSL_get_current_compression = @ILT+130(_SSL_get_current_compression)
274 BB 000016EF SSL_get_current_expansion = @ILT+1770(_SSL_get_current_expansion)
это кусок ssleay32.def:
SSL_get_ciphers @55
SSL_get_client_CA_list @56
;SSL_get_current_cipher @127
SSL_get_current_compression @272
SSL_get_current_expansion @274
В таком варианте network.pro соберется нормально :
INCLUDEPATH += $$PWD/../qt_openssl1_0_2_libs
LIBS += -L$$PWD/../../lib -llibeay32 -lssleay32
DEF_FILE = $$PWD/../qt_openssl1_0_2_libs/ssl/ssleay32.def # делает функции экпортируемыми !
Но отладчик в функции SSL_get_current_cipher не останавливается по-прежнему... То есть вроде бы отладчик останавливается , по F11 входим в любую функцию openssl , но по факту не видим кода этой функции и значениц переменных тоже и не понятно где мы вообще...
Примечание: как оказалось впоследствие, для отладки подключалась совсем не наша библиотека, а другая с таким же именем, но другой версии и совсем не отладочной сборки из каталога C:\WINDOWS.
Между делом приходим к пониманию зачем нужны _cdecl | __stdcall .... Декорирование имен (Name Decoration)
Изучение декорирование имен приводит к пониманию , почему файлы проекта network.pro собираются иначе , чем ssl.pro . Все дело в суффиксе компилируемых файлов cpp | с .
| Calling convention | extern "C" or .c file |
.cpp, .cxx or /TP |
|---|---|---|
C naming convention (__cdecl) |
_test |
?test@@ZAXXZ |
Fast call naming convention (__fastcall) |
@test@0 |
?test@@YIXXZ |
Standard call naming convention (__stdcall) |
_test@0 |
?test@@YGXXZ |
Vector call naming convention (__vectorcall) |
test@@0 |
?test@@YQXXZ |
Вопрос отладки openssl трансформируется в промежуточный вопрос : можно ли отлаживать c файлы *.c, проходить по ним в отладчике?...
Делаем два отдельных проекта и изучаем данный вопрос.
extern "C" - Это для файлов cpp , которые будут использоваться в С проектах.
Exporting C++ Functions for Use in C-Language Executables
И поэтому наверное extern "C" не работает в файле *.с . Но нормально компилируется в *.cpp файле.
Exporting C Functions for Use in C or C++ Language Executables.
Чтобы использовать библиотеки написанные на С в проектах написанных на С++ надо
надо при подключении заголовочного файла такой С библиотеки оборачивать его таким образом:
extern "C" {
#include "old_c_file.h"
}
Иначе линковщик будет искать буквально int __cdecl old_c_file(double)" (?old_c_file@@YAHN@Z), а у нас в библиотеке _old_c_file:
main.obj:-1: ошибка: LNK2019: ссылка на неразрешенный внешний символ
"int __cdecl old_c_file(double)" (?old_c_file@@YAHN@Z) в функции _main
И вот примерно где-то в этот момент возникает подозрение - а может так и должно быть?
И никаким образом символы-кракозябры типа @@YAHN@Z для библиотек С и не должны там присутствовать. И отладка совсем не связана с этими кракозябрами?...
Если внимательно посмотреть какие фалы создаются при сборке библиотеки , то оказывается там есть и файл для отладки - называется PDB :
library.DLL – непосредственно код библиотеки, который необходим программам для исполнения
library.LIB – «импорт библиотеки», который описывает символы в разделяемой библиотеке. Этот файл генерируется только если DLL экспортирует какие-то символы; это файл необходим во время линков для всех, кто пользуется библиотекой
library.EXP – «файл экспорта» для компонуемой библиотеки, необходимый для линковки бинарников с циклическими зависимостями
library.ILK – генерируется если была использована опция /INCREMENTAL (нeобходимая для инкрементальной линковки), и содержит информацию, необходимую для дальнейшей инкрементальной компоновки.
library.PDB – генерируется, если была включена опция /DEBUG. Этот файл – база данных программы, которая содержит информацию для отладки библиотеки
library.MAP – генерируется, если была использована опция /MAP. Содержит информацию о внутренней структуре библиотеки/
pdb файлы - это файлы символов для отладчика . pdb файл создается для библиотеки если указана отладка.
Теперь смотрим исходники openssl на предмет наличия обертки extern "C" :
#ifdef __cplusplus
extern "C" {
#endif
И находим их в большом количестве.