From 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:21:29 +0200 Subject: Adding upstream version 1:0.1.9998svn3589+dfsg. Signed-off-by: Daniel Baumann --- src/lib/kStuff/kLdr/kLdrModMachO.c | 3729 ++++++++++++++++++++++++++++++++++++ 1 file changed, 3729 insertions(+) create mode 100644 src/lib/kStuff/kLdr/kLdrModMachO.c (limited to 'src/lib/kStuff/kLdr/kLdrModMachO.c') diff --git a/src/lib/kStuff/kLdr/kLdrModMachO.c b/src/lib/kStuff/kLdr/kLdrModMachO.c new file mode 100644 index 0000000..9b97ec2 --- /dev/null +++ b/src/lib/kStuff/kLdr/kLdrModMachO.c @@ -0,0 +1,3729 @@ +/* $Id: kLdrModMachO.c 112 2018-07-04 09:37:39Z bird $ */ +/** @file + * kLdr - The Module Interpreter for the MACH-O format. + */ + +/* + * Copyright (c) 2006-2013 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" +#include + + +/******************************************************************************* +* 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) kHlpAssert(expr) +#else +# define KLDRMODMACHO_ASSERT(expr) do {} while (0) +#endif + +/** @def KLDRMODMACHO_CHECK_RETURN + * Checks that an expression is true and return if it isn't. + * This is a debug aid. + */ +#ifdef KLDRMODMACHO_STRICT2 +# define KLDRMODMACHO_CHECK_RETURN(expr, rc) kHlpAssertReturn(expr, rc) +#else +# define KLDRMODMACHO_CHECK_RETURN(expr, rc) do { if (!(expr)) { return (rc); } } while (0) +#endif + +/** @def KLDRMODMACHO_CHECK_RETURN + * Checks that an expression is true and return if it isn't. + * This is a debug aid. + */ +#ifdef KLDRMODMACHO_STRICT2 +# define KLDRMODMACHO_FAILED_RETURN(rc) kHlpAssertFailedReturn(rc) +#else +# define KLDRMODMACHO_FAILED_RETURN(rc) return (rc) +#endif + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * Mach-O section details. + */ +typedef struct KLDRMODMACHOSECT +{ + /** The size of the section (in bytes). */ + KLDRSIZE cb; + /** The link address of this section. */ + KLDRADDR LinkAddress; + /** The RVA of this section. */ + KLDRADDR RVA; + /** The file offset of this section. + * This is -1 if the section doesn't have a file backing. */ + KLDRFOFF offFile; + /** The number of fixups. */ + KU32 cFixups; + /** The array of fixups. (lazy loaded) */ + macho_relocation_info_t *paFixups; + /** The file offset of the fixups for this section. + * This is -1 if the section doesn't have any fixups. */ + KLDRFOFF offFixups; + /** Mach-O section flags. */ + KU32 fFlags; + /** kLdr segment index. */ + KU32 iSegment; + /** Pointer to the Mach-O section structure. */ + void *pvMachoSection; +} KLDRMODMACHOSECT, *PKLDRMODMACHOSECT; + +/** + * Extra per-segment info. + * + * This is corresponds to a kLdr segment, not a Mach-O segment! + */ +typedef struct KLDRMODMACHOSEG +{ + /** The orignal segment number (in case we had to resort it). */ + KU32 iOrgSegNo; + /** The number of sections in the segment. */ + KU32 cSections; + /** Pointer to the sections belonging to this segment. + * The array resides in the big memory chunk allocated for + * the module handle, so it doesn't need freeing. */ + PKLDRMODMACHOSECT paSections; + +} KLDRMODMACHOSEG, *PKLDRMODMACHOSEG; + +/** + * 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 file mapping of the raw file bits. NULL if not mapped. */ + const void *pvBits; + /** Pointer to the user mapping. */ + void *pvMapping; + /** The module open flags. */ + KU32 fOpenFlags; + + /** The offset of the image. (FAT fun.) */ + KLDRFOFF offImage; + /** The link address. */ + KLDRADDR LinkAddress; + /** The size of the mapped image. */ + KLDRADDR cbImage; + /** Whether we're capable of loading the image. */ + KBOOL fCanLoad; + /** Whether we're creating a global offset table segment. + * This dependes on the cputype and image type. */ + KBOOL fMakeGot; + /** The size of a indirect GOT jump stub entry. + * This is 0 if not needed. */ + KU8 cbJmpStub; + /** Effective file type. If the original was a MH_OBJECT file, the + * corresponding MH_DSYM needs the segment translation of a MH_OBJECT too. + * The MH_DSYM normally has a separate __DWARF segment, but this is + * automatically skipped during the transation. */ + KU8 uEffFileType; + /** Pointer to the load commands. (endian converted) */ + KU8 *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. */ + KLDRFOFF offSymbols; + /** The number of symbols. */ + KU32 cSymbols; + /** The pointer to the loaded symbol table. */ + void *pvaSymbols; + /** The offset of the string table. */ + KLDRFOFF offStrings; + /** The size of the of the string table. */ + KU32 cchStrings; + /** Pointer to the loaded string table. */ + char *pchStrings; + + /** The image UUID, all zeros if not found. */ + KU8 abImageUuid[16]; + + /** The RVA of the Global Offset Table. */ + KLDRADDR GotRVA; + /** The RVA of the indirect GOT jump stubs. */ + KLDRADDR JmpStubsRVA; + + /** The number of sections. */ + KU32 cSections; + /** Pointer to the section array running in parallel to the Mach-O one. */ + PKLDRMODMACHOSECT paSections; + + /** Array of segments parallel to the one in KLDRMOD. */ + KLDRMODMACHOSEG aSegments[1]; +} KLDRMODMACHO, *PKLDRMODMACHO; + + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +#if 0 +static KI32 kldrModMachONumberOfImports(PKLDRMOD pMod, const void *pvBits); +#endif +static int kldrModMachORelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress, + PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser); + +static int kldrModMachODoCreate(PKRDR pRdr, KLDRFOFF offImage, KU32 fOpenFlags, PKLDRMODMACHO *ppMod); +static int kldrModMachOPreParseLoadCommands(KU8 *pbLoadCommands, const mach_header_32_t *pHdr, PKRDR pRdr, KLDRFOFF offImage, + KU32 fOpenFlags, KU32 *pcSegments, KU32 *pcSections, KU32 *pcbStringPool, + PKBOOL pfCanLoad, PKLDRADDR pLinkAddress, KU8 *puEffFileType); +static int kldrModMachOParseLoadCommands(PKLDRMODMACHO pModMachO, char *pbStringPool, KU32 cbStringPool); +static int kldrModMachOAdjustBaseAddress(PKLDRMODMACHO pModMachO, PKLDRADDR pBaseAddress); + +/*static int kldrModMachOLoadLoadCommands(PKLDRMODMACHO pModMachO);*/ +static int kldrModMachOLoadObjSymTab(PKLDRMODMACHO pModMachO); +static int kldrModMachOLoadFixups(PKLDRMODMACHO pModMachO, KLDRFOFF offFixups, KU32 cFixups, macho_relocation_info_t **ppaFixups); +static int kldrModMachOMapVirginBits(PKLDRMODMACHO pModMachO); + +static int kldrModMachODoQuerySymbol32Bit(PKLDRMODMACHO pModMachO, const macho_nlist_32_t *paSyms, KU32 cSyms, const char *pchStrings, + KU32 cchStrings, KLDRADDR BaseAddress, KU32 iSymbol, const char *pchSymbol, + KU32 cchSymbol, PKLDRADDR puValue, KU32 *pfKind); +static int kldrModMachODoQuerySymbol64Bit(PKLDRMODMACHO pModMachO, const macho_nlist_64_t *paSyms, KU32 cSyms, const char *pchStrings, + KU32 cchStrings, KLDRADDR BaseAddress, KU32 iSymbol, const char *pchSymbol, + KU32 cchSymbol, PKLDRADDR puValue, KU32 *pfKind); +static int kldrModMachODoEnumSymbols32Bit(PKLDRMODMACHO pModMachO, const macho_nlist_32_t *paSyms, KU32 cSyms, + const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress, + KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser); +static int kldrModMachODoEnumSymbols64Bit(PKLDRMODMACHO pModMachO, const macho_nlist_64_t *paSyms, KU32 cSyms, + const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress, + KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser); +static int kldrModMachOObjDoImports(PKLDRMODMACHO pModMachO, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser); +static int kldrModMachOObjDoFixups(PKLDRMODMACHO pModMachO, void *pvMapping, KLDRADDR NewBaseAddress); +static int kldrModMachOFixupSectionGeneric32Bit(PKLDRMODMACHO pModMachO, KU8 *pbSectBits, PKLDRMODMACHOSECT pFixupSect, + macho_nlist_32_t *paSyms, KU32 cSyms, KLDRADDR NewBaseAddress); +static int kldrModMachOFixupSectionAMD64(PKLDRMODMACHO pModMachO, KU8 *pbSectBits, PKLDRMODMACHOSECT pFixupSect, + macho_nlist_64_t *paSyms, KU32 cSyms, KLDRADDR NewBaseAddress); + +static int kldrModMachOMakeGOT(PKLDRMODMACHO pModMachO, void *pvBits, KLDRADDR NewBaseAddress); + +/*static int kldrModMachODoFixups(PKLDRMODMACHO pModMachO, void *pvMapping, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress); +static int kldrModMachODoImports(PKLDRMODMACHO pModMachO, void *pvMapping, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);*/ + + +/** + * 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 fFlags Flags, MBZ. + * @param enmCpuArch The desired CPU architecture. KCPUARCH_UNKNOWN means + * anything goes, but with a preference for the current + * host architecture. + * @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, PKRDR pRdr, KU32 fFlags, KCPUARCH enmCpuArch, KLDRFOFF offNewHdr, PPKLDRMOD ppMod) +{ + PKLDRMODMACHO pModMachO; + int rc; + + /* + * Create the instance data and do a minimal header validation. + */ + rc = kldrModMachODoCreate(pRdr, offNewHdr == -1 ? 0 : offNewHdr, fFlags, &pModMachO); + if (!rc) + { + + /* + * Match up against the requested CPU architecture. + */ + if ( enmCpuArch == KCPUARCH_UNKNOWN + || pModMachO->pMod->enmArch == enmCpuArch) + { + pModMachO->pMod->pOps = pOps; + pModMachO->pMod->u32Magic = KLDRMOD_MAGIC; + *ppMod = pModMachO->pMod; + return 0; + } + rc = KLDR_ERR_CPU_ARCH_MISMATCH; + } + if (pModMachO) + { + kHlpFree(pModMachO->pbLoadCommands); + kHlpFree(pModMachO); + } + return rc; +} + + +/** + * Separate function for reading creating the Mach-O module instance to + * simplify cleanup on failure. + */ +static int kldrModMachODoCreate(PKRDR pRdr, KLDRFOFF offImage, KU32 fOpenFlags, PKLDRMODMACHO *ppModMachO) +{ + union + { + mach_header_32_t Hdr32; + mach_header_64_t Hdr64; + } s; + PKLDRMODMACHO pModMachO; + PKLDRMOD pMod; + KU8 *pbLoadCommands; + KU32 cSegments = 0; /* (MSC maybe used uninitialized) */ + KU32 cSections = 0; /* (MSC maybe used uninitialized) */ + KU32 cbStringPool = 0; /* (MSC maybe used uninitialized) */ + KSIZE cchFilename; + KSIZE cb; + KBOOL fMakeGot; + KBOOL fCanLoad = K_TRUE; + KLDRADDR LinkAddress = NIL_KLDRADDR; /* (MSC maybe used uninitialized) */ + KU8 cbJmpStub; + KU8 uEffFileType = 0; /* (MSC maybe used uninitialized) */ + int rc; + *ppModMachO = NULL; + + kHlpAssert(&s.Hdr32.magic == &s.Hdr64.magic); + kHlpAssert(&s.Hdr32.flags == &s.Hdr64.flags); + + /* + * Read the Mach-O header. + */ + rc = kRdrRead(pRdr, &s, sizeof(s), offImage); + if (rc) + return rc; + if ( s.Hdr32.magic != IMAGE_MACHO32_SIGNATURE + && s.Hdr32.magic != IMAGE_MACHO64_SIGNATURE + ) + { + if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE + || s.Hdr32.magic == IMAGE_MACHO64_SIGNATURE_OE) + return KLDR_ERR_MACHO_OTHER_ENDIAN_NOT_SUPPORTED; + return KLDR_ERR_UNKNOWN_FORMAT; + } + + /* sanity checks. */ + if ( s.Hdr32.sizeofcmds > kRdrSize(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: + fMakeGot = K_FALSE; + cbJmpStub = 0; + break; + case CPU_TYPE_X86_64: + fMakeGot = s.Hdr32.filetype == MH_OBJECT; + cbJmpStub = fMakeGot ? 8 : 0; + break; + default: + return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE; + } + if ( s.Hdr32.filetype != MH_OBJECT + && s.Hdr32.filetype != MH_EXECUTE + && s.Hdr32.filetype != MH_DYLIB + && s.Hdr32.filetype != MH_BUNDLE + && s.Hdr32.filetype != MH_DSYM + && s.Hdr32.filetype != MH_KEXT_BUNDLE) + 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 = kHlpAlloc(s.Hdr32.sizeofcmds); + if (!pbLoadCommands) + return KERR_NO_MEMORY; + rc = kRdrRead(pRdr, pbLoadCommands, s.Hdr32.sizeofcmds, + s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE + || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE + ? sizeof(mach_header_32_t) + offImage + : sizeof(mach_header_64_t) + offImage); + if (!rc) + rc = kldrModMachOPreParseLoadCommands(pbLoadCommands, &s.Hdr32, pRdr, offImage, fOpenFlags, + &cSegments, &cSections, &cbStringPool, &fCanLoad, &LinkAddress, &uEffFileType); + if (rc) + { + kHlpFree(pbLoadCommands); + return rc; + } + cSegments += fMakeGot; + + + /* + * Calc the instance size, allocate and initialize it. + */ + cchFilename = kHlpStrLen(kRdrName(pRdr)); + cb = K_ALIGN_Z( K_OFFSETOF(KLDRMODMACHO, aSegments[cSegments]) + + sizeof(KLDRMODMACHOSECT) * cSections, 16) + + K_OFFSETOF(KLDRMOD, aSegments[cSegments]) + + cchFilename + 1 + + cbStringPool; + pModMachO = (PKLDRMODMACHO)kHlpAlloc(cb); + if (!pModMachO) + return KERR_NO_MEMORY; + *ppModMachO = pModMachO; + pModMachO->pbLoadCommands = pbLoadCommands; + pModMachO->offImage = offImage; + + /* KLDRMOD */ + pMod = (PKLDRMOD)((KU8 *)pModMachO + K_ALIGN_Z( K_OFFSETOF(KLDRMODMACHO, aSegments[cSegments]) + + sizeof(KLDRMODMACHOSECT) * cSections, 16)); + pMod->pvData = pModMachO; + pMod->pRdr = pRdr; + pMod->pOps = NULL; /* set upon success. */ + pMod->cSegments = cSegments; + pMod->cchFilename = (KU32)cchFilename; + pMod->pszFilename = (char *)&pMod->aSegments[pMod->cSegments]; + kHlpMemCopy((char *)pMod->pszFilename, kRdrName(pRdr), cchFilename + 1); + pMod->pszName = kHlpGetFilename(pMod->pszFilename); + pMod->cchName = (KU32)(cchFilename - (pMod->pszName - pMod->pszFilename)); + pMod->fFlags = 0; + switch (s.Hdr32.cputype) + { + case CPU_TYPE_X86: + pMod->enmArch = KCPUARCH_X86_32; + pMod->enmEndian = KLDRENDIAN_LITTLE; + switch (s.Hdr32.cpusubtype) + { + case CPU_SUBTYPE_I386_ALL: /* == CPU_SUBTYPE_386 */ + pMod->enmCpu = KCPU_X86_32_BLEND; + break; + case CPU_SUBTYPE_486: + pMod->enmCpu = KCPU_I486; + break; + case CPU_SUBTYPE_486SX: + pMod->enmCpu = KCPU_I486SX; + break; + case CPU_SUBTYPE_PENT: /* == CPU_SUBTYPE_586 */ + pMod->enmCpu = KCPU_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 = KCPU_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 = KCPU_P4; + break; + + default: + /* Hack for kextutil output. */ + if ( s.Hdr32.cpusubtype == 0 + && s.Hdr32.filetype == MH_OBJECT) + break; + return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE; + } + break; + + case CPU_TYPE_X86_64: + pMod->enmArch = KCPUARCH_AMD64; + pMod->enmEndian = KLDRENDIAN_LITTLE; + switch (s.Hdr32.cpusubtype & ~CPU_SUBTYPE_MASK) + { + case CPU_SUBTYPE_X86_64_ALL: pMod->enmCpu = KCPU_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; + case MH_EXECUTE: pMod->enmType = KLDRTYPE_EXECUTABLE_FIXED; break; + case MH_DYLIB: pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break; + case MH_BUNDLE: pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break; + case MH_KEXT_BUNDLE:pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break; + case MH_DSYM: pMod->enmType = KLDRTYPE_DEBUG_INFO; 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->fOpenFlags = fOpenFlags; + pModMachO->Hdr = s.Hdr64; + if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE + || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE) + pModMachO->Hdr.reserved = 0; + pModMachO->LinkAddress = LinkAddress; + pModMachO->cbImage = 0; + pModMachO->fCanLoad = fCanLoad; + pModMachO->fMakeGot = fMakeGot; + pModMachO->cbJmpStub = cbJmpStub; + pModMachO->uEffFileType = uEffFileType; + pModMachO->offSymbols = 0; + pModMachO->cSymbols = 0; + pModMachO->pvaSymbols = NULL; + pModMachO->offStrings = 0; + pModMachO->cchStrings = 0; + pModMachO->pchStrings = NULL; + kHlpMemSet(pModMachO->abImageUuid, 0, sizeof(pModMachO->abImageUuid)); + pModMachO->GotRVA = NIL_KLDRADDR; + pModMachO->JmpStubsRVA = NIL_KLDRADDR; + pModMachO->cSections = cSections; + pModMachO->paSections = (PKLDRMODMACHOSECT)&pModMachO->aSegments[pModMachO->pMod->cSegments]; + + /* + * 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, section counting and string pool + * sizing. + * + * Segment are created in two different ways, depending on the file type. + * + * For object files there is only one segment command without a given segment + * name. The sections inside that segment have different segment names and are + * not sorted by their segname attribute. We create one segment for each + * section, with the segment name being 'segname.sectname' in order to hopefully + * keep the names unique. Debug sections does not get segments. + * + * For non-object files, one kLdr segment is created for each Mach-O segment. + * Debug segments is not exposed by kLdr via the kLdr segment table, but via the + * debug enumeration callback API. + * + * @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 offImage The image header (FAT fun). + * @param pcSegments Where to store the segment count. + * @param pcSegments Where to store the section count. + * @param pcbStringPool Where to store the string pool size. + * @param pfCanLoad Where to store the can-load-image indicator. + * @param pLinkAddress Where to store the image link address (i.e. the + * lowest segment address). + */ +static int kldrModMachOPreParseLoadCommands(KU8 *pbLoadCommands, const mach_header_32_t *pHdr, PKRDR pRdr, KLDRFOFF offImage, + KU32 fOpenFlags, KU32 *pcSegments, KU32 *pcSections, KU32 *pcbStringPool, + PKBOOL pfCanLoad, PKLDRADDR pLinkAddress, KU8 *puEffFileType) +{ + union + { + KU8 *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 KU64 cbFile = kRdrSize(pRdr) - offImage; + KU32 cSegments = 0; + KU32 cSections = 0; + KSIZE cbStringPool = 0; + KU32 cLeft = pHdr->ncmds; + KU32 cbLeft = pHdr->sizeofcmds; + KU8 *pb = pbLoadCommands; + int cSegmentCommands = 0; + int cSymbolTabs = 0; + int fConvertEndian = pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE + || pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE; + KU8 uEffFileType = *puEffFileType = pHdr->filetype; + + *pcSegments = 0; + *pcSections = 0; + *pcbStringPool = 0; + *pfCanLoad = K_TRUE; + *pLinkAddress = ~(KLDRADDR)0; + + while (cLeft-- > 0) + { + u.pb = pb; + + /* + * Convert and validate command header. + */ + KLDRMODMACHO_CHECK_RETURN(cbLeft >= sizeof(load_command_t), KLDR_ERR_MACHO_BAD_LOAD_COMMAND); + if (fConvertEndian) + { + u.pLoadCmd->cmd = K_E2E_U32(u.pLoadCmd->cmd); + u.pLoadCmd->cmdsize = K_E2E_U32(u.pLoadCmd->cmdsize); + } + KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize <= cbLeft, 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: + { + segment_command_32_t *pSrcSeg = (segment_command_32_t *)u.pLoadCmd; + section_32_t *pFirstSect = (section_32_t *)(pSrcSeg + 1); + section_32_t *pSect = pFirstSect; + KU32 cSectionsLeft = pSrcSeg->nsects; + KU64 offSect = 0; + + /* Convert and verify the segment. */ + KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_32_t), KLDR_ERR_MACHO_BAD_LOAD_COMMAND); + KLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE + || pHdr->magic == IMAGE_MACHO32_SIGNATURE, KLDR_ERR_MACHO_BIT_MIX); + if (fConvertEndian) + { + pSrcSeg->vmaddr = K_E2E_U32(pSrcSeg->vmaddr); + pSrcSeg->vmsize = K_E2E_U32(pSrcSeg->vmsize); + pSrcSeg->fileoff = K_E2E_U32(pSrcSeg->fileoff); + pSrcSeg->filesize = K_E2E_U32(pSrcSeg->filesize); + pSrcSeg->maxprot = K_E2E_U32(pSrcSeg->maxprot); + pSrcSeg->initprot = K_E2E_U32(pSrcSeg->initprot); + pSrcSeg->nsects = K_E2E_U32(pSrcSeg->nsects); + pSrcSeg->flags = K_E2E_U32(pSrcSeg->flags); + } + + /* Validation code shared with the 64-bit variant. */ + #define VALIDATE_AND_ADD_SEGMENT(a_cBits) \ + do { \ + KBOOL fSkipSeg = !kHlpStrComp(pSrcSeg->segname, "__DWARF") /* Note: Not for non-object files. */ \ + || ( !kHlpStrComp(pSrcSeg->segname, "__CTF") /* Their CTF tool did/does weird things, */ \ + && pSrcSeg->vmsize == 0) /* overlapping vmaddr and zero vmsize. */ \ + || (cSectionsLeft > 0 && (pFirstSect->flags & S_ATTR_DEBUG)); \ + \ + /* MH_DSYM files for MH_OBJECT files must have MH_OBJECT segment translation. */ \ + if ( uEffFileType == MH_DSYM \ + && cSegmentCommands == 0 \ + && pSrcSeg->segname[0] == '\0') \ + *puEffFileType = uEffFileType = MH_OBJECT; \ + \ + KLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize == 0 \ + || ( pSrcSeg->fileoff <= cbFile \ + && (KU64)pSrcSeg->fileoff + pSrcSeg->filesize <= cbFile), \ + KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \ + KLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize <= pSrcSeg->vmsize \ + || (fSkipSeg && !kHlpStrComp(pSrcSeg->segname, "__CTF") /* see above */), \ + KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \ + KLDRMODMACHO_CHECK_RETURN(!(~pSrcSeg->maxprot & pSrcSeg->initprot), \ + KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \ + KLDRMODMACHO_CHECK_RETURN(!(pSrcSeg->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1)), \ + KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \ + KLDRMODMACHO_CHECK_RETURN( pSrcSeg->nsects * sizeof(section_##a_cBits##_t) \ + <= u.pLoadCmd->cmdsize - sizeof(segment_command_##a_cBits##_t), \ + KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \ + KLDRMODMACHO_CHECK_RETURN( uEffFileType != MH_OBJECT \ + || cSegmentCommands == 0 \ + || ( cSegmentCommands == 1 \ + && uEffFileType == MH_OBJECT \ + && pHdr->filetype == MH_DSYM \ + && fSkipSeg), \ + KLDR_ERR_MACHO_BAD_OBJECT_FILE); \ + cSegmentCommands++; \ + \ + /* Add the segment, if not object file. */ \ + if (!fSkipSeg && uEffFileType != MH_OBJECT) \ + { \ + cbStringPool += kHlpStrNLen(&pSrcSeg->segname[0], sizeof(pSrcSeg->segname)) + 1; \ + cSegments++; \ + if (cSegments == 1) /* The link address is set by the first segment. */ \ + *pLinkAddress = pSrcSeg->vmaddr; \ + } \ + } while (0) + + VALIDATE_AND_ADD_SEGMENT(32); + + /* + * Convert, validate and parse the sections. + */ + cSectionsLeft = pSrcSeg->nsects; + pFirstSect = pSect = (section_32_t *)(pSrcSeg + 1); + while (cSectionsLeft-- > 0) + { + if (fConvertEndian) + { + pSect->addr = K_E2E_U32(pSect->addr); + pSect->size = K_E2E_U32(pSect->size); + pSect->offset = K_E2E_U32(pSect->offset); + pSect->align = K_E2E_U32(pSect->align); + pSect->reloff = K_E2E_U32(pSect->reloff); + pSect->nreloc = K_E2E_U32(pSect->nreloc); + pSect->flags = K_E2E_U32(pSect->flags); + pSect->reserved1 = K_E2E_U32(pSect->reserved1); + pSect->reserved2 = K_E2E_U32(pSect->reserved2); + } + + /* Validation code shared with the 64-bit variant. */ + #define VALIDATE_AND_ADD_SECTION(a_cBits) \ + do { \ + int fFileBits; \ + \ + /* validate */ \ + if (uEffFileType != MH_OBJECT) \ + KLDRMODMACHO_CHECK_RETURN(!kHlpStrComp(pSect->segname, pSrcSeg->segname),\ + KLDR_ERR_MACHO_BAD_SECTION); \ + \ + switch (pSect->flags & SECTION_TYPE) \ + { \ + case S_ZEROFILL: \ + KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \ + KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \ + fFileBits = 0; \ + break; \ + case S_REGULAR: \ + case S_CSTRING_LITERALS: \ + case S_COALESCED: \ + case S_4BYTE_LITERALS: \ + case S_8BYTE_LITERALS: \ + case S_16BYTE_LITERALS: \ + KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \ + KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \ + fFileBits = 1; \ + break; \ + \ + case S_SYMBOL_STUBS: \ + KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \ + /* reserved2 == stub size. 0 has been seen (corecrypto.kext) */ \ + KLDRMODMACHO_CHECK_RETURN(pSect->reserved2 < 64, KLDR_ERR_MACHO_BAD_SECTION); \ + fFileBits = 1; \ + break; \ + \ + case S_NON_LAZY_SYMBOL_POINTERS: \ + case S_LAZY_SYMBOL_POINTERS: \ + case S_LAZY_DYLIB_SYMBOL_POINTERS: \ + /* (reserved 1 = is indirect symbol table index) */ \ + KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \ + *pfCanLoad = K_FALSE; \ + fFileBits = -1; /* __DATA.__got in the 64-bit mach_kernel has bits, any things without bits? */ \ + break; \ + \ + case S_MOD_INIT_FUNC_POINTERS: \ + /** @todo this requires a query API or flag... (e.g. C++ constructors) */ \ + KLDRMODMACHO_CHECK_RETURN(fOpenFlags & KLDRMOD_OPEN_FLAGS_FOR_INFO, \ + KLDR_ERR_MACHO_UNSUPPORTED_INIT_SECTION); \ + /* Falls through. */ \ + case S_MOD_TERM_FUNC_POINTERS: \ + /** @todo this requires a query API or flag... (e.g. C++ destructors) */ \ + KLDRMODMACHO_CHECK_RETURN(fOpenFlags & KLDRMOD_OPEN_FLAGS_FOR_INFO, \ + KLDR_ERR_MACHO_UNSUPPORTED_TERM_SECTION); \ + KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \ + KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \ + fFileBits = 1; \ + break; /* ignored */ \ + \ + case S_LITERAL_POINTERS: \ + case S_DTRACE_DOF: \ + KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \ + KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \ + fFileBits = 1; \ + break; \ + \ + case S_INTERPOSING: \ + case S_GB_ZEROFILL: \ + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNSUPPORTED_SECTION); \ + \ + default: \ + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNKNOWN_SECTION); \ + } \ + KLDRMODMACHO_CHECK_RETURN(!(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)), \ + KLDR_ERR_MACHO_BAD_SECTION); \ + KLDRMODMACHO_CHECK_RETURN((pSect->flags & S_ATTR_DEBUG) == (pFirstSect->flags & S_ATTR_DEBUG), \ + KLDR_ERR_MACHO_MIXED_DEBUG_SECTION_FLAGS); \ + \ + KLDRMODMACHO_CHECK_RETURN(pSect->addr - pSrcSeg->vmaddr <= pSrcSeg->vmsize, \ + KLDR_ERR_MACHO_BAD_SECTION); \ + KLDRMODMACHO_CHECK_RETURN( pSect->addr - pSrcSeg->vmaddr + pSect->size <= pSrcSeg->vmsize \ + || !kHlpStrComp(pSrcSeg->segname, "__CTF") /* see above */, \ + KLDR_ERR_MACHO_BAD_SECTION); \ + KLDRMODMACHO_CHECK_RETURN(pSect->align < 31, \ + KLDR_ERR_MACHO_BAD_SECTION); \ + /* Workaround for buggy ld64 (or as, llvm, ++) that produces a misaligned __TEXT.__unwind_info. */ \ + /* Seen: pSect->align = 4, pSect->addr = 0x5ebe14. Just adjust the alignment down. */ \ + if ( ((K_BIT32(pSect->align) - KU32_C(1)) & pSect->addr) \ + && pSect->align == 4 \ + && kHlpStrComp(pSect->sectname, "__unwind_info") == 0) \ + pSect->align = 2; \ + KLDRMODMACHO_CHECK_RETURN(!((K_BIT32(pSect->align) - KU32_C(1)) & pSect->addr), \ + KLDR_ERR_MACHO_BAD_SECTION); \ + KLDRMODMACHO_CHECK_RETURN(!((K_BIT32(pSect->align) - KU32_C(1)) & pSrcSeg->vmaddr), \ + KLDR_ERR_MACHO_BAD_SECTION); \ + \ + /* Adjust the section offset before we check file offset. */ \ + offSect = (offSect + K_BIT64(pSect->align) - KU64_C(1)) & ~(K_BIT64(pSect->align) - KU64_C(1)); \ + if (pSect->addr) \ + { \ + KLDRMODMACHO_CHECK_RETURN(offSect <= pSect->addr - pSrcSeg->vmaddr, KLDR_ERR_MACHO_BAD_SECTION); \ + if (offSect < pSect->addr - pSrcSeg->vmaddr) \ + offSect = pSect->addr - pSrcSeg->vmaddr; \ + } \ + \ + if (fFileBits && pSect->offset == 0 && pSrcSeg->fileoff == 0 && pHdr->filetype == MH_DSYM) \ + fFileBits = 0; \ + if (fFileBits) \ + { \ + if (uEffFileType != MH_OBJECT) \ + { \ + KLDRMODMACHO_CHECK_RETURN(pSect->offset == pSrcSeg->fileoff + offSect, \ + KLDR_ERR_MACHO_NON_CONT_SEG_BITS); \ + KLDRMODMACHO_CHECK_RETURN(pSect->offset - pSrcSeg->fileoff <= pSrcSeg->filesize, \ + KLDR_ERR_MACHO_BAD_SECTION); \ + } \ + KLDRMODMACHO_CHECK_RETURN(pSect->offset <= cbFile, \ + KLDR_ERR_MACHO_BAD_SECTION); \ + KLDRMODMACHO_CHECK_RETURN((KU64)pSect->offset + pSect->size <= cbFile, \ + KLDR_ERR_MACHO_BAD_SECTION); \ + } \ + else \ + KLDRMODMACHO_CHECK_RETURN(pSect->offset == 0, KLDR_ERR_MACHO_BAD_SECTION); \ + \ + if (!pSect->nreloc) \ + KLDRMODMACHO_CHECK_RETURN(!pSect->reloff, \ + KLDR_ERR_MACHO_BAD_SECTION); \ + else \ + { \ + KLDRMODMACHO_CHECK_RETURN(pSect->reloff <= cbFile, \ + KLDR_ERR_MACHO_BAD_SECTION); \ + KLDRMODMACHO_CHECK_RETURN( (KU64)pSect->reloff \ + + (KLDRFOFF)pSect->nreloc * sizeof(macho_relocation_info_t) \ + <= cbFile, \ + KLDR_ERR_MACHO_BAD_SECTION); \ + } \ + \ + /* Validate against file type (pointless?) and count the section, for object files add segment. */ \ + switch (uEffFileType) \ + { \ + case MH_OBJECT: \ + if ( !(pSect->flags & S_ATTR_DEBUG) \ + && kHlpStrComp(pSect->segname, "__DWARF")) \ + { \ + cbStringPool += kHlpStrNLen(&pSect->segname[0], sizeof(pSect->segname)) + 1; \ + cbStringPool += kHlpStrNLen(&pSect->sectname[0], sizeof(pSect->sectname)) + 1; \ + cSegments++; \ + if (cSegments == 1) /* The link address is set by the first segment. */ \ + *pLinkAddress = pSect->addr; \ + } \ + /* Falls through. */ \ + case MH_EXECUTE: \ + case MH_DYLIB: \ + case MH_BUNDLE: \ + case MH_DSYM: \ + case MH_KEXT_BUNDLE: \ + cSections++; \ + break; \ + default: \ + KLDRMODMACHO_FAILED_RETURN(KERR_INVALID_PARAMETER); \ + } \ + \ + /* Advance the section offset, since we're also aligning it. */ \ + offSect += pSect->size; \ + } while (0) /* VALIDATE_AND_ADD_SECTION */ + + VALIDATE_AND_ADD_SECTION(32); + + /* next */ + pSect++; + } + break; + } + + case LC_SEGMENT_64: + { + segment_command_64_t *pSrcSeg = (segment_command_64_t *)u.pLoadCmd; + section_64_t *pFirstSect = (section_64_t *)(pSrcSeg + 1); + section_64_t *pSect = pFirstSect; + KU32 cSectionsLeft = pSrcSeg->nsects; + KU64 offSect = 0; + + /* Convert and verify the segment. */ + KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_64_t), KLDR_ERR_MACHO_BAD_LOAD_COMMAND); + KLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE + || pHdr->magic == IMAGE_MACHO64_SIGNATURE, KLDR_ERR_MACHO_BIT_MIX); + if (fConvertEndian) + { + pSrcSeg->vmaddr = K_E2E_U64(pSrcSeg->vmaddr); + pSrcSeg->vmsize = K_E2E_U64(pSrcSeg->vmsize); + pSrcSeg->fileoff = K_E2E_U64(pSrcSeg->fileoff); + pSrcSeg->filesize = K_E2E_U64(pSrcSeg->filesize); + pSrcSeg->maxprot = K_E2E_U32(pSrcSeg->maxprot); + pSrcSeg->initprot = K_E2E_U32(pSrcSeg->initprot); + pSrcSeg->nsects = K_E2E_U32(pSrcSeg->nsects); + pSrcSeg->flags = K_E2E_U32(pSrcSeg->flags); + } + + VALIDATE_AND_ADD_SEGMENT(64); + + /* + * Convert, validate and parse the sections. + */ + while (cSectionsLeft-- > 0) + { + if (fConvertEndian) + { + pSect->addr = K_E2E_U64(pSect->addr); + pSect->size = K_E2E_U64(pSect->size); + pSect->offset = K_E2E_U32(pSect->offset); + pSect->align = K_E2E_U32(pSect->align); + pSect->reloff = K_E2E_U32(pSect->reloff); + pSect->nreloc = K_E2E_U32(pSect->nreloc); + pSect->flags = K_E2E_U32(pSect->flags); + pSect->reserved1 = K_E2E_U32(pSect->reserved1); + pSect->reserved2 = K_E2E_U32(pSect->reserved2); + } + + VALIDATE_AND_ADD_SECTION(64); + + /* next */ + pSect++; + } + break; + } /* LC_SEGMENT_64 */ + + + case LC_SYMTAB: + { + KSIZE cbSym; + if (fConvertEndian) + { + u.pSymTab->symoff = K_E2E_U32(u.pSymTab->symoff); + u.pSymTab->nsyms = K_E2E_U32(u.pSymTab->nsyms); + u.pSymTab->stroff = K_E2E_U32(u.pSymTab->stroff); + u.pSymTab->strsize = K_E2E_U32(u.pSymTab->strsize); + } + + /* verify */ + cbSym = pHdr->magic == IMAGE_MACHO32_SIGNATURE + || pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE + ? sizeof(macho_nlist_32_t) + : sizeof(macho_nlist_64_t); + if ( u.pSymTab->symoff >= cbFile + || (KU64)u.pSymTab->symoff + u.pSymTab->nsyms * cbSym > cbFile) + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND); + if ( u.pSymTab->stroff >= cbFile + || (KU64)u.pSymTab->stroff + u.pSymTab->strsize > cbFile) + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND); + + /* only one string in objects, please. */ + cSymbolTabs++; + if ( uEffFileType == MH_OBJECT + && cSymbolTabs != 1) + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_OBJECT_FILE); + break; + } + + case LC_DYSYMTAB: + /** @todo deal with this! */ + break; + + case LC_THREAD: + case LC_UNIXTHREAD: + { + KU32 *pu32 = (KU32 *)(u.pb + sizeof(load_command_t)); + KU32 cItemsLeft = (u.pThread->cmdsize - sizeof(load_command_t)) / sizeof(KU32); + while (cItemsLeft) + { + /* convert & verify header items ([0] == flavor, [1] == KU32 count). */ + if (cItemsLeft < 2) + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND); + if (fConvertEndian) + { + pu32[0] = K_E2E_U32(pu32[0]); + pu32[1] = K_E2E_U32(pu32[1]); + } + if (pu32[1] + 2 > cItemsLeft) + KLDRMODMACHO_FAILED_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)) + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND); + /** @todo Check anything here need converting? */ + break; + + case LC_CODE_SIGNATURE: + if (u.pUuid->cmdsize != sizeof(linkedit_data_command_t)) + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND); + break; + + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + if (u.pUuid->cmdsize != sizeof(version_min_command_t)) + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND); + break; + + case LC_SOURCE_VERSION: /* Harmless. It just gives a clue regarding the source code revision/version. */ + case LC_DATA_IN_CODE: /* Ignore */ + case LC_DYLIB_CODE_SIGN_DRS:/* Ignore */ + /** @todo valid command size. */ + break; + + case LC_FUNCTION_STARTS: /** @todo dylib++ */ + /* Ignore for now. */ + break; + case LC_ID_DYLIB: /** @todo dylib */ + case LC_LOAD_DYLIB: /** @todo dylib */ + case LC_LOAD_DYLINKER: /** @todo dylib */ + case LC_TWOLEVEL_HINTS: /** @todo dylib */ + case LC_LOAD_WEAK_DYLIB: /** @todo dylib */ + case LC_ID_DYLINKER: /** @todo dylib */ + case LC_RPATH: /** @todo dylib */ + case LC_SEGMENT_SPLIT_INFO: /** @todo dylib++ */ + case LC_REEXPORT_DYLIB: /** @todo dylib */ + case LC_DYLD_INFO: /** @todo dylib */ + case LC_DYLD_INFO_ONLY: /** @todo dylib */ + case LC_LOAD_UPWARD_DYLIB: /** @todo dylib */ + case LC_DYLD_ENVIRONMENT: /** @todo dylib */ + case LC_MAIN: /** @todo parse this and find and entry point or smth. */ + /** @todo valid command size. */ + if (!(fOpenFlags & KLDRMOD_OPEN_FLAGS_FOR_INFO)) + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNSUPPORTED_LOAD_COMMAND); + *pfCanLoad = K_FALSE; + break; + + case LC_LOADFVMLIB: + case LC_IDFVMLIB: + case LC_IDENT: + case LC_FVMFILE: + case LC_PREPAGE: + 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_PREBIND_CKSUM: + case LC_SYMSEG: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNSUPPORTED_LOAD_COMMAND); + + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNKNOWN_LOAD_COMMAND); + } + } + + /* be strict. */ + if (cbLeft) + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND); + + switch (uEffFileType) + { + case MH_OBJECT: + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + case MH_DSYM: + case MH_KEXT_BUNDLE: + if (!cSegments) + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_OBJECT_FILE); + break; + } + + *pcSegments = cSegments; + *pcSections = cSections; + *pcbStringPool = (KU32)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, KU32 cbStringPool) +{ + union + { + const KU8 *pb; + const load_command_t *pLoadCmd; + const segment_command_32_t *pSeg32; + const segment_command_64_t *pSeg64; + const symtab_command_t *pSymTab; + const uuid_command_t *pUuid; + } u; + KU32 cLeft = pModMachO->Hdr.ncmds; + KU32 cbLeft = pModMachO->Hdr.sizeofcmds; + const KU8 *pb = pModMachO->pbLoadCommands; + PKLDRSEG pDstSeg = &pModMachO->pMod->aSegments[0]; + PKLDRMODMACHOSEG pSegExtra = &pModMachO->aSegments[0]; + PKLDRMODMACHOSECT pSectExtra = pModMachO->paSections; + const KU32 cSegments = pModMachO->pMod->cSegments; + PKLDRSEG pSegItr; + K_NOREF(cbStringPool); + + 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: + { + const segment_command_32_t *pSrcSeg = (const segment_command_32_t *)u.pLoadCmd; + section_32_t *pFirstSect = (section_32_t *)(pSrcSeg + 1); + section_32_t *pSect = pFirstSect; + KU32 cSectionsLeft = pSrcSeg->nsects; + + /* Adds a segment, used by the macro below and thus shared with the 64-bit segment variant. */ + #define NEW_SEGMENT(a_cBits, a_achName1, a_fObjFile, a_achName2, a_SegAddr, a_cbSeg, a_fFileBits, a_offFile, a_cbFile) \ + do { \ + pDstSeg->pvUser = NULL; \ + pDstSeg->pchName = pbStringPool; \ + pDstSeg->cchName = (KU32)kHlpStrNLen(a_achName1, sizeof(a_achName1)); \ + kHlpMemCopy(pbStringPool, a_achName1, pDstSeg->cchName); \ + pbStringPool += pDstSeg->cchName; \ + if (a_fObjFile) \ + { /* MH_OBJECT: Add '.sectname' - sections aren't sorted by segments. */ \ + KSIZE cchName2 = kHlpStrNLen(a_achName2, sizeof(a_achName2)); \ + *pbStringPool++ = '.'; \ + kHlpMemCopy(pbStringPool, a_achName2, cchName2); \ + pbStringPool += cchName2; \ + pDstSeg->cchName += (KU32)cchName2; \ + } \ + *pbStringPool++ = '\0'; \ + pDstSeg->SelFlat = 0; \ + pDstSeg->Sel16bit = 0; \ + pDstSeg->fFlags = 0; \ + pDstSeg->enmProt = KPROT_EXECUTE_WRITECOPY; /** @todo fixme! */ \ + pDstSeg->cb = (a_cbSeg); \ + pDstSeg->Alignment = 1; /* updated while parsing sections. */ \ + pDstSeg->LinkAddress = (a_SegAddr); \ + if (a_fFileBits) \ + { \ + pDstSeg->offFile = (KLDRFOFF)((a_offFile) + pModMachO->offImage); \ + pDstSeg->cbFile = (KLDRFOFF)(a_cbFile); \ + } \ + else \ + { \ + pDstSeg->offFile = -1; \ + pDstSeg->cbFile = -1; \ + } \ + pDstSeg->RVA = (a_SegAddr) - pModMachO->LinkAddress; \ + pDstSeg->cbMapped = 0; \ + pDstSeg->MapAddress = 0; \ + \ + pSegExtra->iOrgSegNo = (KU32)(pSegExtra - &pModMachO->aSegments[0]); \ + pSegExtra->cSections = 0; \ + pSegExtra->paSections = pSectExtra; \ + } while (0) + + /* Closes the new segment - part of NEW_SEGMENT. */ + #define CLOSE_SEGMENT() \ + do { \ + pSegExtra->cSections = (KU32)(pSectExtra - pSegExtra->paSections); \ + pSegExtra++; \ + pDstSeg++; \ + } while (0) + + + /* Shared with the 64-bit variant. */ + #define ADD_SEGMENT_AND_ITS_SECTIONS(a_cBits) \ + do { \ + KBOOL fAddSegOuter = K_FALSE; \ + \ + /* \ + * Check that the segment name is unique. We couldn't do that \ + * in the preparsing stage. \ + */ \ + if (pModMachO->uEffFileType != MH_OBJECT) \ + for (pSegItr = &pModMachO->pMod->aSegments[0]; pSegItr != pDstSeg; pSegItr++) \ + if (!kHlpStrNComp(pSegItr->pchName, pSrcSeg->segname, sizeof(pSrcSeg->segname))) \ + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_DUPLICATE_SEGMENT_NAME); \ + \ + /* \ + * Create a new segment, unless we're supposed to skip this one. \ + */ \ + if ( pModMachO->uEffFileType != MH_OBJECT \ + && (cSectionsLeft == 0 || !(pFirstSect->flags & S_ATTR_DEBUG)) \ + && kHlpStrComp(pSrcSeg->segname, "__DWARF") \ + && kHlpStrComp(pSrcSeg->segname, "__CTF") ) \ + { \ + NEW_SEGMENT(a_cBits, pSrcSeg->segname, K_FALSE /*a_fObjFile*/, 0 /*a_achName2*/, \ + pSrcSeg->vmaddr, pSrcSeg->vmsize, \ + pSrcSeg->filesize != 0, pSrcSeg->fileoff, pSrcSeg->filesize); \ + fAddSegOuter = K_TRUE; \ + } \ + \ + /* \ + * Convert and parse the sections. \ + */ \ + while (cSectionsLeft-- > 0) \ + { \ + /* New segment if object file. */ \ + KBOOL fAddSegInner = K_FALSE; \ + if ( pModMachO->uEffFileType == MH_OBJECT \ + && !(pSect->flags & S_ATTR_DEBUG) \ + && kHlpStrComp(pSrcSeg->segname, "__DWARF") \ + && kHlpStrComp(pSrcSeg->segname, "__CTF") ) \ + { \ + kHlpAssert(!fAddSegOuter); \ + NEW_SEGMENT(a_cBits, pSect->segname, K_TRUE /*a_fObjFile*/, pSect->sectname, \ + pSect->addr, pSect->size, \ + pSect->offset != 0, pSect->offset, pSect->size); \ + fAddSegInner = K_TRUE; \ + } \ + \ + /* Section data extract. */ \ + pSectExtra->cb = pSect->size; \ + pSectExtra->RVA = pSect->addr - pDstSeg->LinkAddress; \ + pSectExtra->LinkAddress = pSect->addr; \ + if (pSect->offset) \ + pSectExtra->offFile = pSect->offset + pModMachO->offImage; \ + else \ + pSectExtra->offFile = -1; \ + pSectExtra->cFixups = pSect->nreloc; \ + pSectExtra->paFixups = NULL; \ + if (pSect->nreloc) \ + pSectExtra->offFixups = pSect->reloff + pModMachO->offImage; \ + else \ + pSectExtra->offFixups = -1; \ + pSectExtra->fFlags = pSect->flags; \ + pSectExtra->iSegment = (KU32)(pSegExtra - &pModMachO->aSegments[0]); \ + pSectExtra->pvMachoSection = pSect; \ + \ + /* Update the segment alignment, if we're not skipping it. */ \ + if ( (fAddSegOuter || fAddSegInner) \ + && pDstSeg->Alignment < ((KLDRADDR)1 << pSect->align)) \ + pDstSeg->Alignment = (KLDRADDR)1 << pSect->align; \ + \ + /* Next section, and if object file next segment. */ \ + pSectExtra++; \ + pSect++; \ + if (fAddSegInner) \ + CLOSE_SEGMENT(); \ + } \ + \ + /* Close the segment and advance. */ \ + if (fAddSegOuter) \ + CLOSE_SEGMENT(); \ + } while (0) /* ADD_SEGMENT_AND_ITS_SECTIONS */ + + ADD_SEGMENT_AND_ITS_SECTIONS(32); + break; + } + + case LC_SEGMENT_64: + { + const segment_command_64_t *pSrcSeg = (const segment_command_64_t *)u.pLoadCmd; + section_64_t *pFirstSect = (section_64_t *)(pSrcSeg + 1); + section_64_t *pSect = pFirstSect; + KU32 cSectionsLeft = pSrcSeg->nsects; + + ADD_SEGMENT_AND_ITS_SECTIONS(64); + break; + } + + case LC_SYMTAB: + switch (pModMachO->uEffFileType) + { + case MH_OBJECT: + case MH_EXECUTE: + case MH_DYLIB: /** @todo ??? */ + case MH_BUNDLE: /** @todo ??? */ + case MH_DSYM: + case MH_KEXT_BUNDLE: + pModMachO->offSymbols = u.pSymTab->symoff + pModMachO->offImage; + pModMachO->cSymbols = u.pSymTab->nsyms; + pModMachO->offStrings = u.pSymTab->stroff + pModMachO->offImage; + pModMachO->cchStrings = u.pSymTab->strsize; + break; + } + break; + + case LC_UUID: + kHlpMemCopy(pModMachO->abImageUuid, u.pUuid->uuid, sizeof(pModMachO->abImageUuid)); + break; + + default: + break; + } /* command switch */ + } /* while more commands */ + + kHlpAssert(pDstSeg == &pModMachO->pMod->aSegments[cSegments - pModMachO->fMakeGot]); + + /* + * Adjust mapping addresses calculating the image size. + */ + { + KBOOL fLoadLinkEdit = K_FALSE; + PKLDRMODMACHOSECT pSectExtraItr; + KLDRADDR uNextRVA = 0; + KLDRADDR cb; + KU32 cSegmentsToAdjust = cSegments - pModMachO->fMakeGot; + KU32 c; + + for (;;) + { + /* Check if there is __DWARF segment at the end and make sure it's left + out of the RVA negotiations and image loading. */ + if ( cSegmentsToAdjust > 0 + && !kHlpStrComp(pModMachO->pMod->aSegments[cSegmentsToAdjust - 1].pchName, "__DWARF")) + { + cSegmentsToAdjust--; + pModMachO->pMod->aSegments[cSegmentsToAdjust].RVA = NIL_KLDRADDR; + pModMachO->pMod->aSegments[cSegmentsToAdjust].cbMapped = 0; + continue; + } + + /* If we're skipping the __LINKEDIT segment, check for it and adjust + the number of segments we'll be messing with here. ASSUMES it's + last (by now anyway). */ + if ( !fLoadLinkEdit + && cSegmentsToAdjust > 0 + && !kHlpStrComp(pModMachO->pMod->aSegments[cSegmentsToAdjust - 1].pchName, "__LINKEDIT")) + { + cSegmentsToAdjust--; + pModMachO->pMod->aSegments[cSegmentsToAdjust].RVA = NIL_KLDRADDR; + pModMachO->pMod->aSegments[cSegmentsToAdjust].cbMapped = 0; + continue; + } + break; + } + + /* Adjust RVAs. */ + c = cSegmentsToAdjust; + for (pDstSeg = &pModMachO->pMod->aSegments[0]; c-- > 0; pDstSeg++) + { + cb = pDstSeg->RVA - uNextRVA; + if (cb >= 0x00100000) /* 1MB */ + { + pDstSeg->RVA = uNextRVA; + pModMachO->pMod->fFlags |= KLDRMOD_FLAGS_NON_CONTIGUOUS_LINK_ADDRS; + } + uNextRVA = pDstSeg->RVA + KLDR_ALIGN_ADDR(pDstSeg->cb, pDstSeg->Alignment); + } + + /* Calculate the cbMapping members. */ + c = cSegmentsToAdjust; + for (pDstSeg = &pModMachO->pMod->aSegments[0]; c-- > 1; pDstSeg++) + { + + cb = pDstSeg[1].RVA - pDstSeg->RVA; + pDstSeg->cbMapped = (KSIZE)cb == cb ? (KSIZE)cb : KSIZE_MAX; + } + + cb = KLDR_ALIGN_ADDR(pDstSeg->cb, pDstSeg->Alignment); + pDstSeg->cbMapped = (KSIZE)cb == cb ? (KSIZE)cb : KSIZE_MAX; + + /* Set the image size. */ + pModMachO->cbImage = pDstSeg->RVA + cb; + + /* Fixup the section RVAs (internal). */ + c = cSegmentsToAdjust; + uNextRVA = pModMachO->cbImage; + pDstSeg = &pModMachO->pMod->aSegments[0]; + for (pSectExtraItr = pModMachO->paSections; pSectExtraItr != pSectExtra; pSectExtraItr++) + { + if (pSectExtraItr->iSegment < c) + pSectExtraItr->RVA += pDstSeg[pSectExtraItr->iSegment].RVA; + else + { + pSectExtraItr->RVA = uNextRVA; + uNextRVA += KLDR_ALIGN_ADDR(pSectExtraItr->cb, 64); + } + } + } + + /* + * Make the GOT segment if necessary. + */ + if (pModMachO->fMakeGot) + { + KU32 cbPtr = ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE + || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE) + ? sizeof(KU32) + : sizeof(KU64); + KU32 cbGot = pModMachO->cSymbols * cbPtr; + KU32 cbJmpStubs; + + pModMachO->GotRVA = pModMachO->cbImage; + + if (pModMachO->cbJmpStub) + { + cbGot = K_ALIGN_Z(cbGot, 64); + pModMachO->JmpStubsRVA = pModMachO->GotRVA + cbGot; + cbJmpStubs = pModMachO->cbJmpStub * pModMachO->cSymbols; + } + else + { + pModMachO->JmpStubsRVA = NIL_KLDRADDR; + cbJmpStubs = 0; + } + + pDstSeg = &pModMachO->pMod->aSegments[cSegments - 1]; + pDstSeg->pvUser = NULL; + pDstSeg->pchName = "GOT"; + pDstSeg->cchName = 3; + pDstSeg->SelFlat = 0; + pDstSeg->Sel16bit = 0; + pDstSeg->fFlags = 0; + pDstSeg->enmProt = KPROT_READONLY; + pDstSeg->cb = cbGot + cbJmpStubs; + pDstSeg->Alignment = 64; + pDstSeg->LinkAddress = pModMachO->LinkAddress + pModMachO->GotRVA; + pDstSeg->offFile = -1; + pDstSeg->cbFile = -1; + pDstSeg->RVA = pModMachO->GotRVA; + pDstSeg->cbMapped = (KSIZE)KLDR_ALIGN_ADDR(cbGot + cbJmpStubs, pDstSeg->Alignment); + pDstSeg->MapAddress = 0; + + pSegExtra->iOrgSegNo = KU32_MAX; + pSegExtra->cSections = 0; + pSegExtra->paSections = NULL; + + pModMachO->cbImage += pDstSeg->cbMapped; + } + + return 0; +} + + +/** @copydoc KLDRMODOPS::pfnDestroy */ +static int kldrModMachODestroy(PKLDRMOD pMod) +{ + PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; + int rc = 0; + KU32 i, j; + KLDRMODMACHO_ASSERT(!pModMachO->pvMapping); + + i = pMod->cSegments; + while (i-- > 0) + { + j = pModMachO->aSegments[i].cSections; + while (j-- > 0) + { + kHlpFree(pModMachO->aSegments[i].paSections[j].paFixups); + pModMachO->aSegments[i].paSections[j].paFixups = NULL; + } + } + + if (pMod->pRdr) + { + rc = kRdrClose(pMod->pRdr); + pMod->pRdr = NULL; + } + pMod->u32Magic = 0; + pMod->pOps = NULL; + kHlpFree(pModMachO->pbLoadCommands); + pModMachO->pbLoadCommands = NULL; + kHlpFree(pModMachO->pchStrings); + pModMachO->pchStrings = NULL; + kHlpFree(pModMachO->pvaSymbols); + pModMachO->pvaSymbols = NULL; + kHlpFree(pModMachO); + return rc; +} + + +/** + * Gets the right base address. + * + * @returns 0 on success. + * @returns A non-zero status code if the BaseAddress isn't right. + * @param pModMachO The interpreter module instance + * @param pBaseAddress The base address, IN & OUT. Optional. + */ +static int kldrModMachOAdjustBaseAddress(PKLDRMODMACHO pModMachO, PKLDRADDR pBaseAddress) +{ + /* + * Adjust the base address. + */ + if (*pBaseAddress == KLDRMOD_BASEADDRESS_MAP) + *pBaseAddress = pModMachO->pMod->aSegments[0].MapAddress; + else if (*pBaseAddress == KLDRMOD_BASEADDRESS_LINK) + *pBaseAddress = pModMachO->LinkAddress; + + return 0; +} + + +/** + * Resolves a linker generated symbol. + * + * The Apple linker generates symbols indicating the start and end of sections + * and segments. This function checks for these and returns the right value. + * + * @returns 0 or KLDR_ERR_SYMBOL_NOT_FOUND. + * @param pModMachO The interpreter module instance. + * @param pMod The generic module instance. + * @param pchSymbol The symbol. + * @param cchSymbol The length of the symbol. + * @param BaseAddress The base address to apply when calculating the + * value. + * @param puValue Where to return the symbol value. + */ +static int kldrModMachOQueryLinkerSymbol(PKLDRMODMACHO pModMachO, PKLDRMOD pMod, const char *pchSymbol, KSIZE cchSymbol, + KLDRADDR BaseAddress, PKLDRADDR puValue) +{ + /* + * Match possible name prefixes. + */ + static const struct + { + const char *pszPrefix; + KU8 cchPrefix; + KBOOL fSection; + KBOOL fStart; + } s_aPrefixes[] = + { + { "section$start$", (KU8)sizeof("section$start$") - 1, K_TRUE, K_TRUE }, + { "section$end$", (KU8)sizeof("section$end$") - 1, K_TRUE, K_FALSE}, + { "segment$start$", (KU8)sizeof("segment$start$") - 1, K_FALSE, K_TRUE }, + { "segment$end$", (KU8)sizeof("segment$end$") - 1, K_FALSE, K_FALSE}, + }; + KSIZE cchSectName = 0; + const char *pchSectName = ""; + KSIZE cchSegName = 0; + const char *pchSegName = NULL; + KU32 iPrefix = K_ELEMENTS(s_aPrefixes) - 1; + KU32 iSeg; + KLDRADDR uValue; + + for (;;) + { + KU8 const cchPrefix = s_aPrefixes[iPrefix].cchPrefix; + if ( cchSymbol > cchPrefix + && kHlpStrNComp(pchSymbol, s_aPrefixes[iPrefix].pszPrefix, cchPrefix) == 0) + { + pchSegName = pchSymbol + cchPrefix; + cchSegName = cchSymbol - cchPrefix; + break; + } + + /* next */ + if (!iPrefix) + return KLDR_ERR_SYMBOL_NOT_FOUND; + iPrefix--; + } + + /* + * Split the remainder into segment and section name, if necessary. + */ + if (s_aPrefixes[iPrefix].fSection) + { + pchSectName = kHlpMemChr(pchSegName, '$', cchSegName); + if (!pchSectName) + return KLDR_ERR_SYMBOL_NOT_FOUND; + cchSegName = pchSectName - pchSegName; + pchSectName++; + cchSectName = cchSymbol - (pchSectName - pchSymbol); + } + + /* + * Locate the segment. + */ + if (!pMod->cSegments) + return KLDR_ERR_SYMBOL_NOT_FOUND; + for (iSeg = 0; iSeg < pMod->cSegments; iSeg++) + { + if ( pMod->aSegments[iSeg].cchName >= cchSegName + && kHlpMemComp(pMod->aSegments[iSeg].pchName, pchSegName, cchSegName) == 0) + { + section_32_t const *pSect; + if ( pMod->aSegments[iSeg].cchName == cchSegName + && pModMachO->Hdr.filetype != MH_OBJECT /* Good enough for __DWARF segs in MH_DHSYM, I hope. */) + break; + + pSect = (section_32_t *)pModMachO->aSegments[iSeg].paSections[0].pvMachoSection; + if ( pModMachO->uEffFileType == MH_OBJECT + && pMod->aSegments[iSeg].cchName > cchSegName + 1 + && pMod->aSegments[iSeg].pchName[cchSegName] == '.' + && kHlpStrNComp(&pMod->aSegments[iSeg].pchName[cchSegName + 1], pSect->sectname, sizeof(pSect->sectname)) == 0 + && pMod->aSegments[iSeg].cchName - cchSegName - 1 <= sizeof(pSect->sectname) ) + break; + } + } + if (iSeg >= pMod->cSegments) + return KLDR_ERR_SYMBOL_NOT_FOUND; + + if (!s_aPrefixes[iPrefix].fSection) + { + /* + * Calculate the segment start/end address. + */ + uValue = pMod->aSegments[iSeg].RVA; + if (!s_aPrefixes[iPrefix].fStart) + uValue += pMod->aSegments[iSeg].cb; + } + else + { + /* + * Locate the section. + */ + KU32 iSect = pModMachO->aSegments[iSeg].cSections; + if (!iSect) + return KLDR_ERR_SYMBOL_NOT_FOUND; + for (;;) + { + section_32_t *pSect = (section_32_t *)pModMachO->aSegments[iSeg].paSections[iSect].pvMachoSection; + if ( cchSectName <= sizeof(pSect->sectname) + && kHlpMemComp(pSect->sectname, pchSectName, cchSectName) == 0 + && ( cchSectName == sizeof(pSect->sectname) + || pSect->sectname[cchSectName] == '\0') ) + break; + /* next */ + if (!iSect) + return KLDR_ERR_SYMBOL_NOT_FOUND; + iSect--; + } + + uValue = pModMachO->aSegments[iSeg].paSections[iSect].RVA; + if (!s_aPrefixes[iPrefix].fStart) + uValue += pModMachO->aSegments[iSeg].paSections[iSect].cb; + } + + /* + * Convert from RVA to load address. + */ + uValue += BaseAddress; + if (puValue) + *puValue = uValue; + + return 0; +} + + +/** @copydoc kLdrModQuerySymbol */ +static int kldrModMachOQuerySymbol(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) +{ + PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; + int rc; + K_NOREF(pvBits); + K_NOREF(pszVersion); + K_NOREF(pfnGetForwarder); + K_NOREF(pvUser); + + /* + * Resolve defaults. + */ + rc = kldrModMachOAdjustBaseAddress(pModMachO, &BaseAddress); + if (rc) + return rc; + + /* + * Refuse segmented requests for now. + */ + KLDRMODMACHO_CHECK_RETURN( !pfKind + || (*pfKind & KLDRSYMKIND_REQ_TYPE_MASK) == KLDRSYMKIND_REQ_FLAT, + KLDR_ERR_TODO); + + /* + * Take action according to file type. + */ + if ( pModMachO->Hdr.filetype == MH_OBJECT + || pModMachO->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */ + || pModMachO->Hdr.filetype == MH_DYLIB + || pModMachO->Hdr.filetype == MH_BUNDLE + || pModMachO->Hdr.filetype == MH_DSYM + || pModMachO->Hdr.filetype == MH_KEXT_BUNDLE) + { + rc = kldrModMachOLoadObjSymTab(pModMachO); + if (!rc) + { + if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE + || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE) + rc = kldrModMachODoQuerySymbol32Bit(pModMachO, (macho_nlist_32_t *)pModMachO->pvaSymbols, pModMachO->cSymbols, + pModMachO->pchStrings, pModMachO->cchStrings, BaseAddress, iSymbol, pchSymbol, + (KU32)cchSymbol, puValue, pfKind); + else + rc = kldrModMachODoQuerySymbol64Bit(pModMachO, (macho_nlist_64_t *)pModMachO->pvaSymbols, pModMachO->cSymbols, + pModMachO->pchStrings, pModMachO->cchStrings, BaseAddress, iSymbol, pchSymbol, + (KU32)cchSymbol, puValue, pfKind); + } + + /* + * Check for link-editor generated symbols and supply what we can. + * + * As small service to clients that insists on adding a '_' prefix + * before querying symbols, we will ignore the prefix. + */ + if ( rc == KLDR_ERR_SYMBOL_NOT_FOUND + && cchSymbol > sizeof("section$end$") - 1 + && ( pchSymbol[0] == 's' + || (pchSymbol[1] == 's' && pchSymbol[0] == '_') ) + && kHlpMemChr(pchSymbol, '$', cchSymbol) ) + { + if (pchSymbol[0] == '_') + rc = kldrModMachOQueryLinkerSymbol(pModMachO, pMod, pchSymbol + 1, cchSymbol - 1, BaseAddress, puValue); + else + rc = kldrModMachOQueryLinkerSymbol(pModMachO, pMod, pchSymbol, cchSymbol, BaseAddress, puValue); + } + } + else + rc = KLDR_ERR_TODO; + + return rc; +} + + +/** + * Lookup a symbol in a 32-bit symbol table. + * + * @returns See kLdrModQuerySymbol. + * @param pModMachO + * @param paSyms Pointer to the symbol table. + * @param cSyms Number of symbols in the table. + * @param pchStrings Pointer to the string table. + * @param cchStrings Size of the string table. + * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol. + * @param iSymbol See kLdrModQuerySymbol. + * @param pchSymbol See kLdrModQuerySymbol. + * @param cchSymbol See kLdrModQuerySymbol. + * @param puValue See kLdrModQuerySymbol. + * @param pfKind See kLdrModQuerySymbol. + */ +static int kldrModMachODoQuerySymbol32Bit(PKLDRMODMACHO pModMachO, const macho_nlist_32_t *paSyms, KU32 cSyms, + const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress, KU32 iSymbol, + const char *pchSymbol, KU32 cchSymbol, PKLDRADDR puValue, KU32 *pfKind) +{ + /* + * Find a valid symbol matching the search criteria. + */ + if (iSymbol == NIL_KLDRMOD_SYM_ORDINAL) + { + /* simplify validation. */ + if (cchStrings <= cchSymbol) + return KLDR_ERR_SYMBOL_NOT_FOUND; + cchStrings -= cchSymbol; + + /* external symbols are usually at the end, so search the other way. */ + for (iSymbol = cSyms - 1; iSymbol != KU32_MAX; iSymbol--) + { + const char *psz; + + /* Skip irrellevant and non-public symbols. */ + if (paSyms[iSymbol].n_type & MACHO_N_STAB) + continue; + if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF) + continue; + if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/ + continue; + if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/ + continue; + + /* get name */ + if (!paSyms[iSymbol].n_un.n_strx) + continue; + if ((KU32)paSyms[iSymbol].n_un.n_strx >= cchStrings) + continue; + psz = &pchStrings[paSyms[iSymbol].n_un.n_strx]; + if (psz[cchSymbol]) + continue; + if (kHlpMemComp(psz, pchSymbol, cchSymbol)) + continue; + + /* match! */ + break; + } + if (iSymbol == KU32_MAX) + return KLDR_ERR_SYMBOL_NOT_FOUND; + } + else + { + if (iSymbol >= cSyms) + return KLDR_ERR_SYMBOL_NOT_FOUND; + if (paSyms[iSymbol].n_type & MACHO_N_STAB) + return KLDR_ERR_SYMBOL_NOT_FOUND; + if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF) + return KLDR_ERR_SYMBOL_NOT_FOUND; + } + + /* + * Calc the return values. + */ + if (pfKind) + { + if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE + || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE) + *pfKind = KLDRSYMKIND_32BIT | KLDRSYMKIND_NO_TYPE; + else + *pfKind = KLDRSYMKIND_64BIT | KLDRSYMKIND_NO_TYPE; + if (paSyms[iSymbol].n_desc & N_WEAK_DEF) + *pfKind |= KLDRSYMKIND_WEAK; + } + + switch (paSyms[iSymbol].n_type & MACHO_N_TYPE) + { + case MACHO_N_SECT: + { + PKLDRMODMACHOSECT pSect; + KLDRADDR offSect; + KLDRMODMACHO_CHECK_RETURN((KU32)(paSyms[iSymbol].n_sect - 1) < pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL); + pSect = &pModMachO->paSections[paSyms[iSymbol].n_sect - 1]; + + offSect = paSyms[iSymbol].n_value - pSect->LinkAddress; + KLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb + || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */ + && offSect == 0U - pSect->RVA + && pModMachO->uEffFileType != MH_OBJECT), + KLDR_ERR_MACHO_BAD_SYMBOL); + if (puValue) + *puValue = BaseAddress + pSect->RVA + offSect; + + if ( pfKind + && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))) + *pfKind = (*pfKind & ~KLDRSYMKIND_TYPE_MASK) | KLDRSYMKIND_CODE; + break; + } + + case MACHO_N_ABS: + if (puValue) + *puValue = paSyms[iSymbol].n_value; + /*if (pfKind) + pfKind |= KLDRSYMKIND_ABS;*/ + break; + + case MACHO_N_PBUD: + case MACHO_N_INDR: + /** @todo implement indirect and prebound symbols. */ + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO); + } + + return 0; +} + + +/** + * Lookup a symbol in a 64-bit symbol table. + * + * @returns See kLdrModQuerySymbol. + * @param pModMachO + * @param paSyms Pointer to the symbol table. + * @param cSyms Number of symbols in the table. + * @param pchStrings Pointer to the string table. + * @param cchStrings Size of the string table. + * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol. + * @param iSymbol See kLdrModQuerySymbol. + * @param pchSymbol See kLdrModQuerySymbol. + * @param cchSymbol See kLdrModQuerySymbol. + * @param puValue See kLdrModQuerySymbol. + * @param pfKind See kLdrModQuerySymbol. + */ +static int kldrModMachODoQuerySymbol64Bit(PKLDRMODMACHO pModMachO, const macho_nlist_64_t *paSyms, KU32 cSyms, + const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress, KU32 iSymbol, + const char *pchSymbol, KU32 cchSymbol, PKLDRADDR puValue, KU32 *pfKind) +{ + /* + * Find a valid symbol matching the search criteria. + */ + if (iSymbol == NIL_KLDRMOD_SYM_ORDINAL) + { + /* simplify validation. */ + if (cchStrings <= cchSymbol) + return KLDR_ERR_SYMBOL_NOT_FOUND; + cchStrings -= cchSymbol; + + /* external symbols are usually at the end, so search the other way. */ + for (iSymbol = cSyms - 1; iSymbol != KU32_MAX; iSymbol--) + { + const char *psz; + + /* Skip irrellevant and non-public symbols. */ + if (paSyms[iSymbol].n_type & MACHO_N_STAB) + continue; + if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF) + continue; + if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/ + continue; + if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/ + continue; + + /* get name */ + if (!paSyms[iSymbol].n_un.n_strx) + continue; + if ((KU32)paSyms[iSymbol].n_un.n_strx >= cchStrings) + continue; + psz = &pchStrings[paSyms[iSymbol].n_un.n_strx]; + if (psz[cchSymbol]) + continue; + if (kHlpMemComp(psz, pchSymbol, cchSymbol)) + continue; + + /* match! */ + break; + } + if (iSymbol == KU32_MAX) + return KLDR_ERR_SYMBOL_NOT_FOUND; + } + else + { + if (iSymbol >= cSyms) + return KLDR_ERR_SYMBOL_NOT_FOUND; + if (paSyms[iSymbol].n_type & MACHO_N_STAB) + return KLDR_ERR_SYMBOL_NOT_FOUND; + if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF) + return KLDR_ERR_SYMBOL_NOT_FOUND; + } + + /* + * Calc the return values. + */ + if (pfKind) + { + if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE + || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE) + *pfKind = KLDRSYMKIND_32BIT | KLDRSYMKIND_NO_TYPE; + else + *pfKind = KLDRSYMKIND_64BIT | KLDRSYMKIND_NO_TYPE; + if (paSyms[iSymbol].n_desc & N_WEAK_DEF) + *pfKind |= KLDRSYMKIND_WEAK; + } + + switch (paSyms[iSymbol].n_type & MACHO_N_TYPE) + { + case MACHO_N_SECT: + { + PKLDRMODMACHOSECT pSect; + KLDRADDR offSect; + KLDRMODMACHO_CHECK_RETURN((KU32)(paSyms[iSymbol].n_sect - 1) < pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL); + pSect = &pModMachO->paSections[paSyms[iSymbol].n_sect - 1]; + + offSect = paSyms[iSymbol].n_value - pSect->LinkAddress; + KLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb + || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */ + && offSect == 0U - pSect->RVA + && pModMachO->uEffFileType != MH_OBJECT), + KLDR_ERR_MACHO_BAD_SYMBOL); + if (puValue) + *puValue = BaseAddress + pSect->RVA + offSect; + + if ( pfKind + && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))) + *pfKind = (*pfKind & ~KLDRSYMKIND_TYPE_MASK) | KLDRSYMKIND_CODE; + break; + } + + case MACHO_N_ABS: + if (puValue) + *puValue = paSyms[iSymbol].n_value; + /*if (pfKind) + pfKind |= KLDRSYMKIND_ABS;*/ + break; + + case MACHO_N_PBUD: + case MACHO_N_INDR: + /** @todo implement indirect and prebound symbols. */ + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO); + } + + return 0; +} + + +/** @copydoc kLdrModEnumSymbols */ +static int kldrModMachOEnumSymbols(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, + KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser) +{ + PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; + int rc; + K_NOREF(pvBits); + + /* + * Resolve defaults. + */ + rc = kldrModMachOAdjustBaseAddress(pModMachO, &BaseAddress); + if (rc) + return rc; + + /* + * Take action according to file type. + */ + if ( pModMachO->Hdr.filetype == MH_OBJECT + || pModMachO->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */ + || pModMachO->Hdr.filetype == MH_DYLIB + || pModMachO->Hdr.filetype == MH_BUNDLE + || pModMachO->Hdr.filetype == MH_DSYM + || pModMachO->Hdr.filetype == MH_KEXT_BUNDLE) + { + rc = kldrModMachOLoadObjSymTab(pModMachO); + if (!rc) + { + if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE + || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE) + rc = kldrModMachODoEnumSymbols32Bit(pModMachO, (macho_nlist_32_t *)pModMachO->pvaSymbols, pModMachO->cSymbols, + pModMachO->pchStrings, pModMachO->cchStrings, BaseAddress, + fFlags, pfnCallback, pvUser); + else + rc = kldrModMachODoEnumSymbols64Bit(pModMachO, (macho_nlist_64_t *)pModMachO->pvaSymbols, pModMachO->cSymbols, + pModMachO->pchStrings, pModMachO->cchStrings, BaseAddress, + fFlags, pfnCallback, pvUser); + } + } + else + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO); + + return rc; +} + + +/** + * Enum a 32-bit symbol table. + * + * @returns See kLdrModQuerySymbol. + * @param pModMachO + * @param paSyms Pointer to the symbol table. + * @param cSyms Number of symbols in the table. + * @param pchStrings Pointer to the string table. + * @param cchStrings Size of the string table. + * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols. + * @param fFlags See kLdrModEnumSymbols. + * @param pfnCallback See kLdrModEnumSymbols. + * @param pvUser See kLdrModEnumSymbols. + */ +static int kldrModMachODoEnumSymbols32Bit(PKLDRMODMACHO pModMachO, const macho_nlist_32_t *paSyms, KU32 cSyms, + const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress, + KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser) +{ + const KU32 fKindBase = pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE + || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE + ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT; + KU32 iSym; + int rc; + + /* + * Iterate the symbol table. + */ + for (iSym = 0; iSym < cSyms; iSym++) + { + KU32 fKind; + KLDRADDR uValue; + const char *psz; + KSIZE cch; + + /* Skip debug symbols and undefined symbols. */ + if (paSyms[iSym].n_type & MACHO_N_STAB) + continue; + if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF) + continue; + + /* Skip non-public symbols unless they are requested explicitly. */ + if (!(fFlags & KLDRMOD_ENUM_SYMS_FLAGS_ALL)) + { + if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/ + continue; + if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/ + continue; + if (!paSyms[iSym].n_un.n_strx) + continue; + } + + /* + * Gather symbol info + */ + + /* name */ + KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_un.n_strx < cchStrings, KLDR_ERR_MACHO_BAD_SYMBOL); + psz = &pchStrings[paSyms[iSym].n_un.n_strx]; + cch = kHlpStrLen(psz); + if (!cch) + psz = NULL; + + /* kind & value */ + fKind = fKindBase; + if (paSyms[iSym].n_desc & N_WEAK_DEF) + fKind |= KLDRSYMKIND_WEAK; + switch (paSyms[iSym].n_type & MACHO_N_TYPE) + { + case MACHO_N_SECT: + { + PKLDRMODMACHOSECT pSect; + KLDRMODMACHO_CHECK_RETURN((KU32)(paSyms[iSym].n_sect - 1) < pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL); + pSect = &pModMachO->paSections[paSyms[iSym].n_sect - 1]; + + uValue = paSyms[iSym].n_value - pSect->LinkAddress; + KLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb + || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */ + && uValue == 0U - pSect->RVA + && pModMachO->uEffFileType != MH_OBJECT), + KLDR_ERR_MACHO_BAD_SYMBOL); + uValue += BaseAddress + pSect->RVA; + + if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)) + fKind |= KLDRSYMKIND_CODE; + else + fKind |= KLDRSYMKIND_NO_TYPE; + break; + } + + case MACHO_N_ABS: + uValue = paSyms[iSym].n_value; + fKind |= KLDRSYMKIND_NO_TYPE /*KLDRSYMKIND_ABS*/; + break; + + case MACHO_N_PBUD: + case MACHO_N_INDR: + /** @todo implement indirect and prebound symbols. */ + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO); + } + + /* + * Do callback. + */ + rc = pfnCallback(pModMachO->pMod, iSym, psz, cch, NULL, uValue, fKind, pvUser); + if (rc) + return rc; + } + return 0; +} + + +/** + * Enum a 64-bit symbol table. + * + * @returns See kLdrModQuerySymbol. + * @param pModMachO + * @param paSyms Pointer to the symbol table. + * @param cSyms Number of symbols in the table. + * @param pchStrings Pointer to the string table. + * @param cchStrings Size of the string table. + * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols. + * @param fFlags See kLdrModEnumSymbols. + * @param pfnCallback See kLdrModEnumSymbols. + * @param pvUser See kLdrModEnumSymbols. + */ +static int kldrModMachODoEnumSymbols64Bit(PKLDRMODMACHO pModMachO, const macho_nlist_64_t *paSyms, KU32 cSyms, + const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress, + KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser) +{ + const KU32 fKindBase = pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE + || pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE + ? KLDRSYMKIND_64BIT : KLDRSYMKIND_32BIT; + KU32 iSym; + int rc; + + /* + * Iterate the symbol table. + */ + for (iSym = 0; iSym < cSyms; iSym++) + { + KU32 fKind; + KLDRADDR uValue; + const char *psz; + KSIZE cch; + + /* Skip debug symbols and undefined symbols. */ + if (paSyms[iSym].n_type & MACHO_N_STAB) + continue; + if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF) + continue; + + /* Skip non-public symbols unless they are requested explicitly. */ + if (!(fFlags & KLDRMOD_ENUM_SYMS_FLAGS_ALL)) + { + if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/ + continue; + if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/ + continue; + if (!paSyms[iSym].n_un.n_strx) + continue; + } + + /* + * Gather symbol info + */ + + /* name */ + KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_un.n_strx < cchStrings, KLDR_ERR_MACHO_BAD_SYMBOL); + psz = &pchStrings[paSyms[iSym].n_un.n_strx]; + cch = kHlpStrLen(psz); + if (!cch) + psz = NULL; + + /* kind & value */ + fKind = fKindBase; + if (paSyms[iSym].n_desc & N_WEAK_DEF) + fKind |= KLDRSYMKIND_WEAK; + switch (paSyms[iSym].n_type & MACHO_N_TYPE) + { + case MACHO_N_SECT: + { + PKLDRMODMACHOSECT pSect; + KLDRMODMACHO_CHECK_RETURN((KU32)(paSyms[iSym].n_sect - 1) < pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL); + pSect = &pModMachO->paSections[paSyms[iSym].n_sect - 1]; + + uValue = paSyms[iSym].n_value - pSect->LinkAddress; + KLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb + || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */ + && uValue == 0U - pSect->RVA + && pModMachO->uEffFileType != MH_OBJECT), + KLDR_ERR_MACHO_BAD_SYMBOL); + uValue += BaseAddress + pSect->RVA; + + if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)) + fKind |= KLDRSYMKIND_CODE; + else + fKind |= KLDRSYMKIND_NO_TYPE; + break; + } + + case MACHO_N_ABS: + uValue = paSyms[iSym].n_value; + fKind |= KLDRSYMKIND_NO_TYPE /*KLDRSYMKIND_ABS*/; + break; + + case MACHO_N_PBUD: + case MACHO_N_INDR: + /** @todo implement indirect and prebound symbols. */ + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO); + } + + /* + * Do callback. + */ + rc = pfnCallback(pModMachO->pMod, iSym, psz, cch, NULL, uValue, fKind, pvUser); + if (rc) + return rc; + } + return 0; +} + + +/** @copydoc kLdrModGetImport */ +static int kldrModMachOGetImport(PKLDRMOD pMod, const void *pvBits, KU32 iImport, char *pszName, KSIZE cchName) +{ + PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; + K_NOREF(pvBits); + K_NOREF(iImport); + K_NOREF(pszName); + K_NOREF(cchName); + + if (pModMachO->Hdr.filetype == MH_OBJECT) + return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS; + + /* later */ + return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS; +} + + +/** @copydoc kLdrModNumberOfImports */ +static KI32 kldrModMachONumberOfImports(PKLDRMOD pMod, const void *pvBits) +{ + PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; + K_NOREF(pvBits); + + if (pModMachO->Hdr.filetype == MH_OBJECT) + return 0; + + /* later */ + return 0; +} + + +/** @copydoc kLdrModGetStackInfo */ +static int kldrModMachOGetStackInfo(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo) +{ + /*PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;*/ + K_NOREF(pMod); + K_NOREF(pvBits); + K_NOREF(BaseAddress); + + pStackInfo->Address = NIL_KLDRADDR; + pStackInfo->LinkAddress = NIL_KLDRADDR; + pStackInfo->cbStack = pStackInfo->cbStackThread = 0; + /* later */ + + 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; + K_NOREF(pvBits); + K_NOREF(BaseAddress); + K_NOREF(pMod); +#endif + return 0; +} + + +/** @copydoc kLdrModQueryImageUuid */ +static int kldrModMachOQueryImageUuid(PKLDRMOD pMod, const void *pvBits, void *pvUuid, KSIZE cbUuid) +{ + PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; + K_NOREF(pvBits); + + kHlpMemSet(pvUuid, 0, cbUuid); + if (kHlpMemComp(pvUuid, pModMachO->abImageUuid, sizeof(pModMachO->abImageUuid)) == 0) + return KLDR_ERR_NO_IMAGE_UUID; + + kHlpMemCopy(pvUuid, pModMachO->abImageUuid, sizeof(pModMachO->abImageUuid)); + return 0; +} + + +/** @copydoc kLdrModEnumDbgInfo */ +static int kldrModMachOEnumDbgInfo(PKLDRMOD pMod, const void *pvBits, PFNKLDRENUMDBG pfnCallback, void *pvUser) +{ + PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; + int rc = 0; + KU32 iSect; + K_NOREF(pvBits); + + for (iSect = 0; iSect < pModMachO->cSections; iSect++) + { + section_32_t *pMachOSect = pModMachO->paSections[iSect].pvMachoSection; /* (32-bit & 64-bit starts the same way) */ + char szTmp[sizeof(pMachOSect->sectname) + 1]; + + if (kHlpStrComp(pMachOSect->segname, "__DWARF")) + continue; + + kHlpMemCopy(szTmp, pMachOSect->sectname, sizeof(pMachOSect->sectname)); + szTmp[sizeof(pMachOSect->sectname)] = '\0'; + + rc = pfnCallback(pMod, iSect, KLDRDBGINFOTYPE_DWARF, 0, 0, szTmp, + pModMachO->paSections[iSect].offFile, + pModMachO->paSections[iSect].LinkAddress, + pModMachO->paSections[iSect].cb, + NULL, pvUser); + if (rc != 0) + break; + } + + return rc; +} + + +/** @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 + K_NOREF(pMod); + K_NOREF(pvBits); + return KLDR_ERR_NO_DEBUG_INFO; +#endif +} + + +/** @copydoc kLdrModMap */ +static int kldrModMachOMap(PKLDRMOD pMod) +{ + PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; + unsigned fFixed; + KU32 i; + void *pvBase; + int rc; + + if (!pModMachO->fCanLoad) + return KLDR_ERR_TODO; + + /* + * Already mapped? + */ + if (pModMachO->pvMapping) + return KLDR_ERR_ALREADY_MAPPED; + + /* + * Map it. + */ + /* fixed image? */ + fFixed = pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED + || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED; + if (!fFixed) + pvBase = NULL; + else + { + pvBase = (void *)(KUPTR)pMod->aSegments[0].LinkAddress; + if ((KUPTR)pvBase != pMod->aSegments[0].LinkAddress) + return KLDR_ERR_ADDRESS_OVERFLOW; + } + + /* try do the prepare */ + rc = kRdrMap(pMod->pRdr, &pvBase, pMod->cSegments, pMod->aSegments, fFixed); + if (rc) + return rc; + + /* + * Update the segments with their map addresses. + */ + for (i = 0; i < pMod->cSegments; i++) + { + if (pMod->aSegments[i].RVA != NIL_KLDRADDR) + pMod->aSegments[i].MapAddress = (KUPTR)pvBase + (KUPTR)pMod->aSegments[i].RVA; + } + pModMachO->pvMapping = pvBase; + + return 0; +} + + +/** @copydoc kLdrModUnmap */ +static int kldrModMachOUnmap(PKLDRMOD pMod) +{ + PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; + KU32 i; + int rc; + + /* + * Mapped? + */ + if (!pModMachO->pvMapping) + return KLDR_ERR_NOT_MAPPED; + + /* + * Try unmap the image. + */ + rc = kRdrUnmap(pMod->pRdr, pModMachO->pvMapping, pMod->cSegments, pMod->aSegments); + if (rc) + return rc; + + /* + * Update the segments to reflect that they aren't mapped any longer. + */ + pModMachO->pvMapping = NULL; + for (i = 0; i < pMod->cSegments; i++) + pMod->aSegments[i].MapAddress = 0; + + return 0; +} + + +/** @copydoc kLdrModAllocTLS */ +static int kldrModMachOAllocTLS(PKLDRMOD pMod, void *pvMapping) +{ + PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; + + /* + * Mapped? + */ + if ( pvMapping == KLDRMOD_INT_MAP + && !pModMachO->pvMapping ) + return KLDR_ERR_NOT_MAPPED; + return 0; +} + + +/** @copydoc kLdrModFreeTLS */ +static void kldrModMachOFreeTLS(PKLDRMOD pMod, void *pvMapping) +{ + K_NOREF(pMod); + K_NOREF(pvMapping); +} + + +/** @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 kRdrRefresh(pMod->pRdr, 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 = kRdrProtect(pMod->pRdr, pModMachO->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */); + if (rc) + return rc; + + /* + * Resolve imports and apply base relocations. + */ + rc = kldrModMachORelocateBits(pMod, pModMachO->pvMapping, (KUPTR)pModMachO->pvMapping, pModMachO->LinkAddress, + pfnGetImport, pvUser); + + /* + * Restore protection. + */ + rc2 = kRdrProtect(pMod->pRdr, pModMachO->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */); + if (!rc && rc2) + rc = rc2; + return rc; +} + + +/** + * MH_OBJECT: Resolves undefined symbols (imports). + * + * @returns 0 on success, non-zero kLdr status code on failure. + * @param pModMachO The Mach-O module interpreter instance. + * @param pfnGetImport The callback for resolving an imported symbol. + * @param pvUser User argument to the callback. + */ +static int kldrModMachOObjDoImports(PKLDRMODMACHO pModMachO, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) +{ + const KU32 cSyms = pModMachO->cSymbols; + KU32 iSym; + int rc; + + /* + * Ensure that we've got the symbol table and section fixups handy. + */ + rc = kldrModMachOLoadObjSymTab(pModMachO); + if (rc) + return rc; + + /* + * Iterate the symbol table and resolve undefined symbols. + * We currently ignore REFERENCE_TYPE. + */ + if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE + || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE) + { + macho_nlist_32_t *paSyms = (macho_nlist_32_t *)pModMachO->pvaSymbols; + for (iSym = 0; iSym < cSyms; iSym++) + { + /* skip stabs */ + if (paSyms[iSym].n_type & MACHO_N_STAB) + continue; + + if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF) + { + const char *pszSymbol; + KSIZE cchSymbol; + KU32 fKind = KLDRSYMKIND_REQ_FLAT; + KLDRADDR Value = NIL_KLDRADDR; + + /** @todo Implement N_REF_TO_WEAK. */ + KLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), KLDR_ERR_TODO); + + /* Get the symbol name. */ + KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_un.n_strx < pModMachO->cchStrings, KLDR_ERR_MACHO_BAD_SYMBOL); + pszSymbol = &pModMachO->pchStrings[paSyms[iSym].n_un.n_strx]; + cchSymbol = kHlpStrLen(pszSymbol); + + /* Check for linker defined symbols relating to sections and segments. */ + if ( cchSymbol > sizeof("section$end$") - 1 + && *pszSymbol == 's' + && kHlpMemChr(pszSymbol, '$', cchSymbol)) + rc = kldrModMachOQueryLinkerSymbol(pModMachO, pModMachO->pMod, pszSymbol, cchSymbol, BaseAddress, &Value); + else + rc = KLDR_ERR_SYMBOL_NOT_FOUND; + + /* Ask the user for an address to the symbol. */ + if (rc) + rc = pfnGetImport(pModMachO->pMod, NIL_KLDRMOD_IMPORT, iSym, pszSymbol, cchSymbol, NULL, + &Value, &fKind, pvUser); + if (rc) + { + /* weak reference? */ + if (!(paSyms[iSym].n_desc & N_WEAK_REF)) + break; + Value = 0; + } + + /* Update the symbol. */ + paSyms[iSym].n_value = (KU32)Value; + if (paSyms[iSym].n_value != Value) + { + rc = KLDR_ERR_ADDRESS_OVERFLOW; + break; + } + } + else if (paSyms[iSym].n_desc & N_WEAK_DEF) + { + /** @todo implement weak symbols. */ + /*return KLDR_ERR_TODO; - ignored for now. */ + } + } + } + else + { + /* (Identical to the 32-bit code, just different paSym type. (and n_strx is unsigned)) */ + macho_nlist_64_t *paSyms = (macho_nlist_64_t *)pModMachO->pvaSymbols; + for (iSym = 0; iSym < cSyms; iSym++) + { + /* skip stabs */ + if (paSyms[iSym].n_type & MACHO_N_STAB) + continue; + + if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF) + { + const char *pszSymbol; + KSIZE cchSymbol; + KU32 fKind = KLDRSYMKIND_REQ_FLAT; + KLDRADDR Value = NIL_KLDRADDR; + + /** @todo Implement N_REF_TO_WEAK. */ + KLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), KLDR_ERR_TODO); + + /* Get the symbol name. */ + KLDRMODMACHO_CHECK_RETURN(paSyms[iSym].n_un.n_strx < pModMachO->cchStrings, KLDR_ERR_MACHO_BAD_SYMBOL); + pszSymbol = &pModMachO->pchStrings[paSyms[iSym].n_un.n_strx]; + cchSymbol = kHlpStrLen(pszSymbol); + + /* Check for linker defined symbols relating to sections and segments. */ + if ( cchSymbol > sizeof("section$end$") - 1 + && *pszSymbol == 's' + && kHlpMemChr(pszSymbol, '$', cchSymbol)) + rc = kldrModMachOQueryLinkerSymbol(pModMachO, pModMachO->pMod, pszSymbol, cchSymbol, BaseAddress, &Value); + else + rc = KLDR_ERR_SYMBOL_NOT_FOUND; + + /* Ask the user for an address to the symbol. */ + if (rc) + rc = pfnGetImport(pModMachO->pMod, NIL_KLDRMOD_IMPORT, iSym, pszSymbol, cchSymbol, NULL, + &Value, &fKind, pvUser); + if (rc) + { + /* weak reference? */ + if (!(paSyms[iSym].n_desc & N_WEAK_REF)) + break; + Value = 0; + } + + /* Update the symbol. */ + paSyms[iSym].n_value = Value; + if (paSyms[iSym].n_value != Value) + { + rc = KLDR_ERR_ADDRESS_OVERFLOW; + break; + } + } + else if (paSyms[iSym].n_desc & N_WEAK_DEF) + { + /** @todo implement weak symbols. */ + /*return KLDR_ERR_TODO; - ignored for now. */ + } + } + } + + return rc; +} + + +/** + * MH_OBJECT: Applies base relocations to a (unprotected) image mapping. + * + * @returns 0 on success, non-zero kLdr status code on failure. + * @param pModMachO The Mach-O 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 kldrModMachOObjDoFixups(PKLDRMODMACHO pModMachO, void *pvMapping, KLDRADDR NewBaseAddress) +{ + KU32 iSeg; + int rc; + + + /* + * Ensure that we've got the symbol table and section fixups handy. + */ + rc = kldrModMachOLoadObjSymTab(pModMachO); + if (rc) + return rc; + + /* + * Iterate over the segments and their sections and apply fixups. + */ + for (iSeg = rc = 0; !rc && iSeg < pModMachO->pMod->cSegments; iSeg++) + { + PKLDRMODMACHOSEG pSeg = &pModMachO->aSegments[iSeg]; + KU32 iSect; + + for (iSect = 0; iSect < pSeg->cSections; iSect++) + { + PKLDRMODMACHOSECT pSect = &pSeg->paSections[iSect]; + KU8 *pbSectBits; + + /* skip sections without fixups. */ + if (!pSect->cFixups) + continue; + + /* lazy load (and endian convert) the fixups. */ + if (!pSect->paFixups) + { + rc = kldrModMachOLoadFixups(pModMachO, pSect->offFixups, pSect->cFixups, &pSect->paFixups); + if (rc) + break; + } + + /* + * Apply the fixups. + */ + pbSectBits = (KU8 *)pvMapping + (KUPTR)pSect->RVA; + if (pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE) /** @todo this aint right. */ + rc = kldrModMachOFixupSectionGeneric32Bit(pModMachO, pbSectBits, pSect, + (macho_nlist_32_t *)pModMachO->pvaSymbols, + pModMachO->cSymbols, NewBaseAddress); + else if ( pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE + && pModMachO->Hdr.cputype == CPU_TYPE_X86_64) + rc = kldrModMachOFixupSectionAMD64(pModMachO, pbSectBits, pSect, + (macho_nlist_64_t *)pModMachO->pvaSymbols, + pModMachO->cSymbols, NewBaseAddress); + else + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO); + if (rc) + break; + } + } + + return rc; +} + + +/** + * Applies generic fixups to a section in an image of the same endian-ness + * as the host CPU. + * + * @returns 0 on success, non-zero kLdr status code on failure. + * @param pModMachO The Mach-O module interpreter instance. + * @param pbSectBits Pointer to the section bits. + * @param pFixupSect The section being fixed up. + * @param NewBaseAddress The new base image address. + */ +static int kldrModMachOFixupSectionGeneric32Bit(PKLDRMODMACHO pModMachO, KU8 *pbSectBits, PKLDRMODMACHOSECT pFixupSect, + macho_nlist_32_t *paSyms, KU32 cSyms, KLDRADDR NewBaseAddress) +{ + const macho_relocation_info_t *paFixups = pFixupSect->paFixups; + const KU32 cFixups = pFixupSect->cFixups; + KSIZE cbSectBits = (KSIZE)pFixupSect->cb; + const KU8 *pbSectVirginBits; + KU32 iFixup; + KLDRPU uFixVirgin; + KLDRPU uFix; + KLDRADDR SymAddr = ~(KLDRADDR)0; + int rc; + + /* + * Find the virgin bits. + */ + if (pFixupSect->offFile != -1) + { + rc = kldrModMachOMapVirginBits(pModMachO); + if (rc) + return rc; + pbSectVirginBits = (const KU8 *)pModMachO->pvBits + pFixupSect->offFile; + } + else + pbSectVirginBits = NULL; + + /* + * Iterate the fixups and apply them. + */ + for (iFixup = 0; iFixup < cFixups; iFixup++) + { + union + { + macho_relocation_info_t r; + scattered_relocation_info_t s; + } Fixup; + Fixup.r = paFixups[iFixup]; + + if (!(Fixup.r.r_address & R_SCATTERED)) + { + /* sanity */ + if ((KU32)Fixup.r.r_address >= cbSectBits) + return KLDR_ERR_BAD_FIXUP; + + /* calc fixup addresses. */ + uFix.pv = pbSectBits + Fixup.r.r_address; + uFixVirgin.pv = pbSectVirginBits ? (KU8 *)pbSectVirginBits + Fixup.r.r_address : 0; + + /* + * Calc the symbol value. + */ + /* Calc the linked symbol address / addend. */ + switch (Fixup.r.r_length) + { + /** @todo Deal with unaligned accesses on non x86 platforms. */ + case 0: SymAddr = *uFixVirgin.pi8; break; + case 1: SymAddr = *uFixVirgin.pi16; break; + case 2: SymAddr = *uFixVirgin.pi32; break; + case 3: SymAddr = *uFixVirgin.pi64; break; + } + if (Fixup.r.r_pcrel) + SymAddr += Fixup.r.r_address + pFixupSect->LinkAddress; + + /* Add symbol / section address. */ + if (Fixup.r.r_extern) + { + const macho_nlist_32_t *pSym; + if (Fixup.r.r_symbolnum >= cSyms) + return KLDR_ERR_BAD_FIXUP; + pSym = &paSyms[Fixup.r.r_symbolnum]; + + if (pSym->n_type & MACHO_N_STAB) + return KLDR_ERR_BAD_FIXUP; + + switch (pSym->n_type & MACHO_N_TYPE) + { + case MACHO_N_SECT: + { + PKLDRMODMACHOSECT pSymSect; + KLDRMODMACHO_CHECK_RETURN((KU32)pSym->n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL); + pSymSect = &pModMachO->paSections[pSym->n_sect - 1]; + + SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress; + break; + } + + case MACHO_N_UNDF: + case MACHO_N_ABS: + SymAddr += pSym->n_value; + break; + + case MACHO_N_INDR: + case MACHO_N_PBUD: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO); + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL); + } + } + else if (Fixup.r.r_symbolnum != R_ABS) + { + PKLDRMODMACHOSECT pSymSect; + if (Fixup.r.r_symbolnum > pModMachO->cSections) + return KLDR_ERR_BAD_FIXUP; + pSymSect = &pModMachO->paSections[Fixup.r.r_symbolnum - 1]; + + SymAddr -= pSymSect->LinkAddress; + SymAddr += pSymSect->RVA + NewBaseAddress; + } + + /* adjust for PC relative */ + if (Fixup.r.r_pcrel) + SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress; + } + else + { + PKLDRMODMACHOSECT pSymSect; + KU32 iSymSect; + KLDRADDR Value; + + /* sanity */ + KLDRMODMACHO_ASSERT(Fixup.s.r_scattered); + if ((KU32)Fixup.s.r_address >= cbSectBits) + return KLDR_ERR_BAD_FIXUP; + + /* calc fixup addresses. */ + uFix.pv = pbSectBits + Fixup.s.r_address; + uFixVirgin.pv = pbSectVirginBits ? (KU8 *)pbSectVirginBits + Fixup.s.r_address : 0; + + /* + * Calc the symbol value. + */ + /* The addend is stored in the code. */ + switch (Fixup.s.r_length) + { + case 0: SymAddr = *uFixVirgin.pi8; break; + case 1: SymAddr = *uFixVirgin.pi16; break; + case 2: SymAddr = *uFixVirgin.pi32; break; + case 3: SymAddr = *uFixVirgin.pi64; break; + } + if (Fixup.s.r_pcrel) + SymAddr += Fixup.s.r_address; + Value = Fixup.s.r_value; + SymAddr -= Value; /* (-> addend only) */ + + /* Find the section number from the r_value. */ + pSymSect = NULL; + for (iSymSect = 0; iSymSect < pModMachO->cSections; iSymSect++) + { + KLDRADDR off = Value - pModMachO->paSections[iSymSect].LinkAddress; + if (off < pModMachO->paSections[iSymSect].cb) + { + pSymSect = &pModMachO->paSections[iSymSect]; + break; + } + else if (off == pModMachO->paSections[iSymSect].cb) /* edge case */ + pSymSect = &pModMachO->paSections[iSymSect]; + } + if (!pSymSect) + return KLDR_ERR_BAD_FIXUP; + + /* Calc the symbol address. */ + SymAddr += Value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress; + if (Fixup.s.r_pcrel) + SymAddr -= Fixup.s.r_address + pFixupSect->RVA + NewBaseAddress; + + Fixup.r.r_length = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_length; + Fixup.r.r_type = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_type; + } + + /* + * Write back the fixed up value. + */ + if (Fixup.r.r_type == GENERIC_RELOC_VANILLA) + { + switch (Fixup.r.r_length) + { + case 0: *uFix.pu8 = (KU8)SymAddr; break; + case 1: *uFix.pu16 = (KU16)SymAddr; break; + case 2: *uFix.pu32 = (KU32)SymAddr; break; + case 3: *uFix.pu64 = (KU64)SymAddr; break; + } + } + else if (Fixup.r.r_type <= GENERIC_RELOC_LOCAL_SECTDIFF) + return KLDR_ERR_MACHO_UNSUPPORTED_FIXUP_TYPE; + else + return KLDR_ERR_BAD_FIXUP; + } + + return 0; +} + + +/** + * Applies AMD64 fixups to a section. + * + * @returns 0 on success, non-zero kLdr status code on failure. + * @param pModMachO The Mach-O module interpreter instance. + * @param pbSectBits Pointer to the section bits. + * @param pFixupSect The section being fixed up. + * @param NewBaseAddress The new base image address. + */ +static int kldrModMachOFixupSectionAMD64(PKLDRMODMACHO pModMachO, KU8 *pbSectBits, PKLDRMODMACHOSECT pFixupSect, + macho_nlist_64_t *paSyms, KU32 cSyms, KLDRADDR NewBaseAddress) +{ + const macho_relocation_info_t *paFixups = pFixupSect->paFixups; + const KU32 cFixups = pFixupSect->cFixups; + KSIZE cbSectBits = (KSIZE)pFixupSect->cb; + const KU8 *pbSectVirginBits; + KU32 iFixup; + KLDRPU uFixVirgin; + KLDRPU uFix; + KLDRADDR SymAddr; + int rc; + + /* + * Find the virgin bits. + */ + if (pFixupSect->offFile != -1) + { + rc = kldrModMachOMapVirginBits(pModMachO); + if (rc) + return rc; + pbSectVirginBits = (const KU8 *)pModMachO->pvBits + pFixupSect->offFile; + } + else + pbSectVirginBits = NULL; + + /* + * Iterate the fixups and apply them. + */ + for (iFixup = 0; iFixup < cFixups; iFixup++) + { + union + { + macho_relocation_info_t r; + scattered_relocation_info_t s; + } Fixup; + Fixup.r = paFixups[iFixup]; + + /* AMD64 doesn't use scattered fixups. */ + KLDRMODMACHO_CHECK_RETURN(!(Fixup.r.r_address & R_SCATTERED), KLDR_ERR_BAD_FIXUP); + + /* sanity */ + KLDRMODMACHO_CHECK_RETURN((KU32)Fixup.r.r_address < cbSectBits, KLDR_ERR_BAD_FIXUP); + + /* calc fixup addresses. */ + uFix.pv = pbSectBits + Fixup.r.r_address; + uFixVirgin.pv = pbSectVirginBits ? (KU8 *)pbSectVirginBits + Fixup.r.r_address : 0; + + /* + * Calc the symbol value. + */ + /* Calc the linked symbol address / addend. */ + switch (Fixup.r.r_length) + { + /** @todo Deal with unaligned accesses on non x86 platforms. */ + case 2: SymAddr = *uFixVirgin.pi32; break; + case 3: SymAddr = *uFixVirgin.pi64; break; + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP); + } + + /* Add symbol / section address. */ + if (Fixup.r.r_extern) + { + const macho_nlist_64_t *pSym; + + KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum < cSyms, KLDR_ERR_BAD_FIXUP); + pSym = &paSyms[Fixup.r.r_symbolnum]; + KLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), KLDR_ERR_BAD_FIXUP); + + switch (Fixup.r.r_type) + { + /* GOT references just needs to have their symbol verified. + Later, we'll optimize GOT building here using a parallel sym->got array. */ + case X86_64_RELOC_GOT_LOAD: + case X86_64_RELOC_GOT: + switch (pSym->n_type & MACHO_N_TYPE) + { + case MACHO_N_SECT: + case MACHO_N_UNDF: + case MACHO_N_ABS: + break; + case MACHO_N_INDR: + case MACHO_N_PBUD: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO); + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL); + } + SymAddr = sizeof(KU64) * Fixup.r.r_symbolnum + pModMachO->GotRVA + NewBaseAddress; + KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, KLDR_ERR_BAD_FIXUP); + SymAddr -= 4; + break; + + /* Verify the r_pcrel field for signed fixups on the way into the default case. */ + case X86_64_RELOC_BRANCH: + case X86_64_RELOC_SIGNED: + case X86_64_RELOC_SIGNED_1: + case X86_64_RELOC_SIGNED_2: + case X86_64_RELOC_SIGNED_4: + KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP); + /* Falls through. */ + default: + { + /* Adjust with fixup specific addend and vierfy unsigned/r_pcrel. */ + switch (Fixup.r.r_type) + { + case X86_64_RELOC_UNSIGNED: + KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP); + break; + case X86_64_RELOC_BRANCH: + KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, KLDR_ERR_BAD_FIXUP); + SymAddr -= 4; + break; + case X86_64_RELOC_SIGNED: + case X86_64_RELOC_SIGNED_1: + case X86_64_RELOC_SIGNED_2: + case X86_64_RELOC_SIGNED_4: + SymAddr -= 4; + break; + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP); + } + + switch (pSym->n_type & MACHO_N_TYPE) + { + case MACHO_N_SECT: + { + PKLDRMODMACHOSECT pSymSect; + KLDRMODMACHO_CHECK_RETURN((KU32)pSym->n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL); + pSymSect = &pModMachO->paSections[pSym->n_sect - 1]; + SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress; + break; + } + + case MACHO_N_UNDF: + /* branch to an external symbol may have to take a short detour. */ + if ( Fixup.r.r_type == X86_64_RELOC_BRANCH + && SymAddr + Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress + - pSym->n_value + + KU64_C(0x80000000) + >= KU64_C(0xffffff20)) + SymAddr += pModMachO->cbJmpStub * Fixup.r.r_symbolnum + pModMachO->JmpStubsRVA + NewBaseAddress; + else + SymAddr += pSym->n_value; + break; + + case MACHO_N_ABS: + SymAddr += pSym->n_value; + break; + + case MACHO_N_INDR: + case MACHO_N_PBUD: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO); + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL); + } + break; + } + + /* + * This is a weird customer, it will always be follows by an UNSIGNED fixup. + */ + case X86_64_RELOC_SUBTRACTOR: + { + macho_relocation_info_t Fixup2; + + /* Deal with the SUBTRACT symbol first, by subtracting it from SymAddr. */ + switch (pSym->n_type & MACHO_N_TYPE) + { + case MACHO_N_SECT: + { + PKLDRMODMACHOSECT pSymSect; + KLDRMODMACHO_CHECK_RETURN((KU32)pSym->n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL); + pSymSect = &pModMachO->paSections[pSym->n_sect - 1]; + SymAddr -= pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress; + break; + } + + case MACHO_N_UNDF: + case MACHO_N_ABS: + SymAddr -= pSym->n_value; + break; + + case MACHO_N_INDR: + case MACHO_N_PBUD: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO); + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL); + } + + /* Load the 2nd fixup, check sanity. */ + iFixup++; + KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel && iFixup < cFixups, KLDR_ERR_BAD_FIXUP); + Fixup2 = paFixups[iFixup]; + KLDRMODMACHO_CHECK_RETURN( Fixup2.r_address == Fixup.r.r_address + && Fixup2.r_length == Fixup.r.r_length + && Fixup2.r_type == X86_64_RELOC_UNSIGNED + && !Fixup2.r_pcrel + && Fixup2.r_symbolnum < cSyms, + KLDR_ERR_BAD_FIXUP); + + if (Fixup2.r_extern) + { + KLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum < cSyms, KLDR_ERR_BAD_FIXUP); + pSym = &paSyms[Fixup2.r_symbolnum]; + KLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), KLDR_ERR_BAD_FIXUP); + + /* Add it's value to SymAddr. */ + switch (pSym->n_type & MACHO_N_TYPE) + { + case MACHO_N_SECT: + { + PKLDRMODMACHOSECT pSymSect; + KLDRMODMACHO_CHECK_RETURN((KU32)pSym->n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL); + pSymSect = &pModMachO->paSections[pSym->n_sect - 1]; + SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress; + break; + } + + case MACHO_N_UNDF: + case MACHO_N_ABS: + SymAddr += pSym->n_value; + break; + + case MACHO_N_INDR: + case MACHO_N_PBUD: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO); + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL); + } + } + else if (Fixup2.r_symbolnum != R_ABS) + { + PKLDRMODMACHOSECT pSymSect; + KLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum <= pModMachO->cSections, KLDR_ERR_BAD_FIXUP); + pSymSect = &pModMachO->paSections[Fixup2.r_symbolnum - 1]; + SymAddr += pSymSect->RVA + NewBaseAddress; + } + else + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP); + } + break; + } + } + else + { + /* verify against fixup type and make adjustments */ + switch (Fixup.r.r_type) + { + case X86_64_RELOC_UNSIGNED: + KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP); + break; + case X86_64_RELOC_BRANCH: + KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP); + SymAddr += 4; /* dunno what the assmbler/linker really is doing here... */ + break; + case X86_64_RELOC_SIGNED: + case X86_64_RELOC_SIGNED_1: + case X86_64_RELOC_SIGNED_2: + case X86_64_RELOC_SIGNED_4: + KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP); + break; + /*case X86_64_RELOC_GOT_LOAD:*/ + /*case X86_64_RELOC_GOT: */ + /*case X86_64_RELOC_SUBTRACTOR: - must be r_extern=1 says as. */ + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP); + } + if (Fixup.r.r_symbolnum != R_ABS) + { + PKLDRMODMACHOSECT pSymSect; + KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum <= pModMachO->cSections, KLDR_ERR_BAD_FIXUP); + pSymSect = &pModMachO->paSections[Fixup.r.r_symbolnum - 1]; + + SymAddr -= pSymSect->LinkAddress; + SymAddr += pSymSect->RVA + NewBaseAddress; + if (Fixup.r.r_pcrel) + SymAddr += Fixup.r.r_address; + } + } + + /* adjust for PC relative */ + if (Fixup.r.r_pcrel) + SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress; + + /* + * Write back the fixed up value. + */ + switch (Fixup.r.r_length) + { + case 3: + *uFix.pu64 = (KU64)SymAddr; + break; + case 2: + KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel || Fixup.r.r_type == X86_64_RELOC_SUBTRACTOR, KLDR_ERR_BAD_FIXUP); + KLDRMODMACHO_CHECK_RETURN((KI32)SymAddr == (KI64)SymAddr, KLDR_ERR_ADDRESS_OVERFLOW); + *uFix.pu32 = (KU32)SymAddr; + break; + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP); + } + } + + return 0; +} + + +/** + * Loads the symbol table for a MH_OBJECT file. + * + * The symbol table is pointed to by KLDRMODMACHO::pvaSymbols. + * + * @returns 0 on success, non-zero kLdr status code on failure. + * @param pModMachO The Mach-O module interpreter instance. + */ +static int kldrModMachOLoadObjSymTab(PKLDRMODMACHO pModMachO) +{ + int rc = 0; + + if ( !pModMachO->pvaSymbols + && pModMachO->cSymbols) + { + KSIZE cbSyms; + KSIZE cbSym; + void *pvSyms; + void *pvStrings; + + /* sanity */ + KLDRMODMACHO_CHECK_RETURN( pModMachO->offSymbols + && (!pModMachO->cchStrings || pModMachO->offStrings), + KLDR_ERR_MACHO_BAD_OBJECT_FILE); + + /* allocate */ + cbSym = pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE + || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE + ? sizeof(macho_nlist_32_t) + : sizeof(macho_nlist_64_t); + cbSyms = pModMachO->cSymbols * cbSym; + KLDRMODMACHO_CHECK_RETURN(cbSyms / cbSym == pModMachO->cSymbols, KLDR_ERR_SIZE_OVERFLOW); + rc = KERR_NO_MEMORY; + pvSyms = kHlpAlloc(cbSyms); + if (pvSyms) + { + if (pModMachO->cchStrings) + pvStrings = kHlpAlloc(pModMachO->cchStrings); + else + pvStrings = kHlpAllocZ(4); + if (pvStrings) + { + /* read */ + rc = kRdrRead(pModMachO->pMod->pRdr, pvSyms, cbSyms, pModMachO->offSymbols); + if (!rc && pModMachO->cchStrings) + rc = kRdrRead(pModMachO->pMod->pRdr, pvStrings, pModMachO->cchStrings, pModMachO->offStrings); + if (!rc) + { + pModMachO->pvaSymbols = pvSyms; + pModMachO->pchStrings = (char *)pvStrings; + + /* perform endian conversion? */ + if (pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE) + { + KU32 cLeft = pModMachO->cSymbols; + macho_nlist_32_t *pSym = (macho_nlist_32_t *)pvSyms; + while (cLeft-- > 0) + { + pSym->n_un.n_strx = K_E2E_U32(pSym->n_un.n_strx); + pSym->n_desc = (KI16)K_E2E_U16(pSym->n_desc); + pSym->n_value = K_E2E_U32(pSym->n_value); + pSym++; + } + } + else if (pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE) + { + KU32 cLeft = pModMachO->cSymbols; + macho_nlist_64_t *pSym = (macho_nlist_64_t *)pvSyms; + while (cLeft-- > 0) + { + pSym->n_un.n_strx = K_E2E_U32(pSym->n_un.n_strx); + pSym->n_desc = (KI16)K_E2E_U16(pSym->n_desc); + pSym->n_value = K_E2E_U64(pSym->n_value); + pSym++; + } + } + + return 0; + } + kHlpFree(pvStrings); + } + kHlpFree(pvSyms); + } + } + else + KLDRMODMACHO_ASSERT(pModMachO->pchStrings || pModMachO->Hdr.filetype == MH_DSYM); + + return rc; +} + + +/** + * Loads the fixups at the given address and performs endian + * conversion if necessary. + * + * @returns 0 on success, non-zero kLdr status code on failure. + * @param pModMachO The Mach-O module interpreter instance. + * @param offFixups The file offset of the fixups. + * @param cFixups The number of fixups to load. + * @param ppaFixups Where to put the pointer to the allocated fixup array. + */ +static int kldrModMachOLoadFixups(PKLDRMODMACHO pModMachO, KLDRFOFF offFixups, KU32 cFixups, macho_relocation_info_t **ppaFixups) +{ + macho_relocation_info_t *paFixups; + KSIZE cbFixups; + int rc; + + /* allocate the memory. */ + cbFixups = cFixups * sizeof(*paFixups); + KLDRMODMACHO_CHECK_RETURN(cbFixups / sizeof(*paFixups) == cFixups, KLDR_ERR_SIZE_OVERFLOW); + paFixups = (macho_relocation_info_t *)kHlpAlloc(cbFixups); + if (!paFixups) + return KERR_NO_MEMORY; + + /* read the fixups. */ + rc = kRdrRead(pModMachO->pMod->pRdr, paFixups, cbFixups, offFixups); + if (!rc) + { + *ppaFixups = paFixups; + + /* do endian conversion if necessary. */ + if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE + || pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE) + { + KU32 iFixup; + for (iFixup = 0; iFixup < cFixups; iFixup++) + { + KU32 *pu32 = (KU32 *)&paFixups[iFixup]; + pu32[0] = K_E2E_U32(pu32[0]); + pu32[1] = K_E2E_U32(pu32[1]); + } + } + } + else + kHlpFree(paFixups); + return rc; +} + + +/** + * Maps the virgin file bits into memory if not already done. + * + * @returns 0 on success, non-zero kLdr status code on failure. + * @param pModMachO The Mach-O module interpreter instance. + */ +static int kldrModMachOMapVirginBits(PKLDRMODMACHO pModMachO) +{ + int rc = 0; + if (!pModMachO->pvBits) + rc = kRdrAllMap(pModMachO->pMod->pRdr, &pModMachO->pvBits); + return rc; +} + + +/** @copydoc kLdrModCallInit */ +static int kldrModMachOCallInit(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle) +{ + /* later */ + K_NOREF(pMod); + K_NOREF(pvMapping); + K_NOREF(uHandle); + return 0; +} + + +/** @copydoc kLdrModCallTerm */ +static int kldrModMachOCallTerm(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle) +{ + /* later */ + K_NOREF(pMod); + K_NOREF(pvMapping); + K_NOREF(uHandle); + return 0; +} + + +/** @copydoc kLdrModCallThread */ +static int kldrModMachOCallThread(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle, unsigned fAttachingOrDetaching) +{ + /* Relevant for Mach-O? */ + K_NOREF(pMod); + K_NOREF(pvMapping); + K_NOREF(uHandle); + K_NOREF(fAttachingOrDetaching); + return 0; +} + + +/** @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) +{ + PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; + KU32 i; + int rc; + + if (!pModMachO->fCanLoad) + return KLDR_ERR_TODO; + + /* + * Zero the entire buffer first to simplify things. + */ + kHlpMemSet(pvBits, 0, (KSIZE)pModMachO->cbImage); + + /* + * When possible use the segment table to load the data. + */ + 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 = kRdrRead(pMod->pRdr, + (KU8 *)pvBits + pMod->aSegments[i].RVA, + pMod->aSegments[i].cbFile, + pMod->aSegments[i].offFile); + if (rc) + return rc; + } + + /* + * Perform relocations. + */ + return kldrModMachORelocateBits(pMod, pvBits, BaseAddress, pModMachO->LinkAddress, pfnGetImport, pvUser); +} + + +/** @copydoc kLdrModRelocateBits */ +static int kldrModMachORelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress, + PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser) +{ + PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData; + int rc; + K_NOREF(OldBaseAddress); + + /* + * Call workers to do the jobs. + */ + if (pModMachO->Hdr.filetype == MH_OBJECT) + { + rc = kldrModMachOObjDoImports(pModMachO, NewBaseAddress, pfnGetImport, pvUser); + if (!rc) + rc = kldrModMachOObjDoFixups(pModMachO, pvBits, NewBaseAddress); + + } + else + rc = KLDR_ERR_TODO; + /*{ + rc = kldrModMachODoFixups(pModMachO, pvBits, NewBaseAddress, OldBaseAddress, pfnGetImport, pvUser); + if (!rc) + rc = kldrModMachODoImports(pModMachO, pvBits, pfnGetImport, pvUser); + }*/ + + /* + * Construct the global offset table if necessary, it's always the last + * segment when present. + */ + if (!rc && pModMachO->fMakeGot) + rc = kldrModMachOMakeGOT(pModMachO, pvBits, NewBaseAddress); + + return rc; +} + + +/** + * Builds the GOT. + * + * Assumes the symbol table has all external symbols resolved correctly and that + * the bits has been cleared up front. + */ +static int kldrModMachOMakeGOT(PKLDRMODMACHO pModMachO, void *pvBits, KLDRADDR NewBaseAddress) +{ + KU32 iSym = pModMachO->cSymbols; + if ( pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE + || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE) + { + macho_nlist_32_t const *paSyms = (macho_nlist_32_t const *)pModMachO->pvaSymbols; + KU32 *paGOT = (KU32 *)((KU8 *)pvBits + pModMachO->GotRVA); + while (iSym-- > 0) + switch (paSyms[iSym].n_type & MACHO_N_TYPE) + { + case MACHO_N_SECT: + { + PKLDRMODMACHOSECT pSymSect; + KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL); + pSymSect = &pModMachO->paSections[paSyms[iSym].n_sect - 1]; + paGOT[iSym] = (KU32)(paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress); + break; + } + + case MACHO_N_UNDF: + case MACHO_N_ABS: + paGOT[iSym] = paSyms[iSym].n_value; + break; + } + } + else + { + macho_nlist_64_t const *paSyms = (macho_nlist_64_t const *)pModMachO->pvaSymbols; + KU64 *paGOT = (KU64 *)((KU8 *)pvBits + pModMachO->GotRVA); + while (iSym-- > 0) + { + switch (paSyms[iSym].n_type & MACHO_N_TYPE) + { + case MACHO_N_SECT: + { + PKLDRMODMACHOSECT pSymSect; + KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL); + pSymSect = &pModMachO->paSections[paSyms[iSym].n_sect - 1]; + paGOT[iSym] = paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress; + break; + } + + case MACHO_N_UNDF: + case MACHO_N_ABS: + paGOT[iSym] = paSyms[iSym].n_value; + break; + } + } + + if (pModMachO->JmpStubsRVA != NIL_KLDRADDR) + { + iSym = pModMachO->cSymbols; + switch (pModMachO->Hdr.cputype) + { + /* + * AMD64 is simple since the GOT and the indirect jmps are parallel + * arrays with entries of the same size. The relative offset will + * be the the same for each entry, kind of nice. :-) + */ + case CPU_TYPE_X86_64: + { + KU64 *paJmps = (KU64 *)((KU8 *)pvBits + pModMachO->JmpStubsRVA); + KI32 off; + KU64 u64Tmpl; + union + { + KU8 ab[8]; + KU64 u64; + } Tmpl; + + /* create the template. */ + off = (KI32)(pModMachO->GotRVA - (pModMachO->JmpStubsRVA + 6)); + Tmpl.ab[0] = 0xff; /* jmp [GOT-entry wrt RIP] */ + Tmpl.ab[1] = 0x25; + Tmpl.ab[2] = off & 0xff; + Tmpl.ab[3] = (off >> 8) & 0xff; + Tmpl.ab[4] = (off >> 16) & 0xff; + Tmpl.ab[5] = (off >> 24) & 0xff; + Tmpl.ab[6] = 0xcc; + Tmpl.ab[7] = 0xcc; + u64Tmpl = Tmpl.u64; + + /* copy the template to every jmp table entry. */ + while (iSym-- > 0) + paJmps[iSym] = u64Tmpl; + break; + } + + default: + KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO); + } + } + } + return 0; +} + + +/** + * 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, + kldrModMachOQueryImageUuid, + NULL, + NULL, + kldrModMachOEnumDbgInfo, + kldrModMachOHasDbgInfo, + kldrModMachOMap, + kldrModMachOUnmap, + kldrModMachOAllocTLS, + kldrModMachOFreeTLS, + kldrModMachOReload, + kldrModMachOFixupMapping, + kldrModMachOCallInit, + kldrModMachOCallTerm, + kldrModMachOCallThread, + kldrModMachOSize, + kldrModMachOGetBits, + kldrModMachORelocateBits, + NULL, /** @todo mostly done */ + 42 /* the end */ +}; + -- cgit v1.2.3