/* $Id: kLdrModLX.c 2879 2006-11-12 17:21:16Z bird $ */ /** @file * * kLdr - The Module Interpreter for the Linear eXecutable (LX) Format. * * Copyright (c) 2006 knut st. osmundsen * * * This file is part of kLdr. * * kLdr is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * kLdr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with kLdr; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /******************************************************************************* * Header Files * *******************************************************************************/ #include #include "kLdrHlp.h" #include "kLdrInternal.h" #include "kLdrModLX.h" /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** @def KLDRMODLX_STRICT * Define KLDRMODLX_STRICT to enabled strict checks in KLDRMODLX. */ #define KLDRMODLX_STRICT 1 /** @def KLDRMODLX_ASSERT * Assert that an expression is true when KLDR_STRICT is defined. */ #ifdef KLDRMODLX_STRICT # define KLDRMODLX_ASSERT(expr) kldrHlpAssert(expr) #else # define KLDRMODLX_ASSERT(expr) do {} while (0) #endif /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /** * Instance data for the LX module interpreter. */ typedef struct KLDRMODLX { /** Pointer to the module. (Follows the section table.) */ PKLDRMOD pMod; /** Pointer to the user mapping. */ const void *pvMapping; /** The size of the mapped LX image. */ size_t cbMapped; /** Reserved flags. */ uint32_t f32Reserved; /** The offset of the LX header. */ off_t offHdr; /** Copy of the LX header. */ struct e32_exe Hdr; /** Pointer to the loader section. * Allocated together with this strcture. */ const uint8_t *pbLoaderSection; /** Pointer to the last byte in the loader section. */ const uint8_t *pbLoaderSectionLast; /** Pointer to the object table in the loader section. */ const struct o32_obj *paObjs; /** Pointer to the object page map table in the loader section. */ const struct o32_map *paPageMappings; /** Pointer to the resource table in the loader section. */ const struct rsrc32 *paRsrcs; /** Pointer to the resident name table in the loader section. */ const uint8_t *pbResNameTab; /** Pointer to the entry table in the loader section. */ const uint8_t *pbEntryTab; /** Pointer to the non-resident name table. */ uint8_t *pbNonResNameTab; /** Pointer to the last byte in the non-resident name table. */ const uint8_t *pbNonResNameTabLast; /** Pointer to the fixup section. */ uint8_t *pbFixupSection; /** Pointer to the last byte in the fixup section. */ const uint8_t *pbFixupSectionLast; /** Pointer to the fixup page table within pvFixupSection. */ const uint32_t *paoffPageFixups; /** Pointer to the fixup record table within pvFixupSection. */ const uint8_t *pbFixupRecs; /** Pointer to the import module name table within pvFixupSection. */ const uint8_t *pbImportMods; /** Pointer to the import module name table within pvFixupSection. */ const uint8_t *pbImportProcs; } KLDRMODLX, *PKLDRMODLX; /******************************************************************************* * Internal Functions * *******************************************************************************/ static int kldrModLXHasDbgInfo(PKLDRMOD pMod, const void *pvBits); static int kldrModLXRelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser); static int kldrModLXDoCreate(PKLDRRDR pRdr, off_t offNewHdr, PKLDRMODLX *ppModLX); static int kldrModLXDoLoadBits(PKLDRMODLX pModLX, void *pvBits); static int kldrModLXDoProtect(PKLDRMODLX pModLX, void *pvBits, unsigned fUnprotectOrProtect); static int kldrModLXDoCallDLL(PKLDRMODLX pModLX, unsigned uOp, uintptr_t uHandle); static int32_t kldrModLXDoCall(uintptr_t uEntrypoint, uintptr_t uHandle, uint32_t uOp, void *pvReserved); /** * Create a loader module instance interpreting the executable image found * in the specified file provider instance. * * @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 kldrModLXCreate(PCKLDRMODOPS pOps, PKLDRRDR pRdr, off_t offNewHdr, PPKLDRMOD ppMod) { PKLDRMODLX pModLX; int rc; /* * Create the instance data and do a minimal header validation. */ rc = kldrModLXDoCreate(pRdr, offNewHdr, &pModLX); if (!rc) { pModLX->pMod->pOps = pOps; pModLX->pMod->u32Magic = KLDRMOD_MAGIC; *ppMod = pModLX->pMod; return 0; } kldrHlpFree(pModLX); return rc; } /** * Separate function for reading creating the LX module instance to * simplify cleanup on failure. */ static int kldrModLXDoCreate(PKLDRRDR pRdr, off_t offNewHdr, PKLDRMODLX *ppModLX) { struct e32_exe Hdr; PKLDRMODLX pModLX; PKLDRMOD pMod; size_t cb; size_t cchFilename; uint32_t off, offEnd; uint32_t i; int rc; *ppModLX = NULL; /* * Read the signature and file header. */ rc = kLdrRdrRead(pRdr, &Hdr, sizeof(Hdr), offNewHdr > 0 ? offNewHdr : 0); if (rc) return rc; if (Hdr.e32_magic[0] != E32MAGIC1 || Hdr.e32_magic[0] != E32MAGIC2) return KLDR_ERR_UNKNOWN_FORMAT; /* We're not interested in anything but x86 images. */ if ( Hdr.e32_level != E32LEVEL || Hdr.e32_border != E32LEBO || Hdr.e32_worder != E32LEWO || Hdr.e32_cpu < E32CPU286 || Hdr.e32_cpu > E32CPU486 || Hdr.e32_pagesize != OBJPAGELEN ) return KLDR_ERR_LX_BAD_HEADER; /* Some rough sanity checks. */ offEnd = kLdrRdrSize(pRdr) >= (off_t)~(uint32_t)16 ? ~(uint32_t)16 : (uint32_t)kLdrRdrSize(pRdr); if ( Hdr.e32_itermap > offEnd || Hdr.e32_datapage > offEnd || Hdr.e32_nrestab > offEnd || Hdr.e32_nrestab + Hdr.e32_cbnrestab > offEnd || Hdr.e32_ldrsize > offEnd - offNewHdr - sizeof(Hdr) || Hdr.e32_fixupsize > offEnd - offNewHdr - sizeof(Hdr) || Hdr.e32_fixupsize + Hdr.e32_ldrsize > offEnd - offNewHdr - sizeof(Hdr)) return KLDR_ERR_LX_BAD_HEADER; /* Verify the loader section. */ offEnd = Hdr.e32_objtab + Hdr.e32_ldrsize; if (Hdr.e32_objtab < sizeof(Hdr)) return KLDR_ERR_LX_BAD_LOADER_SECTION; off = Hdr.e32_objtab + sizeof(struct o32_obj) * Hdr.e32_objcnt; if (off > offEnd) return KLDR_ERR_LX_BAD_LOADER_SECTION; if ( Hdr.e32_objmap && (Hdr.e32_objmap < off || Hdr.e32_objmap > offEnd)) return KLDR_ERR_LX_BAD_LOADER_SECTION; if ( Hdr.e32_rsrccnt && ( Hdr.e32_rsrctab < off || Hdr.e32_rsrctab > offEnd || Hdr.e32_rsrctab + sizeof(struct rsrc32) * Hdr.e32_rsrccnt > offEnd)) return KLDR_ERR_LX_BAD_LOADER_SECTION; if ( Hdr.e32_restab && (Hdr.e32_restab < off || Hdr.e32_restab > offEnd - 2)) return KLDR_ERR_LX_BAD_LOADER_SECTION; if ( Hdr.e32_enttab && (Hdr.e32_enttab < off || Hdr.e32_enttab > offEnd - 2)) return KLDR_ERR_LX_BAD_LOADER_SECTION; if ( Hdr.e32_dircnt && (Hdr.e32_dirtab < off || Hdr.e32_dirtab > offEnd)) return KLDR_ERR_LX_BAD_LOADER_SECTION; /* Verify the fixup section. */ off = offEnd; offEnd = off + Hdr.e32_fixupsize; if ( Hdr.e32_fpagetab && (Hdr.e32_fpagetab < off || Hdr.e32_fpagetab > offEnd)) return KLDR_ERR_LX_BAD_FIXUP_SECTION; if ( Hdr.e32_frectab && (Hdr.e32_frectab < off || Hdr.e32_frectab > offEnd)) return KLDR_ERR_LX_BAD_FIXUP_SECTION; if ( Hdr.e32_impmod && (Hdr.e32_impmod < off || Hdr.e32_impmod > offEnd || Hdr.e32_impmod + Hdr.e32_impmodcnt > offEnd)) return KLDR_ERR_LX_BAD_FIXUP_SECTION; if ( Hdr.e32_impproc && (Hdr.e32_impproc < off || Hdr.e32_impproc > offEnd)) return KLDR_ERR_LX_BAD_FIXUP_SECTION; /* * Calc the instance size, allocate and initialize it. */ cchFilename = kLdrHlpStrLen(kLdrRdrName(pRdr)); cb = KLDR_ALIGN_Z(sizeof(KLDRMODLX), 8) + KLDR_ALIGN_Z(KLDR_OFFSETOF(KLDRMOD, aSegments[Hdr.e32_objcnt + 1]), 8) + KLDR_ALIGN_Z(cchFilename + 1, 8), + Hdr.e32_ldrsize; pModLX = (PKLDRMODLX)kldrHlpAlloc(cb); if (!pModLX) return KLDR_ERR_NO_MEMORY; *ppModLX = pModLX; /* KLDRMOD */ pMod = (PKLDRMOD)((uint8_t *)pModLX + KLDR_ALIGN_Z(sizeof(KLDRMODLX), 8)); pMod->pvData = pModLX; pMod->pRdr = pRdr; pMod->pOps = NULL; /* set upon success. */ pMod->cSegments = Hdr.e32_objcnt; pMod->cchFilename = cchFilename; pMod->pszFilename = (char *)KLDR_ALIGN_P(&pMod->aSegments[pMod->cSegments], 8); kLdrHlpMemCopy((char *)pMod->pszFilename, kLdrRdrName(pRdr), cchFilename + 1); pMod->pszName = NULL; /* finalized further down */ pMod->cchName = 0; switch (Hdr.e32_cpu) { case E32CPU286: pMod->enmCpu = KLDRCPU_I80286; pMod->enmArch = KLDRARCH_X86_16; break; case E32CPU386: pMod->enmCpu = KLDRCPU_I386; pMod->enmArch = KLDRARCH_X86_32; break; case E32CPU486: pMod->enmCpu = KLDRCPU_I486; pMod->enmArch = KLDRARCH_X86_32; break; } pMod->enmEndian = KLDRENDIAN_LITTLE; pMod->enmFmt = KLDRFMT_LX; switch (Hdr.e32_mflags & E32MODMASK) { case E32MODEXE: pMod->enmType = !(Hdr.e32_mflags & E32NOINTFIX) ? KLDRTYPE_EXECUTABLE_RELOCATABLE : KLDRTYPE_EXECUTABLE_FIXED; break; case E32MODDLL: case E32PROTDLL: case E32MODPROTDLL: pMod->enmType = !(Hdr.e32_mflags & E32SYSDLL) ? KLDRTYPE_SHARED_LIBRARY_RELOCATABLE : KLDRTYPE_SHARED_LIBRARY_FIXED; break; case E32MODPDEV: case E32MODVDEV: pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break; } pMod->u32Magic = 0; /* set upon success. */ /* KLDRMODLX */ pModLX->pMod = pMod; pModLX->pvMapping = 0; pModLX->cbMapped = Hdr.e32_mpages * Hdr.e32_pagesize; pModLX->f32Reserved = 0; pModLX->offHdr = offNewHdr >= 0 ? offNewHdr : 0; pModLX->Hdr = Hdr; pModLX->pbLoaderSection = KLDR_ALIGN_P(pMod->pszFilename + pMod->cchFilename + 1, 16); pModLX->pbLoaderSectionLast = pModLX->pbLoaderSection + pModLX->Hdr.e32_ldrsize; pModLX->paObjs = NULL; pModLX->paPageMappings = NULL; pModLX->paRsrcs = NULL; pModLX->pbResNameTab = NULL; pModLX->pbEntryTab = NULL; pModLX->pbNonResNameTab = NULL; pModLX->pbNonResNameTabLast = NULL; pModLX->pbFixupSection = NULL; pModLX->pbFixupSectionLast = NULL; pModLX->paoffPageFixups = NULL; pModLX->pbFixupRecs = NULL; pModLX->pbImportMods = NULL; pModLX->pbImportProcs = NULL; /* * Read the loader data. */ rc = kLdrRdrRead(pRdr, (void *)pModLX->pbLoaderSection, pModLX->Hdr.e32_ldrsize, pModLX->Hdr.e32_objtab + pModLX->offHdr); if (rc) return rc; if (pModLX->Hdr.e32_objcnt) pModLX->paObjs = (const struct o32_obj *)pModLX->pbLoaderSection; if (pModLX->Hdr.e32_objmap) pModLX->paPageMappings = (const struct o32_map *)(pModLX->pbLoaderSection + pModLX->Hdr.e32_objmap - pModLX->Hdr.e32_objtab); if (pModLX->Hdr.e32_rsrccnt) pModLX->paRsrcs = (const struct rsrc32 *)(pModLX->pbLoaderSection + pModLX->Hdr.e32_rsrctab - pModLX->Hdr.e32_objtab); if (pModLX->Hdr.e32_restab) pModLX->pbResNameTab = pModLX->pbLoaderSection + pModLX->Hdr.e32_restab - pModLX->Hdr.e32_objtab; if (pModLX->Hdr.e32_enttab) pModLX->pbEntryTab = pModLX->pbLoaderSection + pModLX->Hdr.e32_enttab - pModLX->Hdr.e32_objtab; /* * Get the soname from the resident name table. */ /** @todo */ /* * Quick validation of the object table. */ cb = 0; for (i = 0; i < pMod->cSegments; i++) { if (pModLX->paObjs[i].o32_base & (OBJPAGELEN - 1)) return KLDR_ERR_LX_BAD_OBJECT_TABLE; if (pModLX->paObjs[i].o32_base + pModLX->paObjs[i].o32_size <= pModLX->paObjs[i].o32_base) return KLDR_ERR_LX_BAD_OBJECT_TABLE; if (pModLX->paObjs[i].o32_mapsize > pModLX->paObjs[i].o32_size / OBJPAGELEN) return KLDR_ERR_LX_BAD_OBJECT_TABLE; if ( pModLX->paObjs[i].o32_mapsize && ( (uint8_t *)&pModLX->paPageMappings[pModLX->paObjs[i].o32_pagemap] > pModLX->pbLoaderSectionLast || (uint8_t *)&pModLX->paPageMappings[pModLX->paObjs[i].o32_pagemap + pModLX->paObjs[i].o32_mapsize] > pModLX->pbLoaderSectionLast)) return KLDR_ERR_LX_BAD_OBJECT_TABLE; if (i > 0) { if (pModLX->paObjs[i].o32_base <= pModLX->paObjs[i - 1].o32_base) return KLDR_ERR_LX_BAD_OBJECT_TABLE; if (pModLX->paObjs[i].o32_base < pModLX->paObjs[i - 1].o32_base + pModLX->paObjs[i - 1].o32_mapsize) return KLDR_ERR_LX_BAD_OBJECT_TABLE; } } pModLX->cbMapped = pMod->cSegments ? pModLX->paObjs[pMod->cSegments - 1].o32_base + pModLX->paObjs[pMod->cSegments - 1].o32_size - pModLX->paObjs[pMod->cSegments - 1].o32_size : 0; /* * Setup the KLDRMOD segment array. */ for (i = 0; i < pMod->cSegments; i++) { /* unused */ pMod->aSegments[i].pvUser = NULL; pMod->aSegments[i].MapAddress = 0; pMod->aSegments[i].pchName = NULL; pMod->aSegments[i].cchName = 0; pMod->aSegments[i].offFile = -1; pMod->aSegments[i].cbFile = 0; /* size and addresses */ pMod->aSegments[i].Alignment = OBJPAGELEN; pMod->aSegments[i].cb = pModLX->paObjs[i].o32_size; pMod->aSegments[i].LinkAddress = pModLX->paObjs[i].o32_base; pMod->aSegments[i].RVA = pModLX->paObjs[i].o32_base - pModLX->paObjs[0].o32_base; pMod->aSegments[i].cbMapped = KLDR_ALIGN_Z(pModLX->paObjs[i].o32_size, 16); if (i + 1 < pMod->cSegments) pMod->aSegments[i].cbMapped = pModLX->paObjs[i + 1].o32_base - pModLX->paObjs[i].o32_base; /* protection */ switch ( pModLX->paObjs[i].o32_flags & (OBJSHARED | OBJREAD | OBJWRITE | OBJEXEC)) { case 0: case OBJSHARED: pMod->aSegments[i].enmProt = KLDRPROT_NOACCESS; break; case OBJREAD: case OBJREAD | OBJSHARED: pMod->aSegments[i].enmProt = KLDRPROT_READONLY; break; case OBJWRITE: case OBJWRITE | OBJREAD: pMod->aSegments[i].enmProt = KLDRPROT_WRITECOPY; break; case OBJWRITE | OBJSHARED: case OBJWRITE | OBJSHARED | OBJREAD: pMod->aSegments[i].enmProt = KLDRPROT_READWRITE; break; case OBJEXEC: case OBJEXEC | OBJSHARED: pMod->aSegments[i].enmProt = KLDRPROT_EXECUTE; break; case OBJEXEC | OBJREAD: case OBJEXEC | OBJREAD | OBJSHARED: pMod->aSegments[i].enmProt = KLDRPROT_EXECUTE_READ; break; case OBJEXEC | OBJWRITE: case OBJEXEC | OBJWRITE | OBJREAD: pMod->aSegments[i].enmProt = KLDRPROT_EXECUTE_WRITECOPY; break; case OBJEXEC | OBJWRITE | OBJSHARED: case OBJEXEC | OBJWRITE | OBJSHARED | OBJREAD: pMod->aSegments[i].enmProt = KLDRPROT_EXECUTE_READWRITE; break; } if ((pModLX->paObjs[i].o32_flags & (OBJREAD | OBJWRITE | OBJEXEC | OBJRSRC)) == OBJRSRC) pMod->aSegments[i].enmProt = KLDRPROT_READONLY; //pMod->aSegments[i].f16bit = !(pModLX->paObjs[i].o32_flags & OBJBIGDEF) //pMod->aSegments[i].fIOPL = !(pModLX->paObjs[i].o32_flags & OBJIOPL) //pMod->aSegments[i].fConforming = !(pModLX->paObjs[i].o32_flags & OBJCONFORM) } /* * We're done. */ *ppModLX = pModLX; return 0; } /** @copydoc KLDRMODOPS::pfnDestroy */ static int kldrModLXDestroy(PKLDRMOD pMod) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; int rc = 0; KLDRMODLX_ASSERT(pModLX->pvMapping); if (pMod->pRdr) { rc = kLdrRdrClose(pMod->pRdr); pMod->pRdr = NULL; } if (pModLX->pbNonResNameTab) { kldrHlpFree(pModLX->pbNonResNameTab); pModLX->pbNonResNameTab = NULL; } if (pModLX->pbFixupSection) { kldrHlpFree(pModLX->pbFixupSection); pModLX->pbFixupSection = NULL; } if (pMod->pszName) { kldrHlpFree((void *)pMod->pszName); pMod->pszName = NULL; } pMod->u32Magic = 0; pMod->pOps = NULL; kldrHlpFree(pModLX); return rc; } /** * Resolved base address aliases. * * @param pModLX The interpreter module instance * @param pBaseAddress The base address, IN & OUT. */ static void kldrModLXResolveBaseAddress(PKLDRMODLX pModLX, PKLDRADDR pBaseAddress) { if (*pBaseAddress == KLDRMOD_BASEADDRESS_MAP) *pBaseAddress = pModLX->pMod->aSegments[0].MapAddress; else if (*pBaseAddress == KLDRMOD_BASEADDRESS_LINK) *pBaseAddress = pModLX->pMod->aSegments[0].LinkAddress; } /** @copydoc kLdrModQuerySymbol */ static int kldrModLXQuerySymbol(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, uint32_t iSymbol, const char *pszSymbol, PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, uint32_t *pfKind) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; //int rc; kldrModLXResolveBaseAddress(pModLX, &BaseAddress); return -1; } /** @copydoc kLdrModEnumSymbols */ static int kldrModLXEnumSymbols(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, uint32_t fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; // int rc; kldrModLXResolveBaseAddress(pModLX, &BaseAddress); return -1; } /** @copydoc kLdrModGetImport */ static int kldrModLXGetImport(PKLDRMOD pMod, const void *pvBits, uint32_t iImport, char *pszName, size_t cchName) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; const uint8_t * pb = NULL; int rc; /** @todo */ if (*pb < cchName) { kLdrHlpMemCopy(pszName, pb + 1, *pb); pszName[*pb] = '\0'; rc = 0; } else { kLdrHlpMemCopy(pszName, pb + 1, cchName); if (cchName) pszName[cchName - 1] = '\0'; rc = KLDR_ERR_BUFFER_OVERFLOW; } return rc; } /** @copydoc kLdrModNumberOfImports */ static int32_t kldrModLXNumberOfImports(PKLDRMOD pMod, const void *pvBits) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; return pModLX->Hdr.e32_impmodcnt; } /** @copydoc kLdrModGetStackInfo */ static int kldrModLXGetStackInfo(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; const uint32_t i = pModLX->Hdr.e32_stackobj; if ( i && i <= pMod->cSegments && pModLX->Hdr.e32_esp <= pMod->aSegments[i - 1].LinkAddress + pMod->aSegments[i - 1].cb && pModLX->Hdr.e32_stacksize && pModLX->Hdr.e32_esp - pModLX->Hdr.e32_stacksize >= pMod->aSegments[i - 1].LinkAddress) { kldrModLXResolveBaseAddress(pModLX, &BaseAddress); pStackInfo->LinkAddress = pModLX->Hdr.e32_esp - pModLX->Hdr.e32_stacksize; pStackInfo->Address = BaseAddress + pMod->aSegments[i - 1].RVA + pModLX->Hdr.e32_esp - pModLX->Hdr.e32_stacksize - pMod->aSegments[i - 1].LinkAddress; } else { pStackInfo->Address = NIL_KLDRADDR; pStackInfo->LinkAddress = NIL_KLDRADDR; } pStackInfo->cbStack = pModLX->Hdr.e32_stacksize; pStackInfo->cbStackThread = 0; return 0; } /** @copydoc kLdrModQueryMainEntrypoint */ static int kldrModLXQueryMainEntrypoint(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRADDR pMainEPAddress) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; /* * Convert the address from the header. */ kldrModLXResolveBaseAddress(pModLX, &BaseAddress); *pMainEPAddress = pModLX->Hdr.e32_startobj && pModLX->Hdr.e32_startobj <= pMod->cSegments && pModLX->Hdr.e32_eip < pMod->aSegments[pModLX->Hdr.e32_startobj - 1].cb ? BaseAddress + pMod->aSegments[pModLX->Hdr.e32_startobj - 1].RVA + pModLX->Hdr.e32_eip : NIL_KLDRADDR; return 0; } /** @copydoc kLdrModEnumDbgInfo */ static int kldrModLXEnumDbgInfo(PKLDRMOD pMod, const void *pvBits, PFNKLDRENUMDBG pfnCallback, void *pvUser) { //PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; /* * Quit immediately if no debug info. */ if (kldrModLXHasDbgInfo(pMod, pvBits)) return 0; #if 0 /* * Read the debug info and look for familiar magics and structures. */ /** @todo */ #endif return 0; } /** @copydoc kLdrModHasDbgInfo */ static int kldrModLXHasDbgInfo(PKLDRMOD pMod, const void *pvBits) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; /* * Don't curretnly bother with linkers which doesn't advertise it in the header. */ if ( !pModLX->Hdr.e32_debuginfo || !pModLX->Hdr.e32_debuglen) return KLDR_ERR_NO_DEBUG_INFO; return 0; } /** @copydoc kLdrModMap */ static int kldrModLXMap(PKLDRMOD pMod) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; unsigned fFixed; void *pvBase; int rc; /* * Already mapped? */ if (pModLX->pvMapping) return KLDR_ERR_ALREADY_MAPPED; /* * Allocate memory for it. */ /* fixed image? */ fFixed = pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED; if (!fFixed) pvBase = NULL; else { pvBase = (void *)(uintptr_t)pMod->aSegments[0].LinkAddress; if ((uintptr_t)pvBase != pMod->aSegments[0].LinkAddress) return KLDR_ERR_ADDRESS_OVERFLOW; } rc = kldrHlpPageAlloc(&pvBase, pModLX->cbMapped, KLDRPROT_EXECUTE_READWRITE, fFixed); if (rc) return rc; /* * Load the bits, apply page protection, and update the segment table. */ rc = kldrModLXDoLoadBits(pModLX, pvBase); if (!rc) rc = kldrModLXDoProtect(pModLX, pvBase, 0 /* protect */); if (!rc) { uint32_t i; for (i = 0; i < pMod->cSegments; i++) { if (pMod->aSegments[i].RVA != NIL_KLDRADDR) pMod->aSegments[i].MapAddress = (uintptr_t)pvBase + (uintptr_t)pMod->aSegments[i].RVA; } pModLX->pvMapping = pvBase; } else kldrHlpPageFree(pvBase, pModLX->cbMapped); return rc; } /** * Loads the LX pages into the specified memory mapping. * * @returns 0 on success. * @returns non-zero kLdr or OS status code on failure. * * @param pModLX The LX module interpreter instance. * @param pvBits Where to load the bits. */ static int kldrModLXDoLoadBits(PKLDRMODLX pModLX, void *pvBits) { //E32NOLOAD return -1; } /** * Unprotects or protects the specified image mapping. * * @returns 0 on success. * @returns non-zero kLdr or OS status code on failure. * * @param pModLX The LX module interpreter instance. * @param pvBits The mapping to protect. * @param UnprotectOrProtect If 1 unprotect (i.e. make all writable), otherwise * protect according to the object table. */ static int kldrModLXDoProtect(PKLDRMODLX pMod, void *pvBits, unsigned fUnprotectOrProtect) { return -1; } /** @copydoc kLdrModUnmap */ static int kldrModLXUnmap(PKLDRMOD pMod) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; uint32_t i; int rc; /* * Mapped? */ if (!pModLX->pvMapping) return KLDR_ERR_NOT_MAPPED; /* * Free the mapping and update the segments. */ rc = kldrHlpPageFree((void *)pModLX->pvMapping, pModLX->cbMapped); KLDRMODLX_ASSERT(!rc); pModLX->pvMapping = NULL; for (i = 0; i < pMod->cSegments; i++) pMod->aSegments[i].MapAddress = 0; return rc; } /** @copydoc kLdrModAllocTLS */ static int kldrModLXAllocTLS(PKLDRMOD pMod) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; /* just do the error checking. */ if (!pModLX->pvMapping) return KLDR_ERR_NOT_MAPPED; return 0; } /** @copydoc kLdrModFreeTLS */ static void kldrModLXFreeTLS(PKLDRMOD pMod) { } /** @copydoc kLdrModReload */ static int kldrModLXReload(PKLDRMOD pMod) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; /* * Mapped? */ if (!pModLX->pvMapping) return KLDR_ERR_NOT_MAPPED; /* the file provider does it all */ return kLdrRdrRefresh(pMod->pRdr, (void *)pModLX->pvMapping, pMod->cSegments, pMod->aSegments); } /** @copydoc kLdrModFixupMapping */ static int kldrModLXFixupMapping(PKLDRMOD pMod, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; int rc, rc2; /* * Mapped? */ if (!pModLX->pvMapping) return KLDR_ERR_NOT_MAPPED; /* * Before doing anything we'll have to make all pages writable. */ rc = kLdrRdrProtect(pMod->pRdr, (void *)pModLX->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */); if (rc) return rc; /* * Apply fixups and resolve imports. */ rc = kldrModLXRelocateBits(pMod, (void *)pModLX->pvMapping, (uintptr_t)pModLX->pvMapping, pMod->aSegments[0].LinkAddress, pfnGetImport, pvUser); /* * Restore protection. */ rc2 = kLdrRdrProtect(pMod->pRdr, (void *)pModLX->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */); if (!rc && rc2) rc = rc2; return rc; } /** @copydoc kLdrModCallInit */ static int kldrModLXCallInit(PKLDRMOD pMod, uintptr_t uHandle) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; int rc; /* * Mapped? */ if (!pModLX->pvMapping) return KLDR_ERR_NOT_MAPPED; /* * Do TLS callbacks first and then call the init/term function if it's a DLL. */ if ((pModLX->Hdr.e32_mflags & E32MODMASK) == E32MODDLL) rc = kldrModLXDoCallDLL(pModLX, 0 /* attach */, uHandle); else rc = 0; return rc; } /** * Call the DLL entrypoint. * * @returns 0 on success. * @returns KLDR_ERR_MODULE_INIT_FAILED or KLDR_ERR_THREAD_ATTACH_FAILED on failure. * @param pModLX The LX module interpreter instance. * @param uOp The operation (DLL_*). * @param uHandle The module handle to present. */ static int kldrModLXDoCallDLL(PKLDRMODLX pModLX, unsigned uOp, uintptr_t uHandle) { int rc; /* * If no entrypoint there isn't anything to be done. */ if ( !pModLX->Hdr.e32_startobj || pModLX->Hdr.e32_startobj > pModLX->Hdr.e32_objcnt) return 0; /* * Invoke the entrypoint and convert the boolean result to a kLdr status code. */ rc = kldrModLXDoCall((uintptr_t)pModLX->pvMapping + (uintptr_t)pModLX->pMod->aSegments[pModLX->Hdr.e32_startobj - 1].RVA + pModLX->Hdr.e32_eip, uHandle, uOp, NULL); if (rc) rc = 0; else if (uOp == 0 /* attach */) rc = KLDR_ERR_MODULE_INIT_FAILED; else /* detach: ignore failures */ rc = 0; return rc; } /** * Do a 3 parameter callback. * * @returns 32-bit callback return. * @param uEntrypoint The address of the function to be called. * @param uHandle The first argument, the module handle. * @param uOp The second argumnet, the reason we're calling. * @param pvReserved The third argument, reserved argument. (figure this one out) */ static int32_t kldrModLXDoCall(uintptr_t uEntrypoint, uintptr_t uHandle, uint32_t uOp, void *pvReserved) { #if defined(__X86__) || defined(__i386__) || defined(_M_IX86) int32_t rc; /** @todo try/except */ /* * Paranoia. */ # ifdef __GNUC__ __asm__ __volatile__( "pushl %2\n\t" "pushl %1\n\t" "pushl %0\n\t" "lea 12(%%esp), %2\n\t" "call *%3\n\t" "movl %2, %%esp\n\t" : "=a" (rc) : "d" (uOp), "S" (0), "c" (uEntrypoint), "0" (uHandle)); # elif defined(_MSC_VER) __asm { mov eax, [uHandle] mov edx, [uOp] mov ecx, 0 mov ebx, [uEntrypoint] push edi mov edi, esp push ecx push edx push eax call ebx mov esp, edi pop edi mov [rc], eax } # else # error "port me!" # endif return rc; #else return KLDR_ERR_ARCH_CPU_NOT_COMPATIBLE; #endif } /** @copydoc kLdrModCallTerm */ static int kldrModLXCallTerm(PKLDRMOD pMod, uintptr_t uHandle) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; /* * Mapped? */ if (!pModLX->pvMapping) return KLDR_ERR_NOT_MAPPED; /* * Do the call. */ if ((pModLX->Hdr.e32_mflags & E32MODMASK) == E32MODDLL) kldrModLXDoCallDLL(pModLX, 1 /* detach */, uHandle); return 0; } /** @copydoc kLdrModCallThread */ static int kldrModLXCallThread(PKLDRMOD pMod, uintptr_t uHandle, unsigned fAttachingOrDetaching) { return 0; } /** @copydoc kLdrModSize */ static KLDRADDR kldrModLXSize(PKLDRMOD pMod) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; return pModLX->cbMapped; } /** @copydoc kLdrModGetBits */ static int kldrModLXGetBits(PKLDRMOD pMod, void *pvBits, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) { PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; int rc; /* * Load the image bits. */ rc = kldrModLXDoLoadBits(pModLX, pvBits); if (rc) return rc; /* * Perform relocations. */ return kldrModLXRelocateBits(pMod, pvBits, BaseAddress, pMod->aSegments[0].LinkAddress, pfnGetImport, pvUser); } /** @copydoc kLdrModRelocateBits */ static int kldrModLXRelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) { //PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData; //int rc; /** @todo Implement this. */ return -1; } /** * The LX module interpreter method table. */ KLDRMODOPS g_kLdrModLXOps = { "LX", NULL, kldrModLXCreate, kldrModLXDestroy, kldrModLXQuerySymbol, kldrModLXEnumSymbols, kldrModLXGetImport, kldrModLXNumberOfImports, NULL /* can execute one is optional */, kldrModLXGetStackInfo, kldrModLXQueryMainEntrypoint, kldrModLXEnumDbgInfo, kldrModLXHasDbgInfo, kldrModLXMap, kldrModLXUnmap, kldrModLXAllocTLS, kldrModLXFreeTLS, kldrModLXReload, kldrModLXFixupMapping, kldrModLXCallInit, kldrModLXCallTerm, kldrModLXCallThread, kldrModLXSize, kldrModLXGetBits, kldrModLXRelocateBits, 42 /* the end */ };