diff options
Diffstat (limited to 'xbmc/cores/DllLoader')
44 files changed, 8572 insertions, 0 deletions
diff --git a/xbmc/cores/DllLoader/CMakeLists.txt b/xbmc/cores/DllLoader/CMakeLists.txt new file mode 100644 index 0000000..313b8fe --- /dev/null +++ b/xbmc/cores/DllLoader/CMakeLists.txt @@ -0,0 +1,38 @@ +set(SOURCES coff.cpp + dll.cpp + DllLoader.cpp + DllLoaderContainer.cpp + dll_tracker.cpp + dll_tracker_file.cpp + dll_tracker_library.cpp + dll_util.cpp + LibraryLoader.cpp) + +set(HEADERS coff.h + coffldr.h + dll.h + DllLoader.h + DllLoaderContainer.h + dll_tracker.h + dll_tracker_file.h + dll_tracker_library.h + dll_util.h + LibraryLoader.h) + +if(NOT CORE_SYSTEM_NAME STREQUAL windows AND NOT CORE_SYSTEM_NAME STREQUAL windowsstore) + list(APPEND SOURCES mmap_anon.c + SoLoader.cpp) + list(APPEND HEADERS mmap_anon.h + SoLoader.h) + if(NOT CORE_SYSTEM_NAME STREQUAL freebsd) + list(APPEND SOURCES ldt_keeper.c) + list(APPEND HEADERS ldt_keeper.h) + endif() +else() + list(APPEND SOURCES Win32DllLoader.cpp) + list(APPEND HEADERS Win32DllLoader.h) +endif() + +add_definitions(-DAPI_DEBUG) + +core_add_library(dllloader) diff --git a/xbmc/cores/DllLoader/DllLoader-linux.cpp b/xbmc/cores/DllLoader/DllLoader-linux.cpp new file mode 100644 index 0000000..4d56e30 --- /dev/null +++ b/xbmc/cores/DllLoader/DllLoader-linux.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DllLoader.h" +#include "DllLoaderContainer.h" + +CoffLoader::CoffLoader() : + hModule (NULL ), + CoffFileHeader (NULL ), + OptionHeader (NULL ), + WindowsHeader (NULL ), + Directory (NULL ), + SectionHeader (NULL ), + SymTable (NULL ), + StringTable (NULL ), + SectionData (NULL ), + EntryAddress (0 ), + NumberOfSymbols (0 ), + SizeOfStringTable (0 ), + NumOfDirectories (0 ), + NumOfSections (0 ), + FileHeaderOffset (0 ) +{ +} + +CoffLoader::~CoffLoader() +{ +} + +DllLoaderContainer::DllLoaderContainer() +{ +} + +DllLoader* DllLoaderContainer::LoadModule(const char* sName, const char* sCurrentDir, bool bLoadSymbols) +{ + return NULL; +} + +bool DllLoader::Load() +{ + return false; +} + +void DllLoader::Unload() +{ +} + +void DllLoaderContainer::ReleaseModule(DllLoader*& pDll) +{ +} + +DllLoader::DllLoader(const char *dll, bool track, bool bSystemDll, bool bLoadSymbols, Export* exp) +{ +} + +DllLoader::~DllLoader() +{ +} + +int DllLoader::ResolveExport(const char* x, void** y) +{ +} + +DllLoaderContainer g_dlls; diff --git a/xbmc/cores/DllLoader/DllLoader.cpp b/xbmc/cores/DllLoader/DllLoader.cpp new file mode 100644 index 0000000..d7953de --- /dev/null +++ b/xbmc/cores/DllLoader/DllLoader.cpp @@ -0,0 +1,808 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include <stdlib.h> +#include <algorithm> + +#include "DllLoader.h" +#include "DllLoaderContainer.h" +#include "filesystem/SpecialProtocol.h" +#include "dll_tracker.h" +#include "dll_util.h" +#include <limits> +#include "utils/log.h" + +#ifdef TARGET_WINDOWS +extern "C" FILE *fopen_utf8(const char *_Filename, const char *_Mode); +#else +#define fopen_utf8 fopen +#endif + +#include "commons/Exception.h" + +#define DLL_PROCESS_DETACH 0 +#define DLL_PROCESS_ATTACH 1 +#define DLL_THREAD_ATTACH 2 +#define DLL_THREAD_DETACH 3 +#define DLL_PROCESS_VERIFIER 4 + + +#ifndef APIENTRY +#define APIENTRY __stdcall +#endif + +// Entry point of a dll (DllMain) +typedef int (APIENTRY *EntryFunc)(HINSTANCE hinstDLL, DWORD fdwReason, void* lpvReserved); + + +#ifdef TARGET_POSIX +/* + * This is a dirty hack. + * The win32 DLLs contain an alloca routine, that first probes the soon + * to be allocated new memory *below* the current stack pointer in 4KByte + * increments. After the mem probing below the current %esp, the stack + * pointer is finally decremented to make room for the "alloca"ed memory. + * Maybe the probing code is intended to extend the stack on a windows box. + * Anyway, the linux kernel does *not* extend the stack by simply accessing + * memory below %esp; it segfaults. + * The extend_stack_for_dll_alloca() routine just preallocates a big chunk + * of memory on the stack, for use by the DLLs alloca routine. + * Added the noinline attribute as e.g. gcc 3.2.2 inlines this function + * in a way that breaks it. + */ +static void __attribute__((noinline)) extend_stack_for_dll_alloca(void) +{ + volatile int* mem =(volatile int*)alloca(0x20000); + *mem=0x1234; +} +#endif + + +DllLoader::DllLoader(const char *sDll, bool bTrack, bool bSystemDll, bool bLoadSymbols, Export* exps) : LibraryLoader(sDll) +{ + ImportDirTable = 0; + m_pExportHead = NULL; + m_pStaticExports = exps; + m_bTrack = bTrack; + m_bSystemDll = bSystemDll; + m_pDlls = NULL; + + + if(!bSystemDll) + { + // Initialize FS segment, important for quicktime dll's +#if defined(USE_LDT_KEEPER) + m_ldt_fs = Setup_LDT_Keeper(); +#endif + } + + DllLoaderContainer::RegisterDll(this); + if (m_bTrack) tracker_dll_add(this); + m_bLoadSymbols=bLoadSymbols; + + m_bUnloadSymbols=false; + + /* system dll's are never loaded in any way, so let's just use the pointer */ + /* to this object as their base address */ + if (m_bSystemDll) + hModule = (HMODULE)this; + +} + +DllLoader::~DllLoader() +{ + while (m_pExportHead) + { + ExportEntry* entry = m_pExportHead; + m_pExportHead = entry->next; + + free(entry); + } + + while (m_pDlls) + { + LoadedList* entry = m_pDlls; + m_pDlls = entry->pNext; + LibraryLoader* lib = entry->pDll; + if (entry->pDll) DllLoaderContainer::ReleaseModule(lib); + delete entry; + } + + // can't unload a system dll, as this might be happening during xbmc destruction + if(!m_bSystemDll) + { + DllLoaderContainer::UnRegisterDll(this); + +#ifdef USE_LDT_KEEPER + Restore_LDT_Keeper(m_ldt_fs); +#endif + } + if (m_bTrack) tracker_dll_free(this); + + ImportDirTable = 0; + + // hModule points to DllLoader in this case + if (m_bSystemDll) + hModule = NULL; +} + +int DllLoader::Parse() +{ + int iResult = 0; + + std::string strFileName= GetFileName(); + FILE* fp = fopen_utf8(CSpecialProtocol::TranslatePath(strFileName).c_str(), "rb"); + + if (fp) + { + if (CoffLoader::ParseCoff(fp)) + { + if(WindowsHeader) + tracker_dll_set_addr(this, (uintptr_t)hModule, + (uintptr_t)hModule + WindowsHeader->SizeOfImage - 1); + else + { + uintptr_t iMinAddr = std::numeric_limits<uintptr_t>::max(); + uintptr_t iMaxAddr = 0; + // dll is loaded now, this means we also know the base address of it and its size + for (int i = 0; i < NumOfSections; ++i) + { + iMinAddr = std::min<uintptr_t>(iMinAddr, + (uintptr_t)SectionHeader[i].VirtualAddress); + iMaxAddr = std::max<uintptr_t>(iMaxAddr, + (uintptr_t)(SectionHeader[i].VirtualAddress + + SectionHeader[i].VirtualSize)); + } + if(iMaxAddr > iMinAddr) + { + iMinAddr += (uintptr_t)hModule; + iMaxAddr += (uintptr_t)hModule; + tracker_dll_set_addr(this, iMinAddr, iMaxAddr - 1); + } + } + LoadExports(); + iResult = 1; + } + fclose(fp); + } + if (iResult == 0) + { + m_bTrack = false; + } + return iResult; +} + +void DllLoader::PrintImportLookupTable(unsigned long ImportLookupTable_RVA) +{ + unsigned long *Table = (unsigned long*)RVA2Data(ImportLookupTable_RVA); + + while (*Table) + { + if (*Table & 0x80000000) + { + // Process Ordinal... + CLog::Log(LOGDEBUG, " Ordinal: {:01X}", *Table & 0x7fffffff); + } + else + { + CLog::Log(LOGDEBUG, " Don't process Hint/Name Table yet..."); + } + Table++; + } +} + +void DllLoader::PrintImportTable(ImportDirTable_t *ImportDirTable) +{ + ImportDirTable_t *Imp = ImportDirTable; + int HavePrinted = 0; + + CLog::Log(LOGDEBUG, "The Coff Image contains the following imports:"); + while ( Imp->ImportLookupTable_RVA != 0 || + Imp->TimeStamp != 0 || + Imp->ForwarderChain != 0 || + Imp->Name_RVA != 0 || + Imp->ImportAddressTable_RVA != 0) + { + char *Name; + HavePrinted = 1; + + Name = (char*)RVA2Data(Imp->Name_RVA); + + CLog::Log(LOGDEBUG, " {}:", Name); + CLog::Log(LOGDEBUG, " ImportAddressTable: {:04X}", Imp->ImportAddressTable_RVA); + CLog::Log(LOGDEBUG, " ImportLookupTable: {:04X}", Imp->ImportLookupTable_RVA); + CLog::Log(LOGDEBUG, " TimeStamp: {:01X}", Imp->TimeStamp); + CLog::Log(LOGDEBUG, " Forwarder Chain: {:01X}", Imp->ForwarderChain); + + PrintImportLookupTable(Imp->ImportLookupTable_RVA); + CLog::Log(LOGDEBUG, ""); + Imp++; + } + if (!HavePrinted) CLog::Log(LOGDEBUG, "None."); +} + +void DllLoader::PrintExportTable(ExportDirTable_t *ExportDirTable) +{ + char *Name = (char*)RVA2Data(ExportDirTable->Name_RVA); + + unsigned long *ExportAddressTable = (unsigned long*)RVA2Data(ExportDirTable->ExportAddressTable_RVA); + unsigned long *NamePointerTable = (unsigned long*)RVA2Data(ExportDirTable->NamePointerTable_RVA); + unsigned short *OrdinalTable = (unsigned short*)RVA2Data(ExportDirTable->OrdinalTable_RVA); + + + CLog::Log(LOGDEBUG, "Export Table for {}:", Name); + + CLog::Log(LOGDEBUG, "ExportFlags: {:04X}", ExportDirTable->ExportFlags); + CLog::Log(LOGDEBUG, "TimeStamp: {:04X}", ExportDirTable->TimeStamp); + CLog::Log(LOGDEBUG, "Major Ver: {:02X}", ExportDirTable->MajorVersion); + CLog::Log(LOGDEBUG, "Minor Ver: {:02X}", ExportDirTable->MinorVersion); + CLog::Log(LOGDEBUG, "Name RVA: {:04X}", ExportDirTable->Name_RVA); + CLog::Log(LOGDEBUG, "OrdinalBase {}", ExportDirTable->OrdinalBase); + CLog::Log(LOGDEBUG, "NumAddrTable {}", ExportDirTable->NumAddrTable); + CLog::Log(LOGDEBUG, "NumNamePtrs {}", ExportDirTable->NumNamePtrs); + CLog::Log(LOGDEBUG, "ExportAddressTable_RVA {:04X}", ExportDirTable->ExportAddressTable_RVA); + CLog::Log(LOGDEBUG, "NamePointerTable_RVA {:04X}", ExportDirTable->NamePointerTable_RVA); + CLog::Log(LOGDEBUG, "OrdinalTable_RVA {:04X}", ExportDirTable->OrdinalTable_RVA); + + CLog::Log(LOGDEBUG, "Public Exports:"); + CLog::Log(LOGDEBUG, " ordinal hint RVA name"); + for (unsigned int i = 0; i < ExportDirTable->NumNamePtrs; i++) + { + char *Name = (char*)RVA2Data(NamePointerTable[i]); + + CLog::Log(LOGDEBUG, " {}", OrdinalTable[i] + ExportDirTable->OrdinalBase); + CLog::Log(LOGDEBUG, " {}", OrdinalTable[i]); + CLog::Log(LOGDEBUG, " {:08X}", ExportAddressTable[OrdinalTable[i]]); + CLog::Log(LOGDEBUG, " {}", Name); + } +} + +int DllLoader::ResolveImports(void) +{ + int bResult = 1; + if ( NumOfDirectories >= 2 && Directory[IMPORT_TABLE].Size > 0 ) + { + ImportDirTable = (ImportDirTable_t*)RVA2Data(Directory[IMPORT_TABLE].RVA); + +#ifdef DUMPING_DATA + PrintImportTable(ImportDirTable); +#endif + + ImportDirTable_t *Imp = ImportDirTable; + + while ( Imp->ImportLookupTable_RVA != 0 || + Imp->TimeStamp != 0 || + Imp->ForwarderChain != 0 || + Imp->Name_RVA != 0 || + Imp->ImportAddressTable_RVA != 0) + { + const char *Name = (const char*)RVA2Data(Imp->Name_RVA); + + const char* FileName=ResolveReferencedDll(Name); + // If possible use the dll name WITH path to resolve exports. We could have loaded + // a dll with the same name as another dll but from a different directory + if (FileName) Name=FileName; + + unsigned long *Table = (unsigned long*)RVA2Data(Imp->ImportLookupTable_RVA); + unsigned long *Addr = (unsigned long*)RVA2Data(Imp->ImportAddressTable_RVA); + + while (*Table) + { + if (*Table & 0x80000000) + { + void *Fixup; + if ( !ResolveOrdinal(Name, *Table&0x7ffffff, &Fixup) ) + { + bResult = 0; + char szBuf[128]; + CLog::Log(LOGDEBUG, "Unable to resolve ordinal {} {}", Name, *Table & 0x7ffffff); + sprintf(szBuf, "%lu", *Table&0x7ffffff); + *Addr = create_dummy_function(Name, szBuf); + tracker_dll_data_track(this, *Addr); + } + else + { + *Addr = (unsigned long)Fixup; //woohoo!! + } + } + else + { + // We don't handle Hint/Name tables yet!!! + char *ImpName = (char*)RVA2Data(*Table + 2); + + void *Fixup; + if ( !ResolveName(Name, ImpName, &Fixup) ) + { + *Addr=get_win_function_address(Name, ImpName); + if(!*Addr) + { + CLog::Log(LOGDEBUG, "Unable to resolve {} {}", Name, ImpName); + *Addr = create_dummy_function(Name, ImpName); + tracker_dll_data_track(this, *Addr); + bResult = 0; + } + } + else + { + *Addr = (unsigned long)Fixup; + } + } + Table++; + Addr++; + } + Imp++; + } + } + return bResult; +} + +const char* DllLoader::ResolveReferencedDll(const char* dll) +{ + DllLoader* pDll = static_cast<DllLoader*>(DllLoaderContainer::LoadModule(dll, GetPath(), m_bLoadSymbols)); + + if (!pDll) + { + CLog::Log(LOGDEBUG, "Unable to load referenced dll {} - Dll: {}", dll, GetFileName()); + return NULL; + } + else if (!pDll->IsSystemDll()) + { + LoadedList* entry=new LoadedList; + entry->pDll=pDll; + entry->pNext=m_pDlls; + m_pDlls=entry; + } + + return pDll->GetFileName(); +} + +int DllLoader::LoadExports() +{ + if ( NumOfDirectories > EXPORT_TABLE && Directory[EXPORT_TABLE].Size > 0 ) + { + ExportDirTable = (ExportDirTable_t*)RVA2Data(Directory[EXPORT_TABLE].RVA); + +#ifdef DUMPING_DATA + PrintExportTable(ExportDirTable); +#endif + + //! @todo Validate all pointers are valid. Is a zero RVA valid or not? I'd guess not as it would + //! point to the coff file header, thus not right. + + unsigned long *ExportAddressTable = (unsigned long*)RVA2Data(ExportDirTable->ExportAddressTable_RVA); + unsigned long *NamePointerTable = (unsigned long*)RVA2Data(ExportDirTable->NamePointerTable_RVA); + unsigned short *OrdinalTable = (unsigned short*)RVA2Data(ExportDirTable->OrdinalTable_RVA); + + for (unsigned int i = 0; i < ExportDirTable->NumNamePtrs; i++) + { + char *Name = (char*)RVA2Data(NamePointerTable[i]); + void* Addr = (void*)RVA2Data(ExportAddressTable[OrdinalTable[i]]); + AddExport(Name, OrdinalTable[i]+ExportDirTable->OrdinalBase, Addr); + } + } + return 0; +} + +int DllLoader::ResolveExport(const char *sName, void **pAddr, bool logging) +{ + Export* pExport=GetExportByFunctionName(sName); + + if (pExport) + { + if (m_bTrack && pExport->track_function) + *pAddr=(void*)pExport->track_function; + else + *pAddr=(void*)pExport->function; + + return 1; + } + + const char* sDllName = strrchr(GetFileName(), '\\'); + if (sDllName) sDllName += 1; + else sDllName = GetFileName(); + + if (logging) + CLog::Log(LOGWARNING, "Unable to resolve: {} {}", sDllName, sName); + return 0; +} + +int DllLoader::ResolveOrdinal(unsigned long ordinal, void **pAddr) +{ + Export* pExport=GetExportByOrdinal(ordinal); + + if (pExport) + { + if (m_bTrack && pExport->track_function) + *pAddr=(void*)pExport->track_function; + else + *pAddr=(void*)pExport->function; + + return 1; + } + + const char* sDllName = strrchr(GetFileName(), '\\'); + if (sDllName) sDllName += 1; + else sDllName = GetFileName(); + + CLog::Log(LOGWARNING, "Unable to resolve: {} {}", sDllName, ordinal); + return 0; +} + +Export* DllLoader::GetExportByOrdinal(unsigned long ordinal) +{ + ExportEntry* entry = m_pExportHead; + + while (entry) + { + if (ordinal == entry->exp.ordinal) + { + return &entry->exp; + } + entry = entry->next; + } + + if( m_pStaticExports ) + { + Export* exp = m_pStaticExports; + while(exp->function || exp->track_function || exp->name) + { + if (ordinal == exp->ordinal) + return exp; + exp++; + } + } + + return NULL; +} + +Export* DllLoader::GetExportByFunctionName(const char* sFunctionName) +{ + ExportEntry* entry = m_pExportHead; + + while (entry) + { + if (entry->exp.name && strcmp(sFunctionName, entry->exp.name) == 0) + { + return &entry->exp; + } + entry = entry->next; + } + + if( m_pStaticExports ) + { + Export* exp = m_pStaticExports; + while(exp->function || exp->track_function || exp->name) + { + if (exp->name && strcmp(sFunctionName, exp->name) == 0) + return exp; + exp++; + } + } + + return NULL; +} + +int DllLoader::ResolveOrdinal(const char *sName, unsigned long ordinal, void **fixup) +{ + DllLoader* pDll = static_cast<DllLoader*>(DllLoaderContainer::GetModule(sName)); + + if (pDll) + { + Export* pExp = pDll->GetExportByOrdinal(ordinal); + if(pExp) + { + if (m_bTrack && pExp->track_function) + *fixup = (void*)(pExp->track_function); + else + *fixup = (void*)(pExp->function); + + return 1; + } + } + + return 0; +} + +int DllLoader::ResolveName(const char *sName, char* sFunction, void **fixup) +{ + DllLoader* pDll = static_cast<DllLoader*>(DllLoaderContainer::GetModule(sName)); + + if (pDll) + { + Export* pExp = pDll->GetExportByFunctionName(sFunction); + if(pExp) + { + if (m_bTrack && pExp->track_function) + *fixup = (void*)(pExp->track_function); + else + *fixup = (void*)(pExp->function); + return 1; + } + } + + return 0; +} + +void DllLoader::AddExport(unsigned long ordinal, void* function, void* track_function) +{ + ExportEntry* entry = (ExportEntry*)malloc(sizeof(ExportEntry)); + if (!entry) + return; + entry->exp.function = function; + entry->exp.ordinal = ordinal; + entry->exp.track_function = track_function; + entry->exp.name = NULL; + + entry->next = m_pExportHead; + m_pExportHead = entry; +} + +void DllLoader::AddExport(char* sFunctionName, unsigned long ordinal, void* function, void* track_function) +{ + int len = sizeof(ExportEntry); + + ExportEntry* entry = (ExportEntry*)malloc(len + strlen(sFunctionName) + 1); + if (!entry) + return; + entry->exp.function = function; + entry->exp.ordinal = ordinal; + entry->exp.track_function = track_function; + entry->exp.name = ((char*)(entry)) + len; + strcpy(const_cast<char*>(entry->exp.name), sFunctionName); + + entry->next = m_pExportHead; + m_pExportHead = entry; +} + +void DllLoader::AddExport(char* sFunctionName, void* function, void* track_function) +{ + int len = sizeof(ExportEntry); + + ExportEntry* entry = (ExportEntry*)malloc(len + strlen(sFunctionName) + 1); + if (!entry) + return; + entry->exp.function = (void*)function; + entry->exp.ordinal = -1; + entry->exp.track_function = track_function; + entry->exp.name = ((char*)(entry)) + len; + strcpy(const_cast<char*>(entry->exp.name), sFunctionName); + + entry->next = m_pExportHead; + m_pExportHead = entry; +} + +bool DllLoader::Load() +{ + if (!Parse()) + { + CLog::Log(LOGERROR, "Unable to open dll {}", GetFileName()); + return false; + } + + ResolveImports(); + LoadSymbols(); + + // only execute DllMain if no EntryPoint is found + if (!EntryAddress) + ResolveExport("DllMain", (void**)&EntryAddress); + +#ifdef LOGALL + CLog::Log(LOGDEBUG, "Executing EntryPoint with DLL_PROCESS_ATTACH at: 0x{:x} - Dll: {}", + pLoader->EntryAddress, sName); +#endif + + if(EntryAddress) + { + EntryFunc initdll = (EntryFunc)EntryAddress; + /* since we are handing execution over to unknown code, safeguard here */ + try + { +#ifdef TARGET_POSIX + extend_stack_for_dll_alloca(); +#endif + initdll((HINSTANCE)hModule, DLL_PROCESS_ATTACH , 0); //call "DllMain" with DLL_PROCESS_ATTACH + +#ifdef LOGALL + CLog::Log(LOGDEBUG, "EntryPoint with DLL_PROCESS_ATTACH called - Dll: {}", sName); +#endif + + } + XBMCCOMMONS_HANDLE_UNCHECKED + catch(...) + { + CLog::Log(LOGERROR, "{} - Unhandled exception during DLL_PROCESS_ATTACH", __FUNCTION__); + + // vp7vfw.dll throws a CUserException due to a missing export + // but the export isn't really needed for normal operation + // and dll works anyway, so let's ignore it + + if (StringUtils::CompareNoCase(GetName(), "vp7vfw.dll") != 0) + return false; + + + CLog::Log(LOGDEBUG, "{} - Ignoring exception during DLL_PROCESS_ATTACH", __FUNCTION__); + } + + // init function may have fixed up the export table + // this is what I expect should happens on PECompact2 + // dll's if export table is compressed. + if(!m_pExportHead) + LoadExports(); + } + + return true; +} + +void DllLoader::Unload() +{ +#ifdef LOGALL + CLog::Log(LOGDEBUG, "Executing EntryPoint with DLL_PROCESS_DETACH at: 0x{:x} - Dll: {}", + pDll->EntryAddress, pDll->GetFileName()); +#endif + + //call "DllMain" with DLL_PROCESS_DETACH + if(EntryAddress) + { + EntryFunc initdll = (EntryFunc)EntryAddress; + initdll((HINSTANCE)hModule, DLL_PROCESS_DETACH , 0); + } + +#ifdef LOGALL + CLog::Log(LOGDEBUG, "EntryPoint with DLL_PROCESS_DETACH called - Dll: {}", pDll->GetFileName()); +#endif + + if (m_bUnloadSymbols) + UnloadSymbols(); +} + +// This function is a hack to get symbols loaded for +// dlls. The function FFinishImageLoad internally allocates +// memory which is/can never be freed. And the dll can not be +// unloaded. +void DllLoader::LoadSymbols() +{ +#ifdef ENABLE_SYMBOL_LOADING + if (!m_bLoadSymbols ) return; + + // don't load debug symbols unless we have a debugger present + // seems these calls break on some bioses. i suppose it could + // be related to if the bios has debug capabilities. + if (!DmIsDebuggerPresent()) + { + m_bLoadSymbols=false; + return; + } + + LPVOID pBaseAddress=GetXbdmBaseAddress(); + + if (pBaseAddress) + { + CoffLoader dllxbdm; + if (dllxbdm.ParseHeaders(pBaseAddress)) + { + int offset=GetFFinishImageLoadOffset(dllxbdm.WindowsHeader->CheckSum); + + if (offset==0) + { + CLog::Log(LOGDEBUG, + "DllLoader: Unable to load symbols for {}. No offset for xbdm.dll with checksum " + "{:#08X} found", + GetName(), dllxbdm.WindowsHeader->CheckSum); + return; + } + + // Get a function pointer to the unexported function FFinishImageLoad + fnFFinishImageLoad FFinishImageLoad=(fnFFinishImageLoad)((LPBYTE)pBaseAddress+offset); + + // Prepare parameter for the function call + LDR_DATA_TABLE_ENTRY ldte; + LPLDR_DATA_TABLE_ENTRY pldteout; + ldte.DllBase=hModule; // Address where this dll is loaded into memory + char* szName=GetName(); // Name of this dll without path + + try + { + // Call FFinishImageLoad to register this dll to the debugger and load its symbols. + FFinishImageLoad(&ldte, szName, &pldteout); + } + catch(...) + { + CLog::Log(LOGDEBUG, "DllLoader: Loading symbols for {} failed with an exception.", + GetName()); + } + } + } + else + CLog::Log(LOGDEBUG, "DllLoader: Can't load symbols for {}. xbdm.dll is needed and not loaded", + GetName()); + +#ifdef ENABLE_SYMBOL_UNLOADING + m_bUnloadSymbols=true; // Do this to allow unloading this dll from dllloadercontainer +#endif + +#else + m_bLoadSymbols=false; +#endif +} + +// This function is even more a hack +// It will remove the dll from the Debug manager +// but vs.net does not unload the symbols (don't know why) +// The dll can be loaded again after unloading. +// This function leaks memory. +void DllLoader::UnloadSymbols() +{ +#ifdef ENABLE_SYMBOL_UNLOADING + ANSI_STRING name; + OBJECT_ATTRIBUTES attributes; + RtlInitAnsiString(&name, GetName()); + InitializeObjectAttributes(&attributes, &name, OBJ_CASE_INSENSITIVE, NULL); + + // Try to unload the symbols from vs.net debugger + DbgUnLoadImageSymbols(&name, (ULONG)hModule, 0xFFFFFFFF); + + LPVOID pBaseAddress=GetXbdmBaseAddress(); + + if (pBaseAddress) + { + CoffLoader dllxbdm; + if (dllxbdm.ParseHeaders(pBaseAddress)) + { + int offset=GetDmiOffset(dllxbdm.WindowsHeader->CheckSum); + + if (offset==0) + { + CLog::Log(LOGDEBUG, + "DllLoader: Unable to unload symbols for {}. No offset for xbdm.dll with " + "checksum {:#08X} found", + GetName(), dllxbdm.WindowsHeader->CheckSum); + return; + } + + try + { + std::wstring strNameW; + g_charsetConverter.utf8ToW(GetName(), strNameW); + + // Get the address of the global struct g_dmi + // It is located inside the xbdm.dll and + // get the LoadedModuleList member (here the entry var) + // of the structure. + LPBYTE g_dmi=((LPBYTE)pBaseAddress)+offset; + LIST_ENTRY* entry=(LIST_ENTRY*)(g_dmi+4); + + // Search for the dll we are unloading... + while (entry) + { + std::wstring baseName=(wchar_t*)((LDR_DATA_TABLE_ENTRY*)entry)->BaseDllName.Buffer; + if (baseName == strNameW) + { + // ...and remove it from the LoadedModuleList and free its memory. + LIST_ENTRY* back=entry->Blink; + LIST_ENTRY* front=entry->Flink; + back->Flink=front; + front->Blink=back; + DmFreePool(entry); + break; + } + + entry=entry->Flink; + } + } + catch(...) + { + CLog::Log(LOGDEBUG, "DllLoader: Unloading symbols for {} failed with an exception.", + GetName()); + } + } + } + else + CLog::Log(LOGDEBUG, "DllLoader: Can't unload symbols for {}. xbdm.dll is needed and not loaded", + GetName()); +#endif +} diff --git a/xbmc/cores/DllLoader/DllLoader.h b/xbmc/cores/DllLoader/DllLoader.h new file mode 100644 index 0000000..79e019d --- /dev/null +++ b/xbmc/cores/DllLoader/DllLoader.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "coffldr.h" +#include "LibraryLoader.h" + +// clang-format off +#if defined(__linux__) && \ + !defined(__aarch64__) && \ + !defined(__alpha__) && \ + !defined(__arc__) && \ + !defined(__arm__) && \ + !defined(__loongarch__) && \ + !defined(__mips__) && \ + !defined(__powerpc__) && \ + !defined(__or1k__) && \ + !defined(__riscv) && \ + !defined(__SH4__) && \ + !defined(__s390x__) && \ + !defined(__sparc__) && \ + !defined(__xtensa__) +#define USE_LDT_KEEPER +#include "ldt_keeper.h" +#endif +// clang-format on + +#ifndef NULL +#define NULL 0 +#endif + +class DllLoader; + + +typedef struct Export +{ + const char* name; + unsigned long ordinal; + void* function; + void* track_function; +} Export; + +typedef struct ExportEntry +{ + Export exp; + ExportEntry* next; +} ExportEntry; + +typedef struct _LoadedList +{ + DllLoader* pDll; + _LoadedList* pNext; +} LoadedList; + +class DllLoader : public CoffLoader, public LibraryLoader +{ +public: + DllLoader(const char *dll, bool track = false, bool bSystemDll = false, bool bLoadSymbols = false, Export* exports = NULL); + ~DllLoader() override; + + bool Load() override; + void Unload() override; + + int ResolveExport(const char*, void** ptr, bool logging = true) override; + int ResolveOrdinal(unsigned long ordinal, void** ptr) override; + bool HasSymbols() override { return m_bLoadSymbols && !m_bUnloadSymbols; } + bool IsSystemDll() override { return m_bSystemDll; } + HMODULE GetHModule() override { return (HMODULE)hModule; } + + Export* GetExportByFunctionName(const char* sFunctionName); + Export* GetExportByOrdinal(unsigned long ordinal); +protected: + int Parse(); + int ResolveImports(); + + void AddExport(unsigned long ordinal, void* function, void* track_function = NULL); + void AddExport(char* sFunctionName, unsigned long ordinal, void* function, void* track_function = NULL); + void AddExport(char* sFunctionName, void* function, void* track_function = NULL); + void SetExports(Export* exports) { m_pStaticExports = exports; } + +protected: + // Just pointers; don't delete... + ImportDirTable_t *ImportDirTable; + ExportDirTable_t *ExportDirTable; + bool m_bTrack; + bool m_bSystemDll; // true if this dll should not be removed + bool m_bLoadSymbols; // when true this dll should not be removed + bool m_bUnloadSymbols; + ExportEntry* m_pExportHead; + Export* m_pStaticExports; + LoadedList* m_pDlls; + +#ifdef USE_LDT_KEEPER + ldt_fs_t* m_ldt_fs; +#endif + + void PrintImportLookupTable(unsigned long ImportLookupTable_RVA); + void PrintImportTable(ImportDirTable_t *ImportDirTable); + void PrintExportTable(ExportDirTable_t *ExportDirTable); + + int ResolveOrdinal(const char*, unsigned long, void**); + int ResolveName(const char*, char*, void **); + const char* ResolveReferencedDll(const char* dll); + int LoadExports(); + void LoadSymbols(); + static void UnloadSymbols(); +}; diff --git a/xbmc/cores/DllLoader/DllLoaderContainer.cpp b/xbmc/cores/DllLoader/DllLoaderContainer.cpp new file mode 100644 index 0000000..e56b02f --- /dev/null +++ b/xbmc/cores/DllLoader/DllLoaderContainer.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DllLoaderContainer.h" +#ifdef TARGET_POSIX +#include "SoLoader.h" +#endif +#ifdef TARGET_WINDOWS +#include "Win32DllLoader.h" +#endif +#include "DllLoader.h" +#include "dll_tracker.h" // for python unload hack +#include "filesystem/File.h" +#include "utils/URIUtils.h" +#include "utils/StringUtils.h" +#include "utils/log.h" +#include "URL.h" + +#if defined(TARGET_WINDOWS) +#define ENV_PARTIAL_PATH \ + "special://xbmcbin/;" \ + "special://xbmcbin/system/;" \ + "special://xbmcbin/system/python/;" \ + "special://xbmc/;" \ + "special://xbmc/system/;" \ + "special://xbmc/system/python/" +#else +#define ENV_PARTIAL_PATH \ + "special://xbmcbin/system/;" \ + "special://xbmcbin/system/players/mplayer/;" \ + "special://xbmcbin/system/players/VideoPlayer/;" \ + "special://xbmcbin/system/players/paplayer/;" \ + "special://xbmcbin/system/python/;" \ + "special://xbmc/system/;" \ + "special://xbmc/system/players/mplayer/;" \ + "special://xbmc/system/players/VideoPlayer/;" \ + "special://xbmc/system/players/paplayer/;" \ + "special://xbmc/system/python/" +#endif +#if defined(TARGET_DARWIN) +#define ENV_PATH ENV_PARTIAL_PATH \ + ";special://frameworks/" +#else +#define ENV_PATH ENV_PARTIAL_PATH +#endif + +//Define this to get logging on all calls to load/unload of dlls +//#define LOGALL + + +using namespace XFILE; + +LibraryLoader* DllLoaderContainer::m_dlls[64] = {}; +int DllLoaderContainer::m_iNrOfDlls = 0; +bool DllLoaderContainer::m_bTrack = true; + +void DllLoaderContainer::Clear() +{ +} + +HMODULE DllLoaderContainer::GetModuleAddress(const char* sName) +{ + return (HMODULE)GetModule(sName); +} + +LibraryLoader* DllLoaderContainer::GetModule(const char* sName) +{ + for (int i = 0; i < m_iNrOfDlls && m_dlls[i] != NULL; i++) + { + if (StringUtils::CompareNoCase(m_dlls[i]->GetName(), sName) == 0) + return m_dlls[i]; + if (!m_dlls[i]->IsSystemDll() && + StringUtils::CompareNoCase(m_dlls[i]->GetFileName(), sName) == 0) + return m_dlls[i]; + } + + return NULL; +} + +LibraryLoader* DllLoaderContainer::GetModule(const HMODULE hModule) +{ + for (int i = 0; i < m_iNrOfDlls && m_dlls[i] != NULL; i++) + { + if (m_dlls[i]->GetHModule() == hModule) return m_dlls[i]; + } + return NULL; +} + +LibraryLoader* DllLoaderContainer::LoadModule(const char* sName, const char* sCurrentDir/*=NULL*/, bool bLoadSymbols/*=false*/) +{ + LibraryLoader* pDll=NULL; + + if (IsSystemDll(sName)) + { + pDll = GetModule(sName); + } + else if (sCurrentDir) + { + std::string strPath=sCurrentDir; + strPath+=sName; + pDll = GetModule(strPath.c_str()); + } + + if (!pDll) + { + pDll = GetModule(sName); + } + + if (!pDll) + { + pDll = FindModule(sName, sCurrentDir, bLoadSymbols); + } + else if (!pDll->IsSystemDll()) + { + pDll->IncrRef(); + +#ifdef LOGALL + CLog::Log(LOGDEBUG, "Already loaded Dll {} at 0x{:x}", pDll->GetFileName(), pDll); +#endif + + } + + return pDll; +} + +LibraryLoader* DllLoaderContainer::FindModule(const char* sName, const char* sCurrentDir, bool bLoadSymbols) +{ + if (URIUtils::IsInArchive(sName)) + { + CURL url(sName); + std::string newName = "special://temp/"; + newName += url.GetFileName(); + CFile::Copy(sName, newName); + return FindModule(newName.c_str(), sCurrentDir, bLoadSymbols); + } + + if (CURL::IsFullPath(sName)) + { // Has a path, just try to load + return LoadDll(sName, bLoadSymbols); + } +#ifdef TARGET_POSIX + else if (strcmp(sName, "xbmc.so") == 0) + return LoadDll(sName, bLoadSymbols); +#endif + else if (sCurrentDir) + { // in the path of the parent dll? + std::string strPath=sCurrentDir; + strPath+=sName; + + if (CFile::Exists(strPath)) + return LoadDll(strPath.c_str(), bLoadSymbols); + } + + // in environment variable? + std::vector<std::string> vecEnv; + +#if defined(TARGET_ANDROID) + std::string systemLibs = getenv("KODI_ANDROID_SYSTEM_LIBS"); + vecEnv = StringUtils::Split(systemLibs, ':'); + std::string localLibs = getenv("KODI_ANDROID_LIBS"); + vecEnv.insert(vecEnv.begin(),localLibs); +#else + vecEnv = StringUtils::Split(ENV_PATH, ';'); +#endif + LibraryLoader* pDll = NULL; + + for (std::vector<std::string>::const_iterator i = vecEnv.begin(); i != vecEnv.end(); ++i) + { + std::string strPath = *i; + URIUtils::AddSlashAtEnd(strPath); + +#ifdef LOGALL + CLog::Log(LOGDEBUG, "Searching for the dll {} in directory {}", sName, strPath); +#endif + + strPath+=sName; + + // Have we already loaded this dll + if ((pDll = GetModule(strPath.c_str())) != NULL) + return pDll; + + if (CFile::Exists(strPath)) + return LoadDll(strPath.c_str(), bLoadSymbols); + } + + // can't find it in any of our paths - could be a system dll + if ((pDll = LoadDll(sName, bLoadSymbols)) != NULL) + return pDll; + + CLog::Log(LOGDEBUG, "Dll {} was not found in path", sName); + return NULL; +} + +void DllLoaderContainer::ReleaseModule(LibraryLoader*& pDll) +{ + if (!pDll) + return; + if (pDll->IsSystemDll()) + { + CLog::Log(LOGFATAL, "{} is a system dll and should never be released", pDll->GetName()); + return; + } + + int iRefCount=pDll->DecrRef(); + if (iRefCount==0) + { + +#ifdef LOGALL + CLog::Log(LOGDEBUG, "Releasing Dll {}", pDll->GetFileName()); +#endif + + if (!pDll->HasSymbols()) + { + pDll->Unload(); + delete pDll; + pDll=NULL; + } + else + CLog::Log(LOGINFO, "{} has symbols loaded and can never be unloaded", pDll->GetName()); + } +#ifdef LOGALL + else + { + CLog::Log(LOGDEBUG, "Dll {} is still referenced with a count of {}", pDll->GetFileName(), + iRefCount); + } +#endif +} + +LibraryLoader* DllLoaderContainer::LoadDll(const char* sName, bool bLoadSymbols) +{ + +#ifdef LOGALL + CLog::Log(LOGDEBUG, "Loading dll {}", sName); +#endif + + LibraryLoader* pLoader; +#ifdef TARGET_POSIX + pLoader = new SoLoader(sName, bLoadSymbols); +#elif defined(TARGET_WINDOWS) + pLoader = new Win32DllLoader(sName, false); +#else + pLoader = new DllLoader(sName, m_bTrack, false, bLoadSymbols); +#endif + + if (!pLoader) + { + CLog::Log(LOGERROR, "Unable to create dll {}", sName); + return NULL; + } + + if (!pLoader->Load()) + { + delete pLoader; + return NULL; + } + + return pLoader; +} + +bool DllLoaderContainer::IsSystemDll(const char* sName) +{ + for (int i = 0; i < m_iNrOfDlls && m_dlls[i] != NULL; i++) + { + if (m_dlls[i]->IsSystemDll() && StringUtils::CompareNoCase(m_dlls[i]->GetName(), sName) == 0) + return true; + } + + return false; +} + +int DllLoaderContainer::GetNrOfModules() +{ + return m_iNrOfDlls; +} + +LibraryLoader* DllLoaderContainer::GetModule(int iPos) +{ + if (iPos < m_iNrOfDlls) return m_dlls[iPos]; + return NULL; +} + +void DllLoaderContainer::RegisterDll(LibraryLoader* pDll) +{ + for (LibraryLoader*& dll : m_dlls) + { + if (dll == NULL) + { + dll = pDll; + m_iNrOfDlls++; + break; + } + } +} + +void DllLoaderContainer::UnRegisterDll(LibraryLoader* pDll) +{ + if (pDll) + { + if (pDll->IsSystemDll()) + { + CLog::Log(LOGFATAL, "{} is a system dll and should never be removed", pDll->GetName()); + } + else + { + // remove from the list + bool bRemoved = false; + for (int i = 0; i < m_iNrOfDlls && m_dlls[i]; i++) + { + if (m_dlls[i] == pDll) bRemoved = true; + if (bRemoved && i + 1 < m_iNrOfDlls) + { + m_dlls[i] = m_dlls[i + 1]; + } + } + if (bRemoved) + { + m_iNrOfDlls--; + m_dlls[m_iNrOfDlls] = NULL; + } + } + } +} + +void DllLoaderContainer::UnloadPythonDlls() +{ + // unload all dlls that python could have loaded + for (int i = 0; i < m_iNrOfDlls && m_dlls[i] != NULL; i++) + { + const char* name = m_dlls[i]->GetName(); + if (strstr(name, ".pyd") != NULL) + { + LibraryLoader* pDll = m_dlls[i]; + ReleaseModule(pDll); + i = 0; + } + } + +} diff --git a/xbmc/cores/DllLoader/DllLoaderContainer.h b/xbmc/cores/DllLoader/DllLoaderContainer.h new file mode 100644 index 0000000..3769ff8 --- /dev/null +++ b/xbmc/cores/DllLoader/DllLoaderContainer.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "LibraryLoader.h" + +class DllLoaderContainer +{ +public: + static void Clear(); + static HMODULE GetModuleAddress(const char* sName); + static int GetNrOfModules(); + static LibraryLoader* GetModule(int iPos); + static LibraryLoader* GetModule(const char* sName); + static LibraryLoader* GetModule(const HMODULE hModule); + static LibraryLoader* LoadModule(const char* sName, const char* sCurrentDir=NULL, bool bLoadSymbols=false); + static void ReleaseModule(LibraryLoader*& pDll); + + static void RegisterDll(LibraryLoader* pDll); + static void UnRegisterDll(LibraryLoader* pDll); + static void UnloadPythonDlls(); + +private: + static LibraryLoader* FindModule(const char* sName, const char* sCurrentDir, bool bLoadSymbols); + static LibraryLoader* LoadDll(const char* sName, bool bLoadSymbols); + static bool IsSystemDll(const char* sName); + + static LibraryLoader* m_dlls[64]; + static int m_iNrOfDlls; + static bool m_bTrack; +}; diff --git a/xbmc/cores/DllLoader/LibraryLoader.cpp b/xbmc/cores/DllLoader/LibraryLoader.cpp new file mode 100644 index 0000000..5689900 --- /dev/null +++ b/xbmc/cores/DllLoader/LibraryLoader.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "LibraryLoader.h" + +#include "utils/log.h" + +#include <stdlib.h> +#include <string.h> + +LibraryLoader::LibraryLoader(const std::string& libraryFile): + m_fileName(libraryFile) +{ + size_t pos = m_fileName.find_last_of("\\/"); + if (pos != std::string::npos) + m_path = m_fileName.substr(0, pos); + + m_iRefCount = 1; +} + +LibraryLoader::~LibraryLoader() = default; + +const char *LibraryLoader::GetName() const +{ + size_t pos = m_fileName.find_last_of('/'); + if (pos != std::string::npos) + return &m_fileName.at(pos + 1); // don't include / + return m_fileName.c_str(); +} + +const char *LibraryLoader::GetFileName() const +{ + return m_fileName.c_str(); +} + +const char *LibraryLoader::GetPath() const +{ + return m_path.c_str(); +} + +int LibraryLoader::IncrRef() +{ + m_iRefCount++; + return m_iRefCount; +} + +int LibraryLoader::DecrRef() +{ + m_iRefCount--; + return m_iRefCount; +} + +int LibraryLoader::ResolveOrdinal(unsigned long ordinal, void** ptr) +{ + CLog::Log(LOGWARNING, "{} - Unable to resolve {} in dll {}", __FUNCTION__, ordinal, GetName()); + return 0; +} diff --git a/xbmc/cores/DllLoader/LibraryLoader.h b/xbmc/cores/DllLoader/LibraryLoader.h new file mode 100644 index 0000000..a8db986 --- /dev/null +++ b/xbmc/cores/DllLoader/LibraryLoader.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <string> + +#ifdef TARGET_POSIX +#include "PlatformDefs.h" +#endif + +class LibraryLoader +{ +public: + explicit LibraryLoader(const std::string& libraryFile); + virtual ~LibraryLoader(); + + virtual bool Load() = 0; + virtual void Unload() = 0; + + virtual int ResolveExport(const char* symbol, void** ptr, bool logging = true) = 0; + virtual int ResolveOrdinal(unsigned long ordinal, void** ptr); + virtual bool IsSystemDll() = 0; + virtual HMODULE GetHModule() = 0; + virtual bool HasSymbols() = 0; + + const char *GetName() const; // eg "mplayer.dll" + const char *GetFileName() const; // "special://xbmcbin/system/mplayer/players/mplayer.dll" + const char *GetPath() const; // "special://xbmcbin/system/mplayer/players/" + + int IncrRef(); + int DecrRef(); + int GetRef(); + +private: + LibraryLoader(const LibraryLoader&); + LibraryLoader& operator=(const LibraryLoader&); + std::string m_fileName; + std::string m_path; + int m_iRefCount; +}; diff --git a/xbmc/cores/DllLoader/SoLoader.cpp b/xbmc/cores/DllLoader/SoLoader.cpp new file mode 100644 index 0000000..2b5cbce --- /dev/null +++ b/xbmc/cores/DllLoader/SoLoader.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "SoLoader.h" + +#include "filesystem/SpecialProtocol.h" +#include "utils/log.h" + +#include <dlfcn.h> + +SoLoader::SoLoader(const std::string &so, bool bGlobal) : LibraryLoader(so) +{ + m_soHandle = NULL; + m_bGlobal = bGlobal; + m_bLoaded = false; +} + +SoLoader::~SoLoader() +{ + if (m_bLoaded) + Unload(); +} + +bool SoLoader::Load() +{ + if (m_soHandle != NULL) + return true; + + std::string strFileName= CSpecialProtocol::TranslatePath(GetFileName()); + if (strFileName == "xbmc.so") + { + CLog::Log(LOGDEBUG, "Loading Internal Library"); + m_soHandle = RTLD_DEFAULT; + } + else + { + CLog::Log(LOGDEBUG, "Loading: {}", strFileName); + int flags = RTLD_LAZY; + m_soHandle = dlopen(strFileName.c_str(), flags); + if (!m_soHandle) + { + CLog::Log(LOGERROR, "Unable to load {}, reason: {}", strFileName, dlerror()); + return false; + } + } + m_bLoaded = true; + return true; +} + +void SoLoader::Unload() +{ + + if (m_soHandle) + { + if (dlclose(m_soHandle) != 0) + CLog::Log(LOGERROR, "Unable to unload {}, reason: {}", GetName(), dlerror()); + } + m_bLoaded = false; + m_soHandle = NULL; +} + +int SoLoader::ResolveExport(const char* symbol, void** f, bool logging) +{ + if (!m_bLoaded && !Load()) + { + if (logging) + CLog::Log(LOGWARNING, "Unable to resolve: {} {}, reason: so not loaded", GetName(), symbol); + return 0; + } + + void* s = dlsym(m_soHandle, symbol); + if (!s) + { + if (logging) + CLog::Log(LOGWARNING, "Unable to resolve: {} {}, reason: {}", GetName(), symbol, dlerror()); + return 0; + } + + *f = s; + return 1; +} + +bool SoLoader::IsSystemDll() +{ + return false; +} + +HMODULE SoLoader::GetHModule() +{ + return m_soHandle; +} + +bool SoLoader::HasSymbols() +{ + return false; +} diff --git a/xbmc/cores/DllLoader/SoLoader.h b/xbmc/cores/DllLoader/SoLoader.h new file mode 100644 index 0000000..4adc7e9 --- /dev/null +++ b/xbmc/cores/DllLoader/SoLoader.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <stdio.h> +#ifdef TARGET_POSIX +#include "PlatformDefs.h" +#endif +#include "DllLoader.h" + +class SoLoader : public LibraryLoader +{ +public: + SoLoader(const std::string &so, bool bGlobal = false); + ~SoLoader() override; + + bool Load() override; + void Unload() override; + + int ResolveExport(const char* symbol, void** ptr, bool logging = true) override; + bool IsSystemDll() override; + HMODULE GetHModule() override; + bool HasSymbols() override; + +private: + void* m_soHandle; + bool m_bGlobal; + bool m_bLoaded; +}; diff --git a/xbmc/cores/DllLoader/Win32DllLoader.cpp b/xbmc/cores/DllLoader/Win32DllLoader.cpp new file mode 100644 index 0000000..be0d4a6 --- /dev/null +++ b/xbmc/cores/DllLoader/Win32DllLoader.cpp @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "Win32DllLoader.h" + +#include "DllLoader.h" +#include "DllLoaderContainer.h" +#include "dll_tracker_file.h" +#include "dll_tracker_library.h" +#include "exports/emu_msvcrt.h" +#include "filesystem/SpecialProtocol.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include "platform/win32/CharsetConverter.h" + +#include <limits> + +extern "C" FARPROC WINAPI dllWin32GetProcAddress(HMODULE hModule, LPCSTR function); + +//dllLoadLibraryA, dllFreeLibrary, dllGetProcAddress are from dllLoader, +//they are wrapper functions of COFF/PE32 loader. +extern "C" HMODULE WINAPI dllLoadLibraryA(LPCSTR libname); +extern "C" BOOL WINAPI dllFreeLibrary(HINSTANCE hLibModule); + +// our exports +Export win32_exports[] = +{ + { "LoadLibraryA", -1, (void*)dllLoadLibraryA, (void*)track_LoadLibraryA }, + { "FreeLibrary", -1, (void*)dllFreeLibrary, (void*)track_FreeLibrary }, +// msvcrt + { "_close", -1, (void*)dll_close, (void*)track_close}, + { "_lseek", -1, (void*)dll_lseek, NULL }, + { "_read", -1, (void*)dll_read, NULL }, + { "_write", -1, (void*)dll_write, NULL }, + { "_lseeki64", -1, (void*)dll_lseeki64, NULL }, + { "_open", -1, (void*)dll_open, (void*)track_open }, + { "fflush", -1, (void*)dll_fflush, NULL }, + { "fprintf", -1, (void*)dll_fprintf, NULL }, + { "fwrite", -1, (void*)dll_fwrite, NULL }, + { "putchar", -1, (void*)dll_putchar, NULL }, + { "_fstat", -1, (void*)dll_fstat, NULL }, + { "_mkdir", -1, (void*)dll_mkdir, NULL }, + { "_stat", -1, (void*)dll_stat, NULL }, + { "_fstat32", -1, (void*)dll_fstat, NULL }, + { "_stat32", -1, (void*)dll_stat, NULL }, + { "_findclose", -1, (void*)dll_findclose, NULL }, + { "_findfirst", -1, (void*)dll_findfirst, NULL }, + { "_findnext", -1, (void*)dll_findnext, NULL }, + { "_findfirst64i32", -1, (void*)dll_findfirst64i32, NULL }, + { "_findnext64i32", -1, (void*)dll_findnext64i32, NULL }, + { "fclose", -1, (void*)dll_fclose, (void*)track_fclose}, + { "feof", -1, (void*)dll_feof, NULL }, + { "fgets", -1, (void*)dll_fgets, NULL }, + { "fopen", -1, (void*)dll_fopen, (void*)track_fopen}, + { "fopen_s", -1, (void*)dll_fopen_s, NULL }, + { "putc", -1, (void*)dll_putc, NULL }, + { "fputc", -1, (void*)dll_fputc, NULL }, + { "fputs", -1, (void*)dll_fputs, NULL }, + { "fread", -1, (void*)dll_fread, NULL }, + { "fseek", -1, (void*)dll_fseek, NULL }, + { "ftell", -1, (void*)dll_ftell, NULL }, + { "getc", -1, (void*)dll_getc, NULL }, + { "fgetc", -1, (void*)dll_getc, NULL }, + { "rewind", -1, (void*)dll_rewind, NULL }, + { "vfprintf", -1, (void*)dll_vfprintf, NULL }, + { "fgetpos", -1, (void*)dll_fgetpos, NULL }, + { "fsetpos", -1, (void*)dll_fsetpos, NULL }, + { "_stati64", -1, (void*)dll_stati64, NULL }, + { "_stat64", -1, (void*)dll_stat64, NULL }, + { "_stat64i32", -1, (void*)dll_stat64i32, NULL }, + { "_fstati64", -1, (void*)dll_fstati64, NULL }, + { "_fstat64", -1, (void*)dll_fstat64, NULL }, + { "_fstat64i32", -1, (void*)dll_fstat64i32, NULL }, + { "_telli64", -1, (void*)dll_telli64, NULL }, + { "_tell", -1, (void*)dll_tell, NULL }, + { "_fileno", -1, (void*)dll_fileno, NULL }, + { "ferror", -1, (void*)dll_ferror, NULL }, + { "freopen", -1, (void*)dll_freopen, (void*)track_freopen}, + { "fscanf", -1, (void*)dll_fscanf, NULL }, + { "ungetc", -1, (void*)dll_ungetc, NULL }, + { "_fdopen", -1, (void*)dll_fdopen, NULL }, + { "clearerr", -1, (void*)dll_clearerr, NULL }, + // for debugging + { "printf", -1, (void*)dllprintf, NULL }, + { "vprintf", -1, (void*)dllvprintf, NULL }, + { "perror", -1, (void*)dllperror, NULL }, + { "puts", -1, (void*)dllputs, NULL }, + // workarounds for non-win32 signals + { "signal", -1, (void*)dll_signal, NULL }, + + // libdvdnav + python need this (due to us using dll_putenv() to put stuff only?) + { "getenv", -1, (void*)dll_getenv, NULL }, + { "_environ", -1, (void*)&dll__environ, NULL }, + { "_open_osfhandle", -1, (void*)dll_open_osfhandle, NULL }, + + { NULL, -1, NULL, NULL } +}; + +Win32DllLoader::Win32DllLoader(const std::string& dll, bool isSystemDll) + : LibraryLoader(dll) + , bIsSystemDll(isSystemDll) +{ + m_dllHandle = NULL; + DllLoaderContainer::RegisterDll(this); +} + +Win32DllLoader::~Win32DllLoader() +{ + if (m_dllHandle) + Unload(); + DllLoaderContainer::UnRegisterDll(this); +} + +bool Win32DllLoader::Load() +{ + using namespace KODI::PLATFORM::WINDOWS; + + if (m_dllHandle != NULL) + return true; + + std::string strFileName = GetFileName(); + auto strDllW = ToW(CSpecialProtocol::TranslatePath(strFileName)); + +#ifdef TARGET_WINDOWS_STORE + // The path cannot be an absolute path or a relative path that contains ".." in the path. + auto appPath = winrt::Windows::ApplicationModel::Package::Current().InstalledLocation().Path(); + size_t len = appPath.size(); + + if (!appPath.empty() && wcsnicmp(appPath.c_str(), strDllW.c_str(), len) == 0) + { + if (strDllW.at(len) == '\\' || strDllW.at(len) == '/') + len++; + std::wstring relative = strDllW.substr(len); + m_dllHandle = LoadPackagedLibrary(relative.c_str(), 0); + } + else + m_dllHandle = LoadPackagedLibrary(strDllW.c_str(), 0); +#else + m_dllHandle = LoadLibraryExW(strDllW.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH); +#endif + + if (!m_dllHandle) + { + DWORD dw = GetLastError(); + wchar_t* lpMsgBuf = NULL; + DWORD strLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPWSTR)&lpMsgBuf, 0, NULL); + if (strLen == 0) + strLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), (LPWSTR)&lpMsgBuf, 0, NULL); + + if (strLen != 0) + { + auto strMessage = FromW(lpMsgBuf, strLen); + CLog::Log(LOGERROR, "{}: Failed to load \"{}\" with error {}: \"{}\"", __FUNCTION__, + CSpecialProtocol::TranslatePath(strFileName), dw, strMessage); + } + else + CLog::Log(LOGERROR, "{}: Failed to load \"{}\" with error {}", __FUNCTION__, + CSpecialProtocol::TranslatePath(strFileName), dw); + + LocalFree(lpMsgBuf); + return false; + } + + // handle functions that the dll imports + if (NeedsHooking(strFileName.c_str())) + OverrideImports(strFileName); + + return true; +} + +void Win32DllLoader::Unload() +{ + // restore our imports + RestoreImports(); + + if (m_dllHandle) + { + if (!FreeLibrary(m_dllHandle)) + CLog::Log(LOGERROR, "{} Unable to unload {}", __FUNCTION__, GetName()); + } + + m_dllHandle = NULL; +} + +int Win32DllLoader::ResolveExport(const char* symbol, void** f, bool logging) +{ + if (!m_dllHandle && !Load()) + { + if (logging) + CLog::Log(LOGWARNING, "{} - Unable to resolve: {} {}, reason: DLL not loaded", __FUNCTION__, + GetName(), symbol); + return 0; + } + + void *s = GetProcAddress(m_dllHandle, symbol); + + if (!s) + { + if (logging) + CLog::Log(LOGWARNING, "{} - Unable to resolve: {} {}", __FUNCTION__, GetName(), symbol); + return 0; + } + + *f = s; + return 1; +} + +bool Win32DllLoader::IsSystemDll() +{ + return bIsSystemDll; +} + +HMODULE Win32DllLoader::GetHModule() +{ + return m_dllHandle; +} + +bool Win32DllLoader::HasSymbols() +{ + return false; +} + +void Win32DllLoader::OverrideImports(const std::string &dll) +{ + using KODI::PLATFORM::WINDOWS::ToW; + auto strdllW = ToW(CSpecialProtocol::TranslatePath(dll)); + auto image_base = reinterpret_cast<BYTE*>(m_dllHandle); + + if (!image_base) + { + CLog::Log(LOGERROR, "{} - unable to GetModuleHandle for dll {}", __FUNCTION__, dll); + return; + } + + auto dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(image_base); + auto nt_header = reinterpret_cast<PIMAGE_NT_HEADERS>(image_base + dos_header->e_lfanew); // e_lfanew = value at 0x3c + + auto imp_desc = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>( + image_base + nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + + if (!imp_desc) + { + CLog::Log(LOGERROR, "{} - unable to get import directory for dll {}", __FUNCTION__, dll); + return; + } + + // loop over all imported dlls + for (int i = 0; imp_desc[i].Characteristics != 0; i++) + { + auto dllName = reinterpret_cast<char*>(image_base + imp_desc[i].Name); + + // check whether this is one of our dll's. + if (NeedsHooking(dllName)) + { + // this will do a loadlibrary on it, which should effectively make sure that it's hooked + // Note that the library has obviously already been loaded by the OS (as it's implicitly linked) + // so all this will do is insert our hook and make sure our DllLoaderContainer knows about it + auto hModule = dllLoadLibraryA(dllName); + if (hModule) + m_referencedDlls.push_back(hModule); + } + + PIMAGE_THUNK_DATA orig_first_thunk = reinterpret_cast<PIMAGE_THUNK_DATA>(image_base + imp_desc[i].OriginalFirstThunk); + PIMAGE_THUNK_DATA first_thunk = reinterpret_cast<PIMAGE_THUNK_DATA>(image_base + imp_desc[i].FirstThunk); + + // and then loop over all imported functions + for (int j = 0; orig_first_thunk[j].u1.Function != 0; j++) + { + void *fixup = NULL; + if (orig_first_thunk[j].u1.Function & 0x80000000) + ResolveOrdinal(dllName, (orig_first_thunk[j].u1.Ordinal & 0x7fffffff), &fixup); + else + { // resolve by name + PIMAGE_IMPORT_BY_NAME orig_imports_by_name = (PIMAGE_IMPORT_BY_NAME)( + image_base + orig_first_thunk[j].u1.AddressOfData); + + ResolveImport(dllName, (char*)orig_imports_by_name->Name, &fixup); + }/* + if (!fixup) + { // create a dummy function for tracking purposes + PIMAGE_IMPORT_BY_NAME orig_imports_by_name = (PIMAGE_IMPORT_BY_NAME)( + image_base + orig_first_thunk[j].u1.AddressOfData); + fixup = CreateDummyFunction(dllName, (char*)orig_imports_by_name->Name); + }*/ + if (fixup) + { + // save the old function + Import import; + import.table = &first_thunk[j].u1.Function; + import.function = first_thunk[j].u1.Function; + m_overriddenImports.push_back(import); + + DWORD old_prot = 0; + + // change to protection settings so we can write to memory area + VirtualProtect((PVOID)&first_thunk[j].u1.Function, 4, PAGE_EXECUTE_READWRITE, &old_prot); + + // patch the address of function to point to our overridden version + first_thunk[j].u1.Function = (uintptr_t)fixup; + + // reset to old settings + VirtualProtect((PVOID)&first_thunk[j].u1.Function, 4, old_prot, &old_prot); + } + } + } +} + +bool Win32DllLoader::NeedsHooking(const char *dllName) +{ + if ( !StringUtils::EndsWithNoCase(dllName, "libdvdcss-2.dll") + && !StringUtils::EndsWithNoCase(dllName, "libdvdnav.dll")) + return false; + + LibraryLoader *loader = DllLoaderContainer::GetModule(dllName); + if (loader) + { + // may have hooked this already (we can have repeats in the import table) + for (unsigned int i = 0; i < m_referencedDlls.size(); i++) + { + if (loader->GetHModule() == m_referencedDlls[i]) + return false; + } + } + return true; +} + +void Win32DllLoader::RestoreImports() +{ + // first unhook any referenced dll's + for (auto& module : m_referencedDlls) + dllFreeLibrary(module); + m_referencedDlls.clear(); + + for (auto& import : m_overriddenImports) + { + // change to protection settings so we can write to memory area + DWORD old_prot = 0; + VirtualProtect(import.table, 4, PAGE_EXECUTE_READWRITE, &old_prot); + + *static_cast<uintptr_t *>(import.table) = import.function; + + // reset to old settings + VirtualProtect(import.table, 4, old_prot, &old_prot); + } +} + +bool FunctionNeedsWrapping(Export *exports, const char *functionName, void **fixup) +{ + Export *exp = exports; + while (exp->name) + { + if (strcmp(exp->name, functionName) == 0) + { //! @todo Should we be tracking stuff? + if (0) + *fixup = exp->track_function; + else + *fixup = exp->function; + return true; + } + exp++; + } + return false; +} + +bool Win32DllLoader::ResolveImport(const char *dllName, const char *functionName, void **fixup) +{ + return FunctionNeedsWrapping(win32_exports, functionName, fixup); +} + +bool Win32DllLoader::ResolveOrdinal(const char *dllName, unsigned long ordinal, void **fixup) +{ + Export *exp = win32_exports; + while (exp->name) + { + if (exp->ordinal == ordinal) + { //! @todo Should we be tracking stuff? + if (0) + *fixup = exp->track_function; + else + *fixup = exp->function; + return true; + } + exp++; + } + return false; +} + +extern "C" FARPROC __stdcall dllWin32GetProcAddress(HMODULE hModule, LPCSTR function) +{ + // if the high-order word is zero, then lpProcName is the function's ordinal value + if (reinterpret_cast<uintptr_t>(function) > std::numeric_limits<WORD>::max()) + { + // first check whether this function is one of the ones we need to wrap + void *fixup = NULL; + if (FunctionNeedsWrapping(win32_exports, function, &fixup)) + return (FARPROC)fixup; + } + + // Nope + return GetProcAddress(hModule, function); +} + diff --git a/xbmc/cores/DllLoader/Win32DllLoader.h b/xbmc/cores/DllLoader/Win32DllLoader.h new file mode 100644 index 0000000..f11eb54 --- /dev/null +++ b/xbmc/cores/DllLoader/Win32DllLoader.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "LibraryLoader.h" + +#include <vector> + +class Win32DllLoader : public LibraryLoader +{ +public: + class Import + { + public: + void *table; + uintptr_t function; + }; + + Win32DllLoader(const std::string& dll, bool isSystemDll); + ~Win32DllLoader(); + + virtual bool Load(); + virtual void Unload(); + + virtual int ResolveExport(const char* symbol, void** ptr, bool logging = true); + virtual bool IsSystemDll(); + virtual HMODULE GetHModule(); + virtual bool HasSymbols(); + +private: + void OverrideImports(const std::string &dll); + void RestoreImports(); + static bool ResolveImport(const char *dllName, const char *functionName, void **fixup); + static bool ResolveOrdinal(const char *dllName, unsigned long ordinal, void **fixup); + bool NeedsHooking(const char *dllName); + + HMODULE m_dllHandle; + bool bIsSystemDll; + + std::vector<Import> m_overriddenImports; + std::vector<HMODULE> m_referencedDlls; +}; + diff --git a/xbmc/cores/DllLoader/coff.cpp b/xbmc/cores/DllLoader/coff.cpp new file mode 100644 index 0000000..6e330c9 --- /dev/null +++ b/xbmc/cores/DllLoader/coff.cpp @@ -0,0 +1,997 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include <stdlib.h> +#include <string.h> +#include "coff.h" +#include "coffldr.h" + +//#define DUMPING_DATA 1 + +#ifndef __GNUC__ +#pragma warning (disable:4806) +#endif + +#include "utils/log.h" +#define printf(format, ...) CLog::Log(LOGDEBUG, format , ##__VA_ARGS__) + +const char *DATA_DIR_NAME[16] = + { + "Export Table", + "Import Table", + "Resource Table", + "Exception Table", + "Certificate Table", + "Base Relocation Table", + "Debug", + "Architecture", + "Global Ptr", + "TLS Table", + "Load Config Table", + "Bound Import", + "IAT", + "Delay Import Descriptor", + "COM+ Runtime Header", + "Reserved" + }; + + +CoffLoader::CoffLoader() +{ + CoffFileHeader = 0; + OptionHeader = 0; + WindowsHeader = 0; + Directory = 0; + SectionHeader = 0; + SymTable = 0; + StringTable = 0; + SectionData = 0; + + NumberOfSymbols = 0; + SizeOfStringTable = 0; + NumOfDirectories = 0; + NumOfSections = 0; + FileHeaderOffset = 0; + EntryAddress = 0; + hModule = NULL; +} + +CoffLoader::~CoffLoader() +{ + if ( hModule ) + { +#ifdef TARGET_POSIX + free(hModule); +#else + VirtualFree(hModule, 0, MEM_RELEASE); +#endif + hModule = NULL; + } + if ( SymTable ) + { + delete [] SymTable; + SymTable = 0; + } + if ( StringTable ) + { + delete [] StringTable; + StringTable = 0; + } + if ( SectionData ) + { + delete [] SectionData; + SectionData = 0; + } +} + +// Has nothing to do with the coff loader itself +// it can be used to parse the headers of a dll +// already loaded into memory +int CoffLoader::ParseHeaders(void* hModule) +{ + if (strncmp((char*)hModule, "MZ", 2) != 0) + return 0; + + int* Offset = (int*)((char*)hModule+0x3c); + if (*Offset <= 0) + return 0; + + if (strncmp((char*)hModule+*Offset, "PE\0\0", 4) != 0) + return 0; + + FileHeaderOffset = *Offset + 4; + + CoffFileHeader = (COFF_FileHeader_t *) ( (char*)hModule + FileHeaderOffset ); + NumOfSections = CoffFileHeader->NumberOfSections; + + OptionHeader = (OptionHeader_t *) ( (char*)CoffFileHeader + sizeof(COFF_FileHeader_t) ); + WindowsHeader = (WindowsHeader_t *) ( (char*)OptionHeader + OPTHDR_SIZE ); + EntryAddress = OptionHeader->Entry; + NumOfDirectories = WindowsHeader->NumDirectories; + + Directory = (Image_Data_Directory_t *) ( (char*)WindowsHeader + WINHDR_SIZE); + SectionHeader = (SectionHeader_t *) ( (char*)Directory + sizeof(Image_Data_Directory_t) * NumOfDirectories); + + if (CoffFileHeader->MachineType != IMAGE_FILE_MACHINE_I386) + return 0; + +#ifdef DUMPING_DATA + PrintFileHeader(CoffFileHeader); +#endif + + if ( CoffFileHeader->SizeOfOptionHeader == 0 ) //not an image file, object file maybe + return 0; + + // process Option Header + if (OptionHeader->Magic == OPTMAGIC_PE32P) + { + printf("PE32+ not supported\n"); + return 0; + } + else if (OptionHeader->Magic == OPTMAGIC_PE32) + { + +#ifdef DUMPING_DATA + PrintOptionHeader(OptionHeader); + PrintWindowsHeader(WindowsHeader); +#endif + + } + else + { + //add error message + return 0; + } + +#ifdef DUMPING_DATA + for (int DirCount = 0; DirCount < NumOfDirectories; DirCount++) + { + printf("Data Directory %02d: %s\n", DirCount + 1, DATA_DIR_NAME[DirCount]); + printf(" RVA: %08X\n", Directory[DirCount].RVA); + printf(" Size: %08X\n\n", Directory[DirCount].Size); + } +#endif + + return 1; + +} + +int CoffLoader::LoadCoffHModule(FILE *fp) +{ + //test file signatures + char Sig[4]; + rewind(fp); + memset(Sig, 0, sizeof(Sig)); + if (!fread(Sig, 1, 2, fp) || strncmp(Sig, "MZ", 2) != 0) + return 0; + + if (fseek(fp, 0x3c, SEEK_SET) != 0) + return 0; + + int Offset = 0; + if (!fread(&Offset, sizeof(int), 1, fp) || (Offset <= 0)) + return 0; + + if (fseek(fp, Offset, SEEK_SET) != 0) + return 0; + + memset(Sig, 0, sizeof(Sig)); + if (!fread(Sig, 1, 4, fp) || strncmp(Sig, "PE\0\0", 4) != 0) + return 0; + + Offset += 4; + FileHeaderOffset = Offset; + + // Load and process Header + if (fseek(fp, FileHeaderOffset + sizeof(COFF_FileHeader_t) + OPTHDR_SIZE, SEEK_SET)) //skip to winows headers + return 0; + + WindowsHeader_t tempWindowsHeader; + size_t readcount = fread(&tempWindowsHeader, 1, WINHDR_SIZE, fp); + if (readcount != WINHDR_SIZE) //test file size error + return 0; + + // alloc aligned memory +#ifdef TARGET_POSIX + hModule = malloc(tempWindowsHeader.SizeOfImage); +#elif defined TARGET_WINDOWS_STORE + hModule = VirtualAllocFromApp(GetCurrentProcess(), tempWindowsHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); +#else + hModule = VirtualAllocEx(GetCurrentProcess(), (PVOID)tempWindowsHeader.ImageBase, tempWindowsHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (hModule == NULL) + hModule = VirtualAlloc(GetCurrentProcess(), tempWindowsHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); +#endif + if (hModule == NULL) + return 0; //memory allocation fails + + rewind(fp); + readcount = fread(hModule, 1, tempWindowsHeader.SizeOfHeaders, fp); + if (readcount != tempWindowsHeader.SizeOfHeaders) //file size error + return 0; + + CoffFileHeader = (COFF_FileHeader_t *) ( (char*)hModule + FileHeaderOffset ); + NumOfSections = CoffFileHeader->NumberOfSections; + + OptionHeader = (OptionHeader_t *) ( (char*)CoffFileHeader + sizeof(COFF_FileHeader_t) ); + WindowsHeader = (WindowsHeader_t *) ( (char*)OptionHeader + OPTHDR_SIZE ); + EntryAddress = OptionHeader->Entry; + NumOfDirectories = WindowsHeader->NumDirectories; + + Directory = (Image_Data_Directory_t *) ( (char*)WindowsHeader + WINHDR_SIZE); + SectionHeader = (SectionHeader_t *) ( (char*)Directory + sizeof(Image_Data_Directory_t) * NumOfDirectories); + + if (CoffFileHeader->MachineType != IMAGE_FILE_MACHINE_I386) + return 0; + +#ifdef DUMPING_DATA + PrintFileHeader(CoffFileHeader); +#endif + + if ( CoffFileHeader->SizeOfOptionHeader == 0 ) //not an image file, object file maybe + return 0; + + // process Option Header + if (OptionHeader->Magic == OPTMAGIC_PE32P) + { + printf("PE32+ not supported\n"); + return 0; + } + else if (OptionHeader->Magic == OPTMAGIC_PE32) + { + +#ifdef DUMPING_DATA + PrintOptionHeader(OptionHeader); + PrintWindowsHeader(WindowsHeader); +#endif + + } + else + { + //add error message + return 0; + } + +#ifdef DUMPING_DATA + for (int DirCount = 0; DirCount < NumOfDirectories; DirCount++) + { + printf("Data Directory %02d: %s\n", DirCount + 1, DATA_DIR_NAME[DirCount]); + printf(" RVA: %08X\n", Directory[DirCount].RVA); + printf(" Size: %08X\n\n", Directory[DirCount].Size); + } +#endif + + return 1; + +} + +int CoffLoader::LoadSymTable(FILE *fp) +{ + int Offset = ftell(fp); + if (Offset < 0) + return 0; + + if ( CoffFileHeader->PointerToSymbolTable == 0 ) + return 1; + + if (fseek(fp, CoffFileHeader->PointerToSymbolTable /* + CoffBeginOffset*/, SEEK_SET) != 0) + return 0; + + SymbolTable_t *tmp = new SymbolTable_t[CoffFileHeader->NumberOfSymbols]; + if (!tmp) + { + printf("Could not allocate memory for symbol table!\n"); + return 0; + } + if (!fread((void *)tmp, CoffFileHeader->NumberOfSymbols, sizeof(SymbolTable_t), fp)) + { + delete[] tmp; + return 0; + } + NumberOfSymbols = CoffFileHeader->NumberOfSymbols; + SymTable = tmp; + if (fseek(fp, Offset, SEEK_SET) != 0) + return 0; + return 1; +} + +int CoffLoader::LoadStringTable(FILE *fp) +{ + int StringTableSize; + char *tmp = NULL; + + int Offset = ftell(fp); + if (Offset < 0) + return 0; + + if ( CoffFileHeader->PointerToSymbolTable == 0 ) + return 1; + + if (fseek(fp, CoffFileHeader->PointerToSymbolTable + + CoffFileHeader->NumberOfSymbols * sizeof(SymbolTable_t), + SEEK_SET) != 0) + return 0; + + if (!fread(&StringTableSize, 1, sizeof(int), fp)) + return 0; + StringTableSize -= 4; + if (StringTableSize != 0) + { + tmp = new char[StringTableSize]; + if (tmp == NULL) + { + printf("Could not allocate memory for string table\n"); + return 0; + } + if (!fread((void *)tmp, StringTableSize, sizeof(char), fp)) + { + delete[] tmp; + return 0; + } + } + SizeOfStringTable = StringTableSize; + StringTable = tmp; + if (fseek(fp, Offset, SEEK_SET) != 0) + return 0; + return 1; +} + +int CoffLoader::LoadSections(FILE *fp) +{ + NumOfSections = CoffFileHeader->NumberOfSections; + + SectionData = new char * [NumOfSections]; + if ( !SectionData ) + return 0; + + // Bobbin007: for debug dlls this check always fails + + //////check VMA size!!!!! + //unsigned long vma_size = 0; + //for (int SctnCnt = 0; SctnCnt < NumOfSections; SctnCnt++) + //{ + // SectionHeader_t *ScnHdr = (SectionHeader_t *)(SectionHeader + SctnCnt); + // vma_size = max(vma_size, ScnHdr->VirtualAddress + ScnHdr->SizeOfRawData); + // vma_size = max(vma_size, ScnHdr->VirtualAddress + ScnHdr->VirtualSize); + //} + + //if (WindowsHeader->SizeOfImage < vma_size) + // return 0; //something wrong with file + + for (int SctnCnt = 0; SctnCnt < NumOfSections; SctnCnt++) + { + SectionHeader_t *ScnHdr = SectionHeader + SctnCnt; + SectionData[SctnCnt] = ((char*)hModule + ScnHdr->VirtualAddress); + + if (fseek(fp, ScnHdr->PtrToRawData, SEEK_SET) != 0) + return 0; + + if (!fread(SectionData[SctnCnt], 1, ScnHdr->SizeOfRawData, fp)) + return 0; + +#ifdef DUMPING_DATA + //debug blocks + char szBuf[128]; + char namebuf[9]; + for (int i = 0; i < 8; i++) + namebuf[i] = ScnHdr->Name[i]; + namebuf[8] = '\0'; + sprintf(szBuf, "Load code Sections %s Memory %p,Length %x\n", namebuf, + SectionData[SctnCnt], max(ScnHdr->VirtualSize, ScnHdr->SizeOfRawData)); + OutputDebugString(szBuf); +#endif + + if ( ScnHdr->SizeOfRawData < ScnHdr->VirtualSize ) //initialize BSS data in the end of section + { + memset((char*)((long)(SectionData[SctnCnt]) + ScnHdr->SizeOfRawData), 0, ScnHdr->VirtualSize - ScnHdr->SizeOfRawData); + } + + if (ScnHdr->Characteristics & IMAGE_SCN_CNT_BSS) //initialize whole .BSS section, pure .BSS is obsolete + { + memset(SectionData[SctnCnt], 0, ScnHdr->VirtualSize); + } + +#ifdef DUMPING_DATA + PrintSection(SectionHeader + SctnCnt, SectionData[SctnCnt]); +#endif + + } + return 1; +} + +//FIXME: Add the Free Resources functions + +int CoffLoader::RVA2Section(unsigned long RVA) +{ + NumOfSections = CoffFileHeader->NumberOfSections; + for ( int i = 0; i < NumOfSections; i++) + { + if ( SectionHeader[i].VirtualAddress <= RVA ) + { + if ( i + 1 != NumOfSections ) + { + if ( RVA < SectionHeader[i + 1].VirtualAddress ) + { + if ( SectionHeader[i].VirtualAddress + SectionHeader[i].VirtualSize <= RVA ) + printf("Warning! Address outside of Section: %lx!\n", RVA); + // else + return i; + } + } + else + { + if ( SectionHeader[i].VirtualAddress + SectionHeader[i].VirtualSize <= RVA ) + printf("Warning! Address outside of Section: %lx!\n", RVA); + // else + return i; + } + } + } + printf("RVA2Section lookup failure!\n"); + return 0; +} + +void* CoffLoader::RVA2Data(unsigned long RVA) +{ + int Sctn = RVA2Section(RVA); + + if( RVA < SectionHeader[Sctn].VirtualAddress + || RVA >= SectionHeader[Sctn].VirtualAddress + SectionHeader[Sctn].VirtualSize) + { + // RVA2Section is lying, let's use base address of dll instead, only works if + // DLL has been loaded fully into memory, which we normally do + return (void*)(RVA + (unsigned long)hModule); + } + return SectionData[Sctn] + RVA - SectionHeader[Sctn].VirtualAddress; +} + +unsigned long CoffLoader::Data2RVA(void* address) +{ + for ( int i = 0; i < CoffFileHeader->NumberOfSections; i++) + { + if(address >= SectionData[i] && address < SectionData[i] + SectionHeader[i].VirtualSize) + return (unsigned long)address - (unsigned long)SectionData[i] + SectionHeader[i].VirtualAddress; + } + + // Section wasn't found, so use relative to main load of dll + return (unsigned long)address - (unsigned long)hModule; +} + +char *CoffLoader::GetStringTblIndex(int index) +{ + char *table = StringTable; + + while (index--) + table += strlen(table) + 1; + return table; +} + +char *CoffLoader::GetStringTblOff(int Offset) +{ + return StringTable + Offset - 4; +} + +char *CoffLoader::GetSymbolName(SymbolTable_t *sym) +{ + long long index = sym->Name.Offset; + int low = (int)(index & 0xFFFFFFFF); + int high = (int)((index >> 32) & 0xFFFFFFFF); + + if (low == 0) + { + return GetStringTblOff(high); + } + else + { + static char shortname[9]; + memset(shortname, 0, 9); + strncpy(shortname, (char *)sym->Name.ShortName, 8); + return shortname; + } +} + +char *CoffLoader::GetSymbolName(int index) +{ + SymbolTable_t *sym = &(SymTable[index]); + return GetSymbolName(sym); +} + +void CoffLoader::PrintStringTable(void) +{ + int size = SizeOfStringTable; + int index = 0; + char *table = StringTable; + + printf("\nSTRING TABLE\n"); + while (size) + { + printf("%2d: %s\n", index++, table); + size -= strlen(table) + 1; + table += strlen(table) + 1; + } + printf("\n"); +} + + +void CoffLoader::PrintSymbolTable(void) +{ + int SymIndex; + + printf("COFF SYMBOL TABLE\n"); + for (SymIndex = 0; SymIndex < NumberOfSymbols; SymIndex++) + { + printf("%03X ", SymIndex); + printf("%08lX ", SymTable[SymIndex].Value); + + if (SymTable[SymIndex].SectionNumber == IMAGE_SYM_ABSOLUTE) + printf("ABS "); + else if (SymTable[SymIndex].SectionNumber == IMAGE_SYM_DEBUG) + printf("DEBUG "); + else if (SymTable[SymIndex].SectionNumber == IMAGE_SYM_UNDEFINED) + printf("UNDEF "); + else + { + printf("SECT%d ", SymTable[SymIndex].SectionNumber); + if (SymTable[SymIndex].SectionNumber < 10) + printf(" "); + if (SymTable[SymIndex].SectionNumber < 100) + printf(" "); + } + + if (SymTable[SymIndex].Type == 0) + printf("notype "); + else + { + printf("%X ", SymTable[SymIndex].Type); + if (SymTable[SymIndex].Type < 0x10) + printf(" "); + if (SymTable[SymIndex].Type < 0x100) + printf(" "); + if (SymTable[SymIndex].Type < 0x1000) + printf(" "); + } + + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_END_OF_FUNCTION) + printf("End of Function "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_NULL) + printf("Null "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_AUTOMATIC) + printf("Automatic "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_EXTERNAL) + printf("External "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_STATIC) + printf("Static "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_REGISTER) + printf("Register "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_EXTERNAL_DEF) + printf("External Def "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_LABEL) + printf("Label "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_UNDEFINED_LABEL) + printf("Undefined Label "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_MEMBER_OF_STRUCT) + printf("Member Of Struct "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_ARGUMENT) + printf("Argument "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_STRUCT_TAG) + printf("Struct Tag "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_MEMBER_OF_UNION) + printf("Member Of Union "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_UNION_TAG) + printf("Union Tag "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_TYPE_DEFINITION) + printf("Type Definition "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_UNDEFINED_STATIC) + printf("Undefined Static "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_ENUM_TAG) + printf("Enum Tag "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_MEMBER_OF_ENUM) + printf("Member Of Enum "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_REGISTER_PARAM) + printf("Register Param "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_BIT_FIELD) + printf("Bit Field "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_BLOCK) + printf("Block "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_FUNCTION) + printf("Function "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_END_OF_STRUCT) + printf("End Of Struct "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_FILE) + printf("File "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_SECTION) + printf("Section "); + if (SymTable[SymIndex].StorageClass == IMAGE_SYM_CLASS_WEAK_EXTERNAL) + printf("Weak External "); + + printf("| %s", GetSymbolName(SymIndex)); + + SymIndex += SymTable[SymIndex].NumberOfAuxSymbols; + printf("\n"); + } + printf("\n"); + +} + +void CoffLoader::PrintFileHeader(COFF_FileHeader_t *FileHeader) +{ + printf("COFF Header\n"); + printf("------------------------------------------\n\n"); + + printf("MachineType: 0x%04X\n", FileHeader->MachineType); + printf("NumberOfSections: 0x%04X\n", FileHeader->NumberOfSections); + printf("TimeDateStamp: 0x%08lX\n",FileHeader->TimeDateStamp); + printf("PointerToSymbolTable: 0x%08lX\n",FileHeader->PointerToSymbolTable); + printf("NumberOfSymbols: 0x%08lX\n",FileHeader->NumberOfSymbols); + printf("SizeOfOptionHeader: 0x%04X\n", FileHeader->SizeOfOptionHeader); + printf("Characteristics: 0x%04X\n", FileHeader->Characteristics); + + if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) + printf(" IMAGE_FILE_RELOCS_STRIPPED\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) + printf(" IMAGE_FILE_EXECUTABLE_IMAGE\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_LINE_NUMS_STRIPPED) + printf(" IMAGE_FILE_LINE_NUMS_STRIPPED\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_LOCAL_SYMS_STRIPPED) + printf(" IMAGE_FILE_LOCAL_SYMS_STRIPPED\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_AGGRESSIVE_WS_TRIM) + printf(" IMAGE_FILE_AGGRESSIVE_WS_TRIM\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) + printf(" IMAGE_FILE_LARGE_ADDRESS_AWARE\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_16BIT_MACHINE) + printf(" IMAGE_FILE_16BIT_MACHINE\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_BYTES_REVERSED_LO) + printf(" IMAGE_FILE_BYTES_REVERSED_LO\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_32BIT_MACHINE) + printf(" IMAGE_FILE_32BIT_MACHINE\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_DEBUG_STRIPPED) + printf(" IMAGE_FILE_DEBUG_STRIPPED\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP) + printf(" IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_SYSTEM) + printf(" IMAGE_FILE_SYSTEM\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_DLL) + printf(" IMAGE_FILE_DLL\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY) + printf(" IMAGE_FILE_UP_SYSTEM_ONLY\n"); + + if (FileHeader->Characteristics & IMAGE_FILE_BYTES_REVERSED_HI) + printf(" IMAGE_FILE_BYTES_REVERSED_HI\n"); + + printf("\n"); +} + +void CoffLoader::PrintOptionHeader(OptionHeader_t *OptHdr) +{ + printf("Option Header\n"); + printf("------------------------------------------\n\n"); + + printf("Magic: 0x%04X\n", OptHdr->Magic); + printf("Linker Major Ver: 0x%02X\n", VERSION_MAJOR(OptHdr->LinkVersion)); + printf("Linker Minor Ver: 0x%02X\n", VERSION_MINOR(OptHdr->LinkVersion)); + printf("Code Size: 0x%08lX\n", OptHdr->CodeSize); + printf("Data Size: 0x%08lX\n", OptHdr->DataSize); + printf("BSS Size: 0x%08lX\n", OptHdr->BssSize); + printf("Entry: 0x%08lX\n", OptHdr->Entry); + printf("Code Base: 0x%08lX\n", OptHdr->CodeBase); + printf("Data Base: 0x%08lX\n", OptHdr->DataBase); + printf("\n"); +} + +void CoffLoader::PrintWindowsHeader(WindowsHeader_t *WinHdr) +{ + printf("Windows Specific Option Header\n"); + printf("------------------------------------------\n\n"); + + printf("Image Base: 0x%08lX\n", WinHdr->ImageBase); + printf("Section Alignment: 0x%08lX\n", WinHdr->SectionAlignment); + printf("File Alignment: 0x%08lX\n", WinHdr->FileAlignment); + printf("OS Version: %d.%08d\n", BIGVERSION_MAJOR(WinHdr->OSVer), BIGVERSION_MINOR(WinHdr->OSVer)); + printf("Image Version: %d.%08d\n", BIGVERSION_MAJOR(WinHdr->ImgVer), BIGVERSION_MINOR(WinHdr->ImgVer)); + printf("SubSystem Version: %d.%08d\n", BIGVERSION_MAJOR(WinHdr->SubSysVer), BIGVERSION_MINOR(WinHdr->SubSysVer)); + printf("Size of Image: 0x%08lX\n", WinHdr->SizeOfImage); + printf("Size of Headers: 0x%08lX\n", WinHdr->SizeOfHeaders); + printf("Checksum: 0x%08lX\n", WinHdr->CheckSum); + printf("Subsystem: 0x%04X\n", WinHdr->Subsystem); + printf("DLL Flags: 0x%04X\n", WinHdr->DLLFlags); + printf("Sizeof Stack Resv: 0x%08lX\n", WinHdr->SizeOfStackReserve); + printf("Sizeof Stack Comm: 0x%08lX\n", WinHdr->SizeOfStackCommit); + printf("Sizeof Heap Resv: 0x%08lX\n", WinHdr->SizeOfHeapReserve); + printf("Sizeof Heap Comm: 0x%08lX\n", WinHdr->SizeOfHeapCommit); + printf("Loader Flags: 0x%08lX\n", WinHdr->LoaderFlags); + printf("Num Directories: %ld\n", WinHdr->NumDirectories); + printf("\n"); +} + +void CoffLoader::PrintSection(SectionHeader_t* ScnHdr, const char* data) +{ + char SectionName[9]; + + strncpy(SectionName, (char *)ScnHdr->Name, 8); + SectionName[8] = 0; + printf("Section: %s\n", SectionName); + printf("------------------------------------------\n\n"); + + printf("Virtual Size: 0x%08lX\n", ScnHdr->VirtualSize); + printf("Virtual Address: 0x%08lX\n", ScnHdr->VirtualAddress); + printf("Sizeof Raw Data: 0x%08lX\n", ScnHdr->SizeOfRawData); + printf("Ptr To Raw Data: 0x%08lX\n", ScnHdr->PtrToRawData); + printf("Ptr To Relocations: 0x%08lX\n", ScnHdr->PtrToRelocations); + printf("Ptr To Line Nums: 0x%08lX\n", ScnHdr->PtrToLineNums); + printf("Num Relocations: 0x%04X\n", ScnHdr->NumRelocations); + printf("Num Line Numbers: 0x%04X\n", ScnHdr->NumLineNumbers); + printf("Characteristics: 0x%08lX\n", ScnHdr->Characteristics); + if (ScnHdr->Characteristics & IMAGE_SCN_CNT_CODE) + printf(" IMAGE_SCN_CNT_CODE\n"); + if (ScnHdr->Characteristics & IMAGE_SCN_CNT_DATA) + printf(" IMAGE_SCN_CNT_DATA\n"); + if (ScnHdr->Characteristics & IMAGE_SCN_CNT_BSS) + printf(" IMAGE_SCN_CNT_BSS\n"); + if (ScnHdr->Characteristics & IMAGE_SCN_LNK_INFO) + printf(" IMAGE_SCN_LNK_INFO\n"); + if (ScnHdr->Characteristics & IMAGE_SCN_LNK_REMOVE) + printf(" IMAGE_SCN_LNK_REMOVE\n"); + if (ScnHdr->Characteristics & IMAGE_SCN_LNK_COMDAT) + printf(" IMAGE_SCN_LNK_COMDAT\n"); + + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_1BYTES) + printf(" IMAGE_SCN_ALIGN_1BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_2BYTES) + printf(" IMAGE_SCN_ALIGN_2BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_4BYTES) + printf(" IMAGE_SCN_ALIGN_4BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_8BYTES) + printf(" IMAGE_SCN_ALIGN_8BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_16BYTES) + printf(" IMAGE_SCN_ALIGN_16BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_32BYTES) + printf(" IMAGE_SCN_ALIGN_32BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_64BYTES) + printf(" IMAGE_SCN_ALIGN_64BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_128BYTES) + printf(" IMAGE_SCN_ALIGN_128BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_256BYTES) + printf(" IMAGE_SCN_ALIGN_256BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_512BYTES) + printf(" IMAGE_SCN_ALIGN_512BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_1024BYTES) + printf(" IMAGE_SCN_ALIGN_1024BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_2048BYTES) + printf(" IMAGE_SCN_ALIGN_2048BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_4096BYTES) + printf(" IMAGE_SCN_ALIGN_4096BYTES\n"); + if ((ScnHdr->Characteristics & IMAGE_SCN_ALIGN_MASK) == IMAGE_SCN_ALIGN_8192BYTES) + printf(" IMAGE_SCN_ALIGN_8192BYTES\n"); + + if (ScnHdr->Characteristics & IMAGE_SCN_LNK_NRELOC_OVFL) + printf(" IMAGE_SCN_LNK_NRELOC_OVFL\n"); + if (ScnHdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) + printf(" IMAGE_SCN_MEM_DISCARDABLE\n"); + if (ScnHdr->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) + printf(" IMAGE_SCN_MEM_NOT_CACHED\n"); + if (ScnHdr->Characteristics & IMAGE_SCN_MEM_NOT_PAGED) + printf(" IMAGE_SCN_MEM_NOT_PAGED\n"); + if (ScnHdr->Characteristics & IMAGE_SCN_MEM_SHARED) + printf(" IMAGE_SCN_MEM_SHARED\n"); + if (ScnHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE) + printf(" IMAGE_SCN_MEM_EXECUTE\n"); + if (ScnHdr->Characteristics & IMAGE_SCN_MEM_READ) + printf(" IMAGE_SCN_MEM_READ\n"); + if (ScnHdr->Characteristics & IMAGE_SCN_MEM_WRITE) + printf(" IMAGE_SCN_MEM_WRITE\n"); + printf("\n"); + + // Read the section Data, Relocations, & Line Nums + // Save the offset + + if (ScnHdr->SizeOfRawData > 0) + { + unsigned int i; + // Print the Raw Data + + printf("\nRAW DATA"); + for (i = 0; i < ScnHdr->VirtualSize; i++) + { + if ((i % 16) == 0) + printf("\n %08X: ", i); + char ch = data[i]; + printf("%02X ", (unsigned int)ch); + } + printf("\n\n"); + } + + /* + #if 0 + if (ScnHdr->NumRelocations > 0) + { + // Print Section Relocations + ObjReloc_t ObjReloc; + + fseek(fp, ScnHdr->PtrToRelocations/ * + CoffBeginOffset* /, SEEK_SET); + printf("RELOCATIONS\n"); + printf(" Symbol Symbol\n"); + printf(" Offset Type Index Name\n"); + printf(" -------- -------- -------- ------\n"); + for (int i = 0; i < ScnHdr->NumRelocations; i++) + { + fread(&ObjReloc, 1, sizeof(ObjReloc_t), fp); + printf(" %08X ", ObjReloc.VirtualAddress); + + if (ObjReloc.Type == IMAGE_REL_I386_ABSOLUTE) + printf("ABSOLUTE "); + if (ObjReloc.Type == IMAGE_REL_I386_DIR16) + printf("DIR16 "); + if (ObjReloc.Type == IMAGE_REL_I386_REL16) + printf("REL16 "); + if (ObjReloc.Type == IMAGE_REL_I386_DIR32) + printf("DIR32 "); + if (ObjReloc.Type == IMAGE_REL_I386_DIR32NB) + printf("DIR32NB "); + if (ObjReloc.Type == IMAGE_REL_I386_SEG12) + printf("SEG12 "); + if (ObjReloc.Type == IMAGE_REL_I386_SECTION) + printf("SECTION "); + if (ObjReloc.Type == IMAGE_REL_I386_SECREL) + printf("SECREL "); + if (ObjReloc.Type == IMAGE_REL_I386_REL32) + printf("REL32 "); + printf("%8X ", ObjReloc.SymTableIndex); + printf("%s\n", GetSymbolName(ObjReloc.SymTableIndex)); + } + printf("\n"); + } + + if (ScnHdr->NumLineNumbers > 0) + { + // Print The Line Number Info + LineNumbers_t LineNumber; + int LineCnt = 0; + int BaseLineNum = -1; + + fseek(fp, ScnHdr->PtrToLineNums/ * + CoffBeginOffset* /, SEEK_SET); + printf("LINE NUMBERS"); + for (int i = 0; i < ScnHdr->NumLineNumbers; i++) + { + int LNOffset = ftell(fp); + + fread(&LineNumber, 1, sizeof(LineNumbers_t), fp); + if (LineNumber.LineNum == 0) + { + SymbolTable_t *Sym; + int SymIndex; + + printf("\n"); + SymIndex = LineNumber.Type.SymbolTableIndex; + Sym = &(SymTable[SymIndex]); + if (Sym->NumberOfAuxSymbols > 0) + { + Sym = &(SymTable[SymIndex+1]); + AuxFuncDef_t *FuncDef = (AuxFuncDef_t *)Sym; + + if (FuncDef->PtrToLineNumber == LNOffset) + { + Sym = &(SymTable[FuncDef->TagIndex]); + if (Sym->NumberOfAuxSymbols > 0) + { + Sym = &(SymTable[FuncDef->TagIndex+1]); + AuxBfEf_t *Bf = (AuxBfEf_t *)Sym; + BaseLineNum = Bf->LineNumber; + } + } + } + printf(" Symbol Index: %8x ", SymIndex); + printf(" Base line number: %8d\n", BaseLineNum); + printf(" Symbol name = %s", GetSymbolName(SymIndex)); + LineCnt = 0; + } + else + { + if ((LineCnt%4) == 0) + { + printf("\n "); + LineCnt = 0; + } + printf("%08X(%5d) ", LineNumber.Type.VirtualAddress, + LineNumber.LineNum + BaseLineNum); + LineCnt ++; + } + } + printf("\n"); + } + #endif + */ + + printf("\n"); +} + +int CoffLoader::ParseCoff(FILE *fp) +{ + if ( !LoadCoffHModule(fp) ) + { + printf("Failed to load/find COFF hModule header\n"); + return 0; + } + if ( !LoadSymTable(fp) || + !LoadStringTable(fp) || + !LoadSections(fp) ) + return 0; + + PerformFixups(); + +#ifdef DUMPING_DATA + PrintSymbolTable(); + PrintStringTable(); +#endif + return 1; +} + +void CoffLoader::PerformFixups(void) +{ + int FixupDataSize; + char *FixupData; + char *EndData; + + EntryAddress = (unsigned long)RVA2Data(EntryAddress); + + if( reinterpret_cast<void*>(WindowsHeader->ImageBase) == hModule ) + return; + + if ( !Directory ) + return ; + + if ( NumOfDirectories <= BASE_RELOCATION_TABLE ) + return ; + + if ( !Directory[BASE_RELOCATION_TABLE].Size ) + return ; + + FixupDataSize = Directory[BASE_RELOCATION_TABLE].Size; + FixupData = (char*)RVA2Data(Directory[BASE_RELOCATION_TABLE].RVA); + EndData = FixupData + FixupDataSize; + + while (FixupData < EndData) + { + // Starting a new Fixup Block + unsigned long PageRVA = *((unsigned long*)FixupData); + FixupData += 4; + unsigned long BlockSize = *((unsigned long*)FixupData); + FixupData += 4; + + BlockSize -= 8; + for (unsigned int i = 0; i < BlockSize / 2; i++) + { + unsigned short Fixup = *((unsigned short*)FixupData); + FixupData += 2; + int Type = (Fixup >> 12) & 0x0f; + Fixup &= 0xfff; + if (Type == IMAGE_REL_BASED_HIGHLOW) + { + unsigned long *Off = (unsigned long*)RVA2Data(Fixup + PageRVA); + *Off = (unsigned long)RVA2Data(*Off - WindowsHeader->ImageBase); + } + else if (Type == IMAGE_REL_BASED_ABSOLUTE) + {} + else + { + printf("Unsupported fixup type!!\n"); + } + } + } +} diff --git a/xbmc/cores/DllLoader/coff.h b/xbmc/cores/DllLoader/coff.h new file mode 100644 index 0000000..f421d43 --- /dev/null +++ b/xbmc/cores/DllLoader/coff.h @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +//#pragma message("including coff.h") +// +// COFF -- Common Object File Format +// Used commonly by Un*x and is embedded in Windows PE +// file format. +// + +// These structures must be packed +#pragma pack(1) + + +/* + * Some general purpose MACROs + */ + +#define VERSION_MAJOR(x) ((unsigned int)((x)& 0xff)) +#define VERSION_MINOR(x) ((unsigned int)(((x)>8) &0xff)) + +#define BIGVERSION_MAJOR(x) ((unsigned int)((x)& 0xffff)) +#define BIGVERSION_MINOR(x) ((unsigned int)(((x)>16) &0xffff)) + +/* + * COFF File Header (Object & Image) + * Spec section 3.3 + */ + +typedef struct +{ + unsigned short MachineType; /* magic type */ + unsigned short NumberOfSections; /* number of sections */ + unsigned long TimeDateStamp; /* time & date stamp */ + unsigned long PointerToSymbolTable; /* file pointer to symtab */ + unsigned long NumberOfSymbols; /* number of symtab entries */ + unsigned short SizeOfOptionHeader; /* sizeof(optional hdr) */ + unsigned short Characteristics; /* flags */ +} +COFF_FileHeader_t; + +/* + * Machine Types + * Spec section 3.3.1 + * (only i386 relevant for us) + */ + +#if 1 + +#ifndef IMAGE_FILE_MACHINE_I386 +#define IMAGE_FILE_MACHINE_I386 0x14c +#endif + + + +#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 +#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 +#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 +#define IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010 +#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 +#define IMAGE_FILE_16BIT_MACHINE 0x0040 +#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 +#define IMAGE_FILE_32BIT_MACHINE 0x0100 +#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 +#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 +#define IMAGE_FILE_SYSTEM 0x1000 +#define IMAGE_FILE_DLL 0x2000 +#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 +#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 + +#endif + + + +#define OPTMAGIC_PE32 0x010b +#define OPTMAGIC_PE32P 0x020b + +#define OPTHDR_SIZE 28 +#define OPTHDR_SIZEP 24 +#define WINHDR_SIZE 68 +#define WINHDR_SIZEP 88 + +/* + * Optional Header Standard Fields (Image Only) + * Spec section 3.4.1 + */ + +typedef struct +{ + unsigned short Magic; + unsigned short LinkVersion; + unsigned long CodeSize; + unsigned long DataSize; + unsigned long BssSize; + unsigned long Entry; + unsigned long CodeBase; + unsigned long DataBase; +} +OptionHeader_t; + +typedef struct +{ + unsigned short Magic; + unsigned short LinkVersion; + unsigned long CodeSize; + unsigned long DataSize; + unsigned long BssSize; + unsigned long Entry; + unsigned long CodeBase; +} +OptionHeaderPlus_t; + +/* + * Optional Header Windows NT-Specific Fields (Image Only) + * Spec section 3.4.2 + */ + +typedef struct +{ + unsigned long ImageBase; + unsigned long SectionAlignment; + unsigned long FileAlignment; + unsigned long OSVer; + unsigned long ImgVer; + unsigned long SubSysVer; + unsigned long Reserved; + unsigned long SizeOfImage; + unsigned long SizeOfHeaders; + unsigned long CheckSum; + unsigned short Subsystem; + unsigned short DLLFlags; + unsigned long SizeOfStackReserve; + unsigned long SizeOfStackCommit; + unsigned long SizeOfHeapReserve; + unsigned long SizeOfHeapCommit; + unsigned long LoaderFlags; + unsigned long NumDirectories; +} +WindowsHeader_t; + +typedef struct +{ + unsigned long long ImageBase; + unsigned long SectionAlignment; + unsigned long FileAlignment; + unsigned long OSVer; + unsigned long ImgVer; + unsigned long SubSysVer; + unsigned long Reserved; + unsigned long SizeOfImage; + unsigned long SizeOfHeaders; + unsigned long CheckSum; + unsigned short Subsystem; + unsigned short DLLFlags; + unsigned long long SizeOfStackReserve; + unsigned long long SizeOfStackCommit; + unsigned long long SizeOfHeapReserve; + unsigned long long SizeOfHeapCommit; + unsigned long LoaderFlags; + unsigned long NumDirectories; +} +WindowsHeaderPlus_t; + +/* +#define IMAGE_SUBSYSTEM_UNKNOWN 0 +#define IMAGE_SUBSYSTEM_NATIVE 1 +#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 +#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 +#define IMAGE_SUBSYSTEM_POSIX_CUI 7 +#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 +#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 +#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 +#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 + +#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 +#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 +#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0X8000 +*/ + +/* + * Optional Header Data Directories (Image Only) + * Spec section 3.4.3 + */ + +typedef struct +{ + unsigned long RVA; + unsigned long Size; +} +Image_Data_Directory_t; + +enum Directory_Items { + EXPORT_TABLE = 0, + IMPORT_TABLE, + RESOURCE_TABLE, + EXCEPTION_TABLE, + CERTIFICATE_TABLE, + BASE_RELOCATION_TABLE, + DEBUG_, + ARCHITECTURE, + GLOBAL_PTR, + TLS_TABLE, + LOAD_CONFIG_TABLE, + BOUND_IMPORT, + IAT, + DELAY_IMPORT_DESCRIPTOR, + COM_RUNTIME_HEADER, + RESERVED +}; + +/* + * Section Table (Section Headers) + * Spec section 4. + */ + + +typedef struct +{ + unsigned char Name[8]; + unsigned long VirtualSize; + unsigned long VirtualAddress; + unsigned long SizeOfRawData; + unsigned long PtrToRawData; + unsigned long PtrToRelocations; + unsigned long PtrToLineNums; + unsigned short NumRelocations; + unsigned short NumLineNumbers; + unsigned long Characteristics; +} +SectionHeader_t; + +/* + * Section Flags (Characteristics) + * Spec section 4.1 + */ + +#define IMAGE_SCN_CNT_CODE 0x00000020 +#define IMAGE_SCN_CNT_DATA 0x00000040 +#define IMAGE_SCN_CNT_BSS 0x00000080 +#define IMAGE_SCN_LNK_INFO 0x00000200 +#define IMAGE_SCN_LNK_REMOVE 0x00000800 +#define IMAGE_SCN_LNK_COMDAT 0x00001000 +#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 +#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 +#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 +#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 +#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 +#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 +#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 +#define IMAGE_SCN_ALIGN_128BYTES 0x00800000 +#define IMAGE_SCN_ALIGN_256BYTES 0x00900000 +#define IMAGE_SCN_ALIGN_512BYTES 0x00A00000 +#define IMAGE_SCN_ALIGN_1024BYTES 0x00B00000 +#define IMAGE_SCN_ALIGN_2048BYTES 0x00C00000 +#define IMAGE_SCN_ALIGN_4096BYTES 0x00D00000 +#define IMAGE_SCN_ALIGN_8192BYTES 0x00E00000 +#define IMAGE_SCN_ALIGN_MASK 0x00F00000 +#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 +#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 +#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 +#define IMAGE_SCN_MEM_SHARED 0x10000000 +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 + +/* + * COFF Relocations (Object Only) + * Spec section 5.2 + */ + +typedef struct +{ + unsigned long VirtualAddress; + unsigned long SymTableIndex; + unsigned short Type; +} +ObjReloc_t; + +/* + * COFF Relocation Type Indicators + * Spec section 5.2.1 + */ + +#define IMAGE_REL_I386_ABSOLUTE 0x0000 +#define IMAGE_REL_I386_DIR16 0x0001 +#define IMAGE_REL_I386_REL16 0x0002 +#define IMAGE_REL_I386_DIR32 0x0006 +#define IMAGE_REL_I386_DIR32NB 0x0007 +#define IMAGE_REL_I386_SEG12 0x0009 +#define IMAGE_REL_I386_SECTION 0x000A +#define IMAGE_REL_I386_SECREL 0x000B +#define IMAGE_REL_I386_REL32 0x0014 + +/* + * COFF Line Numbers + * Spec section 5.3 + */ + +typedef struct +{ + union { + unsigned long SymbolTableIndex; + unsigned long VirtualAddress; + } Type; + unsigned short LineNum; +} +LineNumbers_t; + +/* + * COFF Symbol Table + * Spec section 5.4 + */ + +typedef struct +{ + union { + unsigned char ShortName[8]; + unsigned long long Offset; + } Name; + unsigned long Value; + unsigned short SectionNumber; + unsigned short Type; + unsigned char StorageClass; + unsigned char NumberOfAuxSymbols; +} +SymbolTable_t; + +#if !defined(TARGET_WINDOWS) + +#define IMAGE_SYM_UNDEFINED 0 +#define IMAGE_SYM_ABSOLUTE 0xFFFF +#define IMAGE_SYM_DEBUG 0xFFFE + + +#define IMAGE_SYM_TYPE_NULL 0 +#define IMAGE_SYM_TYPE_VOID 1 +#define IMAGE_SYM_TYPE_CHAR 2 +#define IMAGE_SYM_TYPE_SHORT 3 +#define IMAGE_SYM_TYPE_INT 4 +#define IMAGE_SYM_TYPE_LONG 5 +#define IMAGE_SYM_TYPE_FLOAT 6 +#define IMAGE_SYM_TYPE_DOUBLE 7 +#define IMAGE_SYM_TYPE_STRUCT 8 +#define IMAGE_SYM_TYPE_UNION 9 +#define IMAGE_SYM_TYPE_ENUM 10 +#define IMAGE_SYM_TYPE_MOE 11 +#define IMAGE_SYM_TYPE_BYTE 12 +#define IMAGE_SYM_TYPE_WORD 13 +#define IMAGE_SYM_TYPE_UINT 14 +#define IMAGE_SYM_TYPE_DWORD 15 + +#define IMAGE_SYM_DWORD_NULL 0 +#define IMAGE_SYM_DWORD_POINTER 1 +#define IMAGE_SYM_DWORD_FUNCTION 2 +#define IMAGE_SYM_DWORD_ARRAY 3 + + +#define IMAGE_SYM_CLASS_END_OF_FUNCTION 0xFF +#define IMAGE_SYM_CLASS_NULL 0 +#define IMAGE_SYM_CLASS_AUTOMATIC 1 +#define IMAGE_SYM_CLASS_EXTERNAL 2 +#define IMAGE_SYM_CLASS_STATIC 3 +#define IMAGE_SYM_CLASS_REGISTER 4 +#define IMAGE_SYM_CLASS_EXTERNAL_DEF 5 +#define IMAGE_SYM_CLASS_LABEL 6 +#define IMAGE_SYM_CLASS_UNDEFINED_LABEL 7 +#define IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8 +#define IMAGE_SYM_CLASS_ARGUMENT 9 +#define IMAGE_SYM_CLASS_STRUCT_TAG 10 +#define IMAGE_SYM_CLASS_MEMBER_OF_UNION 11 +#define IMAGE_SYM_CLASS_UNION_TAG 12 +#define IMAGE_SYM_CLASS_TYPE_DEFINITION 13 +#define IMAGE_SYM_CLASS_UNDEFINED_STATIC 14 +#define IMAGE_SYM_CLASS_ENUM_TAG 15 +#define IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16 +#define IMAGE_SYM_CLASS_REGISTER_PARAM 17 +#define IMAGE_SYM_CLASS_BIT_FIELD 18 +#define IMAGE_SYM_CLASS_BLOCK 100 +#define IMAGE_SYM_CLASS_FUNCTION 101 +#define IMAGE_SYM_CLASS_END_OF_STRUCT 102 +#define IMAGE_SYM_CLASS_FILE 103 +#define IMAGE_SYM_CLASS_SECTION 104 +#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 105 +#endif + +typedef struct +{ + unsigned long TagIndex; + unsigned long TotalSize; + unsigned long PtrToLineNumber; + unsigned long PtrToNextFunc; + unsigned short unused; +} +AuxFuncDef_t; + +/* + * Symbol Auxiliary Record: .bf and .ef + * Spec section 5.5.2 + */ + +typedef struct +{ + unsigned long unused; + unsigned short LineNumber; + unsigned long unused1; + unsigned short unused2; + unsigned long PtrToNextFunc; + unsigned char unused3; +} +AuxBfEf_t; + +/* + * Export Section (Directory) + * Spec section 6.3 + */ + +/* + * Export Directory Table + * Spec section 6.3.1 + */ + +typedef struct +{ + unsigned long ExportFlags; + unsigned long TimeStamp; + unsigned short MajorVersion; + unsigned short MinorVersion; + unsigned long Name_RVA; + unsigned long OrdinalBase; + unsigned long NumAddrTable; + unsigned long NumNamePtrs; + unsigned long ExportAddressTable_RVA; + unsigned long NamePointerTable_RVA; + unsigned long OrdinalTable_RVA; +} +ExportDirTable_t; + + +/* + * Import Section (Directory) + * Spec section 6.4 + */ + +/* + * Import Directory Table + * Spec Section 6.4.1 + */ + +typedef struct +{ + unsigned long ImportLookupTable_RVA; + unsigned long TimeStamp; + unsigned long ForwarderChain; + unsigned long Name_RVA; + unsigned long ImportAddressTable_RVA; +} +ImportDirTable_t; + +/* + * .reloc Relocation types + * spec section 6.6 + */ + +#if 1 +#define IMAGE_REL_BASED_ABSOLUTE 0 +#define IMAGE_REL_BASED_HIGH 1 +#define IMAGE_REL_BASED_LOW 2 +#define IMAGE_REL_BASED_HIGHLOW 3 +#define IMAGE_REL_BASED_HIGHADJ 4 +#define IMAGE_REL_BASED_MIPS_JMPADDR 5 +#define IMAGE_REL_BASED_SECTION 6 +#define IMAGE_REL_BASED_REL32 7 +#define IMAGE_REL_BASED_MIPS_JMPADDR16 9 +#define IMAGE_REL_BASED_DIR64 10 +#define IMAGE_REL_BASED_HIGH3ADJ 11 +#endif + + + + +#pragma pack() + diff --git a/xbmc/cores/DllLoader/coffldr.h b/xbmc/cores/DllLoader/coffldr.h new file mode 100644 index 0000000..9da7801 --- /dev/null +++ b/xbmc/cores/DllLoader/coffldr.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +//#pragma message("including coffldr.h") +#include "coff.h" + +#include <stdio.h> + +class CoffLoader +{ +public: + CoffLoader(); + virtual ~CoffLoader(); + + int ParseCoff(FILE *fp); + int ParseHeaders(void* hModule); + + void *hModule; //standard windows HINSTANCE handle hold the whole image + //Pointers to somewhere in hModule, do not free these pointers + COFF_FileHeader_t *CoffFileHeader; + OptionHeader_t *OptionHeader; + WindowsHeader_t *WindowsHeader; + Image_Data_Directory_t *Directory; + SectionHeader_t *SectionHeader; + +protected: + + // Allocated structures... hModule now hold the master Memory handle + SymbolTable_t *SymTable; + char *StringTable; + char **SectionData; + + unsigned long EntryAddress; //Initialize entry point + + // Unnecessary data + // This is data that is used only during linking and is not necessary + // while the program is running in general + + int NumberOfSymbols; + int SizeOfStringTable; + int NumOfDirectories; + int NumOfSections; + int FileHeaderOffset; + + // Members for printing the structures + static void PrintFileHeader(COFF_FileHeader_t *FileHeader); + static void PrintWindowsHeader(WindowsHeader_t *WinHdr); + static void PrintOptionHeader(OptionHeader_t *OptHdr); + static void PrintSection(SectionHeader_t* ScnHdr, const char* data); + void PrintStringTable(void); + void PrintSymbolTable(void); + + // Members for Loading the Different structures + int LoadCoffHModule(FILE * fp); + int LoadSymTable(FILE *fp); + int LoadStringTable(FILE *fp); + int LoadSections(FILE *fp); + + // Members for access some of the Data + + int RVA2Section(unsigned long RVA); + void* RVA2Data(unsigned long RVA); + unsigned long Data2RVA(void* address); + + char *GetStringTblIndex(int index); + char *GetStringTblOff(int Offset); + char *GetSymbolName(SymbolTable_t *sym); + char *GetSymbolName(int index); + + void PerformFixups(void); +}; + diff --git a/xbmc/cores/DllLoader/dll.cpp b/xbmc/cores/DllLoader/dll.cpp new file mode 100644 index 0000000..9690529 --- /dev/null +++ b/xbmc/cores/DllLoader/dll.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "dll.h" + +#include "DllLoader.h" +#include "DllLoaderContainer.h" +#include "dll_tracker.h" +#include "dll_util.h" +#include "filesystem/SpecialProtocol.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include <climits> + +#define DEFAULT_DLLPATH "special://xbmc/system/players/mplayer/codecs/" +#define HIGH_WORD(a) ((uintptr_t)(a) >> 16) +#define LOW_WORD(a) ((unsigned short)(((uintptr_t)(a)) & MAXWORD)) + +//#define API_DEBUG + +char* getpath(char *buf, const char *full) +{ + const char* pos; + if ((pos = strrchr(full, PATH_SEPARATOR_CHAR))) + { + strncpy(buf, full, pos - full + 1 ); + buf[pos - full + 1] = 0; + return buf; + } + else + { + buf[0] = 0; + return buf; + } +} + +extern "C" HMODULE __stdcall dllLoadLibraryExtended(const char* lib_file, const char* sourcedll) +{ + char libname[MAX_PATH + 1] = {}; + char libpath[MAX_PATH + 1] = {}; + LibraryLoader* dll = NULL; + + /* extract name */ + const char* p = strrchr(lib_file, PATH_SEPARATOR_CHAR); + if (p) + strncpy(libname, p+1, sizeof(libname) - 1); + else + strncpy(libname, lib_file, sizeof(libname) - 1); + libname[sizeof(libname) - 1] = '\0'; + + if( libname[0] == '\0' ) + return NULL; + + /* extract path */ + getpath(libpath, lib_file); + + if (sourcedll) + { + /* also check for invalid paths which begin with a \ */ + if( libpath[0] == '\0' || libpath[0] == PATH_SEPARATOR_CHAR ) + { + /* use calling dll's path as base address for this call */ + getpath(libpath, sourcedll); + + /* mplayer has all it's dlls in a codecs subdirectory */ + if (strstr(sourcedll, "mplayer.dll")) + strcat(libpath, "codecs\\"); + } + } + + /* if we still don't have a path, use default path */ + if( libpath[0] == '\0' ) + strcpy(libpath, DEFAULT_DLLPATH); + + /* msdn docs state */ + /* "If no file name extension is specified in the lpFileName parameter, the default library extension .dll is appended. */ + /* However, the file name string can include a trailing point character (.) to indicate that the module name has no extension." */ + if( strrchr(libname, '.') == NULL ) + strcat(libname, ".dll"); + else if( libname[strlen(libname)-1] == '.' ) + libname[strlen(libname)-1] = '\0'; + + dll = DllLoaderContainer::LoadModule(libname, libpath); + + if (dll) + return (HMODULE)dll->GetHModule(); + + CLog::Log(LOGERROR, "LoadLibrary('{}') failed", libname); + return NULL; +} + +extern "C" HMODULE __stdcall dllLoadLibraryA(const char* file) +{ + return dllLoadLibraryExtended(file, NULL); +} + +#define DONT_RESOLVE_DLL_REFERENCES 0x00000001 +#define LOAD_LIBRARY_AS_DATAFILE 0x00000002 +#define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008 +#define LOAD_IGNORE_CODE_AUTHZ_LEVEL 0x00000010 + +extern "C" HMODULE __stdcall dllLoadLibraryExExtended(const char* lpLibFileName, HANDLE hFile, DWORD dwFlags, const char* sourcedll) +{ + char strFlags[512]; + strFlags[0] = '\0'; + + if (dwFlags & DONT_RESOLVE_DLL_REFERENCES) strcat(strFlags, "\n - DONT_RESOLVE_DLL_REFERENCES"); + if (dwFlags & LOAD_IGNORE_CODE_AUTHZ_LEVEL) strcat(strFlags, "\n - LOAD_IGNORE_CODE_AUTHZ_LEVEL"); + if (dwFlags & LOAD_LIBRARY_AS_DATAFILE) strcat(strFlags, "\n - LOAD_LIBRARY_AS_DATAFILE"); + if (dwFlags & LOAD_WITH_ALTERED_SEARCH_PATH) strcat(strFlags, "\n - LOAD_WITH_ALTERED_SEARCH_PATH"); + + CLog::Log(LOGDEBUG, "LoadLibraryExA called with flags: {}", strFlags); + + return dllLoadLibraryExtended(lpLibFileName, sourcedll); +} + +extern "C" HMODULE __stdcall dllLoadLibraryExA(const char* lpLibFileName, HANDLE hFile, DWORD dwFlags) +{ + return dllLoadLibraryExExtended(lpLibFileName, hFile, dwFlags, NULL); +} + +extern "C" int __stdcall dllFreeLibrary(HINSTANCE hLibModule) +{ + LibraryLoader* dllhandle = DllLoaderContainer::GetModule(hLibModule); + + if( !dllhandle ) + { + CLog::Log(LOGERROR, "{} - Invalid hModule specified", __FUNCTION__); + return 1; + } + + // to make sure systems dlls are never deleted + if (dllhandle->IsSystemDll()) return 1; + + DllLoaderContainer::ReleaseModule(dllhandle); + + return 1; +} + +extern "C" intptr_t (*__stdcall dllGetProcAddress(HMODULE hModule, const char* function))(void) +{ + uintptr_t loc = (uintptr_t)_ReturnAddress(); + + void* address = NULL; + LibraryLoader* dll = DllLoaderContainer::GetModule(hModule); + + if( !dll ) + { + CLog::Log(LOGERROR, "{} - Invalid hModule specified", __FUNCTION__); + return NULL; + } + + /* how can somebody get the stupid idea to create such a stupid function */ + /* where you never know if the given pointer is a pointer or a value */ + if( HIGH_WORD(function) == 0 && LOW_WORD(function) < 1000) + { + if( dll->ResolveOrdinal(LOW_WORD(function), &address) ) + { + CLog::Log(LOGDEBUG, "{}({}({}), {}) => {}", __FUNCTION__, fmt::ptr(hModule), dll->GetName(), + LOW_WORD(function), fmt::ptr(address)); + } + else if( dll->IsSystemDll() ) + { + char ordinal[6] = {}; + sprintf(ordinal, "%u", LOW_WORD(function)); + address = (void*)create_dummy_function(dll->GetName(), ordinal); + + /* add to tracklist if we are tracking this source dll */ + DllTrackInfo* track = tracker_get_dlltrackinfo(loc); + if( track ) + tracker_dll_data_track(track->pDll, (uintptr_t)address); + + CLog::Log(LOGDEBUG, "{} - created dummy function {}!{}", __FUNCTION__, dll->GetName(), + ordinal); + } + else + { + address = NULL; + CLog::Log(LOGDEBUG, "{}({}({}), '{}') => {}", __FUNCTION__, fmt::ptr(hModule), dll->GetName(), + function, fmt::ptr(address)); + } + } + else + { + if( dll->ResolveExport(function, &address) ) + { + CLog::Log(LOGDEBUG, "{}({}({}), '{}') => {}", __FUNCTION__, fmt::ptr(hModule), dll->GetName(), + function, fmt::ptr(address)); + } + else + { + DllTrackInfo* track = tracker_get_dlltrackinfo(loc); + /* some dll's require us to always return a function or it will fail, other's */ + /* decide functionality depending on if the functions exist and may fail */ + if (dll->IsSystemDll() && track && + StringUtils::CompareNoCase(track->pDll->GetName(), "CoreAVCDecoder.ax") == 0) + { + address = (void*)create_dummy_function(dll->GetName(), function); + tracker_dll_data_track(track->pDll, (uintptr_t)address); + CLog::Log(LOGDEBUG, "{} - created dummy function {}!{}", __FUNCTION__, dll->GetName(), + function); + } + else + { + address = NULL; + CLog::Log(LOGDEBUG, "{}({}({}), '{}') => {}", __FUNCTION__, fmt::ptr(hModule), + dll->GetName(), function, fmt::ptr(address)); + } + } + } + + return (intptr_t(*)(void)) address; +} + +extern "C" HMODULE WINAPI dllGetModuleHandleA(const char* lpModuleName) +{ + /* + If the file name extension is omitted, the default library extension .dll is appended. + The file name string can include a trailing point character (.) to indicate that the module name has no extension. + The string does not have to specify a path. When specifying a path, be sure to use backslashes (\), not forward slashes (/). + The name is compared (case independently) + If this parameter is NULL, GetModuleHandle returns a handle to the file used to create the calling process (.exe file). + */ + + if( lpModuleName == NULL ) + return NULL; + + char* strModuleName = new char[strlen(lpModuleName) + 5]; + strcpy(strModuleName, lpModuleName); + + if (strrchr(strModuleName, '.') == 0) strcat(strModuleName, ".dll"); + + //CLog::Log(LOGDEBUG, "GetModuleHandleA({}) .. looking up", lpModuleName); + + LibraryLoader *p = DllLoaderContainer::GetModule(strModuleName); + delete []strModuleName; + + if (p) + { + //CLog::Log(LOGDEBUG, "GetModuleHandleA('{}') => 0x{:x}", lpModuleName, h); + return (HMODULE)p->GetHModule(); + } + + CLog::Log(LOGDEBUG, "GetModuleHandleA('{}') failed", lpModuleName); + return NULL; +} diff --git a/xbmc/cores/DllLoader/dll.h b/xbmc/cores/DllLoader/dll.h new file mode 100644 index 0000000..5a0b605 --- /dev/null +++ b/xbmc/cores/DllLoader/dll.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "PlatformDefs.h" + +extern "C" HMODULE __stdcall dllLoadLibraryExtended(const char* file, const char* sourcedll); +extern "C" HMODULE __stdcall dllLoadLibraryA(const char* file); +extern "C" HMODULE __stdcall dllLoadLibraryExExtended(const char* lpLibFileName, HANDLE hFile, DWORD dwFlags, const char* sourcedll); +extern "C" HMODULE __stdcall dllLoadLibraryExA(const char* lpLibFileName, HANDLE hFile, DWORD dwFlags); +extern "C" int __stdcall dllFreeLibrary(HINSTANCE hLibModule); +extern "C" intptr_t (*__stdcall dllGetProcAddress(HMODULE hModule, const char* function))(void); +extern "C" HMODULE WINAPI dllGetModuleHandleA(const char* lpModuleName); + diff --git a/xbmc/cores/DllLoader/dll_tracker.cpp b/xbmc/cores/DllLoader/dll_tracker.cpp new file mode 100644 index 0000000..252232d --- /dev/null +++ b/xbmc/cores/DllLoader/dll_tracker.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "dll_tracker.h" + +#include "DllLoader.h" +#include "dll_tracker_file.h" +#include "dll_tracker_library.h" +#include "utils/log.h" + +#include <mutex> +#include <stdlib.h> + +#ifdef _cplusplus +extern "C" +{ +#endif + +CCriticalSection g_trackerLock; +TrackedDllList g_trackedDlls; + +void tracker_dll_add(DllLoader* pDll) +{ + DllTrackInfo* trackInfo = new DllTrackInfo; + trackInfo->pDll = pDll; + trackInfo->lMinAddr = 0; + trackInfo->lMaxAddr = 0; + std::unique_lock<CCriticalSection> locktd(g_trackerLock); + g_trackedDlls.push_back(trackInfo); +} + +void tracker_dll_free(DllLoader* pDll) +{ + std::unique_lock<CCriticalSection> locktd(g_trackerLock); + for (TrackedDllsIter it = g_trackedDlls.begin(); it != g_trackedDlls.end();) + { + // NOTE: This code assumes that the same dll pointer can be in more than one + // slot of the vector g_trackedDlls. If it's not, then it can be + // simplified by returning after we've found the one we want, saving + // the iterator shuffling, and reducing potential bugs. + if ((*it)->pDll == pDll) + { + try + { + tracker_library_free_all(*it); + tracker_file_free_all(*it); + } + catch (...) + { + CLog::Log(LOGFATAL, "Error freeing tracked dll resources"); + } + // free all functions which where created at the time we loaded the dll + DummyListIter dit = (*it)->dummyList.begin(); + while (dit != (*it)->dummyList.end()) { free((void*)*dit); ++dit; } + (*it)->dummyList.clear(); + + delete (*it); + it = g_trackedDlls.erase(it); + } + else + ++it; + } +} + +void tracker_dll_set_addr(const DllLoader* pDll, uintptr_t min, uintptr_t max) +{ + std::unique_lock<CCriticalSection> locktd(g_trackerLock); + for (TrackedDllsIter it = g_trackedDlls.begin(); it != g_trackedDlls.end(); ++it) + { + if ((*it)->pDll == pDll) + { + (*it)->lMinAddr = min; + (*it)->lMaxAddr = max; + break; + } + } +} + +const char* tracker_getdllname(uintptr_t caller) +{ + DllTrackInfo *track = tracker_get_dlltrackinfo(caller); + if(track) + return track->pDll->GetFileName(); + return ""; +} + +DllTrackInfo* tracker_get_dlltrackinfo(uintptr_t caller) +{ + std::unique_lock<CCriticalSection> locktd(g_trackerLock); + for (TrackedDllsIter it = g_trackedDlls.begin(); it != g_trackedDlls.end(); ++it) + { + if (caller >= (*it)->lMinAddr && caller <= (*it)->lMaxAddr) + { + return *it; + } + } + + // crap not in any base address, check if it may be in virtual spaces + for (TrackedDllsIter it = g_trackedDlls.begin(); it != g_trackedDlls.end(); ++it) + { + for(VAllocListIter it2 = (*it)->virtualList.begin(); it2 != (*it)->virtualList.end(); ++it2) + { + if( it2->first <= caller && caller < it2->first + it2->second.size ) + return *it; + + } + } + + return NULL; +} + +DllTrackInfo* tracker_get_dlltrackinfo_byobject(const DllLoader* pDll) +{ + std::unique_lock<CCriticalSection> locktd(g_trackerLock); + for (TrackedDllsIter it = g_trackedDlls.begin(); it != g_trackedDlls.end(); ++it) + { + if ((*it)->pDll == pDll) + { + return *it; + } + } + return NULL; +} + +void tracker_dll_data_track(const DllLoader* pDll, uintptr_t addr) +{ + std::unique_lock<CCriticalSection> locktd(g_trackerLock); + for (TrackedDllsIter it = g_trackedDlls.begin(); it != g_trackedDlls.end(); ++it) + { + if (pDll == (*it)->pDll) + { + (*it)->dummyList.push_back((uintptr_t)addr); + break; + } + } +} + +#ifdef _cplusplus +} +#endif diff --git a/xbmc/cores/DllLoader/dll_tracker.h b/xbmc/cores/DllLoader/dll_tracker.h new file mode 100644 index 0000000..68e1779 --- /dev/null +++ b/xbmc/cores/DllLoader/dll_tracker.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "threads/CriticalSection.h" +#include "PlatformDefs.h" +#ifdef TARGET_WINDOWS +#if defined(TARGET_WINDOWS_STORE) +#include <WinSock2.h> +#endif +#endif + +#include <list> +#include <map> + +class DllLoader; + +struct AllocLenCaller +{ + size_t size; + uintptr_t calleraddr; +}; + +enum TrackedFileType +{ + FILE_XBMC_OPEN, + FILE_XBMC_FOPEN, + FILE_OPEN, + FILE_FOPEN +}; + +typedef struct _TrackedFile +{ + TrackedFileType type; + uintptr_t handle; + char* name; +} TrackedFile; + +typedef std::map<uintptr_t, AllocLenCaller> DataList; +typedef std::map<uintptr_t, AllocLenCaller>::iterator DataListIter; + +typedef std::list<TrackedFile*> FileList; +typedef std::list<TrackedFile*>::iterator FileListIter; + +typedef std::list<HMODULE> DllList; +typedef std::list<HMODULE>::iterator DllListIter; + +typedef std::list<uintptr_t> DummyList; +typedef std::list<uintptr_t>::iterator DummyListIter; + +typedef std::list<SOCKET> SocketList; +typedef std::list<SOCKET>::iterator SocketListIter; + +typedef std::list<HANDLE> HeapObjectList; +typedef std::list<HANDLE>::iterator HeapObjectListIter; + +typedef std::map<uintptr_t, AllocLenCaller> VAllocList; +typedef std::map<uintptr_t, AllocLenCaller>::iterator VAllocListIter; + +typedef struct _DllTrackInfo +{ + DllLoader* pDll; + uintptr_t lMinAddr; + uintptr_t lMaxAddr; + + DataList dataList; + + // list with dll's that are loaded by this dll + DllList dllList; + + // for dummy functions that are created if no exported function could be found + DummyList dummyList; + + FileList fileList; + SocketList socketList; + + HeapObjectList heapobjectList; + + VAllocList virtualList; +} DllTrackInfo; + +class TrackedDllList : public std::list<DllTrackInfo*>, public CCriticalSection {}; +typedef std::list<DllTrackInfo*>::iterator TrackedDllsIter; + +#ifdef _cplusplus +extern "C" +{ +#endif + +extern CCriticalSection g_trackerLock; +extern TrackedDllList g_trackedDlls; + +// add a dll for tracking +void tracker_dll_add(DllLoader* pDll); + +// remove a dll, and free all its resources +void tracker_dll_free(DllLoader* pDll); + +// sets the dll base address and size +void tracker_dll_set_addr(const DllLoader* pDll, uintptr_t min, uintptr_t max); + +// returns the name from the dll that contains this address or "" if not found +const char* tracker_getdllname(uintptr_t caller); + +// returns a function pointer if there is one available for it, or NULL if not ofund +void* tracker_dll_get_function(DllLoader* pDll, char* sFunctionName); + +DllTrackInfo* tracker_get_dlltrackinfo_byobject(const DllLoader* pDll); + +DllTrackInfo* tracker_get_dlltrackinfo(uintptr_t caller); + +void tracker_dll_data_track(const DllLoader* pDll, uintptr_t addr); + +#ifdef TARGET_POSIX +#define _ReturnAddress() __builtin_return_address(0) +#endif + +#ifdef _cplusplus +} +#endif + +#ifndef TARGET_POSIX +extern "C" void * _ReturnAddress(void); +#pragma intrinsic(_ReturnAddress) +#endif + diff --git a/xbmc/cores/DllLoader/dll_tracker_file.cpp b/xbmc/cores/DllLoader/dll_tracker_file.cpp new file mode 100644 index 0000000..b5d1214 --- /dev/null +++ b/xbmc/cores/DllLoader/dll_tracker_file.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "dll_tracker_file.h" + +#include "DllLoader.h" +#include "dll_tracker.h" +#include "utils/log.h" + +#include <mutex> +#include <stdlib.h> + +#ifdef TARGET_POSIX +#define dll_open open +#define dll_fopen fopen +#define dll_close close +#define dll_fclose fclose +#define dll_freopen freopen +#else +#include "exports/emu_msvcrt.h" +#include <io.h> +#endif + +extern "C" void tracker_file_track(uintptr_t caller, uintptr_t handle, TrackedFileType type, const char* sFile) +{ + DllTrackInfo* pInfo = tracker_get_dlltrackinfo(caller); + if (pInfo) + { + std::unique_lock<CCriticalSection> lock(g_trackerLock); + TrackedFile* file = new TrackedFile; + file->handle = handle; + file->type = type; + file->name = strdup(sFile); + pInfo->fileList.push_back(file); + } +} + +extern "C" void tracker_file_free(uintptr_t caller, uintptr_t handle, TrackedFileType type) +{ + DllTrackInfo* pInfo = tracker_get_dlltrackinfo(caller); + if (pInfo) + { + std::unique_lock<CCriticalSection> lock(g_trackerLock); + for (FileListIter it = pInfo->fileList.begin(); it != pInfo->fileList.end(); ++it) + { + TrackedFile* file = *it; + if (file->handle == handle && file->type == type) + { + free(file->name); + delete file; + pInfo->fileList.erase(it); + return; + } + } + } + CLog::Log(LOGWARNING, "unable to remove tracked file from tracker"); +} + +extern "C" void tracker_file_free_all(DllTrackInfo* pInfo) +{ + if (!pInfo->fileList.empty()) + { + std::unique_lock<CCriticalSection> lock(g_trackerLock); + CLog::Log(LOGDEBUG, "{0}: Detected open files: {1}", pInfo->pDll->GetFileName(), pInfo->fileList.size()); + for (FileListIter it = pInfo->fileList.begin(); it != pInfo->fileList.end(); ++it) + { + TrackedFile* file = *it; + CLog::Log(LOGDEBUG, "{}", file->name); + free(file->name); + + if (file->type == FILE_XBMC_OPEN) dll_close(file->handle); + else if (file->type == FILE_XBMC_FOPEN) dll_fclose((FILE*)file->handle); + else if (file->type == FILE_OPEN) close(file->handle); + else if (file->type == FILE_FOPEN) fclose((FILE*)file->handle); + + delete file; + } + } + pInfo->fileList.erase(pInfo->fileList.begin(), pInfo->fileList.end()); +} + +extern "C" +{ + int track_open(const char* sFileName, int iMode) + { + uintptr_t loc = (uintptr_t)_ReturnAddress(); + + int fd = dll_open(sFileName, iMode); + if (fd >= 0) tracker_file_track(loc, fd, FILE_XBMC_OPEN, sFileName); + return fd; + } + + int track_close(int fd) + { + uintptr_t loc = (uintptr_t)_ReturnAddress(); + + tracker_file_free(loc, fd, FILE_XBMC_OPEN); + return dll_close(fd); + } + + FILE* track_fopen(const char* sFileName, const char* mode) + { + uintptr_t loc = (uintptr_t)_ReturnAddress(); + + FILE* fd = dll_fopen(sFileName, mode); + if (fd) tracker_file_track(loc, (uintptr_t)fd, FILE_XBMC_FOPEN, sFileName); + return fd; + } + + int track_fclose(FILE* stream) + { + uintptr_t loc = (uintptr_t)_ReturnAddress(); + + tracker_file_free(loc, (uintptr_t)stream, FILE_XBMC_FOPEN); + return dll_fclose(stream); + } + + FILE* track_freopen(const char *path, const char *mode, FILE *stream) + { + uintptr_t loc = (uintptr_t)_ReturnAddress(); + + tracker_file_free(loc, (uintptr_t)stream, FILE_XBMC_FOPEN); + stream = dll_freopen(path, mode, stream); + if (stream) + tracker_file_track(loc, (uintptr_t)stream, FILE_XBMC_FOPEN, path); + return stream; + } + +} diff --git a/xbmc/cores/DllLoader/dll_tracker_file.h b/xbmc/cores/DllLoader/dll_tracker_file.h new file mode 100644 index 0000000..e6be371 --- /dev/null +++ b/xbmc/cores/DllLoader/dll_tracker_file.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "dll_tracker.h" + +#include <stdio.h> + +extern "C" void tracker_file_track(uintptr_t caller, uintptr_t handle, TrackedFileType type, const char* sFile = ""); +extern "C" void tracker_file_free(uintptr_t caller, uintptr_t handle, TrackedFileType type); +extern "C" void tracker_file_free_all(DllTrackInfo* pInfo); + +extern "C" +{ + int track_open(const char* sFileName, int iMode); + int track_close(int fd); + FILE* track_fopen(const char* sFileName, const char* mode); + int track_fclose(FILE* stream); + FILE* track_freopen(const char *path, const char *mode, FILE *stream); +} + diff --git a/xbmc/cores/DllLoader/dll_tracker_library.cpp b/xbmc/cores/DllLoader/dll_tracker_library.cpp new file mode 100644 index 0000000..8cfac13 --- /dev/null +++ b/xbmc/cores/DllLoader/dll_tracker_library.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "dll_tracker_library.h" + +#include "DllLoader.h" +#include "DllLoaderContainer.h" +#include "dll.h" +#include "dll_tracker.h" +#include "utils/log.h" + +#include <mutex> + +extern "C" inline void tracker_library_track(uintptr_t caller, HMODULE hHandle) +{ + DllTrackInfo* pInfo = tracker_get_dlltrackinfo(caller); + if (pInfo && hHandle) + { + std::unique_lock<CCriticalSection> lock(g_trackerLock); + pInfo->dllList.push_back(hHandle); + } +} + +extern "C" inline void tracker_library_free(uintptr_t caller, HMODULE hHandle) +{ + DllTrackInfo* pInfo = tracker_get_dlltrackinfo(caller); + if (pInfo && hHandle) + { + std::unique_lock<CCriticalSection> lock(g_trackerLock); + for (DllListIter it = pInfo->dllList.begin(); it != pInfo->dllList.end(); ++it) + { + if (*it == hHandle) + { + pInfo->dllList.erase(it); + break; + } + } + } +} + +extern "C" void tracker_library_free_all(DllTrackInfo* pInfo) +{ + // unloading unloaded dll's + if (!pInfo->dllList.empty()) + { + std::unique_lock<CCriticalSection> lock(g_trackerLock); + CLog::Log(LOGDEBUG,"{0}: Detected {1} unloaded dll's", pInfo->pDll->GetFileName(), pInfo->dllList.size()); + for (DllListIter it = pInfo->dllList.begin(); it != pInfo->dllList.end(); ++it) + { + LibraryLoader* pDll = DllLoaderContainer::GetModule((HMODULE)*it); + if( !pDll) + { + CLog::Log(LOGERROR, "{} - Invalid module in tracker", __FUNCTION__); + return; + } + + if (!pDll->IsSystemDll()) + { + if (strlen(pDll->GetFileName()) > 0) + CLog::Log(LOGDEBUG, " : {}", pDll->GetFileName()); + } + } + + // now unload the dlls + for (DllListIter it = pInfo->dllList.begin(); it != pInfo->dllList.end(); ++it) + { + LibraryLoader* pDll = DllLoaderContainer::GetModule((HMODULE)*it); + if( !pDll) + { + CLog::Log(LOGERROR, "{} - Invalid module in tracker", __FUNCTION__); + return; + } + + if (!pDll->IsSystemDll()) + { + dllFreeLibrary((HMODULE)pDll->GetHModule()); + } + } + } +} + +extern "C" HMODULE __stdcall track_LoadLibraryA(const char* file) +{ + uintptr_t loc = (uintptr_t)_ReturnAddress(); + + DllTrackInfo* pInfo = tracker_get_dlltrackinfo(loc); + const char* path = NULL; + if (pInfo) path = pInfo->pDll->GetFileName(); + + HMODULE hHandle = dllLoadLibraryExtended(file, path); + tracker_library_track(loc, hHandle); + + return hHandle; +} + +extern "C" HMODULE __stdcall track_LoadLibraryExA(const char* lpLibFileName, HANDLE hFile, DWORD dwFlags) +{ + uintptr_t loc = (uintptr_t)_ReturnAddress(); + + DllTrackInfo* pInfo = tracker_get_dlltrackinfo(loc); + const char* path = NULL; + if (pInfo) path = pInfo->pDll->GetFileName(); + + HMODULE hHandle = dllLoadLibraryExExtended(lpLibFileName, hFile, dwFlags, path); + tracker_library_track(loc, hHandle); + + return hHandle; +} + +extern "C" int __stdcall track_FreeLibrary(HINSTANCE hLibModule) +{ + uintptr_t loc = (uintptr_t)_ReturnAddress(); + + tracker_library_free(loc, hLibModule); + + return dllFreeLibrary(hLibModule); +} diff --git a/xbmc/cores/DllLoader/dll_tracker_library.h b/xbmc/cores/DllLoader/dll_tracker_library.h new file mode 100644 index 0000000..6670d70 --- /dev/null +++ b/xbmc/cores/DllLoader/dll_tracker_library.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "dll_tracker.h" + +extern "C" void tracker_library_free_all(DllTrackInfo* pInfo); + +extern "C" HMODULE __stdcall track_LoadLibraryA(const char* file); +extern "C" HMODULE __stdcall track_LoadLibraryExA(const char* lpLibFileName, HANDLE hFile, DWORD dwFlags); +extern "C" int __stdcall track_FreeLibrary(HINSTANCE hLibModule); diff --git a/xbmc/cores/DllLoader/dll_util.cpp b/xbmc/cores/DllLoader/dll_util.cpp new file mode 100644 index 0000000..b33c02e --- /dev/null +++ b/xbmc/cores/DllLoader/dll_util.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "utils/log.h" +#include "dll_util.h" + +#ifdef TARGET_WINDOWS +#include "platform/win32/CharsetConverter.h" +#include <windows.h> +#endif +#include <stdlib.h> +#include <string.h> + +#ifdef _cplusplus +extern "C" +{ +#endif + +static int iDllDummyOutputCall = 0; +void dll_dummy_output(char* dllname, char* funcname) +{ + CLog::Log(LOGERROR, "{}: Unresolved function called ({}), Count number {}", dllname, funcname, + ++iDllDummyOutputCall); +} + +// this piece of asm code only calls dll_dummy_output(s, s) and will return NULL +unsigned char dummy_func[] = { + 0x55, // push ebp + 0x8b, 0xec, // mov ebp,esp + 0xa1, 0, 0, 0, 0, // mov eax,dword ptr [0 0 0 0] + 0x50, // push eax + 0xa1, 0, 0, 0, 0, // mov eax,dword ptr [0 0 0 0] + 0x50, // push eax + 0xff, 0x15, 0, 0, 0, 0, // call dword ptr[dll_dummy_output] + 0x83, 0xc4, 0x08, // add esp,8 + 0x33, 0xc0, // xor eax,eax // return NULL + 0x5d, // pop ebp + 0xc3 // ret + }; + +/* Create a new callable function + * This allocates a few bytes with the next content + * + * 1 function in assembly code (dummy_func) + * 2 datapointer (pointer to dll string) + * 3 datapointer (pointer to function string) + * 4 datapointer (pointer to function string) + * 5 string (string of chars representing dll name) + * 6 string (string of chars representing function name) + */ +uintptr_t create_dummy_function(const char* strDllName, const char* strFunctionName) +{ + size_t iFunctionSize = sizeof(dummy_func); + size_t iDllNameSize = strlen(strDllName) + 1; + size_t iFunctionNameSize = strlen(strFunctionName) + 1; + + // allocate memory for function + strings + 3 x 4 bytes for three datapointers + char* pData = (char*)malloc(iFunctionSize + 12 + iDllNameSize + iFunctionNameSize); + if (!pData) + return 0; + + char* offDataPointer1 = pData + iFunctionSize; + char* offDataPointer2 = pData + iFunctionSize + 4; + char* offDataPointer3 = pData + iFunctionSize + 8; + char* offStringDll = pData + iFunctionSize + 12; + char* offStringFunc = pData + iFunctionSize + 12 + iDllNameSize; + + // 1 copy assembly code + memcpy(pData, dummy_func, iFunctionSize); + + // insert pointers to datapointers into assembly code (fills 0x00000000 in dummy_func) + *(int*)(pData + 4) = (intptr_t)offDataPointer1; + *(int*)(pData + 10) = (intptr_t)offDataPointer2; + *(int*)(pData + 17) = (intptr_t)offDataPointer3; + + // 2 fill datapointer with pointer to 5 (string) + *(int*)offDataPointer1 = (intptr_t)offStringFunc; + // 3 fill datapointer with pointer to 6 (string) + *(int*)offDataPointer2 = (intptr_t)offStringDll; + // 4 fill datapointer with pointer to dll_dummy_output + *(int*)offDataPointer3 = (intptr_t)dll_dummy_output; + + // copy arguments to 5 (string) and 6 (string) + memcpy(offStringDll, strDllName, iDllNameSize); + memcpy(offStringFunc, strFunctionName, iFunctionNameSize); + + return (uintptr_t)pData; +} + +uintptr_t get_win_function_address(const char* strDllName, const char* strFunctionName) +{ +#ifdef TARGET_WINDOWS_DESKTOP + using KODI::PLATFORM::WINDOWS::ToW; + auto strDllNameW = ToW(strDllName); + HMODULE handle = GetModuleHandle(strDllNameW.c_str()); + if(handle == nullptr) + { + handle = LoadLibrary(strDllNameW.c_str()); + } + if(handle != nullptr) + { + auto pGNSI = reinterpret_cast<uintptr_t>(GetProcAddress(handle, strFunctionName)); + if(pGNSI != NULL) + return pGNSI; + + FreeLibrary(handle); + } +#endif + return 0; +} + +#ifdef _cplusplus +} +#endif diff --git a/xbmc/cores/DllLoader/dll_util.h b/xbmc/cores/DllLoader/dll_util.h new file mode 100644 index 0000000..3b0b6c4 --- /dev/null +++ b/xbmc/cores/DllLoader/dll_util.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <stdint.h> + +#ifdef _cplusplus +extern "C" +{ +#endif + +uintptr_t create_dummy_function(const char* strDllName, const char* strFunctionName); +uintptr_t get_win_function_address(const char* strDllName, const char* strFunctionName); + +#ifdef _cplusplus +} +#endif + diff --git a/xbmc/cores/DllLoader/exports/CMakeLists.txt b/xbmc/cores/DllLoader/exports/CMakeLists.txt new file mode 100644 index 0000000..4039669 --- /dev/null +++ b/xbmc/cores/DllLoader/exports/CMakeLists.txt @@ -0,0 +1,33 @@ +set(SOURCES emu_dummy.cpp + emu_msvcrt.cpp) + +set(HEADERS emu_dummy.h + emu_msvcrt.h) + +core_add_library(dllexports) + +if(APPLE) + add_library(wrapper OBJECT wrapper.c) + add_custom_target(wrapper.def ALL ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/wrapper_mach_alias wrapper.def) + set_target_properties(wrapper PROPERTIES FOLDER "Build Utilities") + set_target_properties(wrapper.def PROPERTIES FOLDER "Build Utilities") + add_dependencies(wrapper.def wrapper) +elseif(NOT CORE_SYSTEM_NAME STREQUAL windows AND NOT CORE_SYSTEM_NAME STREQUAL windowsstore) + add_options(C ALL_BUILDS "-fPIC") + add_library(wrapper OBJECT wrapper.c) + + if(USE_LTO) + add_custom_target(wrapper.def ALL ${CMAKE_NM} ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/wrapper.dir/wrapper.c.o | grep __wrap | awk '{ printf(\"%s \", \$\$3) }' | sed \"s/^/${CMAKE_C_COMPILE_OPTIONS_IPO} /\" | sed \"s/___wrap_/__wrap_/g\" | sed \"s/__wrap_/-Wl,-wrap,/g\" > wrapper.def && test -s wrapper.def) + else() + add_custom_target(wrapper.def ALL ${CMAKE_NM} ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/wrapper.dir/wrapper.c.o | grep __wrap | awk '{ printf(\"%s \", \$\$3) }' | sed \"s/___wrap_/__wrap_/g\" | sed \"s/__wrap_/-Wl,-wrap,/g\" > wrapper.def && test -s wrapper.def) + endif() + + if(CORE_SYSTEM_NAME STREQUAL android) + add_custom_command(TARGET wrapper.def COMMAND echo \"-L${DEPENDS_PATH}/lib/dummy-lib${APP_NAME_LC} -l${APP_NAME_LC}\" >> wrapper.def) + endif() + + set_target_properties(wrapper PROPERTIES FOLDER "Build Utilities") + set_target_properties(wrapper.def PROPERTIES FOLDER "Build Utilities") + add_dependencies(wrapper.def wrapper) +endif() + diff --git a/xbmc/cores/DllLoader/exports/emu_dummy.cpp b/xbmc/cores/DllLoader/exports/emu_dummy.cpp new file mode 100644 index 0000000..813c901 --- /dev/null +++ b/xbmc/cores/DllLoader/exports/emu_dummy.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "emu_dummy.h" + +#include "utils/log.h" + +extern "C" void not_implement( const char* debuginfo) +{ + if (debuginfo) + { + CLog::Log(LOGDEBUG, "{}", debuginfo); + } +} + diff --git a/xbmc/cores/DllLoader/exports/emu_dummy.h b/xbmc/cores/DllLoader/exports/emu_dummy.h new file mode 100644 index 0000000..f49e214 --- /dev/null +++ b/xbmc/cores/DllLoader/exports/emu_dummy.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + + void not_implement( const char* ); + +#ifdef __cplusplus +} +#endif + + diff --git a/xbmc/cores/DllLoader/exports/emu_msvcrt.cpp b/xbmc/cores/DllLoader/exports/emu_msvcrt.cpp new file mode 100644 index 0000000..3f555db --- /dev/null +++ b/xbmc/cores/DllLoader/exports/emu_msvcrt.cpp @@ -0,0 +1,2074 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include <math.h> +#include <mutex> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#ifndef TARGET_POSIX +#include <io.h> +#include <direct.h> +#include <process.h> +#include <errno.h> +#else +#if !defined(TARGET_DARWIN) && !defined(TARGET_FREEBSD) +#include <mntent.h> +#endif +#endif +#include <sys/stat.h> +#include <sys/types.h> +#if !defined(TARGET_FREEBSD) && (!defined(TARGET_ANDROID) && defined(__LP64__)) +#include <sys/timeb.h> +#endif +#ifdef HAS_DVD_DRIVE + #ifdef TARGET_POSIX + #include <sys/ioctl.h> + #if defined(TARGET_DARWIN) + #include <IOKit/storage/IODVDMediaBSDClient.h> + #elif !defined(TARGET_FREEBSD) + #include <linux/cdrom.h> + #endif + #endif +#endif +#include <fcntl.h> +#include <time.h> +#include <signal.h> +#ifdef TARGET_POSIX +#include "PlatformDefs.h" // for __stat64 +#endif +#include "CompileInfo.h" +#include "FileItem.h" +#include "ServiceBroker.h" +#include "URL.h" +#include "Util.h" +#include "emu_dummy.h" +#include "emu_msvcrt.h" +#include "filesystem/Directory.h" +#include "filesystem/File.h" +#include "filesystem/SpecialProtocol.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "util/EmuFileWrapper.h" +#include "utils/log.h" +#ifndef TARGET_POSIX +#include "utils/CharsetConverter.h" +#include "utils/URIUtils.h" +#endif +#if !defined(TARGET_WINDOWS) +#include <dlfcn.h> +#endif +#include "platform/Environment.h" +#include "utils/StringUtils.h" +#include "utils/XTimeUtils.h" + +#if defined(TARGET_WINDOWS) +#include "platform/win32/CharsetConverter.h" +#endif + +using namespace XFILE; + +struct SDirData +{ + CFileItemList items; + int curr_index; + struct dirent *last_entry; + SDirData() + { + curr_index = -1; + last_entry = NULL; + } +}; + +#define MAX_OPEN_DIRS 10 +static SDirData vecDirsOpen[MAX_OPEN_DIRS]; +bool bVecDirsInited = false; +extern void update_cache_dialog(const char* tmp); + +#define EMU_MAX_ENVIRONMENT_ITEMS 100 +static char *dll__environ_imp[EMU_MAX_ENVIRONMENT_ITEMS + 1]; +extern "C" char **dll__environ; +char **dll__environ = dll__environ_imp; + +CCriticalSection dll_cs_environ; + +extern "C" void __stdcall init_emu_environ() +{ + memset(dll__environ, 0, EMU_MAX_ENVIRONMENT_ITEMS + 1); + + // python +#if defined(TARGET_WINDOWS_DESKTOP) + using KODI::PLATFORM::WINDOWS::FromW; + // fill our array with the windows system vars + LPTSTR lpszVariable; + LPTCH lpvEnv = NULL; + lpvEnv = GetEnvironmentStrings(); + if (lpvEnv != NULL) + { + lpszVariable = (LPTSTR) lpvEnv; + while (*lpszVariable) + { + dll_putenv(FromW(lpszVariable).c_str()); + lpszVariable += lstrlen(lpszVariable) + 1; + } + FreeEnvironmentStrings(lpvEnv); + } + dll_putenv("OS=win32"); +#elif defined(TARGET_WINDOWS_STORE) + dll_putenv("OS=win10"); +#elif defined(TARGET_DARWIN) + dll_putenv("OS=darwin"); +#elif defined(TARGET_POSIX) + dll_putenv("OS=linux"); +#else + dll_putenv("OS=unknown"); +#endif + + // check if we are running as real xbmc.app or just binary + if (!CUtil::GetFrameworksPath(true).empty()) + { + // using external python, it's build looking for xxx/lib/python(VERSIONMAJOR.MINOR) + // so point it to frameworks which is where python is located + dll_putenv(("PYTHONPATH=" + + CSpecialProtocol::TranslatePath("special://frameworks")).c_str()); + dll_putenv(("PYTHONHOME=" + + CSpecialProtocol::TranslatePath("special://frameworks")).c_str()); + dll_putenv(("PATH=.;" + + CSpecialProtocol::TranslatePath("special://xbmc") + ";" + + CSpecialProtocol::TranslatePath("special://frameworks")).c_str()); + } + else + { + dll_putenv(("PYTHONPATH=" + + CSpecialProtocol::TranslatePath("special://xbmc/system/python/DLLs") + ";" + + CSpecialProtocol::TranslatePath("special://xbmc/system/python/Lib")).c_str()); + dll_putenv(("PYTHONHOME=" + + CSpecialProtocol::TranslatePath("special://xbmc/system/python")).c_str()); + dll_putenv(("PATH=.;" + CSpecialProtocol::TranslatePath("special://xbmc") + ";" + + CSpecialProtocol::TranslatePath("special://xbmc/system/python")).c_str()); + } + +#if defined(TARGET_ANDROID) + std::string apkPath = getenv("KODI_ANDROID_APK"); + apkPath += "/assets/python" + CCompileInfo::GetPythonVersion(); + dll_putenv(("PYTHONHOME=" + apkPath).c_str()); + dll_putenv("PYTHONOPTIMIZE="); + dll_putenv("PYTHONNOUSERSITE=1"); + dll_putenv("PYTHONPATH="); +#else + dll_putenv("PYTHONOPTIMIZE=1"); +#endif + + //dll_putenv("PYTHONCASEOK=1"); + //dll_putenv("PYTHONDEBUG=1"); + //dll_putenv("PYTHONVERBOSE=2"); // "1" for normal verbose, "2" for more verbose ? + //dll_putenv("PYTHONDUMPREFS=1"); + //dll_putenv("THREADDEBUG=1"); + //dll_putenv("PYTHONMALLOCSTATS=1"); + //dll_putenv("PYTHONY2K=1"); + dll_putenv("TEMP=special://temp/temp"); // for python tempdir + + // libdvdnav + dll_putenv("DVDREAD_NOKEYS=1"); + //dll_putenv("DVDREAD_VERBOSE=1"); + //dll_putenv("DVDREAD_USE_DIRECT=1"); + + // libdvdcss + dll_putenv("DVDCSS_METHOD=key"); + dll_putenv("DVDCSS_VERBOSE=3"); + dll_putenv("DVDCSS_CACHE=special://masterprofile/cache"); +} + +extern "C" void __stdcall update_emu_environ() +{ + const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + + // Use a proxy, if the GUI was configured as such + if (settings->GetBool(CSettings::SETTING_NETWORK_USEHTTPPROXY) + && !settings->GetString(CSettings::SETTING_NETWORK_HTTPPROXYSERVER).empty() + && settings->GetInt(CSettings::SETTING_NETWORK_HTTPPROXYPORT) > 0 + && settings->GetInt(CSettings::SETTING_NETWORK_HTTPPROXYTYPE) == 0) + { + std::string strProxy; + if (!settings->GetString(CSettings::SETTING_NETWORK_HTTPPROXYUSERNAME).empty() && + !settings->GetString(CSettings::SETTING_NETWORK_HTTPPROXYPASSWORD).empty()) + { + strProxy = StringUtils::Format( + "{}:{}@", settings->GetString(CSettings::SETTING_NETWORK_HTTPPROXYUSERNAME), + settings->GetString(CSettings::SETTING_NETWORK_HTTPPROXYPASSWORD)); + } + + strProxy += settings->GetString(CSettings::SETTING_NETWORK_HTTPPROXYSERVER); + strProxy += + StringUtils::Format(":{}", settings->GetInt(CSettings::SETTING_NETWORK_HTTPPROXYPORT)); + + CEnvironment::setenv( "HTTP_PROXY", "http://" + strProxy, true ); + CEnvironment::setenv( "HTTPS_PROXY", "http://" + strProxy, true ); + } + else + { + // is there a better way to delete an environment variable? + // this works but leaves the variable + dll_putenv( "HTTP_PROXY=" ); + dll_putenv( "HTTPS_PROXY=" ); + } +} + +extern "C" void __stdcall cleanup_emu_environ() +{ + for (int i = 0; i < EMU_MAX_ENVIRONMENT_ITEMS; i++) + { + free(dll__environ[i]); + dll__environ[i] = NULL; + } +} + +static int convert_fmode(const char* mode) +{ + int iMode = O_BINARY; + if (strstr(mode, "r+")) + iMode |= O_RDWR; + else if (strchr(mode, 'r')) + iMode |= _O_RDONLY; + if (strstr(mode, "w+")) + iMode |= O_RDWR | _O_TRUNC; + else if (strchr(mode, 'w')) + iMode |= _O_WRONLY | O_CREAT; + return iMode; +} + +#ifdef TARGET_WINDOWS +static void to_finddata64i32(_wfinddata64i32_t *wdata, _finddata64i32_t *data) +{ + std::string strname; + g_charsetConverter.wToUTF8(wdata->name, strname); + size_t size = sizeof(data->name) / sizeof(char); + strncpy(data->name, strname.c_str(), size); + if (size) + data->name[size - 1] = '\0'; + data->attrib = wdata->attrib; + data->time_create = wdata->time_create; + data->time_access = wdata->time_access; + data->time_write = wdata->time_write; + data->size = wdata->size; +} + +static void to_wfinddata64i32(_finddata64i32_t *data, _wfinddata64i32_t *wdata) +{ + std::wstring strwname; + g_charsetConverter.utf8ToW(data->name, strwname, false); + size_t size = sizeof(wdata->name) / sizeof(wchar_t); + wcsncpy(wdata->name, strwname.c_str(), size); + if (size) + wdata->name[size - 1] = '\0'; + wdata->attrib = data->attrib; + wdata->time_create = data->time_create; + wdata->time_access = data->time_access; + wdata->time_write = data->time_write; + wdata->size = data->size; +} +#endif + +extern "C" +{ + void dll_sleep(unsigned long imSec) { KODI::TIME::Sleep(std::chrono::milliseconds(imSec)); } + + // FIXME, XXX, !!!!!! + void dllReleaseAll( ) + { + // close all open dirs... + if (bVecDirsInited) + { + for (SDirData& dir : vecDirsOpen) + { + dir.items.Clear(); + } + bVecDirsInited = false; + } + } + + void* dllmalloc(size_t size) + { + void* pBlock = malloc(size); + if (!pBlock) + { + CLog::Log(LOGFATAL, "malloc {0} bytes failed, crash imminent", size); + } + return pBlock; + } + + void dllfree( void* pPtr ) + { + free(pPtr); + } + + void* dllcalloc(size_t num, size_t size) + { + void* pBlock = calloc(num, size); + if (!pBlock) + { + CLog::Log(LOGFATAL, "calloc {0} bytes failed, crash imminent", size); + } + return pBlock; + } + + void* dllrealloc( void *memblock, size_t size ) + { + void* pBlock = realloc(memblock, size); + if (!pBlock) + { + CLog::Log(LOGFATAL, "realloc {0} bytes failed, crash imminent", size); + } + return pBlock; + } + + void dllexit(int iCode) + { + not_implement("msvcrt.dll fake function exit() called\n"); //warning + } + + void dllabort() + { + not_implement("msvcrt.dll fake function abort() called\n"); //warning + } + + void* dll__dllonexit(PFV input, PFV ** start, PFV ** end) + { + //ported from WINE code + PFV *tmp; + int len; + + if (!start || !*start || !end || !*end) + { + //FIXME("bad table\n"); + return NULL; + } + + len = (*end - *start); + + if (++len <= 0) + return NULL; + + tmp = (PFV*) realloc (*start, len * sizeof(tmp) ); + if (!tmp) + return NULL; + *start = tmp; + *end = tmp + len; + tmp[len - 1] = input; + return (void *)input; + + //wrong handling, this function is used for register functions + //that called before exit use _initterm functions. + + //dllReleaseAll( ); + //return TRUE; + } + + _onexit_t dll_onexit(_onexit_t func) + { + not_implement("msvcrt.dll fake function dll_onexit() called\n"); + + // register to dll unload list + // return func if successfully added to the dll unload list + return NULL; + } + + int dllputs(const char* szLine) + { + if (!szLine[0]) return EOF; + if (szLine[strlen(szLine) - 1] != '\n') + CLog::Log(LOGDEBUG, " msg: {}", szLine); + else + CLog::Log(LOGDEBUG, " msg: {}", szLine); + + // return a non negative value + return 0; + } + + int dllprintf(const char *format, ...) + { + va_list va; + static char tmp[2048]; + va_start(va, format); + _vsnprintf(tmp, 2048, format, va); + va_end(va); + tmp[2048 - 1] = 0; + CLog::Log(LOGDEBUG, " msg: {}", tmp); + + return strlen(tmp); + } + + char *dll_fullpath(char *absPath, const char *relPath, size_t maxLength) + { + unsigned int len = strlen(relPath); + if (len > maxLength && absPath != NULL) return NULL; + + // dll has to make sure it uses the correct path for now + if (len > 1 && relPath[1] == ':') + { + if (absPath == NULL) absPath = dll_strdup(relPath); + else + { + strncpy(absPath, relPath, maxLength); + if (maxLength != 0) + absPath[maxLength-1] = '\0'; + } + return absPath; + } + if (!strncmp(relPath, "\\Device\\Cdrom0", 14)) + { + // needed? + if (absPath == NULL) absPath = strdup(relPath); + else + { + strncpy(absPath, relPath, maxLength); + if (maxLength != 0) + absPath[maxLength-1] = '\0'; + } + return absPath; + } + + not_implement("msvcrt.dll incomplete function _fullpath(...) called\n"); //warning + return NULL; + } + + FILE* dll_popen(const char *command, const char *mode) + { + not_implement("msvcrt.dll fake function _popen(...) called\n"); //warning + return NULL; + } + + void *dll_dlopen(const char *filename, int flag) + { +#if !defined(TARGET_WINDOWS) + return dlopen(filename, flag); +#else + return NULL; +#endif + } + + int dll_pclose(FILE *stream) + { + not_implement("msvcrt.dll fake function _pclose(...) called\n"); //warning + return 0; + } + + FILE* dll_fdopen(int fd, const char* mode) + { + EmuFileObject* o = g_emuFileWrapper.GetFileObjectByDescriptor(fd); + if (o) + { + if(!o->used) + return NULL; + + int nmode = convert_fmode(mode); + if( (o->mode & nmode) != nmode) + CLog::Log(LOGWARNING, "dll_fdopen - mode 0x{:x} differs from fd mode 0x{:x}", nmode, + o->mode); + return reinterpret_cast<FILE*>(o); + } + else if (!IS_STD_DESCRIPTOR(fd)) + { + // it might be something else than a file, or the file is not emulated + // let the operating system handle it + return _fdopen(fd, mode); + } + + not_implement("msvcrt.dll incomplete function _fdopen(...) called\n"); + return NULL; + } + + int dll_open(const char* szFileName, int iMode) + { + char str[1024]; + int size = sizeof(str); + // move to CFile classes + if (strncmp(szFileName, "\\Device\\Cdrom0", 14) == 0) + { + // replace "\\Device\\Cdrom0" with "D:" + strncpy(str, "D:", size); + if (size) + { + str[size-1] = '\0'; + strncat(str, szFileName + 14, size - strlen(str)); + } + } + else + { + strncpy(str, szFileName, size); + if (size) + str[size-1] = '\0'; + } + + CFile* pFile = new CFile(); + bool bWrite = false; + if ((iMode & O_RDWR) || (iMode & O_WRONLY)) + bWrite = true; + bool bOverwrite=false; + if ((iMode & _O_TRUNC) || (iMode & O_CREAT)) + bOverwrite = true; + // currently always overwrites + bool bResult; + + // We need to validate the path here as some calls from ie. libdvdnav + // or the python DLLs have malformed slashes on Win32 + // (-> E:\test\VIDEO_TS/VIDEO_TS.BUP)) + if (bWrite) + bResult = pFile->OpenForWrite(CUtil::ValidatePath(str), bOverwrite); + else + bResult = pFile->Open(CUtil::ValidatePath(str), READ_TRUNCATED); + + if (bResult) + { + EmuFileObject* object = g_emuFileWrapper.RegisterFileObject(pFile); + if (object == NULL) + { + pFile->Close(); + delete pFile; + return -1; + } + object->mode = iMode; + FILE* f = reinterpret_cast<FILE*>(object); + return g_emuFileWrapper.GetDescriptorByStream(f); + } + delete pFile; + return -1; + } + + FILE* dll_freopen(const char *path, const char *mode, FILE *stream) + { + if (g_emuFileWrapper.StreamIsEmulatedFile(stream)) + { + dll_fclose(stream); + return dll_fopen(path, mode); + } + + // error + // close stream and return NULL + dll_fclose(stream); + return NULL; + } + + int dll_read(int fd, void* buffer, unsigned int uiSize) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByDescriptor(fd); + if (pFile != NULL) + { + errno = 0; + const ssize_t ret = pFile->Read(buffer, uiSize); + if (ret < 0) + { + const int err = errno; // help compiler to optimize, "errno" can be macro + if (err == 0 || + (err != EAGAIN && err != EINTR && err != EIO && err != EOVERFLOW && err != EWOULDBLOCK && + err != ECONNRESET && err != ENOTCONN && err != ETIMEDOUT && + err != ENOBUFS && err != ENOMEM && err != ENXIO)) + errno = EIO; // exact errno is unknown or incorrect, use default error number + + return -1; + } + return ret; + } + else if (!IS_STD_DESCRIPTOR(fd)) + { + // it might be something else than a file, or the file is not emulated + // let the operating system handle it + return read(fd, buffer, uiSize); + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + errno = EBADF; + return -1; + } + + int dll_write(int fd, const void* buffer, unsigned int uiSize) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByDescriptor(fd); + if (pFile != NULL) + { + errno = 0; + const ssize_t ret = pFile->Write(buffer, uiSize); + if (ret < 0) + { + const int err = errno; // help compiler to optimize, "errno" can be macro + if (err == 0 || + (err != EAGAIN && err != EFBIG && err != EINTR && err != EIO && err != ENOSPC && err != EPIPE && err != EWOULDBLOCK && + err != ECONNRESET && + err != ENOBUFS && err != ENXIO && + err != EACCES && err != ENETDOWN && err != ENETUNREACH)) + errno = EIO; // exact errno is unknown or incorrect, use default error number + + return -1; + } + return ret; + } + else if (!IS_STD_DESCRIPTOR(fd)) + { + // it might be something else than a file, or the file is not emulated + // let the operating system handle it + return write(fd, buffer, uiSize); + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + errno = EBADF; + return -1; + } + + int dll_fstat64(int fd, struct __stat64 *buf) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByDescriptor(fd); + if (pFile != NULL) + return pFile->Stat(buf); + else if (IS_STD_DESCRIPTOR(fd)) +#if defined(TARGET_WINDOWS) + return _fstat64(fd, buf); +#else + return fstat64(fd, buf); +#endif + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return -1; + } + + int dll_close(int fd) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByDescriptor(fd); + if (pFile != NULL) + { + g_emuFileWrapper.UnRegisterFileObjectByDescriptor(fd); + + pFile->Close(); + delete pFile; + return 0; + } + else if (!IS_STD_DESCRIPTOR(fd) && fd >= 0) + { + // it might be something else than a file, or the file is not emulated + // let the operating system handle it + return close(fd); + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return -1; + } + + __off64_t dll_lseeki64(int fd, __off64_t lPos, int iWhence) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByDescriptor(fd); + if (pFile != NULL) + { + lPos = pFile->Seek(lPos, iWhence); + return lPos; + } + else if (!IS_STD_DESCRIPTOR(fd)) + { + // it might be something else than a file, or the file is not emulated + // let the operating system handle it + // not supported: return lseeki64(fd, lPos, iWhence); + CLog::Log(LOGWARNING, "msvcrt.dll: dll_lseeki64 called, TODO: add 'int64 -> long' type checking"); //warning + return static_cast<long long>(lseek(fd, (long)lPos, iWhence)); + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return -1ll; + } + + __off_t dll_lseek(int fd, __off_t lPos, int iWhence) + { + if (g_emuFileWrapper.DescriptorIsEmulatedFile(fd)) + { + return (__off_t)dll_lseeki64(fd, lPos, iWhence); + } + else if (!IS_STD_DESCRIPTOR(fd)) + { + // it might be something else than a file, or the file is not emulated + // let the operating system handle it + return lseek(fd, lPos, iWhence); + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return -1; + } + + void dll_rewind(FILE* stream) + { + int fd = g_emuFileWrapper.GetDescriptorByStream(stream); + if (fd >= 0) + { + dll_lseeki64(fd, 0, SEEK_SET); + } + else + { + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + } + } + + //--------------------------------------------------------------------------------------------------------- + void dll_flockfile(FILE *stream) + { + int fd = g_emuFileWrapper.GetDescriptorByStream(stream); + if (fd >= 0) + { + g_emuFileWrapper.LockFileObjectByDescriptor(fd); + return; + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + } + + int dll_ftrylockfile(FILE *stream) + { + int fd = g_emuFileWrapper.GetDescriptorByStream(stream); + if (fd >= 0) + { + if (g_emuFileWrapper.TryLockFileObjectByDescriptor(fd)) + return 0; + return -1; + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return -1; + } + + void dll_funlockfile(FILE *stream) + { + int fd = g_emuFileWrapper.GetDescriptorByStream(stream); + if (fd >= 0) + { + g_emuFileWrapper.UnlockFileObjectByDescriptor(fd); + return; + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + } + + int dll_fclose(FILE * stream) + { + int fd = g_emuFileWrapper.GetDescriptorByStream(stream); + if (fd >= 0) + { + return dll_close(fd) == 0 ? 0 : EOF; + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return EOF; + } + +#ifndef TARGET_POSIX + // should be moved to CFile classes + intptr_t dll_findfirst(const char *file, struct _finddata_t *data) + { + struct _finddata64i32_t data64i32; + intptr_t ret = dll_findfirst64i32(file, &data64i32); + if (ret != -1) + { + int size = sizeof(data->name); + strncpy(data->name, data64i32.name, size); + if (size) + data->name[size - 1] = '\0'; + data->size = (_fsize_t)data64i32.size; + data->time_write = (time_t)data64i32.time_write; + data->time_access = (time_t)data64i32.time_access; + } + return ret; + } + + intptr_t dll_findfirst64i32(const char *file, struct _finddata64i32_t *data) + { + char str[1024]; + int size = sizeof(str); + CURL url(CSpecialProtocol::TranslatePath(file)); + if (url.IsLocal()) + { + // move to CFile classes + if (strncmp(file, "\\Device\\Cdrom0", 14) == 0) + { + // replace "\\Device\\Cdrom0" with "D:" + strncpy(str, "D:", size); + if (size) + { + str[size - 1] = '\0'; + strncat(str, file + 14, size - strlen(str)); + } + } + else + { + strncpy(str, file, size); + if (size) + str[size - 1] = '\0'; + } + + // Make sure the slashes are correct & translate the path + struct _wfinddata64i32_t wdata; + std::wstring strwfile; + g_charsetConverter.utf8ToW(CUtil::ValidatePath(CSpecialProtocol::TranslatePath(str)), strwfile, false); + intptr_t ret = _wfindfirst64i32(strwfile.c_str(), &wdata); + if (ret != -1) + to_finddata64i32(&wdata, data); + return ret; + } + // non-local files. handle through IDirectory-class - only supports '*.bah' or '*.*' + std::string strURL(file); + std::string strMask; + if (url.GetFileName().find("*.*") != std::string::npos) + { + std::string strReplaced = url.GetFileName(); + StringUtils::Replace(strReplaced, "*.*",""); + url.SetFileName(strReplaced); + } + else if (url.GetFileName().find("*.") != std::string::npos) + { + strMask = URIUtils::GetExtension(url.GetFileName()); + url.SetFileName(url.GetFileName().substr(0, url.GetFileName().find("*."))); + } + else if (url.GetFileName().find("*") != std::string::npos) + { + std::string strReplaced = url.GetFileName(); + StringUtils::Replace(strReplaced, "*",""); + url.SetFileName(strReplaced); + } + int iDirSlot=0; // locate next free directory + while ((iDirSlot < MAX_OPEN_DIRS) && (vecDirsOpen[iDirSlot].curr_index != -1)) iDirSlot++; + if (iDirSlot >= MAX_OPEN_DIRS) + return -1; // no free slots + strURL = url.Get(); + bVecDirsInited = true; + vecDirsOpen[iDirSlot].items.Clear(); + XFILE::CDirectory::GetDirectory(strURL, vecDirsOpen[iDirSlot].items, strMask, DIR_FLAG_DEFAULTS); + if (vecDirsOpen[iDirSlot].items.Size()) + { + int size = sizeof(data->name); + strncpy(data->name,vecDirsOpen[iDirSlot].items[0]->GetLabel().c_str(), size); + if (size) + data->name[size - 1] = '\0'; + data->size = static_cast<_fsize_t>(vecDirsOpen[iDirSlot].items[0]->m_dwSize); + data->time_write = 0; + data->time_access = 0; + vecDirsOpen[iDirSlot].curr_index = 0; + return (intptr_t)&vecDirsOpen[iDirSlot]; + } + vecDirsOpen[iDirSlot].curr_index = -1; + return -1; // whatever != NULL + } + + // should be moved to CFile classes + int dll_findnext(intptr_t f, _finddata_t* data) + { + struct _finddata64i32_t data64i32; + int ret = dll_findnext64i32(f, &data64i32); + if (ret == 0) + { + int size = sizeof(data->name); + strncpy(data->name, data64i32.name, size); + if (size) + data->name[size - 1] = '\0'; + data->size = (_fsize_t)data64i32.size; + data->time_write = (time_t)data64i32.time_write; + data->time_access = (time_t)data64i32.time_access; + } + return ret; + } + + int dll_findnext64i32(intptr_t f, _finddata64i32_t* data) + { + int found = MAX_OPEN_DIRS; + for (int i = 0; i < MAX_OPEN_DIRS; i++) + { + if (f == (intptr_t)&vecDirsOpen[i] && vecDirsOpen[i].curr_index != -1) + { + found = i; + break; + } + } + if (found >= MAX_OPEN_DIRS) + { + struct _wfinddata64i32_t wdata; + to_wfinddata64i32(data, &wdata); + intptr_t ret = _wfindnext64i32(f, &wdata); // local dir + if (ret != -1) + to_finddata64i32(&wdata, data); + return ret; + } + + // we have a valid data structure. get next item! + int iItem = vecDirsOpen[found].curr_index; + if (iItem+1 < vecDirsOpen[found].items.Size()) // we have a winner! + { + int size = sizeof(data->name); + strncpy(data->name,vecDirsOpen[found].items[iItem+1]->GetLabel().c_str(), size); + if (size) + data->name[size - 1] = '\0'; + data->size = static_cast<_fsize_t>(vecDirsOpen[found].items[iItem+1]->m_dwSize); + vecDirsOpen[found].curr_index++; + return 0; + } + + vecDirsOpen[found].items.Clear(); + return -1; + } + + int dll_findclose(intptr_t handle) + { + int found = MAX_OPEN_DIRS; + for (int i = 0; i < MAX_OPEN_DIRS; i++) + { + if (handle == (intptr_t)&vecDirsOpen[i] && vecDirsOpen[i].curr_index != -1) + { + found = i; + break; + } + } + if (found >= MAX_OPEN_DIRS) + return _findclose(handle); + + vecDirsOpen[found].items.Clear(); + vecDirsOpen[found].curr_index = -1; + return 0; + } + + void dll__security_error_handler(int code, void *data) + { + //NOTE: __security_error_handler has been removed in VS2005 and up + CLog::Log(LOGERROR, "security_error, code {}", code); + } + +#endif + + DIR *dll_opendir(const char *file) + { + CURL url(CSpecialProtocol::TranslatePath(file)); + if (url.IsLocal()) + { // Make sure the slashes are correct & translate the path + return opendir(CUtil::ValidatePath(url.Get().c_str()).c_str()); + } + + // locate next free directory + int iDirSlot=0; + while ((iDirSlot<MAX_OPEN_DIRS) && (vecDirsOpen[iDirSlot].curr_index != -1)) iDirSlot++; + if (iDirSlot >= MAX_OPEN_DIRS) + { + CLog::Log(LOGDEBUG, "Dll: Max open dirs reached"); + return NULL; // no free slots + } + + bVecDirsInited = true; + vecDirsOpen[iDirSlot].items.Clear(); + + if (XFILE::CDirectory::GetDirectory(url.Get(), vecDirsOpen[iDirSlot].items, "", DIR_FLAG_DEFAULTS)) + { + vecDirsOpen[iDirSlot].curr_index = 0; + return (DIR *)&vecDirsOpen[iDirSlot]; + } + else + return NULL; + } + + struct dirent *dll_readdir(DIR *dirp) + { + if (!dirp) + return NULL; + + bool emulated(false); + for (const SDirData& dir : vecDirsOpen) + { + if (dirp == (DIR*)&dir) + { + emulated = true; + break; + } + } + if (!emulated) + return readdir(dirp); // local dir + + // dirp is actually a SDirData* + SDirData* dirData = reinterpret_cast<SDirData*>(dirp); + if (dirData->last_entry) + free(dirData->last_entry); + struct dirent *entry = NULL; + entry = (dirent*) malloc(sizeof(*entry)); + if (dirData->curr_index < dirData->items.Size() + 2) + { // simulate the '.' and '..' dir entries + if (dirData->curr_index == 0) + strncpy(entry->d_name, ".\0", 2); + else if (dirData->curr_index == 1) + strncpy(entry->d_name, "..\0", 3); + else + { + strncpy(entry->d_name, dirData->items[dirData->curr_index - 2]->GetLabel().c_str(), sizeof(entry->d_name)); + entry->d_name[sizeof(entry->d_name)-1] = '\0'; // null-terminate any truncated paths + } + dirData->last_entry = entry; + dirData->curr_index++; + return entry; + } + free(entry); + return NULL; + } + + int dll_closedir(DIR *dirp) + { + bool emulated(false); + for (const SDirData& dir : vecDirsOpen) + { + if (dirp == (DIR*)&dir) + { + emulated = true; + break; + } + } + if (!emulated) + return closedir(dirp); + + SDirData* dirData = reinterpret_cast<SDirData*>(dirp); + dirData->items.Clear(); + if (dirData->last_entry) + { + dirData->last_entry = NULL; + } + dirData->curr_index = -1; + return 0; + } + + void dll_rewinddir(DIR *dirp) + { + bool emulated(false); + for (const SDirData& dir : vecDirsOpen) + { + if (dirp == (DIR*)&dir) + { + emulated = true; + break; + } + } + if (!emulated) + { + rewinddir(dirp); + return; + } + + SDirData* dirData = reinterpret_cast<SDirData*>(dirp); + if (dirData->last_entry) + { + dirData->last_entry = NULL; + } + dirData->curr_index = 0; + } + + char* dll_fgets(char* pszString, int num ,FILE * stream) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByStream(stream); + if (pFile != NULL) + { + if (pFile->GetPosition() < pFile->GetLength()) + { + bool bRead = pFile->ReadString(pszString, num); + if (bRead) + { + return pszString; + } + } + else return NULL; //eof + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return NULL; + } + + int dll_feof(FILE * stream) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByStream(stream); + if (pFile != NULL) + { + if (pFile->GetPosition() < pFile->GetLength()) return 0; + else return 1; + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return 1; // eof by default + } + + int dll_fread(void * buffer, size_t size, size_t count, FILE * stream) + { + if (size == 0 || count == 0) + return 0; + + CFile* pFile = g_emuFileWrapper.GetFileXbmcByStream(stream); + if (pFile != NULL) + { + size_t read = 0; + const size_t bufSize = size * count; + do // fread() must read all data until buffer is filled or eof/error occurs + { + const ssize_t r = pFile->Read(((int8_t*)buffer) + read, bufSize - read); + if (r <= 0) + break; + read += r; + } while (bufSize > read); + return read / size; + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return 0; + } + + int dll_fgetc(FILE* stream) + { + if (g_emuFileWrapper.StreamIsEmulatedFile(stream)) + { + // it is a emulated file + unsigned char buf; + + if (dll_fread(&buf, 1, 1, stream) <= 0) + return EOF; + + return (int)buf; + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return EOF; + } + + int dll_getc(FILE* stream) + { + if (g_emuFileWrapper.StreamIsEmulatedFile(stream)) + { + // This routine is normally implemented as a macro with the same result as fgetc(). + return dll_fgetc(stream); + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return EOF; + } + + FILE* dll_fopen(const char* filename, const char* mode) + { + FILE* file = NULL; +#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) + if (strcmp(filename, _PATH_MOUNTED) == 0 + || strcmp(filename, _PATH_MNTTAB) == 0) + { + CLog::Log(LOGINFO, + "{} - something opened the mount file, let's hope it knows what it's doing", + __FUNCTION__); + return fopen(filename, mode); + } +#endif + int fd = dll_open(filename, convert_fmode(mode)); + if (fd >= 0) + { + file = g_emuFileWrapper.GetStreamByDescriptor(fd); + } + + return file; + } + + int dll_fopen_s(FILE** pFile, const char * filename, const char * mode) + { + if (pFile == NULL || filename == NULL || mode == NULL) + return EINVAL; + + *pFile = dll_fopen(filename, mode); + if (*pFile == NULL) + return errno; + + return 0; + } + + int dll_putc(int c, FILE *stream) + { + if (g_emuFileWrapper.StreamIsEmulatedFile(stream) || IS_STD_STREAM(stream)) + { + return dll_fputc(c, stream); + } + return EOF; + } + + int dll_putchar(int c) + { + return dll_putc(c, stdout); + } + + int dll_fputc(int character, FILE* stream) + { + if (IS_STDOUT_STREAM(stream) || IS_STDERR_STREAM(stream) || !IS_VALID_STREAM(stream)) + { + unsigned char tmp[2] = { (unsigned char)character, 0 }; + dllputs((char *)tmp); + return character; + } + else + { + if (g_emuFileWrapper.StreamIsEmulatedFile(stream)) + { + int fd = g_emuFileWrapper.GetDescriptorByStream(stream); + if (fd >= 0) + { + unsigned char c = (unsigned char)character; + int iItemsWritten = dll_write(fd, &c, 1); + if (iItemsWritten == 1) + return character; + } + } + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return EOF; + } + + int dll_fputs(const char * szLine, FILE* stream) + { + if (IS_STDOUT_STREAM(stream) || IS_STDERR_STREAM(stream) || !IS_VALID_STREAM(stream)) + { + dllputs(szLine); + return 0; + } + else + { + if (g_emuFileWrapper.StreamIsEmulatedFile(stream)) + { + size_t len = strlen(szLine); + return dll_fwrite(static_cast<const void*>(szLine), sizeof(char), len, stream); + } + } + + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return EOF; + } + + int dll_fseek64(FILE* stream, off64_t offset, int origin) + { + int fd = g_emuFileWrapper.GetDescriptorByStream(stream); + if (fd >= 0) + { + if (dll_lseeki64(fd, offset, origin) != -1) + { + return 0; + } + else return -1; + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return -1; + } + + int dll_fseek(FILE *stream, long offset, int origin) + { + return dll_fseek64(stream, offset, origin); + } + + int dll_ungetc(int c, FILE* stream) + { + if (g_emuFileWrapper.StreamIsEmulatedFile(stream)) + { + // it is a emulated file + int d; + if (dll_fseek(stream, -1, SEEK_CUR)!=0) + return EOF; + d = dll_fgetc(stream); + if (d == EOF) + return EOF; + + dll_fseek(stream, -1, SEEK_CUR); + if (c != d) + { + CLog::Log(LOGWARNING, "{}: c != d", __FUNCTION__); + d = fputc(c, stream); + if (d != c) + CLog::Log(LOGERROR, "{}: Write failed!", __FUNCTION__); + else + dll_fseek(stream, -1, SEEK_CUR); + } + return d; + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return EOF; + } + + long dll_ftell(FILE *stream) + { + return (long)dll_ftell64(stream); + } + + off64_t dll_ftell64(FILE *stream) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByStream(stream); + if (pFile != NULL) + { + return (off64_t)pFile->GetPosition(); + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return -1; + } + + long dll_tell(int fd) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByDescriptor(fd); + if (pFile != NULL) + { + return (long)pFile->GetPosition(); + } + else if (!IS_STD_DESCRIPTOR(fd)) + { + // it might be something else than a file, or the file is not emulated + // let the operating system handle it +#ifndef TARGET_POSIX + return tell(fd); +#else + return lseek(fd, 0, SEEK_CUR); +#endif + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return -1; + } + + long long dll_telli64(int fd) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByDescriptor(fd); + if (pFile != NULL) + { + return static_cast<long long>(pFile->GetPosition()); + } + else if (!IS_STD_DESCRIPTOR(fd)) + { + // it might be something else than a file, or the file is not emulated + // let the operating system handle it + // not supported return telli64(fd); + CLog::Log(LOGWARNING, "msvcrt.dll: dll_telli64 called, TODO: add 'int64 -> long' type checking"); //warning +#ifndef TARGET_POSIX + return static_cast<long long>(tell(fd)); +#elif defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) || defined(TARGET_ANDROID) + return lseek(fd, 0, SEEK_CUR); +#else + return lseek64(fd, 0, SEEK_CUR); +#endif + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return -1; + } + + size_t dll_fwrite(const void * buffer, size_t size, size_t count, FILE* stream) + { + if (size == 0 || count == 0) + return 0; + + if (IS_STDOUT_STREAM(stream) || IS_STDERR_STREAM(stream) || !IS_VALID_STREAM(stream)) + { + char* buf = (char*)malloc(size * count + 1); + if (buf) + { + memcpy(buf, buffer, size * count); + buf[size * count] = 0; // string termination + + CLog::Log(LOGDEBUG, "{}", buf); + + free(buf); + return count; + } + } + else + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByStream(stream); + if (pFile != NULL) + { + size_t written = 0; + const size_t bufSize = size * count; + do // fwrite() must write all data until whole buffer is written or error occurs + { + const ssize_t w = pFile->Write(((const int8_t*)buffer) + written, bufSize - written); + if (w <= 0) + break; + written += w; + } while (bufSize > written); + return written / size; + } + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return 0; + } + + int dll_fflush(FILE* stream) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByStream(stream); + if (pFile != NULL) + { + pFile->Flush(); + return 0; + } + + // std stream, no need to flush + return 0; + } + + int dll_ferror(FILE* stream) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByStream(stream); + if (pFile != NULL) + { + // unimplemented + return 0; + } + else if (IS_STD_STREAM(stream)) + return 0; + else + return ferror(stream); + } + + int dllvprintf(const char *format, va_list va) + { + std::string buffer = StringUtils::FormatV(format, va); + CLog::Log(LOGDEBUG, " msg: {}", buffer); + return buffer.length(); + } + + int dll_vfprintf(FILE *stream, const char *format, va_list va) + { + static char tmp[2048]; + + if (_vsnprintf(tmp, 2048, format, va) == -1) + { + CLog::Log(LOGWARNING, "dll_vfprintf: Data lost due to undersized buffer"); + } + tmp[2048 - 1] = 0; + + if (IS_STDOUT_STREAM(stream) || IS_STDERR_STREAM(stream) || !IS_VALID_STREAM(stream)) + { + CLog::Log(LOGINFO, " msg: {}", tmp); + return strlen(tmp); + } + else + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByStream(stream); + if (pFile != NULL) + { + int len = strlen(tmp); + // replace all '\n' occurrences with '\r\n'... + char tmp2[2048]; + int j = 0; + for (int i = 0; i < len; i++) + { + if (j == 2047) + { // out of space + if (i != len-1) + CLog::Log(LOGWARNING, "dll_fprintf: Data lost due to undersized buffer"); + break; + } + if (tmp[i] == '\n' && ((i > 0 && tmp[i-1] != '\r') || i == 0) && j < 2047 - 2) + { // need to add a \r + tmp2[j++] = '\r'; + tmp2[j++] = '\n'; + } + else + { // just add the character as-is + tmp2[j++] = tmp[i]; + } + } + // terminate string + tmp2[j] = 0; + len = strlen(tmp2); + pFile->Write(tmp2, len); + return len; + } + } + + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return strlen(tmp); + } + + int dll_fscanf(FILE* stream, const char* format, ...) + { + CLog::Log(LOGERROR, "{} is not implemented", __FUNCTION__); + return -1; + } + + int dll_fprintf(FILE* stream, const char* format, ...) + { + int res; + va_list va; + va_start(va, format); + res = dll_vfprintf(stream, format, va); + va_end(va); + return res; + } + + int dll_fgetpos(FILE* stream, fpos_t* pos) + { + fpos64_t tmpPos = {}; + int ret; + + ret = dll_fgetpos64(stream, &tmpPos); +#if !defined(TARGET_POSIX) || defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) || defined(TARGET_ANDROID) + *pos = (fpos_t)tmpPos; +#else + pos->__pos = (off_t)tmpPos.__pos; +#endif + return ret; + } + + int dll_fgetpos64(FILE *stream, fpos64_t *pos) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByStream(stream); + if (pFile != NULL) + { +#if !defined(TARGET_POSIX) || defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) || defined(TARGET_ANDROID) + *pos = pFile->GetPosition(); +#else + pos->__pos = pFile->GetPosition(); +#endif + return 0; + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return EINVAL; + } + + int dll_fsetpos64(FILE* stream, const fpos64_t* pos) + { + int fd = g_emuFileWrapper.GetDescriptorByStream(stream); + if (fd >= 0) + { +#if !defined(TARGET_POSIX) || defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) || defined(TARGET_ANDROID) + if (dll_lseeki64(fd, *pos, SEEK_SET) >= 0) +#else + if (dll_lseeki64(fd, (__off64_t)pos->__pos, SEEK_SET) >= 0) +#endif + { + return 0; + } + else + { + return EINVAL; + } + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return EINVAL; + } + + int dll_fsetpos(FILE* stream, const fpos_t* pos) + { + int fd = g_emuFileWrapper.GetDescriptorByStream(stream); + if (fd >= 0) + { + fpos64_t tmpPos; +#if !defined(TARGET_POSIX) || defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) || defined(TARGET_ANDROID) + tmpPos= *pos; +#else + tmpPos.__pos = (off64_t)(pos->__pos); +#endif + return dll_fsetpos64(stream, &tmpPos); + } + CLog::Log(LOGERROR, "{} emulated function failed", __FUNCTION__); + return EINVAL; + } + + int dll_fileno(FILE* stream) + { + int fd = g_emuFileWrapper.GetDescriptorByStream(stream); + if (fd >= 0) + { + return fd; + } + else if (IS_STDIN_STREAM(stream)) + { + return 0; + } + else if (IS_STDOUT_STREAM(stream)) + { + return 1; + } + else if (IS_STDERR_STREAM(stream)) + { + return 2; + } + else + { + return fileno(stream); + } + + return -1; + } + + void dll_clearerr(FILE* stream) + { + if (g_emuFileWrapper.StreamIsEmulatedFile(stream)) + { + // not implemented + } + } + + char* dll_strdup( const char* str) + { + char* pdup; + pdup = strdup(str); + return pdup; + } + + //Critical Section has been fixed in EMUkernel32.cpp + + int dll_initterm(PFV* start, const PFV* end) //pncrt.dll + { + PFV * temp; + for (temp = start; temp < end; temp ++) + if (*temp) + (*temp)(); //call initial function table. + return 0; + } + + //SLOW CODE SHOULD BE REVISED + int dll_stat(const char *path, struct stat *buffer) + { + if (!StringUtils::CompareNoCase(path, "shout://", 8)) // don't stat shoutcast + return -1; + if (!StringUtils::CompareNoCase(path, "mms://", 6)) // don't stat mms + return -1; + +#ifdef TARGET_POSIX + if (!StringUtils::CompareNoCase(path, "D:") || !StringUtils::CompareNoCase(path, "D:\\")) + { + buffer->st_mode = S_IFDIR; + return 0; + } +#endif + if (!StringUtils::CompareNoCase(path, "\\Device\\Cdrom0") || + !StringUtils::CompareNoCase(path, "\\Device\\Cdrom0\\")) + { + buffer->st_mode = _S_IFDIR; + return 0; + } + + struct __stat64 tStat; + if (CFile::Stat(path, &tStat) == 0) + { + CUtil::Stat64ToStat(buffer, &tStat); + return 0; + } + // errno is set by file.Stat(...) + return -1; + } + + int dll_stati64(const char *path, struct _stati64 *buffer) + { + struct __stat64 a; + memset(&a, 0, sizeof(a)); + + if(dll_stat64(path, &a) == 0) + { + CUtil::Stat64ToStatI64(buffer, &a); + return 0; + } + return -1; + } + + int dll_stat64(const char *path, struct __stat64 *buffer) + { + if (!StringUtils::CompareNoCase(path, "shout://", 8)) // don't stat shoutcast + return -1; + if (!StringUtils::CompareNoCase(path, "mms://", 6)) // don't stat mms + return -1; + +#ifdef TARGET_POSIX + if (!StringUtils::CompareNoCase(path, "D:") || !StringUtils::CompareNoCase(path, "D:\\")) + { + buffer->st_mode = _S_IFDIR; + return 0; + } +#endif + if (!StringUtils::CompareNoCase(path, "\\Device\\Cdrom0") || + !StringUtils::CompareNoCase(path, "\\Device\\Cdrom0\\")) + { + buffer->st_mode = _S_IFDIR; + return 0; + } + + return CFile::Stat(path, buffer); + } + +#ifdef TARGET_WINDOWS + int dll_stat64i32(const char *path, struct _stat64i32 *buffer) + { + struct __stat64 a; + if(dll_stat64(path, &a) == 0) + { + CUtil::Stat64ToStat64i32(buffer, &a); + return 0; + } + return -1; + } +#endif + + int dll_fstat(int fd, struct stat* buffer) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByDescriptor(fd); + if (pFile != NULL) + { + struct __stat64 tStat; + if (pFile->Stat(&tStat) == 0) + { + CUtil::Stat64ToStat(buffer, &tStat); + return 0; + } + } + else if (!IS_STD_DESCRIPTOR(fd)) + { + return fstat(fd, buffer); + } + + // fstat on stdin, stdout or stderr should fail + // this is what python expects + return -1; + } + + int dll_fstati64(int fd, struct _stati64 *buffer) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByDescriptor(fd); + if (pFile != NULL) + { + CLog::Log(LOGINFO, "Stating open file"); + + buffer->st_size = pFile->GetLength(); + buffer->st_mode = _S_IFREG; + return 0; + } + else if (!IS_STD_DESCRIPTOR(fd)) + { + CLog::Log(LOGWARNING, "msvcrt.dll: dll_fstati64 called, TODO: add 'int64 <-> long' type checking"); //warning + // need to use fstat and convert everything + struct stat temp; + int res = fstat(fd, &temp); + if (res == 0) + { + CUtil::StatToStatI64(buffer, &temp); + } + return res; + } + + // fstat on stdin, stdout or stderr should fail + // this is what python expects + return -1; + } + +#ifdef TARGET_WINDOWS + int dll_fstat64i32(int fd, struct _stat64i32 *buffer) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByDescriptor(fd); + if (pFile != NULL) + { + struct __stat64 tStat = {}; + if (pFile->Stat(&tStat) == 0) + { + CUtil::Stat64ToStat64i32(buffer, &tStat); + return 0; + } + return -1; + } + else if (!IS_STD_DESCRIPTOR(fd)) + { + CLog::Log(LOGWARNING, "msvcrt.dll: dll_fstati64 called, TODO: add 'int64 <-> long' type checking"); //warning + // need to use fstat and convert everything + struct __stat64 temp; + int res = _fstat64(fd, &temp); + if (res == 0) + { + CUtil::Stat64ToStat64i32(buffer, &temp); + } + return res; + } + + // fstat on stdin, stdout or stderr should fail + // this is what python expects + return -1; + } +#endif + + int dll_setmode ( int handle, int mode ) + { + not_implement("msvcrt.dll fake function dll_setmode() called\n"); + return -1; + } + + void dllperror(const char* s) + { + if (s) + { + CLog::Log(LOGERROR, "perror: {}", s); + } + } + + char* dllstrerror(int iErr) + { + static char szError[32]; + sprintf(szError, "err:%i", iErr); + return (char*)szError; + } + + int dll_mkdir(const char* dir) + { + if (!dir) return -1; + + // Make sure the slashes are correct & translate the path + std::string strPath = CUtil::ValidatePath(CSpecialProtocol::TranslatePath(dir)); +#ifndef TARGET_POSIX + std::wstring strWPath; + g_charsetConverter.utf8ToW(strPath, strWPath, false); + return _wmkdir(strWPath.c_str()); +#else + return mkdir(strPath.c_str(), 0755); +#endif + } + + const char* dll_getcwd(char *buffer, int maxlen) + { + not_implement("msvcrt.dll fake function dll_getcwd() called\n"); + return "special://xbmc/"; + } + + int dll_putenv(const char* envstring) + { + bool added = false; + + if (envstring != NULL) + { + const char *value_start = strchr(envstring, '='); + + if (value_start != NULL) + { + char var[64]; + int size = strlen(envstring) + 1; + char *value = (char*)malloc(size); + + if (!value) + return -1; + value[0] = 0; + + memcpy(var, envstring, value_start - envstring); + var[value_start - envstring] = 0; + char* temp = var; + while (*temp) + { + *temp = (char)toupper(*temp); + temp++; + } + + strncpy(value, value_start + 1, size); + if (size) + value[size - 1] = '\0'; + + { + std::unique_lock<CCriticalSection> lock(dll_cs_environ); + + char** free_position = NULL; + for (int i = 0; i < EMU_MAX_ENVIRONMENT_ITEMS && free_position == NULL; i++) + { + if (dll__environ[i] != NULL) + { + // we only support overwriting the old values + if (StringUtils::CompareNoCase(dll__environ[i], var, strlen(var)) == 0) + { + // free it first + free(dll__environ[i]); + dll__environ[i] = NULL; + free_position = &dll__environ[i]; + } + } + else + { + free_position = &dll__environ[i]; + } + } + + if (free_position != NULL) + { + // free position, copy value + size = strlen(var) + strlen(value) + 2; + *free_position = (char*)malloc(size); // for '=' and 0 termination + if ((*free_position)) + { + strncpy(*free_position, var, size); + (*free_position)[size - 1] = '\0'; + strncat(*free_position, "=", size - strlen(*free_position)); + strncat(*free_position, value, size - strlen(*free_position)); + added = true; + } + } + + } + + free(value); + } + } + + return added ? 0 : -1; + } + + char* dll_getenv(const char* szKey) + { + char* value = NULL; + + { + std::unique_lock<CCriticalSection> lock(dll_cs_environ); + + update_emu_environ();//apply any changes + + for (int i = 0; i < EMU_MAX_ENVIRONMENT_ITEMS && value == NULL; i++) + { + if (dll__environ[i]) + { + if (StringUtils::CompareNoCase(dll__environ[i], szKey, strlen(szKey)) == 0) + { + // found it + value = dll__environ[i] + strlen(szKey) + 1; + } + } + } + } + + if (value != NULL) + { + return value; + } + + return NULL; + } + + int dll_ctype(int i) + { + not_implement("msvcrt.dll fake function dll_ctype() called\n"); + return 0; + } + + int dll_system(const char *command) + { + not_implement("msvcrt.dll fake function dll_system() called\n"); + return 0; //system(command); + } + + void (__cdecl * dll_signal(int sig, void (__cdecl *func)(int)))(int) + { +#if defined(TARGET_WINDOWS) + //vs2008 asserts for known signals, return err for everything unknown to windows. + if (sig == 5 || sig == 7 || sig == 9 || sig == 10 || sig == 12 || sig == 14 || sig == 18 || sig == 19 || sig == 20) + return SIG_ERR; +#endif + return signal(sig, func); + } + + int dll_getpid() + { + return 1; + } + + int dll__commit(int fd) + { + CFile* pFile = g_emuFileWrapper.GetFileXbmcByDescriptor(fd); + if (pFile != NULL) + { + pFile->Flush(); + return 0; + } + else if (!IS_STD_DESCRIPTOR(fd)) + { + // it might be something else than a file, or the file is not emulated + // let the operating system handle it +#ifndef TARGET_POSIX + return _commit(fd); +#else + return fsync(fd); +#endif + } + + // std stream, no need to flush + return 0; + } + + char*** dll___p__environ() + { + static char*** t = &dll__environ; + return (char***)&t; + } + +#ifdef TARGET_POSIX +#if defined(TARGET_ANDROID) + volatile int * __cdecl dll_errno(void) + { + return &errno; + } +#else + int * __cdecl dll_errno(void) + { + return &errno; + } +#endif + + int __cdecl dll_ioctl(int fd, unsigned long int request, va_list va) + { + int ret; + CFile* pFile = g_emuFileWrapper.GetFileXbmcByDescriptor(fd); + if (!pFile) + return -1; + +#if defined(HAS_DVD_DRIVE) && !defined(TARGET_FREEBSD) +#if !defined(TARGET_DARWIN) + if(request == DVD_READ_STRUCT || request == DVD_AUTH) +#else + if(request == DKIOCDVDSENDKEY || request == DKIOCDVDREPORTKEY || request == DKIOCDVDREADSTRUCTURE) +#endif + { + void *p1 = va_arg(va, void*); + SNativeIoControl d; + d.request = request; + d.param = p1; + ret = pFile->IoControl(IOCTRL_NATIVE, &d); + if(ret<0) + CLog::Log(LOGWARNING, "{} - {} request failed with error [{}] {}", __FUNCTION__, request, + errno, strerror(errno)); + } + else +#endif + { + CLog::Log(LOGWARNING, "{} - Unknown request type {}", __FUNCTION__, request); + ret = -1; + } + return ret; + } +#endif + + int dll_setvbuf(FILE *stream, char *buf, int type, size_t size) + { + CLog::Log(LOGWARNING, "{} - May not be implemented correctly", __FUNCTION__); + return 0; + } + + struct mntent *dll_getmntent(FILE *fp) + { + if (!fp) + return nullptr; + +#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) + struct mntent* mountPoint = getmntent(fp); + if (mountPoint) + return mountPoint; + + // warn if this is a kodi vfs file not associated with a mountpoint + CFile* pFile = g_emuFileWrapper.GetFileXbmcByStream(fp); + if (pFile) + { + CLog::LogF(LOGWARNING, "getmntent is not implemented for our virtual filesystem"); + } + return nullptr; +#else + CLog::LogF(LOGWARNING, "Unimplemented function called"); + return nullptr; +#endif + } + + struct mntent* dll_getmntent_r(FILE* fp, struct mntent* result, char* buffer, int bufsize) + { + if (!fp || !result || !buffer) + return nullptr; + +#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) + struct mntent* mountPoint = getmntent_r(fp, result, buffer, bufsize); + if (mountPoint) + return mountPoint; +#endif + return nullptr; + } + + // this needs to be wrapped, since dll's have their own file + // descriptor list, but we always use app's list with our wrappers + int __cdecl dll_open_osfhandle(intptr_t _OSFileHandle, int _Flags) + { +#ifdef TARGET_WINDOWS + return _open_osfhandle(_OSFileHandle, _Flags); +#else + return -1; +#endif + } + +} diff --git a/xbmc/cores/DllLoader/exports/emu_msvcrt.h b/xbmc/cores/DllLoader/exports/emu_msvcrt.h new file mode 100644 index 0000000..cee233b --- /dev/null +++ b/xbmc/cores/DllLoader/exports/emu_msvcrt.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <stdint.h> +#include <stdio.h> + +#include "PlatformDefs.h" + +#ifdef TARGET_POSIX +#define _onexit_t void* +#endif + +#if defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) || defined(TARGET_ANDROID) +typedef off_t __off_t; +typedef int64_t off64_t; +typedef off64_t __off64_t; +typedef fpos_t fpos64_t; +#endif + +#ifdef TARGET_WINDOWS +#include "platform/win32/dirent.h" +#else +#include <dirent.h> +#endif + +typedef void ( *PFV)(void); + +#define __IS_STDIN_STREAM(stream) (stream == stdin || fileno(stream) == fileno(stdin) || fileno(stream) == 0) +#define __IS_STDOUT_STREAM(stream) (stream == stdout || fileno(stream) == fileno(stdout) || fileno(stream) == 1) +#define __IS_STDERR_STREAM(stream) (stream == stderr || fileno(stream) == fileno(stderr) || fileno(stream) == 2) +#define IS_STDIN_STREAM(stream) (stream != NULL && __IS_STDIN_STREAM(stream)) +#define IS_STDOUT_STREAM(stream) (stream != NULL && __IS_STDOUT_STREAM(stream)) +#define IS_STDERR_STREAM(stream) (stream != NULL && __IS_STDERR_STREAM(stream)) +#define IS_VALID_STREAM(stream) (stream != nullptr) + + +#define IS_STD_STREAM(stream) (stream != NULL && (__IS_STDIN_STREAM(stream) || __IS_STDOUT_STREAM(stream) || __IS_STDERR_STREAM(stream))) + +#define IS_STDIN_DESCRIPTOR(fd) (fd == 0) +#define IS_STDOUT_DESCRIPTOR(fd) (fd == 1) +#define IS_STDERR_DESCRIPTOR(fd) (fd == 2) + +#define IS_STD_DESCRIPTOR(fd) (IS_STDIN_DESCRIPTOR(fd) || IS_STDOUT_DESCRIPTOR(fd) || IS_STDERR_DESCRIPTOR(fd)) + + +extern "C" +{ + char* dll_strdup( const char* str); + void dll_sleep(unsigned long imSec); + void InitFiles(); + void dllReleaseAll( ); + void* dllmalloc(size_t size); + void dllfree( void* pPtr ); + void* dllcalloc( size_t num, size_t size ); + void* dllrealloc( void *memblock, size_t size ); + void dllexit(int iCode); + void dllabort(); + void* dll__dllonexit(PFV input, PFV ** start, PFV ** end); + _onexit_t dll_onexit(_onexit_t func); + int dllputs(const char* szLine); + int dll_putchar(int c); + int dll_putc(int c, FILE *stream); + int dllprintf( const char *format, ... ); + int dllvprintf(const char *format, va_list va); + char *dll_fullpath(char *absPath, const char *relPath, size_t maxLength); + FILE *dll_popen(const char *command, const char *mode); + int dll_pclose(FILE *stream); + FILE* dll_fdopen(int i, const char* file); + int dll_open(const char* szFileName, int iMode); + int dll_read(int fd, void* buffer, unsigned int uiSize); + int dll_write(int fd, const void* buffer, unsigned int uiSize); + int dll_close(int fd); + __off64_t dll_lseeki64(int fd, __off64_t lPos, int iWhence); + __off_t dll_lseek(int fd, __off_t lPos, int iWhence); + char* dll_getenv(const char* szKey); + int dll_fclose (FILE * stream); +#ifndef TARGET_POSIX + intptr_t dll_findfirst(const char *file, struct _finddata_t *data); + int dll_findnext(intptr_t f, _finddata_t* data); + int dll_findclose(intptr_t handle); + intptr_t dll_findfirst64i32(const char *file, struct _finddata64i32_t *data); + int dll_findnext64i32(intptr_t f, _finddata64i32_t* data); + void dll__security_error_handler(int code, void *data); +#endif + DIR *dll_opendir(const char *filename); + struct dirent *dll_readdir(DIR *dirp); + int dll_closedir(DIR *dirp); + void dll_rewinddir(DIR *dirp); + char * dll_fgets (char* pszString, int num , FILE * stream); + int dll_fgetc (FILE* stream); + int dll_feof (FILE * stream); + int dll_fread (void * buffer, size_t size, size_t count, FILE * stream); + int dll_getc (FILE * stream); + FILE * dll_fopen(const char * filename, const char * mode); + int dll_fopen_s(FILE** pFile, const char * filename, const char * mode); + int dll_fputc (int character, FILE * stream); + int dll_putcchar (int character); + int dll_fputs (const char * szLine , FILE* stream); + int dll_fseek ( FILE * stream , long offset , int origin ); + int dll_fseek64(FILE *stream, off64_t offset, int origin); + int dll_ungetc (int c, FILE * stream); + long dll_ftell(FILE *stream); + off64_t dll_ftell64(FILE *stream); + long dll_tell ( int fd ); + long long dll_telli64 ( int fd ); + size_t dll_fwrite ( const void * buffer, size_t size, size_t count, FILE * stream ); + int dll_fflush (FILE * stream); + int dll_ferror (FILE * stream); + int dll_vfprintf(FILE *stream, const char *format, va_list va); + int dll_fprintf(FILE* stream , const char * format, ...); + int dll_fgetpos(FILE* stream, fpos_t* pos); + int dll_fgetpos64(FILE *stream, fpos64_t *pos); + int dll_fsetpos(FILE* stream, const fpos_t* pos); + int dll_fsetpos64(FILE* stream, const fpos64_t* pos); + int dll_fileno(FILE* stream); + void dll_rewind(FILE* stream); + void dll_clearerr(FILE* stream); + int dll_initterm(PFV* start, const PFV* end); + uintptr_t dll_beginthread(void( *start_address )( void * ),unsigned stack_size,void *arglist); + int dll_stati64(const char *path, struct _stati64 *buffer); + int dll_stat64(const char *path, struct __stat64 *buffer); +#ifdef TARGET_WINDOWS + int dll_stat64i32(const char *path, struct _stat64i32 *buffer); +#endif + int dll_stat(const char *path, struct stat *buffer); + int dll_fstat(int fd, struct stat *buffer); + int dll_fstati64(int fd, struct _stati64 *buffer); + int dll_setmode(int handle, int mode ); + void dllperror(const char* s); + char* dllstrerror(int iErr); + int dll_mkdir(const char* dir); + const char* dll_getcwd(char *buffer, int maxlen); + int dll_putenv(const char* envstring); + int dll_ctype(int i); + int dll_system(const char *command); + void (__cdecl * dll_signal(int sig, void (__cdecl *func)(int)))(int); + int dll_getpid(); + int dll__commit(int fd); + char*** dll___p__environ(); + FILE* dll_freopen(const char *path, const char *mode, FILE *stream); + int dll_fscanf(FILE *stream, const char *format , ...); + void dll_flockfile(FILE *file); + int dll_ftrylockfile(FILE *file); + void dll_funlockfile(FILE *file); + int dll_fstat64(int fd, struct __stat64 *buf); +#ifdef TARGET_WINDOWS + int dll_fstat64i32(int fd, struct _stat64i32 *buffer); + int dll_open_osfhandle(intptr_t _OSFileHandle, int _Flags); +#endif + int dll_setvbuf(FILE *stream, char *buf, int type, size_t size); + +#if defined(TARGET_ANDROID) + volatile int * __cdecl dll_errno(void); +#elif defined(TARGET_POSIX) + int * __cdecl dll_errno(void); +#endif + + extern char **dll__environ; +} + diff --git a/xbmc/cores/DllLoader/exports/kernel32.def b/xbmc/cores/DllLoader/exports/kernel32.def new file mode 100755 index 0000000..b23a9be --- /dev/null +++ b/xbmc/cores/DllLoader/exports/kernel32.def @@ -0,0 +1,25 @@ +LIBRARY kernel32.dll + +VERSION 1.0 + +EXPORTS + AddAtomA + FindAtomA + GetAtomNameA + CreateThread + FindClose + FindFirstFileA + FindNextFileA + GetFileAttributesA + GetLastError + GetModuleFileNameA + GetNumberOfConsoleInputEvents + GetStdHandle + ReadConsoleInputA + SetUnhandledExceptionFilter + Sleep + TerminateThread + GetCurrentThread + QueryPerformanceCounter + QueryPerformanceFrequency + SetThreadPriority
\ No newline at end of file diff --git a/xbmc/cores/DllLoader/exports/msvcrt.def b/xbmc/cores/DllLoader/exports/msvcrt.def new file mode 100755 index 0000000..73a63b4 --- /dev/null +++ b/xbmc/cores/DllLoader/exports/msvcrt.def @@ -0,0 +1,123 @@ +LIBRARY msvcrt.dll + +VERSION 1.0 + +EXPORTS + _close + _lseek + _read + _write + __dllonexit + __mb_cur_max + _assert + _errno + _ftime + _iob + _isctype + _lseeki64 + _open + _snprintf + _stricmp + _strnicmp + _vsnprintf + abort + atof + atoi + cos + cosh + exp + fflush + floor + fprintf + free + frexp + fwrite + gmtime + ldexp + localtime + log + log10 + malloc + memcpy + memmove + memset + mktime + perror + printf + putchar + puts + qsort + realloc + sin + sinh + sprintf + sqrt + sscanf + strchr + strcmp + strcpy + strlen + strncpy + strrchr + strtod + strtok + strtol + strtoul + tan + tanh + time + toupper + _memccpy + _fstat + _memccpy + _mkdir + _pclose + _popen + _sleep + _stat + _strdup + _swab + _findclose + _findfirst + _findnext + _fullpath + _pctype + calloc + ceil + ctime + exit + fclose + feof + fgets + fopen + fputc + fputs + fread + fseek + ftell + getc + getenv + putc + rand + remove + rewind + setlocale + signal + srand + strcat + strcoll + strerror + strncat + strncmp + strpbrk + strstr + tolower + strspn + strcspn + fsetpos + fgetpos + _fstati64 + _stati64 + _tell + _telli64 + _setmode
\ No newline at end of file diff --git a/xbmc/cores/DllLoader/exports/pncrt.def b/xbmc/cores/DllLoader/exports/pncrt.def new file mode 100755 index 0000000..ba34b22 --- /dev/null +++ b/xbmc/cores/DllLoader/exports/pncrt.def @@ -0,0 +1,114 @@ +LIBRARY msvcrt.dll + +VERSION 1.0 + +EXPORTS + _close + _lseek + _read + _write + __dllonexit + __mb_cur_max + _assert + _errno + _ftime + _iob + _isctype + _lseeki64 + _open + _snprintf + _stricmp + _strnicmp + _vsnprintf + abort + atof + atoi + cos + cosh + exp + fflush + floor + fprintf + free + frexp + fwrite + gmtime + ldexp + localtime + log + log10 + malloc + memcpy + memmove + memset + mktime + perror + printf + putchar + puts + qsort + realloc + sin + sinh + sprintf + sqrt + sscanf + strchr + strcmp + strcpy + strlen + strncpy + strrchr + strtod + strtok + strtol + strtoul + tan + tanh + time + toupper + _memccpy + _fstat + _memccpy + _mkdir + _pclose + _popen + _sleep + _stat + _strdup + _swab + _findclose + _findfirst + _findnext + _fullpath + _pctype + calloc + ceil + ctime + exit + fclose + feof + fgets + fopen + fputc + fputs + fread + fseek + ftell + getc + getenv + putc + rand + remove + rewind + setlocale + signal + srand + strcat + strcoll + strerror + strncat + strncmp + strpbrk + strstr + tolower
\ No newline at end of file diff --git a/xbmc/cores/DllLoader/exports/util/CMakeLists.txt b/xbmc/cores/DllLoader/exports/util/CMakeLists.txt new file mode 100644 index 0000000..3fa6f31 --- /dev/null +++ b/xbmc/cores/DllLoader/exports/util/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SOURCES EmuFileWrapper.cpp) + +set(HEADERS EmuFileWrapper.h) + +core_add_library(exports_utils) diff --git a/xbmc/cores/DllLoader/exports/util/EmuFileWrapper.cpp b/xbmc/cores/DllLoader/exports/util/EmuFileWrapper.cpp new file mode 100644 index 0000000..40d6b61 --- /dev/null +++ b/xbmc/cores/DllLoader/exports/util/EmuFileWrapper.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "EmuFileWrapper.h" + +#include "filesystem/File.h" + +#include <mutex> + +CEmuFileWrapper g_emuFileWrapper; + +namespace +{ + +constexpr bool isValidFilePtr(const FILE* f) +{ + return (f != nullptr); +} + +} +CEmuFileWrapper::CEmuFileWrapper() +{ + // since we always use dlls we might just initialize it directly + for (EmuFileObject& file : m_files) + { + memset(&file, 0, sizeof(EmuFileObject)); + file.used = false; + file.fd = -1; + } +} + +CEmuFileWrapper::~CEmuFileWrapper() +{ + CleanUp(); +} + +void CEmuFileWrapper::CleanUp() +{ + std::unique_lock<CCriticalSection> lock(m_criticalSection); + for (EmuFileObject& file : m_files) + { + if (file.used) + { + file.file_xbmc->Close(); + delete file.file_xbmc; + + if (file.file_lock) + { + delete file.file_lock; + file.file_lock = nullptr; + } + file.used = false; + file.fd = -1; + } + } +} + +EmuFileObject* CEmuFileWrapper::RegisterFileObject(XFILE::CFile* pFile) +{ + EmuFileObject* object = nullptr; + + std::unique_lock<CCriticalSection> lock(m_criticalSection); + + for (int i = 0; i < MAX_EMULATED_FILES; i++) + { + if (!m_files[i].used) + { + // found a free location + object = &m_files[i]; + object->used = true; + object->file_xbmc = pFile; + object->fd = (i + FILE_WRAPPER_OFFSET); + object->file_lock = new CCriticalSection(); + break; + } + } + + return object; +} + +void CEmuFileWrapper::UnRegisterFileObjectByDescriptor(int fd) +{ + int i = fd - FILE_WRAPPER_OFFSET; + if (! (i >= 0 && i < MAX_EMULATED_FILES)) + return; + + if (!m_files[i].used) + return; + + std::unique_lock<CCriticalSection> lock(m_criticalSection); + + // we assume the emulated function already deleted the CFile object + if (m_files[i].file_lock) + { + delete m_files[i].file_lock; + m_files[i].file_lock = nullptr; + } + m_files[i].used = false; + m_files[i].fd = -1; +} + +void CEmuFileWrapper::UnRegisterFileObjectByStream(FILE* stream) +{ + if (isValidFilePtr(stream)) + { + EmuFileObject* o = reinterpret_cast<EmuFileObject*>(stream); + return UnRegisterFileObjectByDescriptor(o->fd); + } +} + +void CEmuFileWrapper::LockFileObjectByDescriptor(int fd) +{ + int i = fd - FILE_WRAPPER_OFFSET; + if (i >= 0 && i < MAX_EMULATED_FILES) + { + if (m_files[i].used) + { + m_files[i].file_lock->lock(); + } + } +} + +bool CEmuFileWrapper::TryLockFileObjectByDescriptor(int fd) +{ + int i = fd - FILE_WRAPPER_OFFSET; + if (i >= 0 && i < MAX_EMULATED_FILES) + { + if (m_files[i].used) + { + return m_files[i].file_lock->try_lock(); + } + } + return false; +} + +void CEmuFileWrapper::UnlockFileObjectByDescriptor(int fd) +{ + int i = fd - FILE_WRAPPER_OFFSET; + if (i >= 0 && i < MAX_EMULATED_FILES) + { + if (m_files[i].used) + { + m_files[i].file_lock->unlock(); + } + } +} + +EmuFileObject* CEmuFileWrapper::GetFileObjectByDescriptor(int fd) +{ + int i = fd - FILE_WRAPPER_OFFSET; + if (i >= 0 && i < MAX_EMULATED_FILES) + { + if (m_files[i].used) + { + return &m_files[i]; + } + } + return nullptr; +} + +EmuFileObject* CEmuFileWrapper::GetFileObjectByStream(FILE* stream) +{ + if (isValidFilePtr(stream)) + { + EmuFileObject* o = reinterpret_cast<EmuFileObject*>(stream); + return GetFileObjectByDescriptor(o->fd); + } + + return nullptr; +} + +XFILE::CFile* CEmuFileWrapper::GetFileXbmcByDescriptor(int fd) +{ + auto object = GetFileObjectByDescriptor(fd); + if (object != nullptr && object->used) + { + return object->file_xbmc; + } + return nullptr; +} + +XFILE::CFile* CEmuFileWrapper::GetFileXbmcByStream(FILE* stream) +{ + if (isValidFilePtr(stream)) + { + EmuFileObject* object = reinterpret_cast<EmuFileObject*>(stream); + if (object != nullptr && object->used) + { + return object->file_xbmc; + } + } + return nullptr; +} + +int CEmuFileWrapper::GetDescriptorByStream(FILE* stream) +{ + if (isValidFilePtr(stream)) + { + EmuFileObject* obj = reinterpret_cast<EmuFileObject*>(stream); + int i = obj->fd - FILE_WRAPPER_OFFSET; + if (i >= 0 && i < MAX_EMULATED_FILES) + { + return i + FILE_WRAPPER_OFFSET; + } + } + return -1; +} + +FILE* CEmuFileWrapper::GetStreamByDescriptor(int fd) +{ + auto object = GetFileObjectByDescriptor(fd); + if (object != nullptr && object->used) + { + return reinterpret_cast<FILE*>(object); + } + return nullptr; +} + +bool CEmuFileWrapper::StreamIsEmulatedFile(FILE* stream) +{ + if (isValidFilePtr(stream)) + { + EmuFileObject* obj = reinterpret_cast<EmuFileObject*>(stream); + return DescriptorIsEmulatedFile(obj->fd); + } + return false; +} diff --git a/xbmc/cores/DllLoader/exports/util/EmuFileWrapper.h b/xbmc/cores/DllLoader/exports/util/EmuFileWrapper.h new file mode 100644 index 0000000..a8a70fa --- /dev/null +++ b/xbmc/cores/DllLoader/exports/util/EmuFileWrapper.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "threads/CriticalSection.h" + +#include <stdio.h> + +#if defined(TARGET_POSIX) && !defined(TARGET_DARWIN) && !defined(TARGET_FREEBSD) && !defined(TARGET_ANDROID) && !defined(__UCLIBC__) +#define _file _fileno +#elif defined(__UCLIBC__) +#define _file __filedes +#endif + +#define MAX_EMULATED_FILES 50 +#define FILE_WRAPPER_OFFSET 0x00000200 + +namespace XFILE +{ + class CFile; +} + +typedef struct stEmuFileObject +{ + XFILE::CFile* file_xbmc; + CCriticalSection *file_lock; + int mode; + //Stick this last to avoid 3-7 bytes of padding + bool used; + int fd; +} EmuFileObject; + +class CEmuFileWrapper +{ +public: + CEmuFileWrapper(); + ~CEmuFileWrapper(); + + /** + * Only to be called when shutting down xbmc + */ + void CleanUp(); + + EmuFileObject* RegisterFileObject(XFILE::CFile* pFile); + void UnRegisterFileObjectByDescriptor(int fd); + void UnRegisterFileObjectByStream(FILE* stream); + void LockFileObjectByDescriptor(int fd); + bool TryLockFileObjectByDescriptor(int fd); + void UnlockFileObjectByDescriptor(int fd); + EmuFileObject* GetFileObjectByDescriptor(int fd); + EmuFileObject* GetFileObjectByStream(FILE* stream); + XFILE::CFile* GetFileXbmcByDescriptor(int fd); + XFILE::CFile* GetFileXbmcByStream(FILE* stream); + static int GetDescriptorByStream(FILE* stream); + FILE* GetStreamByDescriptor(int fd); + static constexpr bool DescriptorIsEmulatedFile(int fd) + { + return fd >= FILE_WRAPPER_OFFSET && fd < FILE_WRAPPER_OFFSET + MAX_EMULATED_FILES; + } + static bool StreamIsEmulatedFile(FILE* stream); +private: + EmuFileObject m_files[MAX_EMULATED_FILES]; + CCriticalSection m_criticalSection; +}; + +extern CEmuFileWrapper g_emuFileWrapper; + diff --git a/xbmc/cores/DllLoader/exports/winMM.def b/xbmc/cores/DllLoader/exports/winMM.def new file mode 100755 index 0000000..c389b3d --- /dev/null +++ b/xbmc/cores/DllLoader/exports/winMM.def @@ -0,0 +1,6 @@ +LIBRARY WINMM.DLL + +VERSION 1.0 + +EXPORTS + timeGetTime
\ No newline at end of file diff --git a/xbmc/cores/DllLoader/exports/wrapper.c b/xbmc/cores/DllLoader/exports/wrapper.c new file mode 100644 index 0000000..455ec0b --- /dev/null +++ b/xbmc/cores/DllLoader/exports/wrapper.c @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +// +// To recreate wrapper.def: +// +// bash# (echo -n "-Wl"; grep __wrap wrapper.c | grep -v bash | sed "s/.*__wrap_//g" | sed "s/(.*//g" | awk '{printf(",-wrap,%s",$0);}') > wrapper.def +// +#include <sys/types.h> +#include <sys/stat.h> +#if !defined(TARGET_ANDROID) +#include <sys/statvfs.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> +#include <dirent.h> +#include <dlfcn.h> + +#if defined(TARGET_DARWIN) || defined(TARGET_FREEBSD) || defined(TARGET_ANDROID) +typedef off_t __off_t; +typedef int64_t off64_t; +typedef off64_t __off64_t; +typedef fpos_t fpos64_t; +#define stat64 stat +#endif + +#ifdef TARGET_POSIX +#define _stat stat +#endif + +struct mntent; + +void* dllmalloc(size_t ); +void* dllcalloc( size_t , size_t ); +void* dllrealloc(void*, size_t); +void dllfree(void*); + +int dll_open(const char* szFileName, int iMode); +int dll_write(int fd, const void* buffer, unsigned int uiSize); +int dll_read(int fd, void* buffer, unsigned int uiSize); +off_t dll_lseek(int fd, __off_t lPos, int iWhence); +__off64_t dll_lseeki64(int fd, __off64_t lPos, int iWhence); +int dll_close(int fd); + +FILE * dll_fopen (const char * filename, const char * mode); +FILE* dll_freopen(const char *path, const char *mode, FILE *stream); +FILE* dll_fdopen(int i, const char* file); +int dll_fclose (FILE * stream); +int dll_ferror (FILE * stream); +int dll_feof (FILE * stream); +int dll_fileno(FILE* stream); +void dll_clearerr(FILE* stream); +int dll_fread (void * buffer, size_t size, size_t count, FILE * stream); +size_t dll_fwrite ( const void * buffer, size_t size, size_t count, FILE * stream ); +int dll_fflush (FILE * stream); +int dll_fputc (int character, FILE * stream); +int dll_fputs (const char * szLine , FILE* stream); +int dll_putc(int c, FILE *stream); +int dll_fseek ( FILE * stream , long offset , int origin ); +int dll_fseek64(FILE *stream, off64_t offset, int origin); +long dll_ftell(FILE *stream); +off64_t dll_ftell64(FILE *stream); +void dll_rewind(FILE* stream); +int dll_fgetpos(FILE* stream, fpos_t* pos); +int dll_fgetpos64(FILE *stream, fpos64_t *pos); +int dll_fsetpos(FILE* stream, const fpos_t* pos); +int dll_fsetpos64(FILE* stream, const fpos64_t* pos); +DIR* dll_opendir(const char* name); +struct dirent* dll_readdir(DIR* dirp); +int dll_closedir(DIR* dirp); +void dll_rewinddir(DIR* dirp); +int dll_fprintf(FILE* stream , const char * format, ...); +int dllprintf(const char *format, ...); +int dll_vfprintf(FILE *stream, const char *format, va_list va); +int dll_fgetc (FILE* stream); +char * dll_fgets (char* pszString, int num , FILE * stream); +int dll_getc (FILE * stream); +int dll_ungetc (int c, FILE * stream); +int dll_ioctl(int d, unsigned long int request, va_list va); +int dll_stat(const char *path, struct _stat *buffer); +int dll_stat64(const char *path, struct stat64 *buffer); +void dll_flockfile(FILE *file); +int dll_ftrylockfile(FILE *file); +void dll_funlockfile(FILE *file); +int dll_fstat64(int fd, struct stat64 *buf); +int dll_fstat(int fd, struct _stat *buf); +FILE* dll_popen(const char *command, const char *mode); +void* dll_dlopen(const char *filename, int flag); +int dll_setvbuf(FILE *stream, char *buf, int type, size_t size); +struct mntent *dll_getmntent(FILE *fp); +struct mntent* dll_getmntent_r(FILE* fp, struct mntent* result, char* buffer, int bufsize); + +void *__wrap_dlopen(const char *filename, int flag) +{ + return dlopen(filename, flag); +} + +FILE *__wrap_popen(const char *command, const char *mode) +{ + return dll_popen(command, mode); +} + +void* __wrap_calloc( size_t num, size_t size ) +{ + return dllcalloc(num, size); +} + +void* __wrap_malloc(size_t size) +{ + return dllmalloc(size); +} + +void* __wrap_realloc( void *memblock, size_t size ) +{ + return dllrealloc(memblock, size); +} + +void __wrap_free( void* pPtr ) +{ + dllfree(pPtr); +} + +int __wrap_open(const char *file, int oflag, ...) +{ + return dll_open(file, oflag); +} + +int __wrap_open64(const char *file, int oflag, ...) +{ + return dll_open(file, oflag); +} + +int __wrap_close(int fd) +{ + return dll_close(fd); +} + +ssize_t __wrap_write(int fd, const void *buf, size_t count) +{ + return dll_write(fd, buf, count); +} + +ssize_t __wrap_read(int fd, void *buf, size_t count) +{ + return dll_read(fd, buf, count); +} + +__off_t __wrap_lseek(int filedes, __off_t offset, int whence) +{ + return dll_lseek(filedes, offset, whence); +} + +__off64_t __wrap_lseek64(int filedes, __off64_t offset, int whence) +{ + __off64_t seekRes = dll_lseeki64(filedes, offset, whence); + return seekRes; +} + +int __wrap_fclose(FILE *fp) +{ + return dll_fclose(fp); +} + +int __wrap_ferror(FILE *stream) +{ + return dll_ferror(stream); +} + +void __wrap_clearerr(FILE *stream) +{ + dll_clearerr(stream); +} + +int __wrap_feof(FILE *stream) +{ + return dll_feof(stream); +} + +int __wrap_fileno(FILE *stream) +{ + return dll_fileno(stream); +} + +FILE *__wrap_fopen(const char *path, const char *mode) +{ + return dll_fopen(path, mode); +} + +FILE *__wrap_fopen64(const char *path, const char *mode) +{ + return dll_fopen(path, mode); +} + +FILE *__wrap_fdopen(int filedes, const char *mode) +{ + return dll_fdopen(filedes, mode); +} + +FILE *__wrap_freopen(const char *path, const char *mode, FILE *stream) +{ + return dll_freopen(path, mode, stream); +} + +size_t __wrap_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + return dll_fread(ptr, size, nmemb, stream); +} + +size_t __wrap_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + return dll_fwrite(ptr, size, nmemb, stream); +} + +int __wrap_fflush(FILE *stream) +{ + return dll_fflush(stream); +} + +int __wrap_fputc(int c, FILE *stream) +{ + return dll_fputc(c, stream); +} + +int __wrap_fputs(const char *s, FILE *stream) +{ + return dll_fputs(s, stream); +} + +int __wrap__IO_putc(int c, FILE *stream) +{ + return dll_putc(c, stream); +} + +int __wrap_fseek(FILE *stream, long offset, int whence) +{ + return dll_fseek(stream, offset, whence); +} + +int __wrap_fseeko64(FILE *stream, off64_t offset, int whence) +{ + return dll_fseek64(stream, offset, whence); +} + +long __wrap_ftell(FILE *stream) +{ + return dll_ftell(stream); +} + +off64_t __wrap_ftello64(FILE *stream) +{ + return dll_ftell64(stream); +} + +void __wrap_rewind(FILE *stream) +{ + dll_rewind(stream); +} + +int __wrap_fgetpos(FILE *stream, fpos_t *pos) +{ + return dll_fgetpos(stream, pos); +} + +int __wrap_fgetpos64(FILE *stream, fpos64_t *pos) +{ + return dll_fgetpos64(stream, pos); +} + +int __wrap_fsetpos(FILE *stream, fpos_t *pos) +{ + return dll_fsetpos(stream, pos); +} + +int __wrap_fsetpos64(FILE *stream, fpos64_t *pos) +{ + return dll_fsetpos64(stream, pos); +} + +DIR * __wrap_opendir(const char *name) +{ + return dll_opendir(name); +} + +struct dirent * __wrap_readdir(DIR* dirp) +{ + return dll_readdir(dirp); +} + +struct dirent * __wrap_readdir64(DIR* dirp) +{ + return dll_readdir(dirp); +} + +int __wrap_closedir(DIR* dirp) +{ + return dll_closedir(dirp); +} + +void __wrap_rewinddir(DIR* dirp) +{ + dll_rewinddir(dirp); +} + +int __wrap_fprintf(FILE *stream, const char *format, ...) +{ + int res; + va_list va; + va_start(va, format); + res = dll_vfprintf(stream, format, va); + va_end(va); + return res; +} + +int __wrap_vfprintf(FILE *stream, const char *format, va_list ap) +{ + return dll_vfprintf(stream, format, ap); +} + +int __wrap_printf(const char *format, ...) +{ + int res; + va_list va; + va_start(va, format); + res = dll_vfprintf(stdout, format, va); + va_end(va); + return res; +} + +int __wrap_fgetc(FILE *stream) +{ + return dll_fgetc(stream); +} + +char *__wrap_fgets(char *s, int size, FILE *stream) +{ + return dll_fgets(s, size, stream); +} + +int __wrap__IO_getc(FILE *stream) +{ + return dll_getc(stream); +} + +int __wrap__IO_getc_unlocked(FILE *stream) +{ + return dll_getc(stream); +} + +int __wrap_getc_unlocked(FILE *stream) +{ + return dll_getc(stream); +} + +int __wrap_ungetc(int c, FILE *stream) +{ + return dll_ungetc(c, stream); +} + +int __wrap_getc(FILE *stream) +{ + return dll_getc(stream); +} + +int __wrap_ioctl(int d, unsigned long int request, ...) +{ + int res; + va_list va; + va_start(va, request); + res = dll_ioctl(d, request, va); + va_end(va); + return res; +} + +int __wrap__stat(const char *path, struct _stat *buffer) +{ + return dll_stat(path, buffer); +} + +int __wrap_stat(const char *path, struct _stat *buffer) +{ + return dll_stat(path, buffer); +} + +int __wrap_stat64(const char* path, struct stat64* buffer) +{ + return dll_stat64(path, buffer); +} + +int __wrap___xstat(int __ver, const char *__filename, struct stat *__stat_buf) +{ + return dll_stat(__filename, __stat_buf); +} + +int __wrap___xstat64(int __ver, const char *__filename, struct stat64 *__stat_buf) +{ + return dll_stat64(__filename, __stat_buf); +} + +int __wrap___lxstat64(int __ver, const char *__filename, struct stat64 *__stat_buf) +{ + return dll_stat64(__filename, __stat_buf); +} + +void __wrap_flockfile(FILE *file) +{ + dll_flockfile(file); +} + +int __wrap_ftrylockfile(FILE *file) +{ + return dll_ftrylockfile(file); +} + +void __wrap_funlockfile(FILE *file) +{ + dll_funlockfile(file); +} + +int __wrap___fxstat64(int ver, int fd, struct stat64 *buf) +{ + return dll_fstat64(fd, buf); +} + +int __wrap___fxstat(int ver, int fd, struct stat *buf) +{ + return dll_fstat(fd, buf); +} + +int __wrap_fstat(int fd, struct _stat *buf) +{ + return dll_fstat(fd, buf); +} + +int __wrap_fstat64(int fd, struct stat64* buf) +{ + return dll_fstat64(fd, buf); +} + +int __wrap_setvbuf(FILE *stream, char *buf, int type, size_t size) +{ + return dll_setvbuf(stream, buf, type, size); +} + +struct mntent *__wrap_getmntent(FILE *fp) +{ +#ifdef TARGET_POSIX + return dll_getmntent(fp); +#endif + return NULL; +} + +struct mntent* __wrap_getmntent_r(FILE* fp, struct mntent* result, char* buffer, int bufsize) +{ +#ifdef TARGET_POSIX + return dll_getmntent_r(fp, result, buffer, bufsize); +#endif + return NULL; +} + +// GCC 4.3 in Ubuntu 8.10 defines _FORTIFY_SOURCE=2 which means, that fread, read etc +// are actually #defines which are inlined when compiled with -O. Those defines +// actually call __*chk (for example, __fread_chk). We need to bypass this whole +// thing to actually call our wrapped functions. +#if _FORTIFY_SOURCE > 1 + +size_t __wrap___fread_chk(void * ptr, size_t ptrlen, size_t size, size_t n, FILE * stream) +{ + return dll_fread(ptr, size, n, stream); +} + +int __wrap___printf_chk(int flag, const char *format, ...) +{ + int res; + va_list va; + va_start(va, format); + res = dll_vfprintf(stdout, format, va); + va_end(va); + return res; +} + +int __wrap___vfprintf_chk(FILE* stream, int flag, const char *format, va_list ap) +{ + return dll_vfprintf(stream, format, ap); +} + +int __wrap___fprintf_chk(FILE * stream, int flag, const char *format, ...) +{ + int res; + va_list va; + va_start(va, format); + res = dll_vfprintf(stream, format, va); + va_end(va); + return res; +} + +char *__wrap___fgets_chk(char *s, size_t size, int n, FILE *stream) +{ + return dll_fgets(s, n, stream); +} + +size_t __wrap___read_chk(int fd, void *buf, size_t nbytes, size_t buflen) +{ + return dll_read(fd, buf, nbytes); +} + +#endif diff --git a/xbmc/cores/DllLoader/exports/wrapper_mach_alias b/xbmc/cores/DllLoader/exports/wrapper_mach_alias new file mode 100644 index 0000000..88ff675 --- /dev/null +++ b/xbmc/cores/DllLoader/exports/wrapper_mach_alias @@ -0,0 +1,62 @@ +# List of wrapper aliases for Mach-O +___wrap_clearerr _clearerr +___wrap_close _close +___wrap_fclose _fclose +___wrap_fdopen _fdopen +___wrap_feof _feof +___wrap_ferror _ferror +___wrap_fflush _fflush +___wrap_fgetc _fgetc +___wrap_fgetpos _fgetpos +___wrap_fgets _fgets +___wrap_fileno _fileno +___wrap_flockfile _flockfile +___wrap_fopen _fopen +___wrap_fopen64 _fopen64 +___wrap_fprintf _fprintf +___wrap_fputc _fputc +___wrap_fputs _fputs +___wrap_fread _fread +___wrap_freopen _freopen +___wrap_fseek _fseek +___wrap_fsetpos _fsetpos +___wrap_fstatvfs64 _fstatvfs64 +___wrap_ftell _ftell +___wrap_ftrylockfile _ftrylockfile +___wrap_funlockfile _funlockfile +___wrap_fwrite _fwrite +___wrap_ioctl _ioctl +___wrap_lseek _lseek +___wrap_lseek64 _lseek64 +___wrap_open _open +___wrap_open64 _open64 +___wrap_popen _popen +___wrap_printf _printf +___wrap_read _read +___wrap_opendir _opendir +___wrap_readdir _readdir +___wrap_closedir _closedir +___wrap_rewinddir _rewinddir +___wrap_rewind _rewind +___wrap__stat _stat +___wrap_fstat _fstat +___wrap_ungetc _ungetc +___wrap_vfprintf _vfprintf +___wrap_write _write +___wrap__IO_putc _putc +___wrap__IO_getc _getc +___wrap__IO_getc_unlocked _getc_unlocked +___wrap_fwrite _fwrite$UNIX2003 +___wrap_close _close$UNIX2003 +#___wrap_fcntl _fcntl$UNIX2003 +___wrap_open _open$UNIX2003 +___wrap_fopen _fopen$UNIX2003 +___wrap_fputs _fputs$UNIX2003 +___wrap_read _read$UNIX2003 +___wrap_write _write$UNIX2003 +___wrap_fstat _fstat$INODE64 +___wrap__stat _stat$INODE64 +___wrap_opendir _opendir$INODE64$UNIX2003 +___wrap_closedir _closedir$UNIX2003 +___wrap_readdir _readdir$INODE64 +___wrap_opendir _opendir$INODE64 diff --git a/xbmc/cores/DllLoader/exports/ws2_32.def b/xbmc/cores/DllLoader/exports/ws2_32.def new file mode 100755 index 0000000..ac7712e --- /dev/null +++ b/xbmc/cores/DllLoader/exports/ws2_32.def @@ -0,0 +1,25 @@ +LIBRARY WS2_32.DLL + +VERSION 1.0 + +EXPORTS + WSACleanup + WSAGetLastError + WSAStartup + bind + closesocket + connect + gethostbyname + getsockopt + htonl + htons + inet_addr + inet_ntoa + ioctlsocket + ntohl + recv + select + send + sendto + setsockopt + socket
\ No newline at end of file diff --git a/xbmc/cores/DllLoader/ldt_keeper.c b/xbmc/cores/DllLoader/ldt_keeper.c new file mode 100644 index 0000000..325d50c --- /dev/null +++ b/xbmc/cores/DllLoader/ldt_keeper.c @@ -0,0 +1,282 @@ +/** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file MUST be in main library because LDT must + * be modified before program creates first thread + * - avifile includes this file from C++ code + * and initializes it at the start of player! + * it might sound like a hack and it really is - but + * as aviplay is decoding video with more than just one + * thread currently it's necessary to do it this way + * this might change in the future + */ + +/* applied some modification to make make our xine friend more happy */ + +/* + * Modified for use with MPlayer, detailed changelog at + * http://svn.mplayerhq.hu/mplayer/trunk/ + * $Id: ldt_keeper.c 22733 2007-03-18 22:18:11Z nicodvb $ + */ + +// clang-format off +#if !defined(__aarch64__) && \ + !defined(__alpha__) &&\ + !defined(__arc__) &&\ + !defined(__arm__) && \ + !defined(__loongarch__) && \ + !defined(__mips__) && \ + !defined(__or1k__) && \ + !defined(__powerpc__) && \ + !defined(__ppc__) && \ + !defined(__riscv) && \ + !defined(__SH4__) && \ + !defined(__s390x__) && \ + !defined(__sparc__) && \ + !defined(__xtensa__) +// clang-format on + +#include "ldt_keeper.h" + +#include <string.h> +#include <stdint.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <stdio.h> +#include <unistd.h> +#include "mmap_anon.h" +#if defined( __linux__ ) && !defined(__powerpc__) +#include <asm/unistd.h> +#include <asm/ldt.h> +/* prototype it here, so we won't depend on kernel headers */ +#ifdef __cplusplus +extern "C" { +#endif +#if defined(TARGET_ANDROID) && (defined(__i386__) || defined(__x86_64__)) && !defined(modify_ldt) +#define modify_ldt(a,b,c) syscall( __NR_modify_ldt, a, b, c); +#else +int modify_ldt(int func, void *ptr, unsigned long bytecount); +#endif +#ifdef __cplusplus +} +#endif +#else +#if defined(__NetBSD__) || defined(TARGET_FREEBSD) || defined(__OpenBSD__) || defined(__DragonFly__) +#include <machine/segments.h> +#include <machine/sysarch.h> +#endif +#if defined(TARGET_DARWIN) +#include <i386/user_ldt.h> +#endif + +#ifdef __svr4__ +#include <sys/segment.h> +#include <sys/sysi86.h> + +/* solaris x86: add missing prototype for sysi86(), but only when sysi86(int, void*) is known to be valid */ +#ifdef HAVE_SYSI86_iv +#ifdef __cplusplus +extern "C" { +#endif +int sysi86(int, void*); +#ifdef __cplusplus +} +#endif +#endif + +#ifndef NUMSYSLDTS /* SunOS 2.5.1 does not define NUMSYSLDTS */ +#define NUMSYSLDTS 6 /* Let's hope the SunOS 5.8 value is OK */ +#endif + +#define TEB_SEL_IDX NUMSYSLDTS +#endif + +#define LDT_ENTRIES 8192 +#define LDT_ENTRY_SIZE 8 +#pragma pack(4) +struct user_desc { + unsigned int entry_number; + unsigned long base_addr; + unsigned int limit; + unsigned int seg_32bit:1; + unsigned int contents:2; + unsigned int read_exec_only:1; + unsigned int limit_in_pages:1; + unsigned int seg_not_present:1; +}; + +#define MODIFY_LDT_CONTENTS_DATA 0 +#define MODIFY_LDT_CONTENTS_STACK 1 +#define MODIFY_LDT_CONTENTS_CODE 2 +#endif + + +/* user level (privilege level: 3) ldt (1<<2) segment selector */ +#define LDT_SEL(idx) ((idx) << 3 | 1 << 2 | 3) + +/* i got this value from wine sources, it's the first free LDT entry */ +#if (defined(TARGET_DARWIN) || defined(TARGET_FREEBSD)) && defined(LDT_AUTO_ALLOC) +#define TEB_SEL_IDX LDT_AUTO_ALLOC +#define USE_LDT_AA +#endif + +#ifndef TEB_SEL_IDX +#define TEB_SEL_IDX 17 +#endif + +static unsigned int fs_ldt = TEB_SEL_IDX; + + +/** + * here is a small logical problem with Restore for multithreaded programs - + * in C++ we use static class for this... + */ + +#ifdef __cplusplus +extern "C" +#endif +void Setup_FS_Segment(void) +{ + unsigned int ldt_desc = LDT_SEL(fs_ldt); + + __asm__ __volatile__( + "movl %0,%%eax; movw %%ax, %%fs" : : "r" (ldt_desc) + :"eax" + ); +} + +#if defined(__NetBSD__) || defined(TARGET_FREEBSD) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(TARGET_DARWIN) +static void LDT_EntryToBytes( unsigned long *buffer, const struct user_desc *content ) +{ + *buffer++ = ((content->base_addr & 0x0000ffff) << 16) | + (content->limit & 0x0ffff); + *buffer = (content->base_addr & 0xff000000) | + ((content->base_addr & 0x00ff0000)>>16) | + (content->limit & 0xf0000) | + (content->contents << 10) | + ((content->read_exec_only == 0) << 9) | + ((content->seg_32bit != 0) << 22) | + ((content->limit_in_pages != 0) << 23) | + 0xf000; +} +#endif + +void* fs_seg=0; + +ldt_fs_t* Setup_LDT_Keeper(void) +{ + struct user_desc array; + int ret; + int sret; + ldt_fs_t* ldt_fs = (ldt_fs_t*) malloc(sizeof(ldt_fs_t)); + + if (!ldt_fs) + return NULL; + +#if defined(TARGET_DARWIN) + if (getenv("DYLD_BIND_AT_LAUNCH") == NULL) + printf("DYLD_BIND_AT_LAUNCH"); +#endif // TARGET_DARWIN + + sret = sysconf(_SC_PAGE_SIZE); + if (sret == -1) + { + perror("ERROR: Couldn't allocate memory for fs segment"); + free(ldt_fs); + return NULL; + } + + fs_seg = ldt_fs->fs_seg = mmap_anon(NULL, sret, PROT_READ | PROT_WRITE, MAP_PRIVATE, 0); + if (ldt_fs->fs_seg == (void*)-1) + { + perror("ERROR: Couldn't allocate memory for fs segment"); + free(ldt_fs); + return NULL; + } + *(void**)((char*)ldt_fs->fs_seg + 0x18) = ldt_fs->fs_seg; + memset(&array, 0, sizeof(array)); + array.base_addr = (long)ldt_fs->fs_seg; + array.entry_number = TEB_SEL_IDX; + array.limit = array.base_addr+sysconf(_SC_PAGE_SIZE) - 1; + array.seg_32bit = 1; + array.read_exec_only = 0; + array.seg_not_present = 0; + array.contents=MODIFY_LDT_CONTENTS_DATA; + array.limit_in_pages = 0; +#ifdef __linux__ + /* ret=LDT_Modify(0x1, &array, sizeof(struct user_desc)); */ + ret = modify_ldt(0x1, &array, sizeof(struct user_desc)); + if (ret < 0) + { + perror("install_fs"); + printf("Couldn't install fs segment, expect segfault\n"); + } +#endif /*linux*/ + +#if defined(__NetBSD__) || defined(TARGET_FREEBSD) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(TARGET_DARWIN) + { + unsigned long d[2]; + + LDT_EntryToBytes( d, &array ); +#ifdef USE_LDT_AA + ret = i386_set_ldt(LDT_AUTO_ALLOC, (const union ldt_entry *)d, 1); + array.entry_number = ret; + fs_ldt = ret; +#else + ret = i386_set_ldt(array.entry_number, (const union ldt_entry *)d, 1); +#endif + if (ret < 0) + { + perror("install_fs"); + printf("Couldn't install fs segment, expect segfault\n"); + printf("Did you reconfigure the kernel with \"options USER_LDT\"?\n"); +#ifdef __OpenBSD__ + printf("On newer OpenBSD systems did you set machdep.userldt to 1?\n"); +#endif + } + } +#endif // __NetBSD__ || TARGET_FREEBSD || __OpenBSD__ || __DragonFly__ || TARGET_DARWIN + +#if defined(__svr4__) + { + struct ssd ssd; + ssd.sel = LDT_SEL(TEB_SEL_IDX); + ssd.bo = array.base_addr; + ssd.ls = array.limit - array.base_addr; + ssd.acc1 = ((array.read_exec_only == 0) << 1) | + (array.contents << 2) | + 0xf0; /* P(resent) | DPL3 | S */ + ssd.acc2 = 0x4; /* byte limit, 32-bit segment */ + if (sysi86(SI86DSCR, &ssd) < 0) + { + perror("sysi86(SI86DSCR)"); + printf("Couldn't install fs segment, expect segfault\n"); + } + } +#endif + + Setup_FS_Segment(); + + ldt_fs->prev_struct = malloc(8); + array.base_addr = (uintptr_t)(ldt_fs->prev_struct); + + return ldt_fs; +} + +void Restore_LDT_Keeper(ldt_fs_t* ldt_fs) +{ + if (ldt_fs == NULL || ldt_fs->fs_seg == 0) + return; + if (ldt_fs->prev_struct) + free(ldt_fs->prev_struct); + + int sret = sysconf(_SC_PAGE_SIZE); + if (sret != -1) + munmap((char*)ldt_fs->fs_seg, sret); + ldt_fs->fs_seg = 0; + free(ldt_fs); +} + +#endif diff --git a/xbmc/cores/DllLoader/ldt_keeper.h b/xbmc/cores/DllLoader/ldt_keeper.h new file mode 100644 index 0000000..f6a262f --- /dev/null +++ b/xbmc/cores/DllLoader/ldt_keeper.h @@ -0,0 +1,38 @@ +/** + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * This file MUST be in main library because LDT must + * be modified before program creates first thread + * - avifile includes this file from C++ code + * and initializes it at the start of player! + * it might sound like a hack and it really is - but + * as aviplay is decoding video with more than just one + * thread currently it's necessary to do it this way + * this might change in the future + */ + +/* applied some modification to make make our xine friend more happy */ + +/* + * Modified for use with MPlayer, detailed changelog at + * http://svn.mplayerhq.hu/mplayer/trunk/ + * $Id: ldt_keeper.c 22733 2007-03-18 22:18:11Z nicodvb $ + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct { + void* fs_seg; + char* prev_struct; + int fd; +} ldt_fs_t; + +void Setup_FS_Segment(void); +ldt_fs_t* Setup_LDT_Keeper(void); +void Restore_LDT_Keeper(ldt_fs_t* ldt_fs); +#ifdef __cplusplus +} +#endif + diff --git a/xbmc/cores/DllLoader/mmap_anon.c b/xbmc/cores/DllLoader/mmap_anon.c new file mode 100644 index 0000000..5bb13cf --- /dev/null +++ b/xbmc/cores/DllLoader/mmap_anon.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +/** + * \file mmap_anon.c + * \brief Provide a compatible anonymous space mapping function + */ + +#include <stdio.h> + +#include <fcntl.h> +#include <sys/mman.h> +#include <unistd.h> + +#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS) +#define MAP_ANONYMOUS MAP_ANON +#endif + +/* + * mmap() anonymous space, depending on the system's mmap() style. On systems + * that use the /dev/zero mapping idiom, zerofd will be set to the file descriptor + * of the opened /dev/zero. + */ + + /** + * \brief mmap() anonymous space, depending on the system's mmap() style. On systems + * that use the /dev/zero mapping idiom, zerofd will be set to the file descriptor + * of the opened /dev/zero. + * + * \param addr address to map at. + * \param len number of bytes from addr to be mapped. + * \param prot protections (region accessibility). + * \param flags specifies the type of the mapped object. + * \param offset start mapping at byte offset. + * \param zerofd + * \return a pointer to the mapped region upon successful completion, -1 otherwise. + */ +void *mmap_anon(void *addr, size_t len, int prot, int flags, off_t offset) +{ + void *result; + +#ifndef MAP_ANONYMOUS + int fd; +#endif + + /* From loader/ext.c: + * "Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap" + * Therefore we preserve the same behavior on all platforms, ie. no + * shared mappings of anon space (if the concepts are supported). */ +#if defined(MAP_SHARED) && defined(MAP_PRIVATE) + flags = (flags & ~MAP_SHARED) | MAP_PRIVATE; +#endif /* defined(MAP_SHARED) && defined(MAP_PRIVATE) */ + +#ifdef MAP_ANONYMOUS + /* BSD-style anonymous mapping */ + result = mmap(addr, len, prot, flags | MAP_ANONYMOUS, -1, offset); +#else + /* SysV-style anonymous mapping */ + fd = open("/dev/zero", O_RDWR); + if(fd < 0){ + perror( "Cannot open /dev/zero for READ+WRITE. Check permissions! error: "); + return NULL; + } + + result = mmap(addr, len, prot, flags, fd, offset); + close(fd); +#endif /* MAP_ANONYMOUS */ + + return result; +} diff --git a/xbmc/cores/DllLoader/mmap_anon.h b/xbmc/cores/DllLoader/mmap_anon.h new file mode 100644 index 0000000..291ccf8 --- /dev/null +++ b/xbmc/cores/DllLoader/mmap_anon.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <sys/types.h> + +void *mmap_anon(void *, size_t, int, int, off_t); + |