/* $Id: $ */ /** @file * * kLdr - The Module Interpreter for the MACH-O 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 "kLdrModMachO.h" /* quick hack for now. */ typedef struct nlist { uint32_t n_strx; uint8_t n_type; int8_t n_other; int16_t n_desc; uint32_t n_value; } nlist_t; /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** @def KLDRMODMACHO_STRICT * Define KLDRMODMACHO_STRICT to enabled strict checks in KLDRMODMACHO. */ #define KLDRMODMACHO_STRICT 1 /** @def KLDRMODMACHO_ASSERT * Assert that an expression is true when KLDR_STRICT is defined. */ #ifdef KLDRMODMACHO_STRICT # define KLDRMODMACHO_ASSERT(expr) kldrHlpAssert(expr) #else # define KLDRMODMACHO_ASSERT(expr) do {} while (0) #endif /** @def KLDRMODMACHO_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 KLDRMODMACHO_RVA2TYPE(pvBits, uRVA, type) \ ( (type) ((uintptr_t)(pvBits) + (uintptr_t)(uRVA)) ) /** @def KLDRMODMACHO_VALID_RVA * Checks that the specified RVA value is non-zero and within the bounds of the image. * @returns true/false. * @param pModPE The PE module interpreter instance. * @param uRVA The RVA to validate. */ #define KLDRMODMACHO_VALID_RVA(pModPE, uRVA) \ ( (uRVA) && (uRVA) < (pModPE)->Hdrs.OptionalHeader.SizeOfImage ) /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /** * Instance data for the Mach-O MH_OBJECT module interpreter. * @todo interpret the other MH_* formats. */ typedef struct KLDRMODMACHO { /** Pointer to the module. (Follows the section table.) */ PKLDRMOD pMod; /** Pointer to the RDR mapping of the raw file bits. NULL if not mapped. */ const void *pvBits; /** Pointer to the user mapping. */ const void *pvMapping; /** The link address. */ KLDRADDR LinkAddress; /** The size of the mapped image. */ KLDRADDR cbImage; /** When set the load commands will be used when mapping / loading the bits. * This is the case when segments are made up of sections that doesn't have * proper ordering and/or aligning in the file alignment. */ int fMapUsingLoadCommands; /** Pointer to the load commands. (endian converted) */ uint8_t *pbLoadCommands; /** The Mach-O header. (endian converted) * @remark The reserved field is only valid for real 64-bit headers. */ mach_header_64_t Hdr; /** The offset of the symbol table. */ off_t offSymbols; /** The number of symbols. */ uint32_t cSymbols; /** The pointer to the loaded symbol table. */ void *paSymbols; /** The offset of the string table. */ off_t offStrings; /** The size of the of the string table. */ uint32_t cbStrings; /** Pointer to the loaded string table. */ char *pbStrings; } KLDRMODMACHO, *PKLDRMODMACHO; /******************************************************************************* * Internal Functions * *******************************************************************************/ #if 0 static int32_t kldrModMachONumberOfImports(PKLDRMOD pMod, const void *pvBits); static int kldrModMachORelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser); #endif static int kldrModMachODoCreate(PKLDRRDR pRdr, PKLDRMODMACHO *ppMod); static int kldrModMachOPreParseLoadCommands(uint8_t *pbLoadCommands, const mach_header_32_t *pHdr, PKLDRRDR pRdr, uint32_t *pcSegments, uint32_t *pcbStringPool); static int kldrModMachOParseLoadCommands(PKLDRMODMACHO pModMachO, char *pbStringPool, uint32_t cbStringPool); #if 0 /*static void kldrModMachODoLoadConfigConversion(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg); */ static int kLdrModPEDoOptionalHeaderValidation(PKLDRMODMACHO pModMachO); static int kLdrModPEDoSectionHeadersValidation(PKLDRMODMACHO pModMachO); static int kldrModMachODoForwarderQuery(PKLDRMODMACHO pModMachO, const void *pvBits, const char *pszForwarder, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser, PKLDRADDR puValue, uint32_t *pfKind); static int kldrModMachODoFixups(PKLDRMODMACHO pModMachO, void *pvMapping, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress); static int kldrModMachODoImports32Bit(PKLDRMODMACHO pModMachO, void *pvMapping, const IMAGE_IMPORT_DESCRIPTOR *pImpDesc, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser); static int kldrModMachODoImports64Bit(PKLDRMODMACHO pModMachO, void *pvMapping, const IMAGE_IMPORT_DESCRIPTOR *pImpDesc, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser); static int kldrModMachODoImports(PKLDRMODMACHO pModMachO, void *pvMapping, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser); static int kldrModMachODoCallDLL(PKLDRMODMACHO pModMachO, unsigned uOp, uintptr_t uHandle); static int kldrModMachODoCallTLS(PKLDRMODMACHO pModMachO, unsigned uOp, uintptr_t uHandle); static int32_t kldrModMachODoCall(uintptr_t uEntrypoint, uintptr_t uHandle, uint32_t uOp, void *pvReserved); #endif /** * 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 kldrModMachOCreate(PCKLDRMODOPS pOps, PKLDRRDR pRdr, off_t offNewHdr, PPKLDRMOD ppMod) { PKLDRMODMACHO pModMachO; int rc; /* * Create the instance data and do a minimal header validation. */ rc = kldrModMachODoCreate(pRdr, &pModMachO); if (!rc) { pModMachO->pMod->pOps = pOps; pModMachO->pMod->u32Magic = KLDRMOD_MAGIC; *ppMod = pModMachO->pMod; return 0; } if (pModMachO) { kldrHlpFree(pModMachO->pbLoadCommands); kldrHlpFree(pModMachO); } return rc; } /** * Separate function for reading creating the PE module instance to * simplify cleanup on failure. */ static int kldrModMachODoCreate(PKLDRRDR pRdr, PKLDRMODMACHO *ppModMachO) { union { mach_header_32_t Hdr32; mach_header_64_t Hdr64; } s; PKLDRMODMACHO pModMachO; PKLDRMOD pMod; uint8_t *pbLoadCommands; uint32_t cSegments; uint32_t cbStringPool; size_t cchFilename; size_t cb; int rc; *ppModMachO = NULL; /* * Read the Mach-O header. */ rc = kLdrRdrRead(pRdr, &s, sizeof(s), 0); if (rc) return rc; if (s.Hdr32.magic != IMAGE_MACHO32_SIGNATURE) { if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE || s.Hdr32.magic == IMAGE_MACHO64_SIGNATURE_OE) return KLDR_ERR_MACHO_OTHER_ENDIAN_NOT_SUPPORTED; if (s.Hdr32.magic == IMAGE_MACHO64_SIGNATURE) return KLDR_ERR_MACHO_64BIT_NOT_SUPPORTED; return KLDR_ERR_UNKNOWN_FORMAT; } /* sanity checks. */ if ( s.Hdr32.sizeofcmds > kLdrRdrSize(pRdr) - sizeof(mach_header_32_t) || s.Hdr32.sizeofcmds < sizeof(load_command_t) * s.Hdr32.ncmds || (s.Hdr32.flags & ~MH_VALID_FLAGS)) return KLDR_ERR_MACHO_BAD_HEADER; switch (s.Hdr32.cputype) { case CPU_TYPE_X86: case CPU_TYPE_X86_64: break; default: return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE; } if (s.Hdr32.filetype != MH_OBJECT) return KLDR_ERR_MACHO_UNSUPPORTED_FILE_TYPE; /* * Read and pre-parse the load commands to figure out how many segments we'll be needing. */ pbLoadCommands = kldrHlpAlloc(s.Hdr32.sizeofcmds); if (!pbLoadCommands) return KLDR_ERR_NO_MEMORY; rc = kLdrRdrRead(pRdr, pbLoadCommands, s.Hdr32.sizeofcmds, s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE ? sizeof(mach_header_32_t) : sizeof(mach_header_64_t)); if (!rc) rc = kldrModMachOPreParseLoadCommands(pbLoadCommands, &s.Hdr32, pRdr, &cSegments, &cbStringPool); if (rc) { kldrHlpFree(pbLoadCommands); return rc; } /* * Calc the instance size, allocate and initialize it. */ cchFilename = kLdrHlpStrLen(kLdrRdrName(pRdr)); cb = KLDR_ALIGN_Z(sizeof(KLDRMODMACHO), 16) + KLDR_OFFSETOF(KLDRMOD, aSegments[cSegments]) + cchFilename + 1 + cbStringPool; pModMachO = (PKLDRMODMACHO)kldrHlpAlloc(cb); if (!pModMachO) return KLDR_ERR_NO_MEMORY; *ppModMachO = pModMachO; pModMachO->pbLoadCommands = pbLoadCommands; /* KLDRMOD */ pMod = (PKLDRMOD)((uint8_t *)pModMachO + KLDR_ALIGN_Z(sizeof(KLDRMODMACHO), 16)); pMod->pvData = pModMachO; pMod->pRdr = pRdr; pMod->pOps = NULL; /* set upon success. */ pMod->cSegments = cSegments; pMod->cchFilename = cchFilename; pMod->pszFilename = (char *)&pMod->aSegments[pMod->cSegments]; kLdrHlpMemCopy((char *)pMod->pszFilename, kLdrRdrName(pRdr), cchFilename + 1); pMod->pszName = kldrHlpGetFilename(pMod->pszFilename); pMod->cchName = cchFilename - (pMod->pszName - pMod->pszFilename); switch (s.Hdr32.cputype) { case CPU_TYPE_X86: pMod->enmArch = KLDRARCH_X86_32; pMod->enmEndian = KLDRENDIAN_LITTLE; switch (s.Hdr32.cpusubtype) { case CPU_SUBTYPE_I386_ALL: pMod->enmCpu = KLDRCPU_X86_32_BLEND; break; /*case CPU_SUBTYPE_386: ^^ pMod->enmCpu = KLDRCPU_I386; break;*/ case CPU_SUBTYPE_486: pMod->enmCpu = KLDRCPU_I486; break; case CPU_SUBTYPE_486SX: pMod->enmCpu = KLDRCPU_I486SX; break; /*case CPU_SUBTYPE_586: vv */ case CPU_SUBTYPE_PENT: pMod->enmCpu = KLDRCPU_I586; break; case CPU_SUBTYPE_PENTPRO: case CPU_SUBTYPE_PENTII_M3: case CPU_SUBTYPE_PENTII_M5: case CPU_SUBTYPE_CELERON: case CPU_SUBTYPE_CELERON_MOBILE: case CPU_SUBTYPE_PENTIUM_3: case CPU_SUBTYPE_PENTIUM_3_M: case CPU_SUBTYPE_PENTIUM_3_XEON: pMod->enmCpu = KLDRCPU_I686; break; case CPU_SUBTYPE_PENTIUM_M: case CPU_SUBTYPE_PENTIUM_4: case CPU_SUBTYPE_PENTIUM_4_M: case CPU_SUBTYPE_XEON: case CPU_SUBTYPE_XEON_MP: pMod->enmCpu = KLDRCPU_P4; break; break; default: return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE; } break; case CPU_TYPE_X86_64: pMod->enmArch = KLDRARCH_AMD64; pMod->enmEndian = KLDRENDIAN_LITTLE; switch (s.Hdr32.cpusubtype) { case CPU_SUBTYPE_X86_64_ALL: pMod->enmCpu = KLDRCPU_AMD64_BLEND; break; default: return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE; } break; default: return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE; } pMod->enmFmt = KLDRFMT_MACHO; switch (s.Hdr32.filetype) { case MH_OBJECT: pMod->enmType = KLDRTYPE_OBJECT; break; default: return KLDR_ERR_MACHO_UNSUPPORTED_FILE_TYPE; } pMod->u32Magic = 0; /* set upon success. */ /* KLDRMODMACHO */ pModMachO->pMod = pMod; pModMachO->pvBits = NULL; pModMachO->pvMapping = NULL; pModMachO->Hdr = s.Hdr64; if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE) pModMachO->Hdr.reserved = 0; pModMachO->LinkAddress = 0; pModMachO->cbImage = 0; pModMachO->fMapUsingLoadCommands = 0; pModMachO->offSymbols = 0; pModMachO->cSymbols = 0; pModMachO->paSymbols = NULL; pModMachO->offStrings = 0; pModMachO->cbStrings = 0; pModMachO->pbStrings = NULL; /* * Setup the KLDRMOD segment array. */ rc = kldrModMachOParseLoadCommands(pModMachO, (char *)pMod->pszFilename + pMod->cchFilename + 1, cbStringPool); if (rc) return rc; /* * We're done. */ return 0; } /** * Converts, validates and preparses the load commands before we carve * out the module instance. * * The conversion that's preformed is format endian to host endian. * The preparsing has to do with segment counting and string pool sizing. * * @returns 0 on success. * @returns KLDR_ERR_MACHO_* on failure. * @param pbLoadCommands The load commands to parse. * @param pHdr The header. * @param pRdr The file reader. * @param pcSegments Where to store the segment count. * @param pcbStringPool Where to store the string pool size. */ static int kldrModMachOPreParseLoadCommands(uint8_t *pbLoadCommands, const mach_header_32_t *pHdr, PKLDRRDR pRdr, uint32_t *pcSegments, uint32_t *pcbStringPool) { union { uint8_t *pb; load_command_t *pLoadCmd; segment_command_32_t *pSeg32; segment_command_64_t *pSeg64; thread_command_t *pThread; symtab_command_t *pSymTab; uuid_command_t *pUuid; } u; const uint64_t cbFile = kLdrRdrSize(pRdr); uint32_t cSegments = 0; uint32_t cbStringPool = 0; uint32_t cLeft = pHdr->ncmds; uint32_t cbLeft = pHdr->sizeofcmds; uint8_t *pb = pbLoadCommands; int cSegmentCommands = 0; int fConvertEndian = pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE || pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE; *pcSegments = 0; *pcbStringPool = 0; while (cLeft-- > 0) { u.pb = pb; /* * Convert and validate command header. */ if (cbLeft < sizeof(load_command_t)) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; if (fConvertEndian) { u.pLoadCmd->cmd = KLDRHLP_E2E_U32(u.pLoadCmd->cmd); u.pLoadCmd->cmdsize = KLDRHLP_E2E_U32(u.pLoadCmd->cmdsize); } if (u.pLoadCmd->cmdsize > cbLeft) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; cbLeft -= u.pLoadCmd->cmdsize; pb += u.pLoadCmd->cmdsize; /* * Convert endian if needed, parse and validate the command. */ switch (u.pLoadCmd->cmd) { case LC_SEGMENT_32: { section_32_t *pSect; section_32_t *pFirstSect; uint32_t cSectionsLeft; /* convert and verify*/ if (u.pLoadCmd->cmdsize < sizeof(segment_command_32_t)) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; if ( pHdr->magic != IMAGE_MACHO32_SIGNATURE_OE && pHdr->magic != IMAGE_MACHO32_SIGNATURE) return KLDR_ERR_MACHO_BIT_MIX; if (fConvertEndian) { u.pSeg32->vmaddr = KLDRHLP_E2E_U32(u.pSeg32->vmaddr); u.pSeg32->vmsize = KLDRHLP_E2E_U32(u.pSeg32->vmsize); u.pSeg32->fileoff = KLDRHLP_E2E_U32(u.pSeg32->fileoff); u.pSeg32->filesize = KLDRHLP_E2E_U32(u.pSeg32->filesize); u.pSeg32->maxprot = KLDRHLP_E2E_U32(u.pSeg32->maxprot); u.pSeg32->initprot = KLDRHLP_E2E_U32(u.pSeg32->initprot); u.pSeg32->nsects = KLDRHLP_E2E_U32(u.pSeg32->nsects); u.pSeg32->flags = KLDRHLP_E2E_U32(u.pSeg32->flags); } if ( u.pSeg32->filesize && ( u.pSeg32->fileoff > cbFile || (uint64_t)u.pSeg32->fileoff + u.pSeg32->filesize > cbFile)) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; if (!u.pSeg32->filesize && u.pSeg32->fileoff) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; if (u.pSeg32->vmsize < u.pSeg32->filesize) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; if ((u.pSeg32->maxprot & u.pSeg32->initprot) != u.pSeg32->initprot) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; if (u.pSeg32->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1)) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; if (u.pSeg32->nsects * sizeof(section_32_t) > u.pLoadCmd->cmdsize - sizeof(segment_command_32_t)) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; if ( pHdr->filetype == MH_OBJECT && cSegmentCommands > 0) return KLDR_ERR_MACHO_BAD_OBJECT_FILE; cSegmentCommands++; /* * convert, validate and parse the sections. */ cSectionsLeft = u.pSeg32->nsects; pFirstSect = pSect = (section_32_t *)(u.pSeg32 + 1); while (cSectionsLeft-- > 0) { int fFileBits; if (fConvertEndian) { pSect->addr = KLDRHLP_E2E_U32(pSect->addr); pSect->size = KLDRHLP_E2E_U32(pSect->size); pSect->offset = KLDRHLP_E2E_U32(pSect->offset); pSect->align = KLDRHLP_E2E_U32(pSect->align); pSect->reloff = KLDRHLP_E2E_U32(pSect->reloff); pSect->nreloc = KLDRHLP_E2E_U32(pSect->nreloc); pSect->flags = KLDRHLP_E2E_U32(pSect->flags); pSect->reserved1 = KLDRHLP_E2E_U32(pSect->reserved1); pSect->reserved2 = KLDRHLP_E2E_U32(pSect->reserved2); } /* validate */ switch (pSect->flags & SECTION_TYPE) { case S_ZEROFILL: if (pSect->reserved1 || pSect->reserved2) return KLDR_ERR_MACHO_BAD_SECTION; fFileBits = 0; break; case S_REGULAR: case S_CSTRING_LITERALS: if (pSect->reserved1 || pSect->reserved2) return KLDR_ERR_MACHO_BAD_SECTION; fFileBits = 1; break; case S_LITERAL_POINTERS: case S_INTERPOSING: case S_GB_ZEROFILL: case S_NON_LAZY_SYMBOL_POINTERS: case S_LAZY_SYMBOL_POINTERS: case S_4BYTE_LITERALS: case S_8BYTE_LITERALS: case S_16BYTE_LITERALS: case S_SYMBOL_STUBS: case S_COALESCED: case S_MOD_INIT_FUNC_POINTERS: case S_MOD_TERM_FUNC_POINTERS: return KLDR_ERR_MACHO_UNSUPPORTED_SECTION; default: return KLDR_ERR_MACHO_UNKNOWN_SECTION; } if (pSect->flags & ~( S_ATTR_PURE_INSTRUCTIONS | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS | S_ATTR_NO_DEAD_STRIP | S_ATTR_LIVE_SUPPORT | S_ATTR_SELF_MODIFYING_CODE | S_ATTR_DEBUG | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_EXT_RELOC | S_ATTR_LOC_RELOC | SECTION_TYPE)) return KLDR_ERR_MACHO_BAD_SECTION; if (!pSect->size) return KLDR_ERR_MACHO_BAD_SECTION; if ( pSect->addr - u.pSeg32->vmaddr > u.pSeg32->vmsize || pSect->addr - u.pSeg32->vmaddr + pSect->size > u.pSeg32->vmsize) return KLDR_ERR_MACHO_BAD_SECTION; if ( pSect->align >= 31 || (((1 << pSect->align) - 1) & pSect->addr) || (((1 << pSect->align) - 1) & u.pSeg32->vmaddr)) return KLDR_ERR_MACHO_BAD_SECTION; if ( fFileBits && ( pSect->offset > cbFile || (uint64_t)pSect->offset + pSect->size > cbFile)) return KLDR_ERR_MACHO_BAD_SECTION; if (!fFileBits && pSect->offset) return KLDR_ERR_MACHO_BAD_SECTION; if (!pSect->nreloc && pSect->reloff) return KLDR_ERR_MACHO_BAD_SECTION; if ( pSect->nreloc && ( pSect->reloff > cbFile || (uint64_t)pSect->reloff + (off_t)pSect->nreloc * sizeof(nlist_t)) > cbFile) return KLDR_ERR_MACHO_BAD_SECTION; /* count segments and strings */ switch (pHdr->filetype) { case MH_OBJECT: { /* Don't load debug symbols. (test this) */ if (pSect->flags & S_ATTR_DEBUG) break; /* a new segment? */ if ( !cSegments || kLdrHlpStrNComp(pSect->segname, (pSect - 1)->segname, sizeof(pSect->segname))) { /* verify that the linker/assembler has ordered sections correctly. */ section_32_t *pCur = (pSect - 2); while ((uintptr_t)pCur >= (uintptr_t)pFirstSect) { if (!kLdrHlpStrNComp(pCur->segname, pSect->segname, sizeof(pSect->segname))) return KLDR_ERR_MACHO_BAD_SECTION_ORDER; pCur--; } /* ok. count it and the string. */ cSegments++; cbStringPool += kLdrHlpStrNLen(&pSect->segname[0], sizeof(pSect->segname)) + 1; } break; } default: return KLDR_ERR_INVALID_PARAMETER; } /* next */ pSect++; } break; } /*case LC_SEGMENT_64: copy 32-bit code break; */ case LC_SYMTAB: if (fConvertEndian) { u.pSymTab->symoff = KLDRHLP_E2E_U32(u.pSymTab->symoff); u.pSymTab->nsyms = KLDRHLP_E2E_U32(u.pSymTab->nsyms); u.pSymTab->stroff = KLDRHLP_E2E_U32(u.pSymTab->stroff); u.pSymTab->strsize = KLDRHLP_E2E_U32(u.pSymTab->strsize); } /* verify */ if ( u.pSymTab->symoff >= cbFile || (uint64_t)u.pSymTab->symoff + u.pSymTab->nsyms * sizeof(nlist_t) > kLdrRdrSize(pRdr)) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; if ( u.pSymTab->stroff >= cbFile || (uint64_t)u.pSymTab->stroff + u.pSymTab->strsize > cbFile) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; break; case LC_THREAD: case LC_UNIXTHREAD: { uint32_t *pu32 = (uint32_t *)(u.pb + sizeof(load_command_t)); uint32_t cItemsLeft = (u.pThread->cmdsize - sizeof(load_command_t)) / sizeof(uint32_t); while (cItemsLeft) { /* convert & verify header items ([0] == flavor, [1] == uint32_t count). */ if (cItemsLeft < 2) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; if (fConvertEndian) { pu32[0] = KLDRHLP_E2E_U32(pu32[0]); pu32[1] = KLDRHLP_E2E_U32(pu32[1]); } if (pu32[1] + 2 > cItemsLeft) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; /* convert & verify according to flavor. */ switch (pu32[0]) { /** @todo */ default: break; } /* next */ cItemsLeft -= pu32[1] + 2; pu32 += pu32[1] + 2; } break; } case LC_UUID: if (u.pUuid->cmdsize != sizeof(uuid_command_t)) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; /** @todo Check anything here need converting? */ break; case LC_SEGMENT_64: case LC_LOADFVMLIB: case LC_IDFVMLIB: case LC_IDENT: case LC_FVMFILE: case LC_PREPAGE: case LC_DYSYMTAB: case LC_LOAD_DYLIB: case LC_ID_DYLIB: case LC_LOAD_DYLINKER: case LC_ID_DYLINKER: case LC_PREBOUND_DYLIB: case LC_ROUTINES: case LC_ROUTINES_64: case LC_SUB_FRAMEWORK: case LC_SUB_UMBRELLA: case LC_SUB_CLIENT: case LC_SUB_LIBRARY: case LC_TWOLEVEL_HINTS: case LC_PREBIND_CKSUM: case LC_LOAD_WEAK_DYLIB: case LC_SYMSEG: return KLDR_ERR_MACHO_UNSUPPORTED_LOAD_COMMAND; default: return KLDR_ERR_MACHO_UNKNOWN_LOAD_COMMAND; } } /* be strict. */ if (cbLeft) return KLDR_ERR_MACHO_BAD_LOAD_COMMAND; switch (pHdr->filetype) { case MH_OBJECT: if (!cSegments) return KLDR_ERR_MACHO_BAD_OBJECT_FILE; break; } *pcSegments = cSegments; *pcbStringPool = cbStringPool; return 0; } /** * Parses the load commands after we've carved out the module instance. * * This fills in the segment table and perhaps some other properties. * * @returns 0 on success. * @returns KLDR_ERR_MACHO_* on failure. * @param pModMachO The module. * @param pbStringPool The string pool * @param cbStringPool The size of the string pool. */ static int kldrModMachOParseLoadCommands(PKLDRMODMACHO pModMachO, char *pbStringPool, uint32_t cbStringPool) { union { const uint8_t *pb; const load_command_t *pLoadCmd; const segment_command_32_t *pSeg32; const segment_command_64_t *pSeg64; const symtab_command_t *pSymTab; } u; uint32_t cLeft = pModMachO->Hdr.ncmds; uint32_t cbLeft = pModMachO->Hdr.sizeofcmds; const uint8_t *pb = pModMachO->pbLoadCommands; int fFirstSegment = 1; PKLDRSEG pSeg = &pModMachO->pMod->aSegments[0]; const uint32_t cSegments = pModMachO->pMod->cSegments; uint32_t i; while (cLeft-- > 0) { u.pb = pb; cbLeft -= u.pLoadCmd->cmdsize; pb += u.pLoadCmd->cmdsize; /* * Convert endian if needed, parse and validate the command. */ switch (u.pLoadCmd->cmd) { case LC_SEGMENT_32: { section_32_t *pSect; section_32_t *pFirstSect; uint32_t cSectionsLeft; pModMachO->LinkAddress = u.pSeg32->vmaddr; /* * convert, validate and parse the sections. */ cSectionsLeft = u.pSeg32->nsects; pFirstSect = pSect = (section_32_t *)(u.pSeg32 + 1); while (cSectionsLeft-- > 0) { switch (pModMachO->Hdr.filetype) { case MH_OBJECT: { /* Don't load debug symbols. (test this) */ if (pSect->flags & S_ATTR_DEBUG) break; if ( fFirstSegment || kLdrHlpStrNComp(pSect->segname, (pSect - 1)->segname, sizeof(pSect->segname))) { /* new segment. */ pSeg->pvUser = NULL; pSeg->pchName = pbStringPool; pSeg->cchName = (uint32_t)kLdrHlpStrNLen(&pSect->segname[0], sizeof(pSect->sectname)); kLdrHlpMemCopy(pbStringPool, &pSect->segname[0], pSeg->cchName); pbStringPool += pSeg->cchName; *pbStringPool++ = '\0'; pSeg->SelFlat = 0; pSeg->Sel16bit = 0; pSeg->fFlags = 0; pSeg->enmProt = KLDRPROT_EXECUTE_WRITECOPY; /** @todo fixme! */ pSeg->cb = pSect->size; pSeg->Alignment = (1 << pSect->align); pSeg->LinkAddress = pSect->addr; pSeg->offFile = pSect->offset ? pSect->offset : -1; pSeg->cbFile = pSect->offset ? pSect->size : -1; pSeg->RVA = pSect->addr - pModMachO->LinkAddress; pSeg->cbMapped = 0; pSeg->MapAddress = 0; pSeg++; fFirstSegment = 0; } else if (!fFirstSegment) { /* update exiting segment */ if (pSeg[-1].Alignment < (1 << pSect->align)) pSeg[-1].Alignment = (1 << pSect->align); if (pSect->addr < pSeg[-1].LinkAddress) return KLDR_ERR_MACHO_BAD_SECTION; /** @todo move up! */ if ( pSect->offset && pSeg[-1].cbFile == pSeg[-1].cb) { if ( pSeg[-1].offFile + pSeg[-1].cbFile == pSect->offset && pSeg[-1].cbFile == pSect->addr - pSeg[-1].LinkAddress) pSeg[-1].cbFile = (off_t)(pSect->addr - pSeg[-1].LinkAddress) + pSect->size; else { pSeg[-1].cbFile = pSeg[-1].offFile = -1; pModMachO->fMapUsingLoadCommands = 1; } } pSeg[-1].cb = pSect->addr - pSeg[-1].LinkAddress + pSect->size; /** @todo update the protection... */ } break; } default: return KLDR_ERR_INVALID_PARAMETER; } /* next */ pSect++; } break; } default: break; } } /* * Adjust mapping addresses calculating the image size. */ pSeg = &pModMachO->pMod->aSegments[0]; switch (pModMachO->Hdr.filetype) { case MH_OBJECT: { KLDRADDR cb1; size_t cb2; for (i = 0; i < cSegments - 1; i++) { cb1 = pSeg[i + 1].LinkAddress - pSeg[i].LinkAddress; cb2 = (size_t)cb1; pSeg[i].cbMapped = cb2 == cb1 ? cb2 : ~(size_t)0; } cb1 = KLDR_ALIGN_ADDR(pSeg[i].cb, pSeg[i].Alignment); cb2 = (size_t)cb1; pSeg[i].cbMapped = cb2 == cb1 ? cb2 : ~(size_t)0; pModMachO->cbImage = pSeg[i].RVA + cb1; break; } } return 0; } /** @copydoc KLDRMODOPS::pfnDestroy */ static int kldrModMachODestroy(PKLDRMOD pMod) { PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; int rc = 0; KLDRMODMACHO_ASSERT(!pModMachO->pvMapping); if (pMod->pRdr) { rc = kLdrRdrClose(pMod->pRdr); pMod->pRdr = NULL; } pMod->u32Magic = 0; pMod->pOps = NULL; kldrHlpFree(pModMachO->pbLoadCommands); pModMachO->pbLoadCommands = NULL; kldrHlpFree(pModMachO->pbStrings); pModMachO->pbStrings = NULL; kldrHlpFree(pModMachO->paSymbols); pModMachO->paSymbols = NULL; kldrHlpFree(pModMachO); return rc; } /** * Performs the mapping of the image. * * This can be used to do the internal mapping as well as the * user requested mapping. fForReal indicates which is desired. * * @returns 0 on success, non-zero OS or kLdr status code on failure. * @param pModMachO The interpreter module instance * @param fForReal If set, do the user mapping. if clear, do the internal mapping. */ static int kldrModMachODoMap(PKLDRMODMACHO pModMachO, unsigned fForReal) { #if 0 PKLDRMOD pMod = pModMachO->pMod; unsigned fFixed; void *pvBase; int rc; uint32_t i; /* * Map it. */ /* fixed image? */ fFixed = fForReal && ( 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; } /* try do the prepare */ rc = kLdrRdrMap(pMod->pRdr, &pvBase, pMod->cSegments, pMod->aSegments, fFixed); if (rc) return rc; /* * Update the segments with their map addresses. */ if (fForReal) { 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; } pModMachO->pvMapping = pvBase; } else pModMachO->pvBits = pvBase; #endif return 0; } /** * Unmaps a image mapping. * * This can be used to do the internal mapping as well as the * user requested mapping. fForReal indicates which is desired. * * @returns 0 on success, non-zero OS or kLdr status code on failure. * @param pModMachO The interpreter module instance * @param pvMapping The mapping to unmap. */ static int kldrModMachODoUnmap(PKLDRMODMACHO pModMachO, const void *pvMapping) { #if 0 PKLDRMOD pMod = pModMachO->pMod; int rc; uint32_t i; /* * Try unmap the image. */ rc = kLdrRdrUnmap(pMod->pRdr, (void *)pvMapping, pMod->cSegments, pMod->aSegments); if (rc) return rc; /* * Update the segments to reflect that they aren't mapped any longer. */ if (pModMachO->pvMapping == pvMapping) { pModMachO->pvMapping = NULL; for (i = 0; i < pMod->cSegments; i++) pMod->aSegments[i].MapAddress = 0; } if (pModMachO->pvBits == pvMapping) pModMachO->pvBits = NULL; #endif return 0; } /** * Gets usable bits and the right base address. * * @returns 0 on success. * @returns A non-zero status code if the BaseAddress isn't right or some problem is encountered * featch in a temp mapping the bits. * @param pModMachO The interpreter module instance * @param ppvBits The bits address, IN & OUT. * @param pBaseAddress The base address, IN & OUT. Optional. */ static int kldrModMachOBitsAndBaseAddress(PKLDRMODMACHO pModMachO, const void **ppvBits, PKLDRADDR pBaseAddress) { int rc = 0; /* * Correct the base address. * * We don't use the base address for interpreting the bits in this * interpreter, which makes things relativly simple. */ if (pBaseAddress) { if (*pBaseAddress == KLDRMOD_BASEADDRESS_MAP) *pBaseAddress = pModMachO->pMod->aSegments[0].MapAddress; else if (*pBaseAddress == KLDRMOD_BASEADDRESS_LINK) *pBaseAddress = pModMachO->LinkAddress; } /* * Get bits. */ if (ppvBits && !*ppvBits) { if (pModMachO->pvMapping) *ppvBits = pModMachO->pvMapping; else if (pModMachO->pvBits) *ppvBits = pModMachO->pvBits; else { /* create an internal mapping. */ rc = kldrModMachODoMap(pModMachO, 0 /* not for real */); if (rc) return rc; KLDRMODMACHO_ASSERT(pModMachO->pvBits); *ppvBits = pModMachO->pvBits; } } return 0; } /** @copydoc kLdrModQuerySymbol */ static int kldrModMachOQuerySymbol(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, uint32_t iSymbol, const char *pchSymbol, size_t cchSymbol, const char *pszVersion, PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, uint32_t *pfKind) { #if 0 PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; const uint32_t *paExportRVAs; const IMAGE_EXPORT_DIRECTORY *pExpDir; uint32_t iExpOrd; uint32_t uRVA; int rc; /* * Make sure we've got mapped bits and resolve any base address aliases. */ rc = kldrModMachOBitsAndBaseAddress(pModMachO, &pvBits, &BaseAddress); if (rc) return rc; if ( pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size < sizeof(IMAGE_EXPORT_DIRECTORY)) return KLDR_ERR_SYMBOL_NOT_FOUND; if (pszVersion && *pszVersion) return KLDR_ERR_SYMBOL_NOT_FOUND; pExpDir = KLDRMODMACHO_RVA2TYPE(pvBits, pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, PIMAGE_EXPORT_DIRECTORY); if (!pchSymbol) { /* * Simple, calculate the unbased ordinal and bounds check it. */ iExpOrd = iSymbol - pExpDir->Base; if (iExpOrd >= KLDR_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions)) return KLDR_ERR_SYMBOL_NOT_FOUND; } else { /* * Do a binary search for the name. * (The name table is sorted in ascending ordered by the linker.) */ const uint32_t *paRVANames = KLDRMODMACHO_RVA2TYPE(pvBits, pExpDir->AddressOfNames, const uint32_t *); const uint16_t *paOrdinals = KLDRMODMACHO_RVA2TYPE(pvBits, pExpDir->AddressOfNameOrdinals, const uint16_t *); int32_t iStart = 1; /* one based binary searching is simpler. */ int32_t iEnd = pExpDir->NumberOfNames; for (;;) { int32_t i; int diff; const char *pszName; /* done? */ if (iStart > iEnd) { #ifdef KLDRMODMACHO_STRICT /* Make sure the linker and we both did our job right. */ for (i = 0; i < (int32_t)pExpDir->NumberOfNames; i++) { pszName = KLDRMODMACHO_RVA2TYPE(pvBits, paRVANames[i], const char *); KLDRMODMACHO_ASSERT(kLdrHlpStrNComp(pszName, pchSymbol, cchSymbol) || pszName[cchSymbol]); KLDRMODMACHO_ASSERT(i == 0 || kLdrHlpStrComp(pszName, KLDRMODMACHO_RVA2TYPE(pvBits, paRVANames[i - 1], const char *))); } #endif return KLDR_ERR_SYMBOL_NOT_FOUND; } i = (iEnd - iStart) / 2 + iStart; pszName = KLDRMODMACHO_RVA2TYPE(pvBits, paRVANames[i - 1], const char *); diff = kLdrHlpStrNComp(pszName, pchSymbol, cchSymbol); if (!diff) diff = pszName[cchSymbol] - 0; if (diff < 0) iStart = i + 1; /* The symbol must be after the current name. */ else if (diff) iEnd = i - 1; /* The symbol must be before the current name. */ else { iExpOrd = paOrdinals[i - 1]; /* match! */ break; } } } /* * Lookup the address in the 'symbol' table. */ paExportRVAs = KLDRMODMACHO_RVA2TYPE(pvBits, pExpDir->AddressOfFunctions, const uint32_t *); uRVA = paExportRVAs[iExpOrd]; if ( uRVA - pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress < pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) return kldrModMachODoForwarderQuery(pModMachO, pvBits, KLDRMODMACHO_RVA2TYPE(pvBits, uRVA, const char *), pfnGetForwarder, pvUser, puValue, pfKind); /* * Set the return value. */ if (puValue) *puValue = BaseAddress + uRVA; if (pfKind) *pfKind = (pModMachO->Hdrs.FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32) ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT) | KLDRSYMKIND_NO_TYPE; #endif return 0; } /** @copydoc kLdrModEnumSymbols */ static int kldrModMachOEnumSymbols(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, uint32_t fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser) { #if 0 PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; const uint32_t *paFunctions; const IMAGE_EXPORT_DIRECTORY *pExpDir; const uint32_t *paRVANames; const uint16_t *paOrdinals; uint32_t iFunction; uint32_t cFunctions; uint32_t cNames; int rc; /* * Make sure we've got mapped bits and resolve any base address aliases. */ rc = kldrModMachOBitsAndBaseAddress(pModMachO, &pvBits, &BaseAddress); if (rc) return rc; if ( pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size < sizeof(IMAGE_EXPORT_DIRECTORY)) return 0; /* no exports to enumerate, return success. */ pExpDir = KLDRMODMACHO_RVA2TYPE(pvBits, pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, PIMAGE_EXPORT_DIRECTORY); /* * Enumerate the ordinal exports. */ paRVANames = KLDRMODMACHO_RVA2TYPE(pvBits, pExpDir->AddressOfNames, const uint32_t *); paOrdinals = KLDRMODMACHO_RVA2TYPE(pvBits, pExpDir->AddressOfNameOrdinals, const uint16_t *); paFunctions = KLDRMODMACHO_RVA2TYPE(pvBits, pExpDir->AddressOfFunctions, const uint32_t *); cFunctions = pExpDir->NumberOfFunctions; cNames = pExpDir->NumberOfNames; for (iFunction = 0; iFunction < cFunctions; iFunction++) { unsigned fFoundName; uint32_t iName; const uint32_t uRVA = paFunctions[iFunction]; const KLDRADDR uValue = BaseAddress + uRVA; uint32_t fKind = (pModMachO->Hdrs.FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32) ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT) | KLDRSYMKIND_NO_TYPE; if ( uRVA - pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress < pModMachO->Hdrs.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 = KLDRMODMACHO_RVA2TYPE(pvBits, paRVANames[iName], const char *); rc = pfnCallback(pMod, iFunction + pExpDir->Base, pszName, kLdrHlpStrLen(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; } } #endif return 0; } /** @copydoc kLdrModGetImport */ static int kldrModMachOGetImport(PKLDRMOD pMod, const void *pvBits, uint32_t iImport, char *pszName, size_t cchName) { #if 0 PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; const IMAGE_IMPORT_DESCRIPTOR *pImpDesc; const char *pszImportName; size_t cchImportName; int rc; /* * Make sure we've got mapped bits and resolve any base address aliases. */ rc = kldrModMachOBitsAndBaseAddress(pModMachO, &pvBits, NULL); if (rc) return rc; /* * Simple bounds check. */ if (iImport >= (uint32_t)kldrModMachONumberOfImports(pMod, pvBits)) return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS; /* * Get the name. */ pImpDesc = KLDRMODMACHO_RVA2TYPE(pvBits, pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + sizeof(IMAGE_IMPORT_DESCRIPTOR) * iImport, const IMAGE_IMPORT_DESCRIPTOR *); pszImportName = KLDRMODMACHO_RVA2TYPE(pvBits, pImpDesc->Name, const char *); cchImportName = kLdrHlpStrLen(pszImportName); if (cchImportName < cchName) { kLdrHlpMemCopy(pszName, pszImportName, cchImportName + 1); rc = 0; } else { kLdrHlpMemCopy(pszName, pszImportName, cchName); if (cchName) pszName[cchName - 1] = '\0'; rc = KLDR_ERR_BUFFER_OVERFLOW; } return rc; #else return -1; #endif } /** @copydoc kLdrModNumberOfImports */ static int32_t kldrModMachONumberOfImports(PKLDRMOD pMod, const void *pvBits) { #if 0 PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; if (pModMachO->cImportModules == ~(uint32_t)0) { /* * We'll have to walk the import descriptors to figure out their number. * First, make sure we've got mapped bits. */ if (kldrModMachOBitsAndBaseAddress(pModMachO, &pvBits, NULL)) return -1; pModMachO->cImportModules = 0; if ( pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size && pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) { const IMAGE_IMPORT_DESCRIPTOR *pImpDesc; pImpDesc = KLDRMODMACHO_RVA2TYPE(pvBits, pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, const IMAGE_IMPORT_DESCRIPTOR *); while (pImpDesc->Name && pImpDesc->FirstThunk) { pModMachO->cImportModules++; pImpDesc++; } } } return pModMachO->cImportModules; #else return 0; #endif } /** @copydoc kLdrModGetStackInfo */ static int kldrModMachOGetStackInfo(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo) { PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; pStackInfo->Address = NIL_KLDRADDR; pStackInfo->LinkAddress = NIL_KLDRADDR; pStackInfo->cbStack = pStackInfo->cbStackThread = 0;//pModMachO->Hdrs.OptionalHeader.SizeOfStackReserve; return 0; } /** @copydoc kLdrModQueryMainEntrypoint */ static int kldrModMachOQueryMainEntrypoint(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRADDR pMainEPAddress) { #if 0 PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; int rc; /* * Resolve base address alias if any. */ rc = kldrModMachOBitsAndBaseAddress(pModMachO, NULL, &BaseAddress); if (rc) return rc; /* * Convert the address from the header. */ *pMainEPAddress = pModMachO->Hdrs.OptionalHeader.AddressOfEntryPoint ? BaseAddress + pModMachO->Hdrs.OptionalHeader.AddressOfEntryPoint : NIL_KLDRADDR; #else *pMainEPAddress = NIL_KLDRADDR; #endif return 0; } /** @copydoc kLdrModEnumDbgInfo */ static int kldrModMachOEnumDbgInfo(PKLDRMOD pMod, const void *pvBits, PFNKLDRENUMDBG pfnCallback, void *pvUser) { #if 0 PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; const IMAGE_DEBUG_DIRECTORY *pDbgDir; uint32_t iDbgInfo; uint32_t cb; int rc; /* * Check that there is a debug directory first. */ cb = pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size; if ( cb < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */ || !pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress) return 0; /* * Make sure we've got mapped bits. */ rc = kldrModMachOBitsAndBaseAddress(pModMachO, &pvBits, NULL); if (rc) return rc; /* * Enumerate the debug directory. */ pDbgDir = KLDRMODMACHO_RVA2TYPE(pvBits, pModMachO->Hdrs.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, pDbgDir->PointerToRawData ? pDbgDir->PointerToRawData : -1, pDbgDir->AddressOfRawData ? pDbgDir->AddressOfRawData : NIL_KLDRADDR, pDbgDir->SizeOfData, NULL, pvUser); if (rc) break; /* next */ if (cb <= sizeof(IMAGE_DEBUG_DIRECTORY)) break; } return rc; #else return 0; #endif } /** @copydoc kLdrModHasDbgInfo */ static int kldrModMachOHasDbgInfo(PKLDRMOD pMod, const void *pvBits) { PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; #if 0 /* * Base this entirely on the presence of a debug directory. */ if ( pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */ || !pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress) return KLDR_ERR_NO_DEBUG_INFO; return 0; #else return KLDR_ERR_NO_DEBUG_INFO; #endif } /** @copydoc kLdrModMap */ static int kldrModMachOMap(PKLDRMOD pMod) { PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; int rc; /* * Already mapped? */ if (pModMachO->pvMapping) return KLDR_ERR_ALREADY_MAPPED; /* * We've got a common worker which does this. */ rc = kldrModMachODoMap(pModMachO, 1 /* the real thing */); if (rc) return rc; KLDRMODMACHO_ASSERT(pModMachO->pvMapping); return 0; } /** @copydoc kLdrModUnmap */ static int kldrModMachOUnmap(PKLDRMOD pMod) { PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; int rc; /* * Mapped? */ if (!pModMachO->pvMapping) return KLDR_ERR_NOT_MAPPED; /* * We've got a common worker which does this. */ rc = kldrModMachODoUnmap(pModMachO, pModMachO->pvMapping); if (rc) return rc; KLDRMODMACHO_ASSERT(pModMachO->pvMapping); return 0; } /** @copydoc kLdrModAllocTLS */ static int kldrModMachOAllocTLS(PKLDRMOD pMod) { PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; /* * Mapped? */ if (!pModMachO->pvMapping) return KLDR_ERR_NOT_MAPPED; return 0; } /** @copydoc kLdrModFreeTLS */ static void kldrModMachOFreeTLS(PKLDRMOD pMod) { } /** @copydoc kLdrModReload */ static int kldrModMachOReload(PKLDRMOD pMod) { PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; /* * Mapped? */ if (!pModMachO->pvMapping) return KLDR_ERR_NOT_MAPPED; /* the file provider does it all */ return kLdrRdrRefresh(pMod->pRdr, (void *)pModMachO->pvMapping, pMod->cSegments, pMod->aSegments); } /** @copydoc kLdrModFixupMapping */ static int kldrModMachOFixupMapping(PKLDRMOD pMod, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) { PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; int rc, rc2; /* * Mapped? */ if (!pModMachO->pvMapping) return KLDR_ERR_NOT_MAPPED; /* * Before doing anything we'll have to make all pages writable. */ rc = kLdrRdrProtect(pMod->pRdr, (void *)pModMachO->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */); if (rc) return rc; #if 0 /* * Apply base relocations. */ rc = kldrModMachODoFixups(pModMachO, (void *)pModMachO->pvMapping, (uintptr_t)pModMachO->pvMapping, pModMachO->Hdrs.OptionalHeader.ImageBase); /* * Resolve imports. */ if (!rc) rc = kldrModMachODoImports(pModMachO, (void *)pModMachO->pvMapping, pfnGetImport, pvUser); #endif /* * Restore protection. */ rc2 = kLdrRdrProtect(pMod->pRdr, (void *)pModMachO->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */); if (!rc && rc2) rc = rc2; return rc; } /** * Applies base relocations to a (unprotected) image mapping. * * @returns 0 on success, non-zero kLdr status code on failure. * @param pModMachO The PE module interpreter instance. * @param pvMapping The mapping to fixup. * @param NewBaseAddress The address to fixup the mapping to. * @param OldBaseAddress The address the mapping is currently fixed up to. */ static int kldrModMachODoFixups(PKLDRMODMACHO pModMachO, void *pvMapping, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress) { #if 0 const KLDRADDR Delta = NewBaseAddress - OldBaseAddress; uint32_t cbLeft = pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; const IMAGE_BASE_RELOCATION *pBR, *pFirstBR; /* * Don't don anything if the delta is 0 or there aren't any relocations. */ if ( !Delta || !cbLeft || !pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) return 0; /* * Process the fixups block by block. * (These blocks appears to be 4KB on all archs despite the native page size.) */ pBR = pFirstBR = KLDRMODMACHO_RVA2TYPE(pvMapping, pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress, const IMAGE_BASE_RELOCATION *); while ( cbLeft > sizeof(IMAGE_BASE_RELOCATION) && pBR->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION) /* paranoia */) { union { uint8_t *pu8; uint16_t *pu16; uint32_t *pu32; uint64_t *pu64; } uChunk, u; const uint16_t *poffFixup = (const uint16_t *)(pBR + 1); const uint32_t cbBlock = KLDR_MIN(cbLeft, pBR->SizeOfBlock) - sizeof(IMAGE_BASE_RELOCATION); /* more caution... */ uint32_t cFixups = cbBlock / sizeof(poffFixup[0]); uChunk.pu8 = KLDRMODMACHO_RVA2TYPE(pvMapping, pBR->VirtualAddress, uint8_t *); /* * Loop thru the fixups in this chunk. */ while (cFixups > 0) { u.pu8 = uChunk.pu8 + (*poffFixup & 0xfff); switch (*poffFixup >> 12) /* ordered by value. */ { /* 0 - Alignment placeholder. */ case IMAGE_REL_BASED_ABSOLUTE: break; /* 1 - 16-bit, add 2nd 16-bit part of the delta. (rare) */ case IMAGE_REL_BASED_HIGH: *u.pu16 += (uint16_t)(Delta >> 16); break; /* 2 - 16-bit, add 1st 16-bit part of the delta. (rare) */ case IMAGE_REL_BASED_LOW: *u.pu16 += (uint16_t)Delta; break; /* 3 - 32-bit, add delta. (frequent in 32-bit images) */ case IMAGE_REL_BASED_HIGHLOW: *u.pu32 += (uint32_t)Delta; break; /* 4 - 16-bit, add 2nd 16-bit of the delta, sign adjust for the lower 16-bit. one arg. (rare) */ case IMAGE_REL_BASED_HIGHADJ: { int32_t i32; if (cFixups <= 1) return KLDR_ERR_PE_BAD_FIXUP; i32 = (uint32_t)*u.pu16 << 16; i32 |= *++poffFixup; cFixups--; /* the addend argument */ i32 += (uint32_t)Delta; i32 += 0x8000; *u.pu16 = (uint16_t)(i32 >> 16); break; } /* 5 - 32-bit MIPS JMPADDR, no implemented. */ case IMAGE_REL_BASED_MIPS_JMPADDR: *u.pu32 = (*u.pu32 & 0xc0000000) | ((uint32_t)((*u.pu32 << 2) + (uint32_t)Delta) >> 2); break; /* 6 - Intra section? Reserved value in later specs. Not implemented. */ case IMAGE_REL_BASED_SECTION: KLDRMODMACHO_ASSERT(!"SECTION"); return KLDR_ERR_PE_BAD_FIXUP; /* 7 - Relative intra section? Reserved value in later specs. Not implemented. */ case IMAGE_REL_BASED_REL32: KLDRMODMACHO_ASSERT(!"SECTION"); return KLDR_ERR_PE_BAD_FIXUP; /* 8 - reserved according to binutils... */ case 8: KLDRMODMACHO_ASSERT(!"RESERVERED8"); return KLDR_ERR_PE_BAD_FIXUP; /* 9 - IA64_IMM64 (/ MIPS_JMPADDR16), no specs nor need to support the platform yet. * Bet this requires more code than all the other fixups put together in good IA64 spirit :-) */ case IMAGE_REL_BASED_IA64_IMM64: KLDRMODMACHO_ASSERT(!"IA64_IMM64 / MIPS_JMPADDR16"); return KLDR_ERR_PE_BAD_FIXUP; /* 10 - 64-bit, add delta. (frequently in 64-bit images) */ case IMAGE_REL_BASED_DIR64: *u.pu64 += (uint64_t)Delta; break; /* 11 - 16-bit, add 3rd 16-bit of the delta, sign adjust for the lower 32-bit. two args. (rare) */ case IMAGE_REL_BASED_HIGH3ADJ: { int64_t i64; if (cFixups <= 2) return KLDR_ERR_PE_BAD_FIXUP; i64 = (uint64_t)*u.pu16 << 32 | ((uint32_t)poffFixup[2] << 16) | poffFixup[1]; i64 += Delta; i64 += 0x80008000UL; *u.pu16 = (uint16_t)(i64 >> 32); /* skip the addends arguments */ poffFixup += 2; cFixups -= 2; break; } /* the rest are yet to be defined.*/ default: return KLDR_ERR_PE_BAD_FIXUP; } /* * Next relocation. */ poffFixup++; cFixups--; } /* * Next block. */ cbLeft -= pBR->SizeOfBlock; pBR = (PIMAGE_BASE_RELOCATION)((uintptr_t)pBR + pBR->SizeOfBlock); } #endif return 0; } /** * Resolves imports. * * @returns 0 on success, non-zero kLdr status code on failure. * @param pModMachO The PE module interpreter instance. * @param pvMapping The mapping which imports should be resolved. * @param pfnGetImport The callback for resolving an imported symbol. * @param pvUser User argument to the callback. */ static int kldrModMachODoImports(PKLDRMODMACHO pModMachO, void *pvMapping, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) { #if 0 const IMAGE_IMPORT_DESCRIPTOR *pImpDesc; /* * If no imports, there is nothing to do. */ kldrModMachONumberOfImports(pModMachO->pMod, pvMapping); if (!pModMachO->cImportModules) return 0; pImpDesc = KLDRMODMACHO_RVA2TYPE(pvMapping, pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, const IMAGE_IMPORT_DESCRIPTOR *); if (pModMachO->Hdrs.FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)) return kldrModMachODoImports32Bit(pModMachO, pvMapping, pImpDesc, pfnGetImport, pvUser); return kldrModMachODoImports64Bit(pModMachO, pvMapping, pImpDesc, pfnGetImport, pvUser); #else return 0; #endif } #if 0 /** * Resolves imports, 32-bit image. * * @returns 0 on success, non-zero kLdr status code on failure. * @param pModMachO The PE module interpreter instance. * @param pvMapping The mapping which imports should be resolved. * @param pImpDesc Pointer to the first import descriptor. * @param pfnGetImport The callback for resolving an imported symbol. * @param pvUser User argument to the callback. */ static int kldrModMachODoImports32Bit(PKLDRMODMACHO pModMachO, void *pvMapping, const IMAGE_IMPORT_DESCRIPTOR *pImpDesc, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) { PKLDRMOD pMod = pModMachO->pMod; uint32_t iImp; /* * Iterate the import descriptors. */ for (iImp = 0; iImp < pModMachO->cImportModules; iImp++, pImpDesc++) { PIMAGE_THUNK_DATA32 pFirstThunk = KLDRMODMACHO_RVA2TYPE(pvMapping, pImpDesc->FirstThunk, PIMAGE_THUNK_DATA32); const IMAGE_THUNK_DATA32 *pThunk = pImpDesc->u.OriginalFirstThunk ? KLDRMODMACHO_RVA2TYPE(pvMapping, pImpDesc->u.OriginalFirstThunk, const IMAGE_THUNK_DATA32 *) : KLDRMODMACHO_RVA2TYPE(pvMapping, pImpDesc->FirstThunk, const IMAGE_THUNK_DATA32 *); /* Iterate the thunks. */ while (pThunk->u1.Ordinal != 0) { KLDRADDR Value; uint32_t fKind; int rc; /* Ordinal or name import? */ if (IMAGE_SNAP_BY_ORDINAL32(pThunk->u1.Ordinal)) rc = pfnGetImport(pMod, iImp, IMAGE_ORDINAL32(pThunk->u1.Ordinal), NULL, 0, NULL, &Value, &fKind, pvUser); else if (KLDRMODMACHO_VALID_RVA(pModMachO, pThunk->u1.Ordinal)) { const IMAGE_IMPORT_BY_NAME *pName = KLDRMODMACHO_RVA2TYPE(pvMapping, pThunk->u1.Ordinal, const IMAGE_IMPORT_BY_NAME *); rc = pfnGetImport(pMod, iImp, NIL_KLDRMOD_SYM_ORDINAL, (const char *)pName->Name, kLdrHlpStrLen((const char *)pName->Name), NULL, &Value, &fKind, pvUser); } else { KLDRMODMACHO_ASSERT(!"bad 32-bit import"); return KLDR_ERR_PE_BAD_IMPORT; } /* Apply it. */ pFirstThunk->u1.Function = (uint32_t)Value; if (pFirstThunk->u1.Function != Value) { KLDRMODMACHO_ASSERT(!"overflow"); return KLDR_ERR_ADDRESS_OVERFLOW; } /* next */ pThunk++; pFirstThunk++; } } return 0; } /** * Resolves imports, 64-bit image. * * @returns 0 on success, non-zero kLdr status code on failure. * @param pModMachO The PE module interpreter instance. * @param pvMapping The mapping which imports should be resolved. * @param pImpDesc Pointer to the first import descriptor. * @param pfnGetImport The callback for resolving an imported symbol. * @param pvUser User argument to the callback. */ static int kldrModMachODoImports64Bit(PKLDRMODMACHO pModMachO, void *pvMapping, const IMAGE_IMPORT_DESCRIPTOR *pImpDesc, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) { PKLDRMOD pMod = pModMachO->pMod; uint32_t iImp; /* * Iterate the import descriptors. */ for (iImp = 0; iImp < pModMachO->cImportModules; iImp++, pImpDesc++) { PIMAGE_THUNK_DATA64 pFirstThunk = KLDRMODMACHO_RVA2TYPE(pvMapping, pImpDesc->FirstThunk, PIMAGE_THUNK_DATA64); const IMAGE_THUNK_DATA64 *pThunk = pImpDesc->u.OriginalFirstThunk ? KLDRMODMACHO_RVA2TYPE(pvMapping, pImpDesc->u.OriginalFirstThunk, const IMAGE_THUNK_DATA64 *) : KLDRMODMACHO_RVA2TYPE(pvMapping, pImpDesc->FirstThunk, const IMAGE_THUNK_DATA64 *); /* Iterate the thunks. */ while (pThunk->u1.Ordinal != 0) { KLDRADDR Value; uint32_t fKind; int rc; /* Ordinal or name import? */ if (IMAGE_SNAP_BY_ORDINAL64(pThunk->u1.Ordinal)) rc = pfnGetImport(pMod, iImp, (uint32_t)IMAGE_ORDINAL64(pThunk->u1.Ordinal), NULL, 0, NULL, &Value, &fKind, pvUser); else if (KLDRMODMACHO_VALID_RVA(pModMachO, pThunk->u1.Ordinal)) { const IMAGE_IMPORT_BY_NAME *pName = KLDRMODMACHO_RVA2TYPE(pvMapping, pThunk->u1.Ordinal, const IMAGE_IMPORT_BY_NAME *); rc = pfnGetImport(pMod, iImp, NIL_KLDRMOD_SYM_ORDINAL, (const char *)pName->Name, kLdrHlpStrLen((const char *)pName->Name), NULL, &Value, &fKind, pvUser); } else { KLDRMODMACHO_ASSERT(!"bad 64-bit import"); return KLDR_ERR_PE_BAD_IMPORT; } /* Apply it. */ pFirstThunk->u1.Function = Value; /* next */ pThunk++; pFirstThunk++; } } return 0; } #endif /** @copydoc kLdrModCallInit */ static int kldrModMachOCallInit(PKLDRMOD pMod, uintptr_t uHandle) { #if 0 PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; int rc; /* * Mapped? */ if (!pModMachO->pvMapping) return KLDR_ERR_NOT_MAPPED; /* * Do TLS callbacks first and then call the init/term function if it's a DLL. */ rc = kldrModMachODoCallTLS(pModMachO, DLL_PROCESS_ATTACH, uHandle); if ( !rc && (pModMachO->Hdrs.FileHeader.Characteristics & IMAGE_FILE_DLL)) { rc = kldrModMachODoCallDLL(pModMachO, DLL_PROCESS_ATTACH, uHandle); if (rc) kldrModMachODoCallTLS(pModMachO, DLL_PROCESS_DETACH, uHandle); } return rc; #else return 0; #endif } /** * Call the DLL entrypoint. * * @returns 0 on success. * @returns KLDR_ERR_MODULE_INIT_FAILED or KLDR_ERR_THREAD_ATTACH_FAILED on failure. * @param pModMachO The PE module interpreter instance. * @param uOp The operation (DLL_*). * @param uHandle The module handle to present. */ static int kldrModMachODoCallDLL(PKLDRMODMACHO pModMachO, unsigned uOp, uintptr_t uHandle) { #if 0 int rc; /* * If no entrypoint there isn't anything to be done. */ if (!pModMachO->Hdrs.OptionalHeader.AddressOfEntryPoint) return 0; /* * Invoke the entrypoint and convert the boolean result to a kLdr status code. */ rc = kldrModMachODoCall((uintptr_t)pModMachO->pvMapping + pModMachO->Hdrs.OptionalHeader.AddressOfEntryPoint, uHandle, uOp, NULL); if (rc) rc = 0; else if (uOp == DLL_PROCESS_ATTACH) rc = KLDR_ERR_MODULE_INIT_FAILED; else if (uOp == DLL_THREAD_ATTACH) rc = KLDR_ERR_THREAD_ATTACH_FAILED; else /* detach: ignore failures */ rc = 0; return rc; #else return 0; #endif } /** * Call the TLS entrypoints. * * @returns 0 on success. * @returns KLDR_ERR_THREAD_ATTACH_FAILED on failure. * @param pModMachO The PE module interpreter instance. * @param uOp The operation (DLL_*). * @param uHandle The module handle to present. */ static int kldrModMachODoCallTLS(PKLDRMODMACHO pModMachO, unsigned uOp, uintptr_t uHandle) { /** @todo implement TLS support. */ return 0; } /** * 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 kldrModMachODoCall(uintptr_t uEntrypoint, uintptr_t uHandle, uint32_t uOp, void *pvReserved) { #if 0 int32_t rc; /** @todo try/except */ #if defined(__X86__) || defined(__i386__) || defined(_M_IX86) /* * Be very careful. * Not everyone will have got the calling convention right. */ # 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 #elif defined(__AMD64__) || defined(__x86_64__) || defined(_M_IX86) /* * For now, let's just get the work done... */ /** @todo Deal with GCC / MSC differences in some sensible way. */ int (*pfn)(uintptr_t uHandle, uint32_t uOp, void *pvReserved); pfn = (int (*)(uintptr_t uHandle, uint32_t uOp, void *pvReserved))uEntrypoint; rc = pfn(uHandle, uOp, NULL); #else # error "port me" #endif return rc; #else return 0; #endif } /** @copydoc kLdrModCallTerm */ static int kldrModMachOCallTerm(PKLDRMOD pMod, uintptr_t uHandle) { #if 0 PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; /* * Mapped? */ if (!pModMachO->pvMapping) return KLDR_ERR_NOT_MAPPED; /* * Do TLS callbacks first. */ kldrModMachODoCallTLS(pModMachO, DLL_PROCESS_DETACH, uHandle); if (pModMachO->Hdrs.FileHeader.Characteristics & IMAGE_FILE_DLL) kldrModMachODoCallDLL(pModMachO, DLL_PROCESS_DETACH, uHandle); #endif return 0; } /** @copydoc kLdrModCallThread */ static int kldrModMachOCallThread(PKLDRMOD pMod, uintptr_t uHandle, unsigned fAttachingOrDetaching) { #if 0 PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; unsigned uOp = fAttachingOrDetaching ? DLL_THREAD_ATTACH : DLL_THREAD_DETACH; int rc; /* * Do TLS callbacks first and then call the init/term function if it's a DLL. */ rc = kldrModMachODoCallTLS(pModMachO, uOp, uHandle); if (!fAttachingOrDetaching) rc = 0; if ( !rc && (pModMachO->Hdrs.FileHeader.Characteristics & IMAGE_FILE_DLL)) { rc = kldrModMachODoCallDLL(pModMachO, uOp, uHandle); if (!fAttachingOrDetaching) rc = 0; if (rc) kldrModMachODoCallTLS(pModMachO, uOp, uHandle); } return rc; #else return 0; #endif } /** @copydoc kLdrModSize */ static KLDRADDR kldrModMachOSize(PKLDRMOD pMod) { PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; return pModMachO->cbImage; } /** @copydoc kLdrModGetBits */ static int kldrModMachOGetBits(PKLDRMOD pMod, void *pvBits, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) { #if 0 PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; uint32_t i; int rc; /* * Zero the entire buffer first to simplify things. */ kLdrHlpMemSet(pvBits, 0, pModMachO->Hdrs.OptionalHeader.SizeOfImage); /* * Iterate the segments and read the data within them. */ for (i = 0; i < pMod->cSegments; i++) { /* skip it? */ if ( pMod->aSegments[i].cbFile == -1 || pMod->aSegments[i].offFile == -1 || pMod->aSegments[i].LinkAddress == NIL_KLDRADDR || !pMod->aSegments[i].Alignment) continue; rc = kLdrRdrRead(pMod->pRdr, (uint8_t *)pvBits + (pMod->aSegments[i].LinkAddress - pModMachO->Hdrs.OptionalHeader.ImageBase), pMod->aSegments[i].cbFile, pMod->aSegments[i].offFile); if (rc) return rc; } /* * Perform relocations. */ return kldrModMachORelocateBits(pMod, pvBits, BaseAddress, pModMachO->Hdrs.OptionalHeader.ImageBase, pfnGetImport, pvUser); #else return 0; #endif } /** @copydoc kLdrModRelocateBits */ static int kldrModMachORelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) { PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; int rc; /* * Call workers to do the jobs. */ rc = kldrModMachODoFixups(pModMachO, pvBits, NewBaseAddress, OldBaseAddress); if (!rc) rc = kldrModMachODoImports(pModMachO, pvBits, pfnGetImport, pvUser); return rc; } /** * The Mach-O module interpreter method table. */ KLDRMODOPS g_kLdrModMachOOps = { "Mach-O", NULL, kldrModMachOCreate, kldrModMachODestroy, kldrModMachOQuerySymbol, kldrModMachOEnumSymbols, kldrModMachOGetImport, kldrModMachONumberOfImports, NULL /* can execute one is optional */, kldrModMachOGetStackInfo, kldrModMachOQueryMainEntrypoint, NULL, NULL, kldrModMachOEnumDbgInfo, kldrModMachOHasDbgInfo, kldrModMachOMap, kldrModMachOUnmap, kldrModMachOAllocTLS, kldrModMachOFreeTLS, kldrModMachOReload, kldrModMachOFixupMapping, kldrModMachOCallInit, kldrModMachOCallTerm, kldrModMachOCallThread, kldrModMachOSize, kldrModMachOGetBits, kldrModMachORelocateBits, NULL, /** @todo mostly done */ 42 /* the end */ };