diff --git a/audiohandler.cpp b/audiohandler.cpp index 9a4cf5e..7b886e1 100644 --- a/audiohandler.cpp +++ b/audiohandler.cpp @@ -313,13 +313,8 @@ void audioHandler::setupLADSP() if (!pfDescriptorFunction) { const char * pcError = dlerror(); if (pcError) - fprintf(stderr, - "Unable to find ladspa_descriptor() function in plugin file " - "\"%s\": %s.\n" - "Are you sure this is a LADSPA plugin file?\n", - pcPluginFilename, - pcError); - exit(1); + qInfo(logAudio()) << __PRETTY_FUNCTION__ << QString("Unable to find ladspa_descriptor() function in plugin library file %1: %2. Are you sure this is a LADSPA plugin file?").arg(pcPluginFilename).arg(pcError); + return; } @@ -349,30 +344,30 @@ void audioHandler::setupLADSP() handle = psDescriptor->instantiate(psDescriptor, SAMPLE_RATE); if (handle == NULL) { - fprintf(stderr, "Can't instantiate plugin %s\n", pcPluginLabel); - exit(1); + qInfo(logAudio()) << QString("Can't instantiate plugin %1").arg(pcPluginLabel); + return; } // get ports int lPortIndex; int lControlPortIndex = 0; - printf("Num ports %lu\n", psDescriptor->PortCount); - for (lPortIndex = 0; + qDebug(logAudio()) << QString("Num ports %1").arg(psDescriptor->PortCount); + for (lPortIndex = 0; lPortIndex < psDescriptor->PortCount; lPortIndex++) { if (LADSPA_IS_PORT_AUDIO (psDescriptor->PortDescriptors[lPortIndex])) { if (LADSPA_IS_PORT_INPUT (psDescriptor->PortDescriptors[lPortIndex])) { - printf("input %d\n", lPortIndex); + qDebug(logAudio()) << QString("input %1").arg(lPortIndex); lInputPortIndex = lPortIndex; psDescriptor->connect_port(handle, lInputPortIndex, pInBuffer[inBufferIndex++]); } else if (LADSPA_IS_PORT_OUTPUT (psDescriptor->PortDescriptors[lPortIndex])) { - printf("output %d\n", lPortIndex); - lOutputPortIndex = lPortIndex; + qDebug(logAudio()) << QString("output %1").arg(lPortIndex); + lOutputPortIndex = lPortIndex; psDescriptor->connect_port(handle, lOutputPortIndex, pOutBuffer[outBufferIndex++]); @@ -381,8 +376,8 @@ void audioHandler::setupLADSP() if (LADSPA_IS_PORT_CONTROL (psDescriptor->PortDescriptors[lPortIndex])) { - printf("control %d\n", lPortIndex); - psDescriptor->connect_port(handle, + qDebug(logAudio()) << QString("control %1").arg(lPortIndex); + psDescriptor->connect_port(handle, lPortIndex, &controls[lControlPortIndex++]); } } @@ -393,8 +388,8 @@ void audioHandler::setupLADSP() if ((psDescriptor == NULL) || (lInputPortIndex == -1) || (lOutputPortIndex == -1)) { - fprintf(stderr, "Can't find plugin information\n"); - exit(1); + qInfo(logAudio()) << "Can't find plugin information"; + return; } if (psDescriptor->activate != NULL) @@ -836,26 +831,24 @@ void audioHandler::incomingAudio(audioPacket inPacket) void* audioHandler::dlopenLADSPA(const char *pcFilename, int iFlag) { - char * pcBuffer; + char * pcBuffer; const char * pcEnd; const char * pcLADSPAPath; const char * pcStart; int iEndsInSO; int iNeedSlash; - size_t iFilenameLength; - void * pvResult; + void* pvResult; + size_t iFilenameLength; iFilenameLength = strlen(pcFilename); pvResult = NULL; - if (pcFilename[0] == '/') { /* The filename is absolute. Assume the user knows what he/she is doing and simply dlopen() it. */ - - pvResult = dlopen(pcFilename, iFlag); - if (pvResult != NULL) - return pvResult; + pvResult = dlopen(pcFilename, iFlag); + if (pvResult != NULL) + return pvResult; } else { @@ -933,11 +926,8 @@ void* audioHandler::loadLADSPAPluginLibrary(const char *pcPluginFilename) pvPluginHandle = dlopenLADSPA(pcPluginFilename, RTLD_NOW); if (!pvPluginHandle) { - fprintf(stderr, - "Failed to load plugin \"%s\": %s\n", - pcPluginFilename, - dlerror()); - exit(1); + qInfo(logAudio()) << QString("Failed to load plugin %1: %2").arg(pcPluginFilename).arg(dlerror()); + return NULL; } return pvPluginHandle; @@ -963,24 +953,17 @@ const LADSPA_Descriptor* audioHandler::findLADSPAPluginDescriptor(void *pvLADSPA if (!pfDescriptorFunction) { const char * pcError = dlerror(); if (pcError) { - fprintf(stderr, - "Unable to find ladspa_descriptor() function in plugin " - "library file \"%s\": %s.\n" - "Are you sure this is a LADSPA plugin file?\n", - pcPluginLibraryFilename, - pcError); - exit(1); + qInfo(logAudio()) << + QString("Unable to find ladspa_descriptor() function in plugin library file %1: %2. Are you sure this is a LADSPA plugin file?").arg(pcPluginLibraryFilename).arg(pcError); + return NULL; } } for (lPluginIndex = 0;; lPluginIndex++) { psDescriptor = pfDescriptorFunction(lPluginIndex); if (psDescriptor == NULL) { - fprintf(stderr, - "Unable to find label \"%s\" in plugin library file \"%s\".\n", - pcPluginLabel, - pcPluginLibraryFilename); - exit(1); + qInfo(logAudio()) << QString("Unable to find label %1: %2").arg(pcPluginLabel).arg(pcPluginLibraryFilename); + return NULL; } if (strcmp(psDescriptor->Label, pcPluginLabel) == 0) return psDescriptor; diff --git a/audiohandler.h b/audiohandler.h index d8867cd..089cef0 100644 --- a/audiohandler.h +++ b/audiohandler.h @@ -41,10 +41,16 @@ typedef signed short MY_TYPE; // Plugin includes: #include #include -#include -#include +#ifdef Q_OS_WIN +#include "dlfcn/dlfcn.h" +#include "ladspa/ladspa.h" +#else +#include +#include +#endif + //#define BUFFER_SIZE (32*1024) #define INTERNAL_SAMPLE_RATE 48000 @@ -198,7 +204,13 @@ private: // char *pcPluginLabel = (char*)"amp_stereo"; // Testing: - char *pcPluginFilename = (char*)"dyson_compress_1403.so"; +#ifdef Q_OS_WIN + char* + pcPluginFilename = (char*)"dyson_compress_1403.dll"; +#else + char* + pcPluginFilename = (char*)"dyson_compress_1403.so"; +#endif char *pcPluginLabel = (char*)"dysonCompress"; int inBufferIndex = 0; diff --git a/dlfcn/dlfcn.c b/dlfcn/dlfcn.c new file mode 100644 index 0000000..f5ca8e3 --- /dev/null +++ b/dlfcn/dlfcn.c @@ -0,0 +1,785 @@ +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * Copyright (c) 2015 Tiancheng "Timothy" Gu + * Copyright (c) 2019 Pali Rohár + * Copyright (c) 2020 Ralf Habacker + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef _DEBUG +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif +#include +#include +#include + + /* Older versions do not have this type */ +#if _WIN32_WINNT < 0x0500 +typedef ULONG ULONG_PTR; +#endif + +/* Older SDK versions do not have these macros */ +#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS +#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x4 +#endif +#ifndef GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT +#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 0x2 +#endif + +#ifdef _MSC_VER +/* https://docs.microsoft.com/en-us/cpp/intrinsics/returnaddress */ +#pragma intrinsic( _ReturnAddress ) +#else +/* https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html */ +#ifndef _ReturnAddress +#define _ReturnAddress( ) ( __builtin_extract_return_addr( __builtin_return_address( 0 ) ) ) +#endif +#endif + +#ifdef DLFCN_WIN32_SHARED +#define DLFCN_WIN32_EXPORTS +#endif +#include "dlfcn.h" + +#if defined( _MSC_VER ) && _MSC_VER >= 1300 +/* https://docs.microsoft.com/en-us/cpp/cpp/noinline */ +#define DLFCN_NOINLINE __declspec( noinline ) +#elif defined( __GNUC__ ) && ( ( __GNUC__ > 3 ) || ( __GNUC__ == 3 && __GNUC_MINOR__ >= 1 ) ) +/* https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html */ +#define DLFCN_NOINLINE __attribute__(( noinline )) +#else +#define DLFCN_NOINLINE +#endif + +/* Note: + * MSDN says these functions are not thread-safe. We make no efforts to have + * any kind of thread safety. + */ + +typedef struct local_object { + HMODULE hModule; + struct local_object* previous; + struct local_object* next; +} local_object; + +static local_object first_object; + +/* These functions implement a double linked list for the local objects. */ +static local_object* local_search(HMODULE hModule) +{ + local_object* pobject; + + if (hModule == NULL) + return NULL; + + for (pobject = &first_object; pobject; pobject = pobject->next) + if (pobject->hModule == hModule) + return pobject; + + return NULL; +} + +static BOOL local_add(HMODULE hModule) +{ + local_object* pobject; + local_object* nobject; + + if (hModule == NULL) + return TRUE; + + pobject = local_search(hModule); + + /* Do not add object again if it's already on the list */ + if (pobject != NULL) + return TRUE; + + for (pobject = &first_object; pobject->next; pobject = pobject->next); + + nobject = (local_object*)malloc(sizeof(local_object)); + + if (!nobject) + return FALSE; + + pobject->next = nobject; + nobject->next = NULL; + nobject->previous = pobject; + nobject->hModule = hModule; + + return TRUE; +} + +static void local_rem(HMODULE hModule) +{ + local_object* pobject; + + if (hModule == NULL) + return; + + pobject = local_search(hModule); + + if (pobject == NULL) + return; + + if (pobject->next) + pobject->next->previous = pobject->previous; + if (pobject->previous) + pobject->previous->next = pobject->next; + + free(pobject); +} + +/* POSIX says dlerror( ) doesn't have to be thread-safe, so we use one + * static buffer. + * MSDN says the buffer cannot be larger than 64K bytes, so we set it to + * the limit. + */ +static char error_buffer[65535]; +static BOOL error_occurred; + +static void save_err_str(const char* str, DWORD dwMessageId) +{ + DWORD ret; + size_t pos, len; + + len = strlen(str); + if (len > sizeof(error_buffer) - 5) + len = sizeof(error_buffer) - 5; + + /* Format error message to: + * "": + */ + pos = 0; + error_buffer[pos++] = '"'; + memcpy(error_buffer + pos, str, len); + pos += len; + error_buffer[pos++] = '"'; + error_buffer[pos++] = ':'; + error_buffer[pos++] = ' '; + + ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwMessageId, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + error_buffer + pos, (DWORD)(sizeof(error_buffer) - pos), NULL); + pos += ret; + + /* When FormatMessageA() fails it returns zero and does not touch buffer + * so add trailing null byte */ + if (ret == 0) + error_buffer[pos] = '\0'; + + if (pos > 1) + { + /* POSIX says the string must not have trailing */ + if (error_buffer[pos - 2] == '\r' && error_buffer[pos - 1] == '\n') + error_buffer[pos - 2] = '\0'; + } + + error_occurred = TRUE; +} + +static void save_err_ptr_str(const void* ptr, DWORD dwMessageId) +{ + char ptr_buf[2 + 2 * sizeof(ptr) + 1]; + char num; + size_t i; + + ptr_buf[0] = '0'; + ptr_buf[1] = 'x'; + + for (i = 0; i < 2 * sizeof(ptr); i++) + { + num = (char)((((ULONG_PTR)ptr) >> (8 * sizeof(ptr) - 4 * (i + 1))) & 0xF); + ptr_buf[2 + i] = num + ((num < 0xA) ? '0' : ('A' - 0xA)); + } + + ptr_buf[2 + 2 * sizeof(ptr)] = 0; + + save_err_str(ptr_buf, dwMessageId); +} + +static HMODULE MyGetModuleHandleFromAddress(const void* addr) +{ + static BOOL(WINAPI * GetModuleHandleExAPtr)(DWORD, LPCSTR, HMODULE*) = NULL; + static BOOL failed = FALSE; + HMODULE kernel32; + HMODULE hModule; + MEMORY_BASIC_INFORMATION info; + SIZE_T sLen; + + if (!failed && GetModuleHandleExAPtr == NULL) + { + kernel32 = GetModuleHandleA("Kernel32.dll"); + if (kernel32 != NULL) + GetModuleHandleExAPtr = (BOOL(WINAPI*)(DWORD, LPCSTR, HMODULE*)) GetProcAddress(kernel32, "GetModuleHandleExA"); + if (GetModuleHandleExAPtr == NULL) + failed = TRUE; + } + + if (!failed) + { + /* If GetModuleHandleExA is available use it with GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS */ + if (!GetModuleHandleExAPtr(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, addr, &hModule)) + return NULL; + } + else + { + /* To get HMODULE from address use undocumented hack from https://stackoverflow.com/a/2396380 + * The HMODULE of a DLL is the same value as the module's base address. + */ + sLen = VirtualQuery(addr, &info, sizeof(info)); + if (sLen != sizeof(info)) + return NULL; + hModule = (HMODULE)info.AllocationBase; + } + + return hModule; +} + +/* Load Psapi.dll at runtime, this avoids linking caveat */ +static BOOL MyEnumProcessModules(HANDLE hProcess, HMODULE* lphModule, DWORD cb, LPDWORD lpcbNeeded) +{ + static BOOL(WINAPI * EnumProcessModulesPtr)(HANDLE, HMODULE*, DWORD, LPDWORD) = NULL; + static BOOL failed = FALSE; + UINT uMode; + HMODULE psapi; + + if (failed) + return FALSE; + + if (EnumProcessModulesPtr == NULL) + { + /* Windows 7 and newer versions have K32EnumProcessModules in Kernel32.dll which is always pre-loaded */ + psapi = GetModuleHandleA("Kernel32.dll"); + if (psapi != NULL) + EnumProcessModulesPtr = (BOOL(WINAPI*)(HANDLE, HMODULE*, DWORD, LPDWORD)) GetProcAddress(psapi, "K32EnumProcessModules"); + + /* Windows Vista and older version have EnumProcessModules in Psapi.dll which needs to be loaded */ + if (EnumProcessModulesPtr == NULL) + { + /* Do not let Windows display the critical-error-handler message box */ + uMode = SetErrorMode(SEM_FAILCRITICALERRORS); + psapi = LoadLibraryA("Psapi.dll"); + if (psapi != NULL) + { + EnumProcessModulesPtr = (BOOL(WINAPI*)(HANDLE, HMODULE*, DWORD, LPDWORD)) GetProcAddress(psapi, "EnumProcessModules"); + if (EnumProcessModulesPtr == NULL) + FreeLibrary(psapi); + } + SetErrorMode(uMode); + } + + if (EnumProcessModulesPtr == NULL) + { + failed = TRUE; + return FALSE; + } + } + + return EnumProcessModulesPtr(hProcess, lphModule, cb, lpcbNeeded); +} + +DLFCN_EXPORT +void* dlopen(const char* file, int mode) +{ + HMODULE hModule; + UINT uMode; + + error_occurred = FALSE; + + /* Do not let Windows display the critical-error-handler message box */ + uMode = SetErrorMode(SEM_FAILCRITICALERRORS); + + if (file == NULL) + { + /* POSIX says that if the value of file is NULL, a handle on a global + * symbol object must be provided. That object must be able to access + * all symbols from the original program file, and any objects loaded + * with the RTLD_GLOBAL flag. + * The return value from GetModuleHandle( ) allows us to retrieve + * symbols only from the original program file. EnumProcessModules() is + * used to access symbols from other libraries. For objects loaded + * with the RTLD_LOCAL flag, we create our own list later on. They are + * excluded from EnumProcessModules() iteration. + */ + hModule = GetModuleHandle(NULL); + + if (!hModule) + save_err_str("(null)", GetLastError()); + } + else + { + HANDLE hCurrentProc; + DWORD dwProcModsBefore, dwProcModsAfter; + char lpFileName[MAX_PATH]; + size_t i, len; + + len = strlen(file); + + if (len >= sizeof(lpFileName)) + { + save_err_str(file, ERROR_FILENAME_EXCED_RANGE); + hModule = NULL; + } + else + { + /* MSDN says backslashes *must* be used instead of forward slashes. */ + for (i = 0; i < len; i++) + { + if (file[i] == '/') + lpFileName[i] = '\\'; + else + lpFileName[i] = file[i]; + } + lpFileName[len] = '\0'; + + hCurrentProc = GetCurrentProcess(); + + if (MyEnumProcessModules(hCurrentProc, NULL, 0, &dwProcModsBefore) == 0) + dwProcModsBefore = 0; + + /* POSIX says the search path is implementation-defined. + * LOAD_WITH_ALTERED_SEARCH_PATH is used to make it behave more closely + * to UNIX's search paths (start with system folders instead of current + * folder). + */ + hModule = LoadLibraryExA(lpFileName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + + if (!hModule) + { + save_err_str(lpFileName, GetLastError()); + } + else + { + if (MyEnumProcessModules(hCurrentProc, NULL, 0, &dwProcModsAfter) == 0) + dwProcModsAfter = 0; + + /* If the object was loaded with RTLD_LOCAL, add it to list of local + * objects, so that its symbols cannot be retrieved even if the handle for + * the original program file is passed. POSIX says that if the same + * file is specified in multiple invocations, and any of them are + * RTLD_GLOBAL, even if any further invocations use RTLD_LOCAL, the + * symbols will remain global. If number of loaded modules was not + * changed after calling LoadLibraryEx(), it means that library was + * already loaded. + */ + if ((mode & RTLD_LOCAL) && dwProcModsBefore != dwProcModsAfter) + { + if (!local_add(hModule)) + { + save_err_str(lpFileName, ERROR_NOT_ENOUGH_MEMORY); + FreeLibrary(hModule); + hModule = NULL; + } + } + else if (!(mode & RTLD_LOCAL) && dwProcModsBefore == dwProcModsAfter) + { + local_rem(hModule); + } + } + } + } + + /* Return to previous state of the error-mode bit flags. */ + SetErrorMode(uMode); + + return (void*)hModule; +} + +DLFCN_EXPORT +int dlclose(void* handle) +{ + HMODULE hModule = (HMODULE)handle; + BOOL ret; + + error_occurred = FALSE; + + ret = FreeLibrary(hModule); + + /* If the object was loaded with RTLD_LOCAL, remove it from list of local + * objects. + */ + if (ret) + local_rem(hModule); + else + save_err_ptr_str(handle, GetLastError()); + + /* dlclose's return value in inverted in relation to FreeLibrary's. */ + ret = !ret; + + return (int)ret; +} + +DLFCN_NOINLINE /* Needed for _ReturnAddress() */ +DLFCN_EXPORT +void* dlsym(void* handle, const char* name) +{ + FARPROC symbol; + HMODULE hCaller; + HMODULE hModule; + DWORD dwMessageId; + + error_occurred = FALSE; + + symbol = NULL; + hCaller = NULL; + hModule = GetModuleHandle(NULL); + dwMessageId = 0; + + if (handle == RTLD_DEFAULT) + { + /* The symbol lookup happens in the normal global scope; that is, + * a search for a symbol using this handle would find the same + * definition as a direct use of this symbol in the program code. + * So use same lookup procedure as when filename is NULL. + */ + handle = hModule; + } + else if (handle == RTLD_NEXT) + { + /* Specifies the next object after this one that defines name. + * This one refers to the object containing the invocation of dlsym(). + * The next object is the one found upon the application of a load + * order symbol resolution algorithm. To get caller function of dlsym() + * use _ReturnAddress() intrinsic. To get HMODULE of caller function + * use MyGetModuleHandleFromAddress() which calls either standard + * GetModuleHandleExA() function or hack via VirtualQuery(). + */ + hCaller = MyGetModuleHandleFromAddress(_ReturnAddress()); + + if (hCaller == NULL) + { + dwMessageId = ERROR_INVALID_PARAMETER; + goto end; + } + } + + if (handle != RTLD_NEXT) + { + symbol = GetProcAddress((HMODULE)handle, name); + + if (symbol != NULL) + goto end; + } + + /* If the handle for the original program file is passed, also search + * in all globally loaded objects. + */ + + if (hModule == handle || handle == RTLD_NEXT) + { + HANDLE hCurrentProc; + HMODULE* modules; + DWORD cbNeeded; + DWORD dwSize; + size_t i; + + hCurrentProc = GetCurrentProcess(); + + /* GetModuleHandle( NULL ) only returns the current program file. So + * if we want to get ALL loaded module including those in linked DLLs, + * we have to use EnumProcessModules( ). + */ + if (MyEnumProcessModules(hCurrentProc, NULL, 0, &dwSize) != 0) + { + modules = malloc(dwSize); + if (modules) + { + if (MyEnumProcessModules(hCurrentProc, modules, dwSize, &cbNeeded) != 0 && dwSize == cbNeeded) + { + for (i = 0; i < dwSize / sizeof(HMODULE); i++) + { + if (handle == RTLD_NEXT && hCaller) + { + /* Next modules can be used for RTLD_NEXT */ + if (hCaller == modules[i]) + hCaller = NULL; + continue; + } + if (local_search(modules[i])) + continue; + symbol = GetProcAddress(modules[i], name); + if (symbol != NULL) + { + free(modules); + goto end; + } + } + + } + free(modules); + } + else + { + dwMessageId = ERROR_NOT_ENOUGH_MEMORY; + goto end; + } + } + } + +end: + if (symbol == NULL) + { + if (!dwMessageId) + dwMessageId = ERROR_PROC_NOT_FOUND; + save_err_str(name, dwMessageId); + } + + return *(void**)(&symbol); +} + +DLFCN_EXPORT +char* dlerror(void) +{ + /* If this is the second consecutive call to dlerror, return NULL */ + if (!error_occurred) + return NULL; + + /* POSIX says that invoking dlerror( ) a second time, immediately following + * a prior invocation, shall result in NULL being returned. + */ + error_occurred = FALSE; + + return error_buffer; +} + +/* See https://docs.microsoft.com/en-us/archive/msdn-magazine/2002/march/inside-windows-an-in-depth-look-into-the-win32-portable-executable-file-format-part-2 + * for details */ + + /* Get specific image section */ +static BOOL get_image_section(HMODULE module, int index, void** ptr, DWORD* size) +{ + IMAGE_DOS_HEADER* dosHeader; + IMAGE_OPTIONAL_HEADER* optionalHeader; + + dosHeader = (IMAGE_DOS_HEADER*)module; + + if (dosHeader->e_magic != 0x5A4D) + return FALSE; + + optionalHeader = (IMAGE_OPTIONAL_HEADER*)((BYTE*)module + dosHeader->e_lfanew + 24); + + if (optionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) + return FALSE; + + if (index < 0 || index > IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR) + return FALSE; + + if (optionalHeader->DataDirectory[index].Size == 0 || optionalHeader->DataDirectory[index].VirtualAddress == 0) + return FALSE; + + if (size != NULL) + *size = optionalHeader->DataDirectory[index].Size; + + *ptr = (void*)((BYTE*)module + optionalHeader->DataDirectory[index].VirtualAddress); + + return TRUE; +} + +/* Return symbol name for a given address from export table */ +static const char* get_export_symbol_name(HMODULE module, IMAGE_EXPORT_DIRECTORY* ied, const void* addr, void** func_address) +{ + DWORD i; + void* candidateAddr = NULL; + int candidateIndex = -1; + BYTE* base = (BYTE*)module; + DWORD* functionAddressesOffsets = (DWORD*)(base + ied->AddressOfFunctions); + DWORD* functionNamesOffsets = (DWORD*)(base + ied->AddressOfNames); + USHORT* functionNameOrdinalsIndexes = (USHORT*)(base + ied->AddressOfNameOrdinals); + + for (i = 0; i < ied->NumberOfFunctions; i++) + { + if ((void*)(base + functionAddressesOffsets[i]) > addr || candidateAddr >= (void*)(base + functionAddressesOffsets[i])) + continue; + + candidateAddr = (void*)(base + functionAddressesOffsets[i]); + candidateIndex = i; + } + + if (candidateIndex == -1) + return NULL; + + *func_address = candidateAddr; + + for (i = 0; i < ied->NumberOfNames; i++) + { + if (functionNameOrdinalsIndexes[i] == candidateIndex) + return (const char*)(base + functionNamesOffsets[i]); + } + + return NULL; +} + +static BOOL is_valid_address(const void* addr) +{ + MEMORY_BASIC_INFORMATION info; + SIZE_T result; + + if (addr == NULL) + return FALSE; + + /* check valid pointer */ + result = VirtualQuery(addr, &info, sizeof(info)); + + if (result == 0 || info.AllocationBase == NULL || info.AllocationProtect == 0 || info.AllocationProtect == PAGE_NOACCESS) + return FALSE; + + return TRUE; +} + +/* Return state if address points to an import thunk + * + * An import thunk is setup with a 'jmp' instruction followed by an + * absolute address (32bit) or relative offset (64bit) pointing into + * the import address table (iat), which is partially maintained by + * the runtime linker. + */ +static BOOL is_import_thunk(const void* addr) +{ + return *(short*)addr == 0x25ff ? TRUE : FALSE; +} + +/* Return adress from the import address table (iat), + * if the original address points to a thunk table entry. + */ +static void* get_address_from_import_address_table(void* iat, DWORD iat_size, const void* addr) +{ + BYTE* thkp = (BYTE*)addr; + /* Get offset from thunk table (after instruction 0xff 0x25) + * 4018c8 <_VirtualQuery>: ff 25 4a 8a 00 00 + */ + ULONG offset = *(ULONG*)(thkp + 2); +#ifdef _WIN64 + /* On 64 bit the offset is relative + * 4018c8: ff 25 4a 8a 00 00 jmpq *0x8a4a(%rip) # 40a318 <__imp_VirtualQuery> + * And can be also negative (MSVC in WDK) + * 100002f20: ff 25 3a e1 ff ff jmpq *-0x1ec6(%rip) # 0x100001060 + * So cast to signed LONG type + */ + BYTE* ptr = (BYTE*)(thkp + 6 + (LONG)offset); +#else + /* On 32 bit the offset is absolute + * 4019b4: ff 25 90 71 40 00 jmp *0x40719 + */ + BYTE* ptr = (BYTE*)offset; +#endif + + if (!is_valid_address(ptr) || ptr < (BYTE*)iat || ptr >(BYTE*) iat + iat_size) + return NULL; + + return *(void**)ptr; +} + +/* Holds module filename */ +static char module_filename[2 * MAX_PATH]; + +static BOOL fill_info(const void* addr, Dl_info* info) +{ + HMODULE hModule; + DWORD dwSize; + IMAGE_EXPORT_DIRECTORY* ied; + void* funcAddress = NULL; + + /* Get module of the specified address */ + hModule = MyGetModuleHandleFromAddress(addr); + + if (hModule == NULL) + return FALSE; + + dwSize = GetModuleFileNameA(hModule, module_filename, sizeof(module_filename)); + + if (dwSize == 0 || dwSize == sizeof(module_filename)) + return FALSE; + + info->dli_fname = module_filename; + info->dli_fbase = (void*)hModule; + + /* Find function name and function address in module's export table */ + if (get_image_section(hModule, IMAGE_DIRECTORY_ENTRY_EXPORT, (void**)&ied, NULL)) + info->dli_sname = get_export_symbol_name(hModule, ied, addr, &funcAddress); + else + info->dli_sname = NULL; + + info->dli_saddr = info->dli_sname == NULL ? NULL : funcAddress != NULL ? funcAddress : (void*)addr; + + return TRUE; +} + +DLFCN_EXPORT +int dladdr(const void* addr, Dl_info* info) +{ + if (info == NULL) + return 0; + + if (!is_valid_address(addr)) + return 0; + + if (is_import_thunk(addr)) + { + void* iat; + DWORD iatSize; + HMODULE hModule; + + /* Get module of the import thunk address */ + hModule = MyGetModuleHandleFromAddress(addr); + + if (hModule == NULL) + return 0; + + if (!get_image_section(hModule, IMAGE_DIRECTORY_ENTRY_IAT, &iat, &iatSize)) + { + /* Fallback for cases where the iat is not defined, + * for example i586-mingw32msvc-gcc */ + IMAGE_IMPORT_DESCRIPTOR* iid; + DWORD iidSize; + + if (!get_image_section(hModule, IMAGE_DIRECTORY_ENTRY_IMPORT, (void**)&iid, &iidSize)) + return 0; + + if (iid == NULL || iid->Characteristics == 0 || iid->FirstThunk == 0) + return 0; + + iat = (void*)((BYTE*)hModule + iid->FirstThunk); + /* We assume that in this case iid and iat's are in linear order */ + iatSize = iidSize - (DWORD)((BYTE*)iat - (BYTE*)iid); + } + + addr = get_address_from_import_address_table(iat, iatSize, addr); + + if (!is_valid_address(addr)) + return 0; + } + + if (!fill_info(addr, info)) + return 0; + + return 1; +} + +#ifdef DLFCN_WIN32_SHARED +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + (void)hinstDLL; + (void)fdwReason; + (void)lpvReserved; + return TRUE; +} +#endif \ No newline at end of file diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h new file mode 100644 index 0000000..af223ef --- /dev/null +++ b/dlfcn/dlfcn.h @@ -0,0 +1,94 @@ +/* + * dlfcn-win32 + * Copyright (c) 2007 Ramiro Polla + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef DLFCN_H +#define DLFCN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(DLFCN_WIN32_SHARED) +#if defined(DLFCN_WIN32_EXPORTS) +# define DLFCN_EXPORT __declspec(dllexport) +#else +# define DLFCN_EXPORT __declspec(dllimport) +#endif +#else +# define DLFCN_EXPORT +#endif + + /* Relocations are performed when the object is loaded. */ +#define RTLD_NOW 0 + +/* Relocations are performed at an implementation-defined time. + * Windows API does not support lazy symbol resolving (when first reference + * to a given symbol occurs). So RTLD_LAZY implementation is same as RTLD_NOW. + */ +#define RTLD_LAZY RTLD_NOW + + /* All symbols are available for relocation processing of other modules. */ +#define RTLD_GLOBAL (1 << 1) + +/* All symbols are not made available for relocation processing by other modules. */ +#define RTLD_LOCAL (1 << 2) + +/* These two were added in The Open Group Base Specifications Issue 6. + * Note: All other RTLD_* flags in any dlfcn.h are not standard compliant. + */ + + /* The symbol lookup happens in the normal global scope. */ +#define RTLD_DEFAULT ((void *)0) + +/* Specifies the next object after this one that defines name. */ +#define RTLD_NEXT ((void *)-1) + +/* Structure filled in by dladdr() */ + typedef struct dl_info + { + const char* dli_fname; /* Filename of defining object (thread unsafe and reused on every call to dladdr) */ + void* dli_fbase; /* Load address of that object */ + const char* dli_sname; /* Name of nearest lower symbol */ + void* dli_saddr; /* Exact value of nearest symbol */ + } Dl_info; + + /* Open a symbol table handle. */ + DLFCN_EXPORT void* dlopen(const char* file, int mode); + + /* Close a symbol table handle. */ + DLFCN_EXPORT int dlclose(void* handle); + + /* Get the address of a symbol from a symbol table handle. */ + DLFCN_EXPORT void* dlsym(void* handle, const char* name); + + /* Get diagnostic information. */ + DLFCN_EXPORT char* dlerror(void); + + /* Translate address to symbolic information (no POSIX standard) */ + DLFCN_EXPORT int dladdr(const void* addr, Dl_info* info); + +#ifdef __cplusplus +} +#endif + +#endif /* DLFCN_H */ \ No newline at end of file diff --git a/wfview.pro b/wfview.pro index bb6a5b1..e952166 100644 --- a/wfview.pro +++ b/wfview.pro @@ -109,6 +109,9 @@ macx:LIBS += -framework CoreAudio -framework CoreFoundation -lpthread INCLUDEPATH += resampler !linux:INCLUDEPATH += rtaudio +win32:SOURCES += dlfcn/dlfcn.c +win32:HEADERS += dlfcn/dlfcn.h + SOURCES += main.cpp\ wfmain.cpp \ commhandler.cpp \ @@ -159,8 +162,7 @@ HEADERS += wfmain.h \ transceiveradjustments.h \ audiotaper.h \ aboutbox.h \ - ladspa/ladspa.h \ - ladspa/load.c + ladspa/ladspa.h FORMS += wfmain.ui \ diff --git a/wfview.vcxproj b/wfview.vcxproj index 2502415..f306026 100644 --- a/wfview.vcxproj +++ b/wfview.vcxproj @@ -57,7 +57,7 @@ Sync release\ MaxSpeed - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT="ea09e1f";HOST="wfview.org";UNAME="build";QT_NO_DEBUG;NDEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT="92b2ab8";HOST="wfview.org";UNAME="build";NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) false MultiThreadedDLL @@ -67,12 +67,13 @@ true shell32.lib;%(AdditionalDependencies) - C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.6.11-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true false true false + true $(OutDir)\wfview.exe true Windows @@ -84,7 +85,7 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT=\"ea09e1f\";HOST=\"wfview.org\";UNAME=\"build\";QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT=\"92b2ab8\";HOST=\"wfview.org\";UNAME=\"build\";NDEBUG;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h @@ -98,7 +99,7 @@ Sync debug\ Disabled - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT="ea09e1f";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT="92b2ab8";HOST="wfview.org";UNAME="build";%(PreprocessorDefinitions) false MultiThreadedDebugDLL true @@ -107,7 +108,7 @@ true shell32.lib;%(AdditionalDependencies) - C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.6.11-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) true true @@ -123,13 +124,15 @@ 0 - _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT=\"ea09e1f\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_DEPRECATED_WARNINGS;QCUSTOMPLOT_COMPILE_LIBRARY;USE_SSE;OUTSIDE_SPEEX;RANDOM_PREFIX=wf;__WINDOWS_WASAPI__;GITSHORT=\"92b2ab8\";HOST=\"wfview.org\";UNAME=\"build\";QT_MULTIMEDIA_LIB;QT_PRINTSUPPORT_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_SERIALPORT_LIB;QT_NETWORK_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) msvc./$(Configuration)/moc_predefs.hMoc'ing %(Identity)...output$(Configuration)moc_%(Filename).cppdefaultRcc'ing %(Identity)...$(Configuration)qrc_%(Filename).cppUic'ing %(Identity)...$(ProjectDir)ui_%(Filename).h + + @@ -144,12 +147,23 @@ + + + + + + + + + + + @@ -161,6 +175,7 @@ + @@ -181,7 +196,9 @@ + + @@ -269,6 +286,16 @@ + + + + + + + + + + @@ -320,6 +347,8 @@ + + Document true @@ -362,6 +391,10 @@ + + + + @@ -369,6 +402,17 @@ + + + + + + + + + + + @@ -401,6 +445,17 @@ + + + + + + + + + + + diff --git a/wfview.vcxproj.filters b/wfview.vcxproj.filters index bc7845e..cf8b386 100644 --- a/wfview.vcxproj.filters +++ b/wfview.vcxproj.filters @@ -57,6 +57,9 @@ + + Source Files + Source Files @@ -66,6 +69,9 @@ Source Files + + Source Files + Source Files @@ -108,6 +114,9 @@ Source Files + + Source Files + Source Files @@ -122,21 +131,33 @@ + + Header Files + Header Files Header Files + + Header Files + Header Files Header Files + + Header Files + Header Files + + Header Files + Header Files @@ -182,6 +203,9 @@ Header Files + + Header Files + Header Files @@ -207,6 +231,8 @@ + + Generated Files @@ -239,6 +265,10 @@ + + + + @@ -246,6 +276,9 @@ + + Form Files + Form Files @@ -255,6 +288,9 @@ Form Files + + Form Files + Form Files