======DLL====== Заметки по работе с DLL. Links: * [[https://www.transmissionzero.co.uk/computing/building-dlls-with-mingw/|Transmission Zero: Building Windows DLLs with MinGW]] * [[https://www.transmissionzero.co.uk/computing/advanced-mingw-dll-topics/|Transmission Zero: Advanced MinGW DLL Topics]] * [[https://www.agner.org/optimize/calling_conventions.pdf|calling_conventions.pdf]] =====Как посмотреть содержимое DLL===== В VS есть утилита dumpbin.exe, а в Delphi утилита TDump.exe. > TDump.exe SM_Dll.dll > dump.txt [[https://www.frolov-lib.ru/books/bsp/v13/ch3_3.htm|Фролов: Анализ DLL-библиотек при помощи утилиты tdump.exe]] =====Экспорт - импорт функций===== В заголовочном файле лежат объявления функций. * При сборке DLL те функции, которые должны экспортироваться из DLL необходимо пометить как __declspec(dllexport). Если ни одна функция не будет помечена, то будут экспортированы вообще все функции которые есть внутри всех модулей DLL, что есть не хорошо. * При сборке проекта, использующего dll, эти-же функции должны быть помечены как __declspec(dllimport) * Если исходники dll просто подключены к проекту, то никаких модификаторов к функциям не требуется. #if defined(SM_DLL_LIBRARY) #define DLL_EXP __declspec(dllexport) #define ADDCALL __stdcall #else # if defined(BUILD_APP) #define DLL_EXP __declspec(dllimport) #define ADDCALL __stdcall # else #define DLL_EXP #define ADDCALL # endif #endif DLL_EXP ret_status_t ADDCALL init(const char * fileCfg, const char * fileValues, const char * fileAppCfg, EventCallBack notifyFunc, LogCallBack logFunc, uint8_t logEna); DLL_EXP ret_status_t ADDCALL initGetCompleted(uint8_t *completed); DLL_EXP ret_status_t ADDCALL setLogEnable(uint8_t logEna); DLL_EXP ret_status_t ADDCALL deinit(); Если DLL собирается в QtCreator, то define можно объявить в *.pro файле: DEFINES += SM_DLL_LIBRARY =====Name Mangling===== В компилируемом файле *.с могут быть функции с одинаковым именем, но с разным набором входных параметров. Линкеру, собирающему несколько объектных файлов //(*.о)// необходимо понимать, какому вызову следует подставить ту или иную реализацию функции. Поэтому компилятор, формируя объектные файлы, "шифрует" в названии функции параметры ее вызова. Это называется - //манглирование//. DLL файл, это тоже своего рода объектный файл. Поэтому внутри DLL названия функций представлены в манглированном виде. * [[https://en.wikipedia.org/wiki/Name_mangling|Wiki: Декорирование имен, eng]] * [[https://en.wikipedia.org/wiki/Name_mangling|Wiki: Декорирование имен]] При этом манглирование у разных компиляторов может быть различным. Если использующее DLL приложение собирается компилятором, манглирование которого отличается от манглирования компилятора, которым собиралась DLL, то импорт функции по имени не получится. Деманглирование даст не то исходное имя, которое ожидает импортировать из библиотеки программист. Но можно с помощью утилит //dumpbin.exe / TDump.exe// узнать как в DLL называется функция и загрузить ее по прямо по манглированному имени. Пример для Delphi: Содержимое dll, полученное TDump.exe: ... Exports from libNT_Spectrm.dll 5 exported name(s), 5 export addresse(s). Ordinal base is 1. Sorted by Name: RVA Ord. Hint Name -------- ---- ---- ---- 0001683B 1 0000 _ZN9NT_SM_DLL10boardCountEPh ... прочие функции ... Загрузка манглированной функции _ZN9NT_SM_DLL10boardCountEPh: * _Z : используется манглирование имен * N : содержит имя класса/пространство имен и т.п. * 9 NT_SM_DLL : 9 букв в NT_SM_DLL * 10 boardCount : 10 букв в boardCount * E : конец имени * ... var SMDLL_GetBoardCount : function(var brdCount: Byte) : Integer; stdcall; function SMDLL_LoadDll(dllFileName: string): THandle; var lFuncNotFoundMsg : string; begin result := 0; if (dllFileName = '') or (not FileExists(dllFileName)) then begin OutputDebugString(PChar('SMDLL: Dll File not found: ' + dllFileName)); exit; end; result := LoadLibrary(PChar(dllFileName)); if result < HINSTANCE_ERROR then begin OutputDebugString(PChar('SMDLL: Can"t load Library: ' + dllFileName)); exit; end; lFuncNotFoundMsg := ''; @SMDLL_GetBoardCount := LoadDllFunc(result, '_ZN9NT_SM_DLL10boardCountEPh', lFuncNotFoundMsg); ... end [[https://delphisources.ru/pages/faq/master-delphi-7/content/LiB0102.html|Подробнее "Using Existing DLLs"]] Посмотреть название функции в неманглированном виде можно командой: $> c++filt -n _ZN9NT_SM_DLL23appSingleLaserGetStatusEPj NT_SM_DLL::appSingleLaserGetStatus(unsigned int*) NOTE: При использовании stdcall в манглировании используются символы @: * _ZN9NT_SM_DLL23appSingleLaserGetStatusEPj@4 - stdcall * _ZN9NT_SM_DLL23appSingleLaserGetStatusEPj - соглашение по умолчанию (скорее всего cdecl) =====stdcall vs cdecl===== [[https://stackoverflow.com/questions/3404372/stdcall-and-cdecl|Обсуждение на stackoverflow]] // 1. Вызов функции i = Func(x, y, z); // 2. Исполнение функции int Func(int a, int b, int c) { return a + b + c; } ====CDECL==== - Вызов функции * PUSH в стек значений x, y, z * CALL Func * значение из регистра А сохраняется в переменную i * POP (удаление) из стека x, y, z - Исполнение функции * Чтение регистров из стека в регистры и вычисление. * Сохранение результата в регистр А. * RET в адрес вызова функции. (Переменные x, y, z по прежнему в стеке.) ====STDCALL==== - Вызов функции * PUSH в стек значений x, y, z * CALL Func * значение из регистра А сохраняется в переменную i - Исполнение функции * Чтение регистров из стека в регистры и вычисление. * Сохранение результата в регистр А. * POP (удаление) из стека x, y, z * RET в адрес вызова функции.