/* $Id: kLdrModNative.c 117 2020-03-15 15:23:36Z bird $ */ /** @file * kLdr - The Module Interpreter for the Native Loaders. */ /* * Copyright (c) 2006-2007 Knut St. Osmundsen * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /******************************************************************************* * Header Files * *******************************************************************************/ #include #include "kLdrInternal.h" #if K_OS == K_OS_OS2 # define INCL_BASE # include # ifndef LIBPATHSTRICT # define LIBPATHSTRICT 3 # endif extern APIRET DosQueryHeaderInfo(HMODULE hmod, ULONG ulIndex, PVOID pvBuffer, ULONG cbBuffer, ULONG ulSubFunction); # define QHINF_EXEINFO 1 /* NE exeinfo. */ # define QHINF_READRSRCTBL 2 /* Reads from the resource table. */ # define QHINF_READFILE 3 /* Reads from the executable file. */ # define QHINF_LIBPATHLENGTH 4 /* Gets the libpath length. */ # define QHINF_LIBPATH 5 /* Gets the entire libpath. */ # define QHINF_FIXENTRY 6 /* NE only */ # define QHINF_STE 7 /* NE only */ # define QHINF_MAPSEL 8 /* NE only */ #elif K_OS == K_OS_WINDOWS # undef IMAGE_NT_SIGNATURE # undef IMAGE_DOS_SIGNATURE # include # ifndef IMAGE_SCN_TYPE_NOLOAD # define IMAGE_SCN_TYPE_NOLOAD 0x00000002 # endif /*#elif defined(__NT__) #include */ #elif K_OS == K_OS_DARWIN # include # include #else # error "port me" #endif /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** @def KLDRMODNATIVE_STRICT * Define KLDRMODNATIVE_STRICT to enabled strict checks in KLDRMODNATIVE. */ #define KLDRMODNATIVE_STRICT 1 /** @def KLDRMODNATIVE_ASSERT * Assert that an expression is true when KLDR_STRICT is defined. */ #ifdef KLDRMODNATIVE_STRICT # define KLDRMODNATIVE_ASSERT(expr) kHlpAssert(expr) #else # define KLDRMODNATIVE_ASSERT(expr) do {} while (0) #endif #if K_OS == K_OS_WINDOWS /** @def KLDRMODNATIVE_RVA2TYPE * Converts a RVA to a pointer of the specified type. * @param pvBits The bits (image base). * @param uRVA The image relative virtual address. * @param type The type to cast to. */ # define KLDRMODNATIVE_RVA2TYPE(pvBits, uRVA, type) \ ( (type) ((KUPTR)(pvBits) + (uRVA)) ) #endif /* PE OSes */ /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /** * Instance data for the module interpreter for the Native Loaders. */ typedef struct KLDRMODNATIVE { /** Pointer to the module. (Follows the section table.) */ PKLDRMOD pMod; /** Reserved flags. */ KU32 f32Reserved; /** The number of imported modules. * If ~(KU32)0 this hasn't been determined yet. */ KU32 cImportModules; #if K_OS == K_OS_OS2 /** The module handle. */ HMODULE hmod; #elif K_OS == K_OS_WINDOWS /** The module handle. */ HANDLE hmod; /** Pointer to the NT headers. */ const IMAGE_NT_HEADERS *pNtHdrs; /** Pointer to the section header array. */ const IMAGE_SECTION_HEADER *paShdrs; #elif K_OS == K_OS_DARWIN /** The dlopen() handle.*/ void *pvMod; #else # error "Port me" #endif } KLDRMODNATIVE, *PKLDRMODNATIVE; /******************************************************************************* * Internal Functions * *******************************************************************************/ static KI32 kldrModNativeNumberOfImports(PKLDRMOD pMod, const void *pvBits); /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ extern KLDRMODOPS g_kLdrModNativeOps; /** * Use native loader to load the file opened by pRdr. * * @returns 0 on success and *ppMod pointing to a module instance. * On failure, a non-zero OS specific error code is returned. * @param pOps Pointer to the registered method table. * @param pRdr The file provider instance to use. * @param offNewHdr The offset of the new header in MZ files. -1 if not found. * @param ppMod Where to store the module instance pointer. */ static int kldrModNativeCreate(PCKLDRMODOPS pOps, PKRDR pRdr, KU32 fFlags, KCPUARCH enmCpuArch, KLDRFOFF offNewHdr, PPKLDRMOD ppMod) { int rc = kLdrModOpenNative(kRdrName(pRdr), fFlags, ppMod); if (rc) return rc; rc = kRdrClose(pRdr); KLDRMODNATIVE_ASSERT(!rc); return 0; } /** * Loads a module using the native module loader. * * @returns 0 on success. * @returns non-zero native or kLdr status code on failure. * @param pszFilename The filename or module name to be loaded. * @param fFlags Module open flags, KLDRMOD_OPEN_FLAGS_XXX. * @param ppMod Where to store the module interpreter instance pointer. */ int kLdrModOpenNative(const char *pszFilename, KU32 fFlags, PPKLDRMOD ppMod) { int rc; /* * Load the image. */ #if K_OS == K_OS_OS2 HMODULE hmod; kHlpAssertReturn(!(fFlags & ~KLDRMOD_OPEN_FLAGS_VALID_MASK), KERR_INVALID_PARAMETER); rc = DosLoadModule(NULL, 0, (PCSZ)pszFilename, &hmod); if (rc) return rc; rc = kLdrModOpenNativeByHandle((KUPTR)hmod, fFlags, ppMod); if (rc) DosFreeModule(hmod); #elif K_OS == K_OS_WINDOWS HMODULE hmod; kHlpAssertReturn(!(fFlags & ~KLDRMOD_OPEN_FLAGS_VALID_MASK), KERR_INVALID_PARAMETER); hmod = LoadLibrary(pszFilename); if (!hmod) return GetLastError(); rc = kLdrModOpenNativeByHandle((KUPTR)hmod, fFlags, ppMod); if (rc) FreeLibrary(hmod); #elif K_OS == K_OS_DARWIN void *pvMod; kHlpAssertReturn(!(fFlags & ~KLDRMOD_OPEN_FLAGS_VALID_MASK), KERR_INVALID_PARAMETER); pvMod = dlopen(pszFilename, 0); if (!pvMod) return ENOENT; rc = kLdrModOpenNativeByHandle((KUPTR)pvMod, fFlags, ppMod); if (rc) dlclose(pvMod); #else # error "Port me" #endif return rc; } /** * Creates a native module interpret for an already module already * loaded by the native loader. * * @returns 0 on success. * @returns non-zero native or kLdr status code on failure. * @param pszFilename The filename or module name to be loaded. * @param fFlags Module open flags, KLDRMOD_OPEN_FLAGS_XXX. * @param ppMod Where to store the module interpreter instance pointer. * @remark This will not make the native loader increment the load count. */ int kLdrModOpenNativeByHandle(KUPTR uHandle, KU32 fFlags, PPKLDRMOD ppMod) { KSIZE cb; KU32 cchFilename; KU32 cSegments; PKLDRMOD pMod; PKLDRMODNATIVE pModNative; /* * Delcare variables, parse the module header or whatever and determin the * size of the module instance. */ #if K_OS == K_OS_OS2 char szFilename[CCHMAXPATH]; int rc; /* get the filename. */ rc = DosQueryModuleName((HMODULE)uHandle, sizeof(szFilename), szFilename); if (rc) { KLDRMODNATIVE_ASSERT(rc); szFilename[0] = '\0'; } /* get the segment count. */ /** @todo DosQueryHeaderInfo should be able to get us what we want on OS/2. */ cSegments = 1; #elif K_OS == K_OS_WINDOWS DWORD dw; char szFilename[MAX_PATH]; const IMAGE_NT_HEADERS *pNtHdrs; const IMAGE_SECTION_HEADER *paShdrs; const IMAGE_DOS_HEADER *pDosHdr = (const IMAGE_DOS_HEADER *)uHandle; unsigned i; /* get the filename. */ dw = GetModuleFileName((HANDLE)uHandle, szFilename, sizeof(szFilename)); if (dw <= 0) { KLDRMODNATIVE_ASSERT(dw <= 0); szFilename[0] = '\0'; } /* get the segment count. */ if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE) pNtHdrs = (const IMAGE_NT_HEADERS *)((KUPTR)pDosHdr + pDosHdr->e_lfanew); else pNtHdrs = (const IMAGE_NT_HEADERS *)pDosHdr; if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE) { KLDRMODNATIVE_ASSERT(!"bad signature"); return KLDR_ERR_UNKNOWN_FORMAT; } if (pNtHdrs->FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER)) { KLDRMODNATIVE_ASSERT(!"bad optional header size"); return KLDR_ERR_UNKNOWN_FORMAT; } cSegments = pNtHdrs->FileHeader.NumberOfSections + 1; paShdrs = (const IMAGE_SECTION_HEADER *)(pNtHdrs + 1); #elif K_OS == K_OS_DARWIN char szFilename[1] = ""; cSegments = 0; /** @todo Figure out the Mac OS X dynamic loader. */ #else # error "Port me" #endif kHlpAssertReturn(!(fFlags & ~KLDRMOD_OPEN_FLAGS_VALID_MASK), KERR_INVALID_PARAMETER); /* * Calc the instance size, allocate and initialize it. */ cchFilename = (KU32)kHlpStrLen(szFilename); cb = K_ALIGN_Z(sizeof(KLDRMODNATIVE), 16) + K_OFFSETOF(KLDRMOD, aSegments[cSegments]) + cchFilename + 1; pModNative = (PKLDRMODNATIVE)kHlpAlloc(cb); if (!pModNative) return KERR_NO_MEMORY; /* KLDRMOD */ pMod = (PKLDRMOD)((KU8 *)pModNative + K_ALIGN_Z(sizeof(KLDRMODNATIVE), 16)); pMod->pvData = pModNative; pMod->pRdr = NULL; pMod->pOps = NULL; /* set upon success. */ pMod->cSegments = cSegments; pMod->cchFilename = cchFilename; pMod->pszFilename = (char *)&pMod->aSegments[pMod->cSegments]; kHlpMemCopy((char *)pMod->pszFilename, szFilename, cchFilename + 1); pMod->pszName = kHlpGetFilename(pMod->pszFilename); /** @todo get soname */ pMod->cchName = cchFilename - (KU32)(pMod->pszName - pMod->pszFilename); pMod->fFlags = fFlags; #if defined(__i386__) || defined(__X86__) || defined(_M_IX86) pMod->enmCpu = KCPU_I386; pMod->enmArch = KCPUARCH_X86_32; pMod->enmEndian = KLDRENDIAN_LITTLE; #elif defined(__X86_64__) || defined(__x86_64__) || defined(__AMD64__) || defined(_M_IX64) pMod->enmCpu = KCPU_K8; pMod->enmArch = KCPUARCH_AMD64; pMod->enmEndian = KLDRENDIAN_LITTLE; #else # error "Port me" #endif pMod->enmFmt = KLDRFMT_NATIVE; pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE; pMod->u32Magic = 0; /* set upon success. */ /* KLDRMODNATIVE */ pModNative->pMod = pMod; pModNative->f32Reserved = 0; pModNative->cImportModules = ~(KU32)0; /* * Set native instance data. */ #if K_OS == K_OS_OS2 pModNative->hmod = (HMODULE)uHandle; /* just fake a segment for now. */ pMod->aSegments[0].pvUser = NULL; pMod->aSegments[0].pchName = "fake"; pMod->aSegments[0].cchName = sizeof("fake") - 1; pMod->aSegments[0].enmProt = KPROT_NOACCESS; pMod->aSegments[0].cb = 0; pMod->aSegments[0].Alignment = 0; pMod->aSegments[0].LinkAddress = NIL_KLDRADDR; pMod->aSegments[0].offFile = -1; pMod->aSegments[0].cbFile = 0; pMod->aSegments[0].RVA = NIL_KLDRADDR; pMod->aSegments[0].cbMapped = 0; pMod->aSegments[0].MapAddress = 0; #elif K_OS == K_OS_WINDOWS pModNative->hmod = (HMODULE)uHandle; pModNative->pNtHdrs = pNtHdrs; pModNative->paShdrs = paShdrs; if (pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL) pMod->enmType = !(pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) ? KLDRTYPE_SHARED_LIBRARY_RELOCATABLE : KLDRTYPE_SHARED_LIBRARY_FIXED; else pMod->enmType = !(pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) ? KLDRTYPE_EXECUTABLE_RELOCATABLE : KLDRTYPE_EXECUTABLE_FIXED; /* The implied headers section. */ pMod->aSegments[0].pvUser = NULL; pMod->aSegments[0].pchName = "TheHeaders"; pMod->aSegments[0].cchName = sizeof("TheHeaders") - 1; pMod->aSegments[0].enmProt = KPROT_READONLY; pMod->aSegments[0].cb = pNtHdrs->OptionalHeader.SizeOfHeaders; pMod->aSegments[0].Alignment = pNtHdrs->OptionalHeader.SectionAlignment; pMod->aSegments[0].LinkAddress = pNtHdrs->OptionalHeader.ImageBase; pMod->aSegments[0].offFile = 0; pMod->aSegments[0].cbFile = pNtHdrs->OptionalHeader.SizeOfHeaders; pMod->aSegments[0].RVA = 0; if (pMod->cSegments > 1) pMod->aSegments[0].cbMapped = paShdrs[0].VirtualAddress; else pMod->aSegments[0].cbMapped = pNtHdrs->OptionalHeader.SizeOfHeaders; pMod->aSegments[0].MapAddress = uHandle; /* The section headers. */ for (i = 0; i < pNtHdrs->FileHeader.NumberOfSections; i++) { const char *pch; KU32 cchSegName; /* unused */ pMod->aSegments[i + 1].pvUser = NULL; /* name */ pMod->aSegments[i + 1].pchName = pch = &paShdrs[i].Name[0]; cchSegName = IMAGE_SIZEOF_SHORT_NAME; while ( cchSegName > 0 && (pch[cchSegName - 1] == ' ' || pch[cchSegName - 1] == '\0')) cchSegName--; pMod->aSegments[i + 1].cchName = cchSegName; /* size and addresses */ if (!(paShdrs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)) { pMod->aSegments[i + 1].cb = paShdrs[i].Misc.VirtualSize; pMod->aSegments[i + 1].RVA = paShdrs[i].VirtualAddress; pMod->aSegments[i + 1].LinkAddress = paShdrs[i].VirtualAddress + pNtHdrs->OptionalHeader.ImageBase; pMod->aSegments[i + 1].MapAddress = paShdrs[i].VirtualAddress + uHandle; pMod->aSegments[i + 1].cbMapped = paShdrs[i].Misc.VirtualSize; if (i + 2 < pMod->cSegments) pMod->aSegments[i + 1].cbMapped = paShdrs[i + 1].VirtualAddress - paShdrs[i].VirtualAddress; } else { pMod->aSegments[i + 1].cb = 0; pMod->aSegments[i + 1].cbMapped = 0; pMod->aSegments[i + 1].LinkAddress = NIL_KLDRADDR; pMod->aSegments[i + 1].RVA = 0; pMod->aSegments[i + 1].MapAddress = 0; } /* file location */ pMod->aSegments[i + 1].offFile = paShdrs[i].PointerToRawData; pMod->aSegments[i + 1].cbFile = paShdrs[i].SizeOfRawData; if ( pMod->aSegments[i + 1].cbMapped > 0 /* if mapped */ && (KLDRSIZE)pMod->aSegments[i + 1].cbFile > pMod->aSegments[i + 1].cbMapped) pMod->aSegments[i + 1].cbFile = (KLDRFOFF)pMod->aSegments[i + 1].cbMapped; /* protection */ switch ( paShdrs[i].Characteristics & (IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) { case 0: case IMAGE_SCN_MEM_SHARED: pMod->aSegments[i + 1].enmProt = KPROT_NOACCESS; break; case IMAGE_SCN_MEM_READ: case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_SHARED: pMod->aSegments[i + 1].enmProt = KPROT_READONLY; break; case IMAGE_SCN_MEM_WRITE: case IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ: pMod->aSegments[i + 1].enmProt = KPROT_WRITECOPY; break; case IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED: case IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_READ: pMod->aSegments[i + 1].enmProt = KPROT_READWRITE; break; case IMAGE_SCN_MEM_EXECUTE: case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_SHARED: pMod->aSegments[i + 1].enmProt = KPROT_EXECUTE; break; case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_SHARED: pMod->aSegments[i + 1].enmProt = KPROT_EXECUTE_READ; break; case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE: case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ: pMod->aSegments[i + 1].enmProt = KPROT_EXECUTE_WRITECOPY; break; case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED: case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_READ: pMod->aSegments[i + 1].enmProt = KPROT_EXECUTE_READWRITE; break; } /* alignment. */ switch (paShdrs[i].Characteristics & IMAGE_SCN_ALIGN_MASK) { case 0: /* hope this is right... */ pMod->aSegments[i + 1].Alignment = pNtHdrs->OptionalHeader.SectionAlignment; break; case IMAGE_SCN_ALIGN_1BYTES: pMod->aSegments[i + 1].Alignment = 1; break; case IMAGE_SCN_ALIGN_2BYTES: pMod->aSegments[i + 1].Alignment = 2; break; case IMAGE_SCN_ALIGN_4BYTES: pMod->aSegments[i + 1].Alignment = 4; break; case IMAGE_SCN_ALIGN_8BYTES: pMod->aSegments[i + 1].Alignment = 8; break; case IMAGE_SCN_ALIGN_16BYTES: pMod->aSegments[i + 1].Alignment = 16; break; case IMAGE_SCN_ALIGN_32BYTES: pMod->aSegments[i + 1].Alignment = 32; break; case IMAGE_SCN_ALIGN_64BYTES: pMod->aSegments[i + 1].Alignment = 64; break; case IMAGE_SCN_ALIGN_128BYTES: pMod->aSegments[i + 1].Alignment = 128; break; case IMAGE_SCN_ALIGN_256BYTES: pMod->aSegments[i + 1].Alignment = 256; break; case IMAGE_SCN_ALIGN_512BYTES: pMod->aSegments[i + 1].Alignment = 512; break; case IMAGE_SCN_ALIGN_1024BYTES: pMod->aSegments[i + 1].Alignment = 1024; break; case IMAGE_SCN_ALIGN_2048BYTES: pMod->aSegments[i + 1].Alignment = 2048; break; case IMAGE_SCN_ALIGN_4096BYTES: pMod->aSegments[i + 1].Alignment = 4096; break; case IMAGE_SCN_ALIGN_8192BYTES: pMod->aSegments[i + 1].Alignment = 8192; break; default: kHlpAssert(0); pMod->aSegments[i + 1].Alignment = 0; break; } } #elif K_OS == K_OS_DARWIN /** @todo Figure out the Mac OS X dynamic loader. */ #else # error "Port me" #endif /* * We're done. */ pMod->u32Magic = KLDRMOD_MAGIC; pMod->pOps = &g_kLdrModNativeOps; *ppMod = pMod; return 0; } /** @copydoc KLDRMODOPS::pfnDestroy */ static int kldrModNativeDestroy(PKLDRMOD pMod) { PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData; int rc; #if K_OS == K_OS_OS2 rc = DosFreeModule(pModNative->hmod); #elif K_OS == K_OS_WINDOWS if (FreeLibrary(pModNative->hmod)) rc = 0; else rc = GetLastError(); #elif K_OS == K_OS_DARWIN dlclose(pModNative->pvMod); #else # error "Port me" #endif pMod->u32Magic = 0; pMod->pOps = NULL; kHlpFree(pModNative); return rc; } /** @copydoc kLdrModQuerySymbol */ static int kldrModNativeQuerySymbol(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, KU32 iSymbol, const char *pchSymbol, KSIZE cchSymbol, const char *pszVersion, PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind) { PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData; const char *pszSymbol = pchSymbol; #if K_OS == K_OS_OS2 APIRET rc; PFN pfn; #elif K_OS == K_OS_WINDOWS FARPROC pfn; #elif K_OS == K_OS_DARWIN void *pfn; #else # error "Port me" #endif /* make stack copy of the symbol if it isn't zero terminated. */ if (pszSymbol && pszSymbol[cchSymbol]) { char *pszCopy = kHlpAllocA(cchSymbol + 1); kHlpMemCopy(pszCopy, pchSymbol, cchSymbol); pszCopy[cchSymbol] = '\0'; pszSymbol = pszCopy; } #if K_OS == K_OS_OS2 if (!pchSymbol && iSymbol >= 0x10000) return KLDR_ERR_SYMBOL_NOT_FOUND; if (puValue) { rc = DosQueryProcAddr(pModNative->hmod, pszSymbol ? 0 : iSymbol, (PCSZ)pszSymbol, &pfn); if (rc) return rc == ERROR_PROC_NOT_FOUND ? KLDR_ERR_SYMBOL_NOT_FOUND : rc; *puValue = (KUPTR)pfn; } if (pfKind) { ULONG ulProcType; rc = DosQueryProcType(pModNative->hmod, pszSymbol ? 0 : iSymbol, (PCSZ)pszSymbol, &ulProcType); if (rc) { if (puValue) *puValue = 0; return rc == ERROR_PROC_NOT_FOUND ? KLDR_ERR_SYMBOL_NOT_FOUND : rc; } *pfKind = (ulProcType & PT_32BIT ? KLDRSYMKIND_32BIT : KLDRSYMKIND_16BIT) | KLDRSYMKIND_NO_TYPE; } #elif K_OS == K_OS_WINDOWS if (!pszSymbol && iSymbol >= 0x10000) return KLDR_ERR_SYMBOL_NOT_FOUND; pfn = GetProcAddress(pModNative->hmod, pszSymbol ? pszSymbol : (const char *)(KUPTR)iSymbol); if (puValue) *puValue = (KUPTR)pfn; if (pfKind) *pfKind = (pModNative->pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32) ? KLDRSYMKIND_32BIT : KLDRSYMKIND_16BIT) | KLDRSYMKIND_NO_TYPE; #elif K_OS == K_OS_DARWIN if (!pszSymbol && iSymbol != NIL_KLDRMOD_SYM_ORDINAL) return KLDR_ERR_SYMBOL_NOT_FOUND; pfn = dlsym(pModNative->pvMod, pszSymbol); if (!pfn) return KLDR_ERR_SYMBOL_NOT_FOUND; if (puValue) *puValue = (KUPTR)pfn; if (pfKind) *pfKind = (sizeof(KUPTR) == 4 ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT) | KLDRSYMKIND_NO_TYPE; #else # error "Port me" #endif return 0; } /** @copydoc kLdrModEnumSymbols */ static int kldrModNativeEnumSymbols(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser) { PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData; #if K_OS == K_OS_OS2 /** @todo implement export enumeration on OS/2. */ (void)pModNative; return ERROR_NOT_SUPPORTED; #elif K_OS == K_OS_WINDOWS || defined(__NT__) const KU32 *paFunctions; const IMAGE_EXPORT_DIRECTORY *pExpDir; const KU32 *paRVANames; const KU16 *paOrdinals; KU32 iFunction; KU32 cFunctions; KU32 cNames; int rc; /* * Make sure we've got mapped bits and resolve any base address aliases. */ if ( pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size < sizeof(IMAGE_EXPORT_DIRECTORY)) return 0; /* no exports to enumerate, return success. */ pExpDir = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, PIMAGE_EXPORT_DIRECTORY); /* * Enumerate the ordinal exports. */ paRVANames = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, pExpDir->AddressOfNames, const KU32 *); paOrdinals = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, pExpDir->AddressOfNameOrdinals, const KU16 *); paFunctions = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, pExpDir->AddressOfFunctions, const KU32 *); cFunctions = pExpDir->NumberOfFunctions; cNames = pExpDir->NumberOfNames; for (iFunction = 0; iFunction < cFunctions; iFunction++) { unsigned fFoundName; KU32 iName; const KU32 uRVA = paFunctions[iFunction]; const KLDRADDR uValue = BaseAddress + uRVA; KU32 fKind = (pModNative->pNtHdrs->FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32) ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT) | KLDRSYMKIND_NO_TYPE; if ( uRVA - pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress < pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) fKind |= KLDRSYMKIND_FORWARDER; /* * Any symbol names? */ fFoundName = 0; for (iName = 0; iName < cNames; iName++) { const char *pszName; if (paOrdinals[iName] != iFunction) continue; fFoundName = 1; pszName = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, paRVANames[iName], const char *); rc = pfnCallback(pMod, iFunction + pExpDir->Base, pszName, strlen(pszName), NULL, uValue, fKind, pvUser); if (rc) return rc; } /* * If no names, call once with the ordinal only. */ if (!fFoundName) { rc = pfnCallback(pMod, iFunction + pExpDir->Base, NULL, 0, NULL, uValue, fKind, pvUser); if (rc) return rc; } } return 0; #elif K_OS == K_OS_DARWIN /** @todo implement enumeration on darwin. */ (void)pModNative; return KLDR_ERR_TODO; #else # error "Port me" #endif } /** @copydoc kLdrModGetImport */ static int kldrModNativeGetImport(PKLDRMOD pMod, const void *pvBits, KU32 iImport, char *pszName, KSIZE cchName) { PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData; #if K_OS == K_OS_OS2 /** @todo implement import enumeration on OS/2. */ (void)pModNative; return ERROR_NOT_SUPPORTED; #elif K_OS == K_OS_WINDOWS || defined(__NT__) const IMAGE_IMPORT_DESCRIPTOR *pImpDesc; const char *pszImportName; KSIZE cchImportName; int rc; /* * Simple bounds check. */ if (iImport >= (KU32)kldrModNativeNumberOfImports(pMod, pvBits)) return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS; /* * Get the name. */ pImpDesc = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + sizeof(IMAGE_IMPORT_DESCRIPTOR) * iImport, const IMAGE_IMPORT_DESCRIPTOR *); pszImportName = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, pImpDesc->Name, const char *); cchImportName = kHlpStrLen(pszImportName); if (cchImportName < cchName) { kHlpMemCopy(pszName, pszImportName, cchImportName + 1); rc = 0; } else { kHlpMemCopy(pszName, pszImportName, cchName); if (cchName) pszName[cchName - 1] = '\0'; rc = KERR_BUFFER_OVERFLOW; } return rc; #elif K_OS == K_OS_DARWIN /** @todo Implement import enumeration on darwin. */ (void)pModNative; return KLDR_ERR_TODO; #else # error "Port me" #endif } /** @copydoc kLdrModNumberOfImports */ static KI32 kldrModNativeNumberOfImports(PKLDRMOD pMod, const void *pvBits) { PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData; #if K_OS == K_OS_OS2 /** @todo implement import counting on OS/2. */ (void)pModNative; return -1; #elif K_OS == K_OS_WINDOWS || defined(__NT__) if (pModNative->cImportModules == ~(KU32)0) { /* * We'll have to walk the import descriptors to figure out their number. */ pModNative->cImportModules = 0; if ( pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size && pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) { const IMAGE_IMPORT_DESCRIPTOR *pImpDesc; pImpDesc = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, const IMAGE_IMPORT_DESCRIPTOR *); while (pImpDesc->Name && pImpDesc->FirstThunk) { pModNative->cImportModules++; pImpDesc++; } } } return pModNative->cImportModules; #elif K_OS == K_OS_DARWIN /** @todo Implement import counting on Darwin. */ (void)pModNative; return -1; #else # error "Port me" #endif } /** @copydoc kLdrModGetStackInfo */ static int kldrModNativeGetStackInfo(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo) { PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData; #if K_OS == K_OS_OS2 /** @todo implement stack info on OS/2. */ (void)pModNative; return ERROR_NOT_SUPPORTED; #elif K_OS == K_OS_WINDOWS || defined(__NT__) pStackInfo->Address = NIL_KLDRADDR; pStackInfo->LinkAddress = NIL_KLDRADDR; pStackInfo->cbStack = pStackInfo->cbStackThread = pModNative->pNtHdrs->OptionalHeader.SizeOfStackReserve; return 0; #elif K_OS == K_OS_DARWIN /** @todo Implement stack info on Darwin. */ (void)pModNative; return KLDR_ERR_TODO; #else # error "Port me" #endif } /** @copydoc kLdrModQueryMainEntrypoint */ static int kldrModNativeQueryMainEntrypoint(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRADDR pMainEPAddress) { PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData; #if K_OS == K_OS_OS2 /** @todo implement me on OS/2. */ (void)pModNative; return ERROR_NOT_SUPPORTED; #elif K_OS == K_OS_WINDOWS || defined(__NT__) /* * Convert the address from the header. */ *pMainEPAddress = pModNative->pNtHdrs->OptionalHeader.AddressOfEntryPoint ? BaseAddress + pModNative->pNtHdrs->OptionalHeader.AddressOfEntryPoint : NIL_KLDRADDR; return 0; #elif K_OS == K_OS_DARWIN /** @todo Implement me on Darwin. */ (void)pModNative; return KLDR_ERR_TODO; #else # error "Port me" #endif } /** @copydoc kLdrModEnumDbgInfo */ static int kldrModNativeEnumDbgInfo(PKLDRMOD pMod, const void *pvBits, PFNKLDRENUMDBG pfnCallback, void *pvUser) { PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData; #if K_OS == K_OS_OS2 /** @todo implement me on OS/2. */ (void)pModNative; return ERROR_NOT_SUPPORTED; #elif K_OS == K_OS_WINDOWS || defined(__NT__) const IMAGE_DEBUG_DIRECTORY *pDbgDir; KU32 iDbgInfo; KU32 cb; int rc; /* * Check that there is a debug directory first. */ cb = pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; if ( cb < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */ || !pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress) return 0; /* * Enumerate the debug directory. */ pDbgDir = KLDRMODNATIVE_RVA2TYPE(pModNative->hmod, pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress, const IMAGE_DEBUG_DIRECTORY *); for (iDbgInfo = 0;; iDbgInfo++, pDbgDir++, cb -= sizeof(IMAGE_DEBUG_DIRECTORY)) { KLDRDBGINFOTYPE enmDbgInfoType; /* convert the type. */ switch (pDbgDir->Type) { case IMAGE_DEBUG_TYPE_UNKNOWN: case IMAGE_DEBUG_TYPE_FPO: case IMAGE_DEBUG_TYPE_COFF: /*stabs dialect??*/ case IMAGE_DEBUG_TYPE_MISC: case IMAGE_DEBUG_TYPE_EXCEPTION: case IMAGE_DEBUG_TYPE_FIXUP: case IMAGE_DEBUG_TYPE_BORLAND: default: enmDbgInfoType = KLDRDBGINFOTYPE_UNKNOWN; break; case IMAGE_DEBUG_TYPE_CODEVIEW: enmDbgInfoType = KLDRDBGINFOTYPE_CODEVIEW; break; } rc = pfnCallback(pMod, iDbgInfo, enmDbgInfoType, pDbgDir->MajorVersion, pDbgDir->MinorVersion, NULL /*pszPartNm*/, pDbgDir->PointerToRawData ? pDbgDir->PointerToRawData : -1, pDbgDir->AddressOfRawData ? pDbgDir->AddressOfRawData : NIL_KLDRADDR, pDbgDir->SizeOfData, NULL /*pszExtFile*/, pvUser); if (rc) break; /* next */ if (cb <= sizeof(IMAGE_DEBUG_DIRECTORY)) break; } return rc; #elif K_OS == K_OS_DARWIN /** @todo Implement me on Darwin. */ (void)pModNative; return KLDR_ERR_TODO; #else # error "Port me" #endif } /** @copydoc kLdrModHasDbgInfo */ static int kldrModNativeHasDbgInfo(PKLDRMOD pMod, const void *pvBits) { PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData; #if K_OS == K_OS_OS2 /** @todo implement me on OS/2. */ (void)pModNative; return KLDR_ERR_NO_DEBUG_INFO; #elif K_OS == K_OS_WINDOWS || defined(__NT__) /* * Base this entirely on the presence of a debug directory. */ if ( pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */ || !pModNative->pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress) return KLDR_ERR_NO_DEBUG_INFO; return 0; #elif K_OS == K_OS_DARWIN /** @todo Implement me on Darwin. */ (void)pModNative; return KLDR_ERR_NO_DEBUG_INFO; #else # error "Port me" #endif } /** @copydoc kLdrModMap */ static int kldrModNativeMap(PKLDRMOD pMod) { return 0; } /** @copydoc kLdrModUnmap */ static int kldrModNativeUnmap(PKLDRMOD pMod) { return 0; } /** @copydoc kLdrModAllocTLS */ static int kldrModNativeAllocTLS(PKLDRMOD pMod, void *pvMapping) { return 0; } /** @copydoc kLdrModFreeTLS */ static void kldrModNativeFreeTLS(PKLDRMOD pMod, void *pvMapping) { } /** @copydoc kLdrModReload */ static int kldrModNativeReload(PKLDRMOD pMod) { return 0; } /** @copydoc kLdrModFixupMapping */ static int kldrModNativeFixupMapping(PKLDRMOD pMod, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) { return 0; } #if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS || defined(__NT__) /** Common worker on platforms where there is one entrypoint which does both. */ static int kLdrModNativeCallInitTerm(PKLDRMODNATIVE pModNative, KUPTR uHandle, KBOOL fInit) { # if K_OS == K_OS_WINDOWS || defined(__NT__) /* No TLS init/term for now. */ KU32 const uRvaEntrypoint = pModNative->pNtHdrs->OptionalHeader.AddressOfEntryPoint; if ( uRvaEntrypoint != 0 && uRvaEntrypoint < pModNative->pNtHdrs->OptionalHeader.SizeOfCode && (pModNative->pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL)) { KI32 rc = kldrModPEDoCall(uRvaEntrypoint + (KUPTR)pModNative->hmod, uHandle, fInit ? DLL_PROCESS_ATTACH : DLL_PROCESS_DETACH, NULL /*pvReserved*/); if (rc) return 0; return fInit ? KLDR_ERR_MODULE_INIT_FAILED : KLDR_ERR_THREAD_ATTACH_FAILED; } # elif K_OS == K_OS_OS2 //KI32 kldrModLXDoCall(KUPTR uEntrypoint, KUPTR uHandle, KU32 uOp, void *pvReserved) # endif return 0; } #endif /** @copydoc kLdrModCallInit */ static int kldrModNativeCallInit(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle) { #if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS || defined(__NT__) if (pMod->fFlags & KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM) return kLdrModNativeCallInitTerm((PKLDRMODNATIVE)pMod->pvData, uHandle, K_TRUE /*fInit*/); #endif return 0; } /** @copydoc kLdrModCallTerm */ static int kldrModNativeCallTerm(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle) { #if K_OS == K_OS_OS2 || K_OS == K_OS_WINDOWS || defined(__NT__) if (pMod->fFlags & KLDRMOD_OPEN_FLAGS_NATIVE_ALLOW_INIT_TERM) return kLdrModNativeCallInitTerm((PKLDRMODNATIVE)pMod->pvData, uHandle, K_FALSE /*fInit*/); #endif return 0; } /** @copydoc kLdrModCallThread */ static int kldrModNativeCallThread(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle, unsigned fAttachingOrDetaching) { return 0; } /** @copydoc kLdrModSize */ static KLDRADDR kldrModNativeSize(PKLDRMOD pMod) { #if K_OS == K_OS_OS2 return 0; /* don't bother */ #elif K_OS == K_OS_WINDOWS || defined(__NT__) /* just because we can. */ PKLDRMODNATIVE pModNative = (PKLDRMODNATIVE)pMod->pvData; return pModNative->pNtHdrs->OptionalHeader.SizeOfImage; #elif K_OS == K_OS_DARWIN /** @todo Implement me on Darwin. */ return 0; #else # error "Port me" #endif } /** @copydoc kLdrModGetBits */ static int kldrModNativeGetBits(PKLDRMOD pMod, void *pvBits, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) { #if K_OS == K_OS_OS2 return ERROR_NOT_SUPPORTED; /* don't bother */ #elif K_OS == K_OS_WINDOWS || defined(__NT__) return ERROR_NOT_SUPPORTED; /* don't bother even if we could implement this. */ #elif K_OS == K_OS_DARWIN return KLDR_ERR_TODO; /* don't bother. */ #else # error "Port me" #endif } /** @copydoc kLdrModRelocateBits */ static int kldrModNativeRelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) { #if K_OS == K_OS_OS2 return ERROR_NOT_SUPPORTED; /* don't bother */ #elif K_OS == K_OS_WINDOWS || defined(__NT__) return ERROR_NOT_SUPPORTED; /* don't bother even if we could implement this. */ #elif K_OS == K_OS_DARWIN return KLDR_ERR_TODO; /* don't bother. */ #else # error "Port me" #endif } /** * The native module interpreter method table. */ KLDRMODOPS g_kLdrModNativeOps = { "Native", NULL, kldrModNativeCreate, kldrModNativeDestroy, kldrModNativeQuerySymbol, kldrModNativeEnumSymbols, kldrModNativeGetImport, kldrModNativeNumberOfImports, NULL /* can execute one is optional */, kldrModNativeGetStackInfo, kldrModNativeQueryMainEntrypoint, NULL /* pfnQueryImageUuid */, NULL /* fixme */, NULL /* fixme */, kldrModNativeEnumDbgInfo, kldrModNativeHasDbgInfo, kldrModNativeMap, kldrModNativeUnmap, kldrModNativeAllocTLS, kldrModNativeFreeTLS, kldrModNativeReload, kldrModNativeFixupMapping, kldrModNativeCallInit, kldrModNativeCallTerm, kldrModNativeCallThread, kldrModNativeSize, kldrModNativeGetBits, kldrModNativeRelocateBits, NULL /* fixme */, 42 /* the end */ };