summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/ldr/ldrMachO.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/ldr/ldrMachO.cpp')
-rw-r--r--src/VBox/Runtime/common/ldr/ldrMachO.cpp5734
1 files changed, 5734 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/ldr/ldrMachO.cpp b/src/VBox/Runtime/common/ldr/ldrMachO.cpp
new file mode 100644
index 00000000..7d5c655a
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/ldrMachO.cpp
@@ -0,0 +1,5734 @@
+/* $Id: ldrMachO.cpp $ */
+/** @file
+ * kLdr - The Module Interpreter for the MACH-O format.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ * --------------------------------------------------------------------
+ *
+ * This code is based on: kLdr/kLdrModMachO.c from kStuff r113.
+ *
+ * Copyright (c) 2006-2013 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/base64.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/sha.h>
+#include <iprt/crypto/digest.h>
+
+#include <iprt/formats/mach-o.h>
+#include <iprt/crypto/applecodesign.h>
+#include "internal/ldr.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def RTLDRMODMACHO_STRICT
+ * Define RTLDRMODMACHO_STRICT to enabled strict checks in RTLDRMODMACHO. */
+#define RTLDRMODMACHO_STRICT 1
+#define RTLDRMODMACHO_STRICT2
+
+/** @def RTLDRMODMACHO_ASSERT
+ * Assert that an expression is true when KLDR_STRICT is defined.
+ */
+#ifdef RTLDRMODMACHO_STRICT
+# define RTLDRMODMACHO_ASSERT(expr) Assert(expr)
+#else
+# define RTLDRMODMACHO_ASSERT(expr) do {} while (0)
+#endif
+
+/** @def RTLDRMODMACHO_CHECK_RETURN
+ * Checks that an expression is true and return if it isn't.
+ * This is a debug aid.
+ */
+#ifdef RTLDRMODMACHO_STRICT2
+# define RTLDRMODMACHO_CHECK_RETURN(expr, rc) AssertReturn(expr, rc)
+#else
+# define RTLDRMODMACHO_CHECK_RETURN(expr, rc) do { if (RT_LIKELY(expr)) {/* likely */ } else return (rc); } while (0)
+#endif
+
+/** @def RTLDRMODMACHO_CHECK_MSG_RETURN
+ * Checks that an expression is true and return if it isn't.
+ * This is a debug aid.
+ */
+#ifdef RTLDRMODMACHO_STRICT2
+# define RTLDRMODMACHO_CHECK_MSG_RETURN(expr, msgargs, rc) AssertMsgReturn(expr, msgargs, rc)
+#else
+# define RTLDRMODMACHO_CHECK_MSG_RETURN(expr, msgargs, rc) do { if (RT_LIKELY(expr)) {/* likely */ } else return (rc); } while (0)
+#endif
+
+/** @def RTLDRMODMACHO_CHECK_RETURN
+ * Checks that an expression is true and return if it isn't.
+ * This is a debug aid.
+ */
+#ifdef RTLDRMODMACHO_STRICT2
+# define RTLDRMODMACHO_FAILED_RETURN(rc) AssertFailedReturn(rc)
+#else
+# define RTLDRMODMACHO_FAILED_RETURN(rc) return (rc)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Mach-O section details.
+ */
+typedef struct RTLDRMODMACHOSECT
+{
+ /** The size of the section (in bytes). */
+ RTLDRADDR cb;
+ /** The link address of this section. */
+ RTLDRADDR LinkAddress;
+ /** The RVA of this section. */
+ RTLDRADDR RVA;
+ /** The file offset of this section.
+ * This is -1 if the section doesn't have a file backing. */
+ RTFOFF offFile;
+ /** The number of fixups. */
+ uint32_t cFixups;
+ /** The array of fixups. (lazy loaded) */
+ macho_relocation_union_t *paFixups;
+ /** Array of virgin data running parallel to paFixups */
+ PRTUINT64U pauFixupVirginData;
+ /** The file offset of the fixups for this section.
+ * This is -1 if the section doesn't have any fixups. */
+ RTFOFF offFixups;
+ /** Mach-O section flags. */
+ uint32_t fFlags;
+ /** kLdr segment index. */
+ uint32_t iSegment;
+ /** Pointer to the Mach-O section structure. */
+ void *pvMachoSection;
+} RTLDRMODMACHOSECT, *PRTLDRMODMACHOSECT;
+
+/**
+ * Extra per-segment info.
+ *
+ * This is corresponds to a kLdr segment, not a Mach-O segment!
+ */
+typedef struct RTLDRMODMACHOSEG
+{
+ /** Common segment info. */
+ RTLDRSEG SegInfo;
+
+ /** The orignal segment number (in case we had to resort it). */
+ uint32_t iOrgSegNo;
+ /** The number of sections in the segment. */
+ uint32_t 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. */
+ PRTLDRMODMACHOSECT paSections;
+
+} RTLDRMODMACHOSEG, *PRTLDRMODMACHOSEG;
+
+/**
+ * Instance data for the Mach-O MH_OBJECT module interpreter.
+ * @todo interpret the other MH_* formats.
+ */
+typedef struct RTLDRMODMACHO
+{
+ /** Core module structure. */
+ RTLDRMODINTERNAL Core;
+
+ /** The minium cpu this module was built for.
+ * This might not be accurate, so use kLdrModCanExecuteOn() to check. */
+ RTLDRCPU enmCpu;
+ /** The number of segments in the module. */
+ uint32_t cSegments;
+
+ /** 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. */
+ uint32_t fOpenFlags;
+
+ /** The offset of the image. (FAT fun.) */
+ RTFOFF offImage;
+ /** The link address. */
+ RTLDRADDR LinkAddress;
+ /** The size of the mapped image. */
+ RTLDRADDR cbImage;
+ /** Whether we're capable of loading the image. */
+ bool fCanLoad;
+ /** Whether we're creating a global offset table segment.
+ * This dependes on the cputype and image type. */
+ bool fMakeGot;
+ /** The size of a indirect GOT jump stub entry.
+ * This is 0 if not needed. */
+ uint32_t 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. */
+ uint32_t uEffFileType;
+ /** Pointer to the load commands. (endian converted) */
+ uint8_t *pbLoadCommands;
+ /** The Mach-O header. (endian converted)
+ * @remark The reserved field is only valid for real 64-bit headers. */
+ mach_header_64_t Hdr;
+
+ /** The offset of the symbol table. */
+ RTFOFF offSymbols;
+ /** The number of symbols. */
+ uint32_t cSymbols;
+ /** The pointer to the loaded symbol table. */
+ void *pvaSymbols;
+ /** The offset of the string table. */
+ RTFOFF offStrings;
+ /** The size of the of the string table. */
+ uint32_t cchStrings;
+ /** Pointer to the loaded string table. */
+ char *pchStrings;
+ /** Pointer to the dynamic symbol table command if present. */
+ dysymtab_command_t *pDySymTab;
+ /** The indirect symbol table (size given by pDySymTab->nindirectsymb).
+ * @remarks Host endian. */
+ uint32_t *paidxIndirectSymbols;
+ /** Dynamic relocations, first pDySymTab->nextrel external relocs followed by
+ * pDySymTab->nlocrel local ones. */
+ macho_relocation_union_t *paRelocations;
+ /** Array of virgin data running parallel to paRelocations */
+ PRTUINT64U pauRelocationsVirginData;
+
+ /** The image UUID, all zeros if not found. */
+ uint8_t abImageUuid[16];
+
+ /** The code signature offset. */
+ uint32_t offCodeSignature;
+ /** The code signature size (0 if not signed). */
+ uint32_t cbCodeSignature;
+ /** Pointer to the code signature blob if loaded. */
+ union
+ {
+ uint8_t *pb;
+ PCRTCRAPLCSSUPERBLOB pSuper;
+ } PtrCodeSignature;
+ /** File offset of segment 0 (relative to Mach-O header). */
+ uint64_t offSeg0ForCodeSign;
+ /** File size of segment 0. */
+ uint64_t cbSeg0ForCodeSign;
+ /** Segment 0 flags. */
+ uint64_t fSeg0ForCodeSign;
+
+ /** The RVA of the Global Offset Table. */
+ RTLDRADDR GotRVA;
+ /** The RVA of the indirect GOT jump stubs. */
+ RTLDRADDR JmpStubsRVA;
+
+ /** The number of sections. */
+ uint32_t cSections;
+ /** Pointer to the section array running in parallel to the Mach-O one. */
+ PRTLDRMODMACHOSECT paSections;
+
+ /** Array of segments parallel to the one in KLDRMOD. */
+ RTLDRMODMACHOSEG aSegments[1];
+} RTLDRMODMACHO;
+/** Pointer instance data for an Mach-O module. */
+typedef RTLDRMODMACHO *PRTLDRMODMACHO;
+
+/**
+ * Code directory data.
+ */
+typedef struct RTLDRMACHCODEDIR
+{
+ PCRTCRAPLCSCODEDIRECTORY pCodeDir;
+ /** The slot type. */
+ uint32_t uSlot;
+ /** The naturalized size. */
+ uint32_t cb;
+ /** The digest type. */
+ RTDIGESTTYPE enmDigest;
+} RTLDRMACHCODEDIR;
+/** Pointer to code directory data. */
+typedef RTLDRMACHCODEDIR *PRTLDRMACHCODEDIR;
+
+/**
+ * Decoded apple Mach-O signature data.
+ * @note The raw signature data lives in RTLDRMODMACHO::PtrCodeSignature.
+ */
+typedef struct RTLDRMACHOSIGNATURE
+{
+ /** Number of code directory slots. */
+ uint32_t cCodeDirs;
+ /** Code directories. */
+ RTLDRMACHCODEDIR aCodeDirs[6];
+
+ /** The index of the PKCS#7 slot. */
+ uint32_t idxPkcs7;
+ /** The size of the PKCS#7 data. */
+ uint32_t cbPkcs7;
+ /** Pointer to the PKCS#7 data. */
+ uint8_t const *pbPkcs7;
+ /** Parsed PKCS#7 data. */
+ RTCRPKCS7CONTENTINFO ContentInfo;
+ /** Pointer to the decoded SignedData inside the ContentInfo member. */
+ PRTCRPKCS7SIGNEDDATA pSignedData;
+} RTLDRMACHOSIGNATURE;
+/** Pointer to decoded apple code signing data. */
+typedef RTLDRMACHOSIGNATURE *PRTLDRMACHOSIGNATURE;
+
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#if 0
+static int32_t kldrModMachONumberOfImports(PRTLDRMODINTERNAL pMod, const void *pvBits);
+#endif
+static DECLCALLBACK(int) rtldrMachO_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
+ RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser);
+
+
+static int kldrModMachOPreParseLoadCommands(uint8_t *pbLoadCommands, const mach_header_32_t *pHdr, PRTLDRREADER pRdr, RTFOFF offImage,
+ uint32_t fOpenFlags, uint32_t *pcSegments, uint32_t *pcSections, uint32_t *pcbStringPool,
+ bool *pfCanLoad, PRTLDRADDR pLinkAddress, uint8_t *puEffFileType, PRTERRINFO pErrInfo);
+static int kldrModMachOParseLoadCommands(PRTLDRMODMACHO pThis, char *pbStringPool, uint32_t cbStringPool);
+
+static int kldrModMachOLoadObjSymTab(PRTLDRMODMACHO pThis);
+static int kldrModMachOLoadFixups(PRTLDRMODMACHO pThis, RTFOFF offFixups, uint32_t cFixups, macho_relocation_union_t **ppaFixups);
+
+static int kldrModMachODoQuerySymbol32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms, const char *pchStrings,
+ uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol, const char *pchSymbol,
+ uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind);
+static int kldrModMachODoQuerySymbol64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms, const char *pchStrings,
+ uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol, const char *pchSymbol,
+ uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind);
+static int kldrModMachODoEnumSymbols32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
+ const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
+ uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser);
+static int kldrModMachODoEnumSymbols64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
+ const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
+ uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser);
+static int kldrModMachOObjDoImports(PRTLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser);
+static int kldrModMachOObjDoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress);
+static int kldrModMachOApplyFixupsGeneric32Bit(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, size_t cbSectBits, RTLDRADDR uBitsRva,
+ RTLDRADDR uBitsLinkAddr, const macho_relocation_union_t *paFixups,
+ const uint32_t cFixups, PCRTUINT64U const pauVirginData,
+ macho_nlist_32_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress);
+static int kldrModMachOApplyFixupsAMD64(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, size_t cbSectBits, RTLDRADDR uBitsRva,
+ const macho_relocation_union_t *paFixups,
+ const uint32_t cFixups, PCRTUINT64U const pauVirginData,
+ macho_nlist_64_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress);
+
+static int kldrModMachOMakeGOT(PRTLDRMODMACHO pThis, void *pvBits, RTLDRADDR NewBaseAddress);
+
+/*static int kldrModMachODoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress, RTLDRADDR OldBaseAddress);
+static int kldrModMachODoImports(PRTLDRMODMACHO pThis, void *pvMapping, PFNRTLDRIMPORT pfnGetImport, void *pvUser);*/
+
+
+
+/**
+ * Separate function for reading creating the Mach-O module instance to
+ * simplify cleanup on failure.
+ */
+static int kldrModMachODoCreate(PRTLDRREADER pRdr, RTFOFF offImage, uint32_t fOpenFlags,
+ PRTLDRMODMACHO *ppModMachO, PRTERRINFO pErrInfo)
+{
+ *ppModMachO = NULL;
+
+ /*
+ * Read the Mach-O header.
+ */
+ union
+ {
+ mach_header_32_t Hdr32;
+ mach_header_64_t Hdr64;
+ } s;
+ Assert(&s.Hdr32.magic == &s.Hdr64.magic);
+ Assert(&s.Hdr32.flags == &s.Hdr64.flags);
+ int rc = pRdr->pfnRead(pRdr, &s, sizeof(s), offImage);
+ if (rc)
+ return RTErrInfoSetF(pErrInfo, rc, "Error reading Mach-O header at %RTfoff: %Rrc", offImage, 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 VERR_LDRMACHO_OTHER_ENDIAN_NOT_SUPPORTED;
+ return VERR_INVALID_EXE_SIGNATURE;
+ }
+
+ /* sanity checks. */
+ if ( s.Hdr32.sizeofcmds > pRdr->pfnSize(pRdr) - sizeof(mach_header_32_t)
+ || s.Hdr32.sizeofcmds < sizeof(load_command_t) * s.Hdr32.ncmds
+ || (s.Hdr32.flags & ~MH_VALID_FLAGS))
+ return VERR_LDRMACHO_BAD_HEADER;
+
+ bool fMakeGot;
+ uint8_t cbJmpStub;
+ switch (s.Hdr32.cputype)
+ {
+ case CPU_TYPE_X86:
+ fMakeGot = false;
+ cbJmpStub = 0;
+ break;
+ case CPU_TYPE_X86_64:
+ fMakeGot = s.Hdr32.filetype == MH_OBJECT || s.Hdr32.filetype == MH_KEXT_BUNDLE;
+ cbJmpStub = fMakeGot ? 8 : 0;
+ break;
+ case CPU_TYPE_ARM64:
+ fMakeGot = s.Hdr32.filetype == MH_OBJECT || s.Hdr32.filetype == MH_KEXT_BUNDLE;
+ cbJmpStub = fMakeGot ? 8 : 0; /** @todo Not sure if this is right. Need to expore ARM64/MachO a bit more... */
+ break;
+ default:
+ return VERR_LDRMACHO_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 VERR_LDRMACHO_UNSUPPORTED_FILE_TYPE;
+
+ /*
+ * Read and pre-parse the load commands to figure out how many segments we'll be needing.
+ */
+ uint8_t *pbLoadCommands = (uint8_t *)RTMemAlloc(s.Hdr32.sizeofcmds);
+ if (!pbLoadCommands)
+ return VERR_NO_MEMORY;
+ rc = pRdr->pfnRead(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);
+
+ uint32_t cSegments = 0;
+ uint32_t cSections = 0;
+ uint32_t cbStringPool = 0;
+ bool fCanLoad = true;
+ RTLDRADDR LinkAddress = NIL_RTLDRADDR;
+ uint8_t uEffFileType = 0;
+ if (RT_SUCCESS(rc))
+ rc = kldrModMachOPreParseLoadCommands(pbLoadCommands, &s.Hdr32, pRdr, offImage, fOpenFlags,
+ &cSegments, &cSections, &cbStringPool, &fCanLoad, &LinkAddress, &uEffFileType,
+ pErrInfo);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pbLoadCommands);
+ return rc;
+ }
+ cSegments += fMakeGot;
+
+
+ /*
+ * Calc the instance size, allocate and initialize it.
+ */
+ size_t const cbModAndSegs = RT_ALIGN_Z(RT_UOFFSETOF_DYN(RTLDRMODMACHO, aSegments[cSegments])
+ + sizeof(RTLDRMODMACHOSECT) * cSections, 16);
+ PRTLDRMODMACHO pThis = (PRTLDRMODMACHO)RTMemAlloc(cbModAndSegs + cbStringPool);
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ *ppModMachO = pThis;
+ pThis->pbLoadCommands = pbLoadCommands;
+ pThis->offImage = offImage;
+
+ /* Core & CPU.*/
+ pThis->Core.u32Magic = 0; /* set by caller */
+ pThis->Core.eState = LDR_STATE_OPENED;
+ pThis->Core.pOps = NULL; /* set by caller. */
+ pThis->Core.pReader = pRdr;
+ switch (s.Hdr32.cputype)
+ {
+ case CPU_TYPE_X86:
+ pThis->Core.enmArch = RTLDRARCH_X86_32;
+ pThis->Core.enmEndian = RTLDRENDIAN_LITTLE;
+ switch (s.Hdr32.cpusubtype)
+ {
+ case CPU_SUBTYPE_I386_ALL: /* == CPU_SUBTYPE_386 */
+ pThis->enmCpu = RTLDRCPU_X86_32_BLEND;
+ break;
+ case CPU_SUBTYPE_486:
+ pThis->enmCpu = RTLDRCPU_I486;
+ break;
+ case CPU_SUBTYPE_486SX:
+ pThis->enmCpu = RTLDRCPU_I486SX;
+ break;
+ case CPU_SUBTYPE_PENT: /* == CPU_SUBTYPE_586 */
+ pThis->enmCpu = RTLDRCPU_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:
+ pThis->enmCpu = RTLDRCPU_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:
+ pThis->enmCpu = RTLDRCPU_P4;
+ break;
+
+ default:
+ /* Hack for kextutil output. */
+ if ( s.Hdr32.cpusubtype == 0
+ && s.Hdr32.filetype == MH_OBJECT)
+ break;
+ return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
+ }
+ break;
+
+ case CPU_TYPE_X86_64:
+ pThis->Core.enmArch = RTLDRARCH_AMD64;
+ pThis->Core.enmEndian = RTLDRENDIAN_LITTLE;
+ switch (s.Hdr32.cpusubtype & ~CPU_SUBTYPE_MASK)
+ {
+ case CPU_SUBTYPE_X86_64_ALL: pThis->enmCpu = RTLDRCPU_AMD64_BLEND; break;
+ default:
+ return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
+ }
+ break;
+
+ case CPU_TYPE_ARM64:
+ pThis->Core.enmArch = RTLDRARCH_ARM64;
+ pThis->Core.enmEndian = RTLDRENDIAN_LITTLE;
+ switch (s.Hdr32.cpusubtype & ~CPU_SUBTYPE_MASK)
+ {
+ case CPU_SUBTYPE_ARM64_ALL: pThis->enmCpu = RTLDRCPU_ARM64_BLEND; break;
+ case CPU_SUBTYPE_ARM64_V8: pThis->enmCpu = RTLDRCPU_ARM64_V8; break;
+ case CPU_SUBTYPE_ARM64E: pThis->enmCpu = RTLDRCPU_ARM64E; break;
+ default:
+ return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
+ }
+ break;
+
+ default:
+ return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
+ }
+
+ pThis->Core.enmFormat = RTLDRFMT_MACHO;
+ switch (s.Hdr32.filetype)
+ {
+ case MH_OBJECT: pThis->Core.enmType = RTLDRTYPE_OBJECT; break;
+ case MH_EXECUTE: pThis->Core.enmType = RTLDRTYPE_EXECUTABLE_FIXED; break;
+ case MH_DYLIB: pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
+ case MH_BUNDLE: pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
+ case MH_KEXT_BUNDLE:pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
+ case MH_DSYM: pThis->Core.enmType = RTLDRTYPE_DEBUG_INFO; break;
+ default:
+ return VERR_LDRMACHO_UNSUPPORTED_FILE_TYPE;
+ }
+
+ /* RTLDRMODMACHO */
+ pThis->cSegments = cSegments;
+ pThis->pvBits = NULL;
+ pThis->pvMapping = NULL;
+ pThis->fOpenFlags = fOpenFlags;
+ pThis->Hdr = s.Hdr64;
+ if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE
+ || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ pThis->Hdr.reserved = 0;
+ pThis->LinkAddress = LinkAddress;
+ pThis->cbImage = 0;
+ pThis->fCanLoad = fCanLoad;
+ pThis->fMakeGot = fMakeGot;
+ pThis->cbJmpStub = cbJmpStub;
+ pThis->uEffFileType = uEffFileType;
+ pThis->offSymbols = 0;
+ pThis->cSymbols = 0;
+ pThis->pvaSymbols = NULL;
+ pThis->pDySymTab = NULL;
+ pThis->paRelocations = NULL;
+ pThis->pauRelocationsVirginData = NULL;
+ pThis->paidxIndirectSymbols = NULL;
+ pThis->offStrings = 0;
+ pThis->cchStrings = 0;
+ pThis->pchStrings = NULL;
+ memset(pThis->abImageUuid, 0, sizeof(pThis->abImageUuid));
+ pThis->offCodeSignature = 0;
+ pThis->cbCodeSignature = 0;
+ pThis->PtrCodeSignature.pb = NULL;
+ pThis->GotRVA = NIL_RTLDRADDR;
+ pThis->JmpStubsRVA = NIL_RTLDRADDR;
+ pThis->cSections = cSections;
+ pThis->paSections = (PRTLDRMODMACHOSECT)&pThis->aSegments[pThis->cSegments];
+
+ /*
+ * Setup the KLDRMOD segment array.
+ */
+ rc = kldrModMachOParseLoadCommands(pThis, (char *)pThis + cbModAndSegs, cbStringPool);
+
+ /*
+ * We're done.
+ */
+ return rc;
+}
+
+
+/**
+ * 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 IPRT status code.
+ * @param pbLoadCommands The load commands to parse.
+ * @param pHdr The header.
+ * @param pRdr The file reader.
+ * @param offImage The image header (FAT fun).
+ * @param fOpenFlags RTLDR_O_XXX.
+ * @param pcSegments Where to store the segment count.
+ * @param pcSections 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).
+ * @param puEffFileType Where to store the effective file type.
+ * @param pErrInfo Where to return additional error info. Optional.
+ */
+static int kldrModMachOPreParseLoadCommands(uint8_t *pbLoadCommands, const mach_header_32_t *pHdr, PRTLDRREADER pRdr,
+ RTFOFF offImage, uint32_t fOpenFlags, uint32_t *pcSegments, uint32_t *pcSections,
+ uint32_t *pcbStringPool, bool *pfCanLoad, PRTLDRADDR pLinkAddress,
+ uint8_t *puEffFileType, PRTERRINFO pErrInfo)
+{
+ union
+ {
+ uint8_t *pb;
+ load_command_t *pLoadCmd;
+ segment_command_32_t *pSeg32;
+ segment_command_64_t *pSeg64;
+ thread_command_t *pThread;
+ symtab_command_t *pSymTab;
+ dysymtab_command_t *pDySymTab;
+ uuid_command_t *pUuid;
+ } u;
+ const uint64_t cbFile = pRdr->pfnSize(pRdr) - offImage;
+ int const fConvertEndian = pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE;
+ uint32_t cSegments = 0;
+ uint32_t cSections = 0;
+ size_t cbStringPool = 0;
+ uint32_t cLeft = pHdr->ncmds;
+ uint32_t cbLeft = pHdr->sizeofcmds;
+ uint8_t *pb = pbLoadCommands;
+ int cSegmentCommands = 0;
+ int cSymbolTabs = 0;
+ uint32_t cSymbols = 0; /* Copy of u.pSymTab->nsyms. */
+ uint32_t cDySymbolTabs = 0;
+ bool fDySymbolTabWithRelocs = false;
+ uint32_t cSectionsWithRelocs = 0;
+ uint8_t uEffFileType = *puEffFileType = pHdr->filetype;
+
+ *pcSegments = 0;
+ *pcSections = 0;
+ *pcbStringPool = 0;
+ *pfCanLoad = true;
+ *pLinkAddress = ~(RTLDRADDR)0;
+
+ while (cLeft-- > 0)
+ {
+ u.pb = pb;
+
+ /*
+ * Convert and validate command header.
+ */
+ RTLDRMODMACHO_CHECK_RETURN(cbLeft >= sizeof(load_command_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ if (fConvertEndian)
+ {
+ u.pLoadCmd->cmd = RT_BSWAP_U32(u.pLoadCmd->cmd);
+ u.pLoadCmd->cmdsize = RT_BSWAP_U32(u.pLoadCmd->cmdsize);
+ }
+ RTLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize <= cbLeft, VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ cbLeft -= u.pLoadCmd->cmdsize;
+ pb += u.pLoadCmd->cmdsize;
+
+ /*
+ * Segment macros for avoiding code duplication.
+ */
+ /* Validation code shared with the 64-bit variant. */
+ #define VALIDATE_AND_ADD_SEGMENT(a_cBits) \
+ do { \
+ bool fSkipSeg = !strcmp(pSrcSeg->segname, "__DWARF") /* Note: Not for non-object files. */ \
+ || ( !strcmp(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; \
+ \
+ RTLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize == 0 \
+ || ( pSrcSeg->fileoff <= cbFile \
+ && (uint64_t)pSrcSeg->fileoff + pSrcSeg->filesize <= cbFile), \
+ VERR_LDRMACHO_BAD_LOAD_COMMAND); \
+ RTLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize <= pSrcSeg->vmsize \
+ || (fSkipSeg && !strcmp(pSrcSeg->segname, "__CTF") /* see above */), \
+ VERR_LDRMACHO_BAD_LOAD_COMMAND); \
+ RTLDRMODMACHO_CHECK_RETURN(!(~pSrcSeg->maxprot & pSrcSeg->initprot), \
+ VERR_LDRMACHO_BAD_LOAD_COMMAND); \
+ RTLDRMODMACHO_CHECK_MSG_RETURN(!(pSrcSeg->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1 | SG_READ_ONLY)), \
+ ("flags=%#x %s\n", pSrcSeg->flags, pSrcSeg->segname), \
+ VERR_LDRMACHO_BAD_LOAD_COMMAND); \
+ RTLDRMODMACHO_CHECK_RETURN( pSrcSeg->nsects * sizeof(section_##a_cBits##_t) \
+ <= u.pLoadCmd->cmdsize - sizeof(segment_command_##a_cBits##_t), \
+ VERR_LDRMACHO_BAD_LOAD_COMMAND); \
+ RTLDRMODMACHO_CHECK_RETURN( uEffFileType != MH_OBJECT \
+ || cSegmentCommands == 0 \
+ || ( cSegmentCommands == 1 \
+ && uEffFileType == MH_OBJECT \
+ && pHdr->filetype == MH_DSYM \
+ && fSkipSeg), \
+ VERR_LDRMACHO_BAD_OBJECT_FILE); \
+ cSegmentCommands++; \
+ \
+ /* Add the segment, if not object file. */ \
+ if (!fSkipSeg && uEffFileType != MH_OBJECT) \
+ { \
+ cbStringPool += RTStrNLen(&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)
+
+
+ /* Validation code shared with the 64-bit variant. */
+ #define VALIDATE_AND_ADD_SECTION(a_cBits) \
+ do { \
+ int fFileBits; \
+ \
+ /* validate */ \
+ if (uEffFileType != MH_OBJECT) \
+ RTLDRMODMACHO_CHECK_RETURN(!strcmp(pSect->segname, pSrcSeg->segname),\
+ VERR_LDRMACHO_BAD_SECTION); \
+ \
+ switch (pSect->flags & SECTION_TYPE) \
+ { \
+ case S_ZEROFILL: \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_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: \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
+ fFileBits = 1; \
+ break; \
+ \
+ case S_SYMBOL_STUBS: \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
+ /* reserved2 == stub size. 0 has been seen (corecrypto.kext) */ \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->reserved2 < 64, VERR_LDRMACHO_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) */ \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
+ Log(("ldrMachO: Can't load because of section flags: %#x\n", pSect->flags & SECTION_TYPE)); \
+ *pfCanLoad = 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) */ \
+ RTLDRMODMACHO_CHECK_RETURN(fOpenFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION), \
+ VERR_LDRMACHO_UNSUPPORTED_INIT_SECTION); \
+ RT_FALL_THRU(); \
+ case S_MOD_TERM_FUNC_POINTERS: \
+ /** @todo this requires a query API or flag... (e.g. C++ destructors) */ \
+ RTLDRMODMACHO_CHECK_RETURN(fOpenFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION), \
+ VERR_LDRMACHO_UNSUPPORTED_TERM_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
+ fFileBits = 1; \
+ break; /* ignored */ \
+ \
+ case S_LITERAL_POINTERS: \
+ case S_DTRACE_DOF: \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
+ fFileBits = 1; \
+ break; \
+ \
+ case S_INTERPOSING: \
+ case S_GB_ZEROFILL: \
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNSUPPORTED_SECTION); \
+ \
+ default: \
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNKNOWN_SECTION); \
+ } \
+ RTLDRMODMACHO_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)), \
+ VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN((pSect->flags & S_ATTR_DEBUG) == (pFirstSect->flags & S_ATTR_DEBUG), \
+ VERR_LDRMACHO_MIXED_DEBUG_SECTION_FLAGS); \
+ \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->addr - pSrcSeg->vmaddr <= pSrcSeg->vmsize, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN( pSect->addr - pSrcSeg->vmaddr + pSect->size <= pSrcSeg->vmsize \
+ || !strcmp(pSrcSeg->segname, "__CTF") /* see above */, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->align < 31, \
+ VERR_LDRMACHO_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 ( ((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSect->addr) \
+ && pSect->align == 4 \
+ && strcmp(pSect->sectname, "__unwind_info") == 0) \
+ pSect->align = 2; \
+ RTLDRMODMACHO_CHECK_RETURN(!((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSect->addr), \
+ VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(!((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSrcSeg->vmaddr), \
+ VERR_LDRMACHO_BAD_SECTION); \
+ \
+ /* Adjust the section offset before we check file offset. */ \
+ offSect = (offSect + RT_BIT_64(pSect->align) - UINT64_C(1)) & ~(RT_BIT_64(pSect->align) - UINT64_C(1)); \
+ if (pSect->addr) \
+ { \
+ RTLDRMODMACHO_CHECK_RETURN(offSect <= pSect->addr - pSrcSeg->vmaddr, VERR_LDRMACHO_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) \
+ { \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->offset == pSrcSeg->fileoff + offSect, \
+ VERR_LDRMACHO_NON_CONT_SEG_BITS); \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->offset - pSrcSeg->fileoff <= pSrcSeg->filesize, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ } \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->offset <= cbFile, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)pSect->offset + pSect->size <= cbFile, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ } \
+ else \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->offset == 0, VERR_LDRMACHO_BAD_SECTION); \
+ \
+ if (!pSect->nreloc) \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reloff, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ else \
+ { \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->reloff <= cbFile, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN( (uint64_t)pSect->reloff \
+ + (RTFOFF)pSect->nreloc * sizeof(macho_relocation_info_t) \
+ <= cbFile, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ cSectionsWithRelocs++; \
+ } \
+ \
+ /* 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) \
+ && strcmp(pSect->segname, "__DWARF")) \
+ { \
+ cbStringPool += RTStrNLen(&pSect->segname[0], sizeof(pSect->segname)) + 1; \
+ cbStringPool += RTStrNLen(&pSect->sectname[0], sizeof(pSect->sectname)) + 1; \
+ cSegments++; \
+ if (cSegments == 1) /* The link address is set by the first segment. */ \
+ *pLinkAddress = pSect->addr; \
+ } \
+ RT_FALL_THRU(); \
+ case MH_EXECUTE: \
+ case MH_DYLIB: \
+ case MH_BUNDLE: \
+ case MH_DSYM: \
+ case MH_KEXT_BUNDLE: \
+ cSections++; \
+ break; \
+ default: \
+ RTLDRMODMACHO_FAILED_RETURN(VERR_INVALID_PARAMETER); \
+ } \
+ \
+ /* Advance the section offset, since we're also aligning it. */ \
+ offSect += pSect->size; \
+ } while (0) /* VALIDATE_AND_ADD_SECTION */
+
+ /*
+ * 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;
+ uint32_t cSectionsLeft = pSrcSeg->nsects;
+ uint64_t offSect = 0;
+
+ /* Convert and verify the segment. */
+ RTLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_32_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ RTLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pHdr->magic == IMAGE_MACHO32_SIGNATURE, VERR_LDRMACHO_BIT_MIX);
+ if (fConvertEndian)
+ {
+ pSrcSeg->vmaddr = RT_BSWAP_U32(pSrcSeg->vmaddr);
+ pSrcSeg->vmsize = RT_BSWAP_U32(pSrcSeg->vmsize);
+ pSrcSeg->fileoff = RT_BSWAP_U32(pSrcSeg->fileoff);
+ pSrcSeg->filesize = RT_BSWAP_U32(pSrcSeg->filesize);
+ pSrcSeg->maxprot = RT_BSWAP_U32(pSrcSeg->maxprot);
+ pSrcSeg->initprot = RT_BSWAP_U32(pSrcSeg->initprot);
+ pSrcSeg->nsects = RT_BSWAP_U32(pSrcSeg->nsects);
+ pSrcSeg->flags = RT_BSWAP_U32(pSrcSeg->flags);
+ }
+
+ 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 = RT_BSWAP_U32(pSect->addr);
+ pSect->size = RT_BSWAP_U32(pSect->size);
+ pSect->offset = RT_BSWAP_U32(pSect->offset);
+ pSect->align = RT_BSWAP_U32(pSect->align);
+ pSect->reloff = RT_BSWAP_U32(pSect->reloff);
+ pSect->nreloc = RT_BSWAP_U32(pSect->nreloc);
+ pSect->flags = RT_BSWAP_U32(pSect->flags);
+ pSect->reserved1 = RT_BSWAP_U32(pSect->reserved1);
+ pSect->reserved2 = RT_BSWAP_U32(pSect->reserved2);
+ }
+
+ 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;
+ uint32_t cSectionsLeft = pSrcSeg->nsects;
+ uint64_t offSect = 0;
+
+ /* Convert and verify the segment. */
+ RTLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_64_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ RTLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE
+ || pHdr->magic == IMAGE_MACHO64_SIGNATURE, VERR_LDRMACHO_BIT_MIX);
+ if (fConvertEndian)
+ {
+ pSrcSeg->vmaddr = RT_BSWAP_U64(pSrcSeg->vmaddr);
+ pSrcSeg->vmsize = RT_BSWAP_U64(pSrcSeg->vmsize);
+ pSrcSeg->fileoff = RT_BSWAP_U64(pSrcSeg->fileoff);
+ pSrcSeg->filesize = RT_BSWAP_U64(pSrcSeg->filesize);
+ pSrcSeg->maxprot = RT_BSWAP_U32(pSrcSeg->maxprot);
+ pSrcSeg->initprot = RT_BSWAP_U32(pSrcSeg->initprot);
+ pSrcSeg->nsects = RT_BSWAP_U32(pSrcSeg->nsects);
+ pSrcSeg->flags = RT_BSWAP_U32(pSrcSeg->flags);
+ }
+
+ VALIDATE_AND_ADD_SEGMENT(64);
+
+ /*
+ * Convert, validate and parse the sections.
+ */
+ while (cSectionsLeft-- > 0)
+ {
+ if (fConvertEndian)
+ {
+ pSect->addr = RT_BSWAP_U64(pSect->addr);
+ pSect->size = RT_BSWAP_U64(pSect->size);
+ pSect->offset = RT_BSWAP_U32(pSect->offset);
+ pSect->align = RT_BSWAP_U32(pSect->align);
+ pSect->reloff = RT_BSWAP_U32(pSect->reloff);
+ pSect->nreloc = RT_BSWAP_U32(pSect->nreloc);
+ pSect->flags = RT_BSWAP_U32(pSect->flags);
+ pSect->reserved1 = RT_BSWAP_U32(pSect->reserved1);
+ pSect->reserved2 = RT_BSWAP_U32(pSect->reserved2);
+ }
+
+ VALIDATE_AND_ADD_SECTION(64);
+
+ /* next */
+ pSect++;
+ }
+ break;
+ } /* LC_SEGMENT_64 */
+
+
+ case LC_SYMTAB:
+ {
+ size_t cbSym;
+ if (fConvertEndian)
+ {
+ u.pSymTab->symoff = RT_BSWAP_U32(u.pSymTab->symoff);
+ u.pSymTab->nsyms = RT_BSWAP_U32(u.pSymTab->nsyms);
+ u.pSymTab->stroff = RT_BSWAP_U32(u.pSymTab->stroff);
+ u.pSymTab->strsize = RT_BSWAP_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
+ || (uint64_t)u.pSymTab->symoff + u.pSymTab->nsyms * cbSym > cbFile)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ if ( u.pSymTab->stroff >= cbFile
+ || (uint64_t)u.pSymTab->stroff + u.pSymTab->strsize > cbFile)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+
+ /* Only one object table, please. */
+ cSymbolTabs++;
+ if (cSymbolTabs != 1)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_OBJECT_FILE);
+
+ cSymbols = u.pSymTab->nsyms;
+ break;
+ }
+
+ case LC_DYSYMTAB:
+ {
+ if (pHdr->filetype == MH_OBJECT)
+ RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSet(pErrInfo, VERR_LDRMACHO_BAD_OBJECT_FILE,
+ "Not expecting LC_DYSYMTAB in MH_OBJECT"));
+ if (fConvertEndian)
+ {
+ u.pDySymTab->ilocalsym = RT_BSWAP_U32(u.pDySymTab->ilocalsym);
+ u.pDySymTab->nlocalsym = RT_BSWAP_U32(u.pDySymTab->nlocalsym);
+ u.pDySymTab->iextdefsym = RT_BSWAP_U32(u.pDySymTab->iextdefsym);
+ u.pDySymTab->nextdefsym = RT_BSWAP_U32(u.pDySymTab->nextdefsym);
+ u.pDySymTab->iundefsym = RT_BSWAP_U32(u.pDySymTab->iundefsym);
+ u.pDySymTab->nundefsym = RT_BSWAP_U32(u.pDySymTab->nundefsym);
+ u.pDySymTab->tocoff = RT_BSWAP_U32(u.pDySymTab->tocoff);
+ u.pDySymTab->ntoc = RT_BSWAP_U32(u.pDySymTab->ntoc);
+ u.pDySymTab->modtaboff = RT_BSWAP_U32(u.pDySymTab->modtaboff);
+ u.pDySymTab->nmodtab = RT_BSWAP_U32(u.pDySymTab->nmodtab);
+ u.pDySymTab->extrefsymoff = RT_BSWAP_U32(u.pDySymTab->extrefsymoff);
+ u.pDySymTab->nextrefsym = RT_BSWAP_U32(u.pDySymTab->nextrefsym);
+ u.pDySymTab->indirectsymboff = RT_BSWAP_U32(u.pDySymTab->indirectsymboff);
+ u.pDySymTab->nindirectsymb = RT_BSWAP_U32(u.pDySymTab->nindirectsymb);
+ u.pDySymTab->extreloff = RT_BSWAP_U32(u.pDySymTab->extreloff);
+ u.pDySymTab->nextrel = RT_BSWAP_U32(u.pDySymTab->nextrel);
+ u.pDySymTab->locreloff = RT_BSWAP_U32(u.pDySymTab->locreloff);
+ u.pDySymTab->nlocrel = RT_BSWAP_U32(u.pDySymTab->nlocrel);
+ }
+
+ /* verify */
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)u.pDySymTab->ilocalsym + u.pDySymTab->nlocalsym <= cSymbols,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "ilocalsym=%#x + nlocalsym=%#x vs cSymbols=%#x",
+ u.pDySymTab->ilocalsym, u.pDySymTab->nlocalsym, cSymbols));
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)u.pDySymTab->iextdefsym + u.pDySymTab->nextdefsym <= cSymbols,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "iextdefsym=%#x + nextdefsym=%#x vs cSymbols=%#x",
+ u.pDySymTab->iextdefsym, u.pDySymTab->nextdefsym, cSymbols));
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)u.pDySymTab->iundefsym + u.pDySymTab->nundefsym <= cSymbols,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "iundefsym=%#x + nundefsym=%#x vs cSymbols=%#x",
+ u.pDySymTab->iundefsym, u.pDySymTab->nundefsym, cSymbols));
+ RTLDRMODMACHO_CHECK_RETURN( (uint64_t)u.pDySymTab->tocoff + u.pDySymTab->ntoc * sizeof(dylib_table_of_contents_t)
+ <= cbFile,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "tocoff=%#x + ntoc=%#x vs cbFile=%#RX64",
+ u.pDySymTab->tocoff, u.pDySymTab->ntoc, cbFile));
+ const uint32_t cbModTabEntry = pHdr->magic == IMAGE_MACHO32_SIGNATURE
+ || pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+ ? sizeof(dylib_module_32_t) : sizeof(dylib_module_64_t);
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)u.pDySymTab->modtaboff + u.pDySymTab->nmodtab * cbModTabEntry <= cbFile,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "modtaboff=%#x + nmodtab=%#x cbModTabEntry=%#x vs cbFile=%#RX64",
+ u.pDySymTab->modtaboff, u.pDySymTab->nmodtab, cbModTabEntry, cbFile));
+ RTLDRMODMACHO_CHECK_RETURN( (uint64_t)u.pDySymTab->extrefsymoff + u.pDySymTab->nextrefsym * sizeof(dylib_reference_t)
+ <= cbFile,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "extrefsymoff=%#x + nextrefsym=%#x vs cbFile=%#RX64",
+ u.pDySymTab->extrefsymoff, u.pDySymTab->nextrefsym, cbFile));
+ RTLDRMODMACHO_CHECK_RETURN( (uint64_t)u.pDySymTab->indirectsymboff + u.pDySymTab->nindirectsymb * sizeof(uint32_t)
+ <= cbFile,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "indirectsymboff=%#x + nindirectsymb=%#x vs cbFile=%#RX64",
+ u.pDySymTab->indirectsymboff, u.pDySymTab->nindirectsymb, cbFile));
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)u.pDySymTab->extreloff + u.pDySymTab->nextrel * sizeof(macho_relocation_info_t) <= cbFile,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "extreloff=%#x + nextrel=%#x vs cbFile=%#RX64",
+ u.pDySymTab->extreloff, u.pDySymTab->nextrel, cbFile));
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)u.pDySymTab->locreloff + u.pDySymTab->nlocrel * sizeof(macho_relocation_info_t) <= cbFile,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "locreloff=%#x + nlocrel=%#x vs cbFile=%#RX64",
+ u.pDySymTab->locreloff, u.pDySymTab->nlocrel, cbFile));
+ cDySymbolTabs++;
+ fDySymbolTabWithRelocs |= (u.pDySymTab->nlocrel + u.pDySymTab->nextrel) != 0;
+ break;
+ }
+
+ case LC_THREAD:
+ case LC_UNIXTHREAD:
+ {
+ uint32_t *pu32 = (uint32_t *)(u.pb + sizeof(load_command_t));
+ uint32_t cItemsLeft = (u.pThread->cmdsize - sizeof(load_command_t)) / sizeof(uint32_t);
+ while (cItemsLeft)
+ {
+ /* convert & verify header items ([0] == flavor, [1] == uint32_t count). */
+ if (cItemsLeft < 2)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ if (fConvertEndian)
+ {
+ pu32[0] = RT_BSWAP_U32(pu32[0]);
+ pu32[1] = RT_BSWAP_U32(pu32[1]);
+ }
+ if (pu32[1] + 2 > cItemsLeft)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_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))
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ /** @todo Check anything here need converting? */
+ break;
+
+ case LC_CODE_SIGNATURE:
+ if (u.pUuid->cmdsize != sizeof(linkedit_data_command_t))
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ break;
+
+ case LC_VERSION_MIN_MACOSX:
+ case LC_VERSION_MIN_IPHONEOS:
+ if (u.pUuid->cmdsize != sizeof(version_min_command_t))
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ break;
+
+ case LC_SOURCE_VERSION: /* Harmless. It just gives a clue regarding the source code revision/version. */
+ case LC_BUILD_VERSION: /* Harmless. It just gives a clue regarding the tool/sdk versions. */
+ 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 & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
+ RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNSUPPORTED_LOAD_COMMAND,
+ "cmd=%#x", u.pLoadCmd->cmd));
+ Log(("ldrMachO: Can't load because of load command: %#x\n", u.pLoadCmd->cmd));
+ *pfCanLoad = 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:
+ RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNSUPPORTED_LOAD_COMMAND,
+ "cmd=%#x", u.pLoadCmd->cmd));
+
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNKNOWN_LOAD_COMMAND,
+ "cmd=%#x", u.pLoadCmd->cmd));
+ }
+ }
+
+ /* be strict. */
+ if (cbLeft)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+
+ RTLDRMODMACHO_CHECK_RETURN(cDySymbolTabs <= 1,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "More than one LC_DYSYMTAB command: %u", cDySymbolTabs));
+ RTLDRMODMACHO_CHECK_RETURN(!fDySymbolTabWithRelocs || cSectionsWithRelocs == 0,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "Have relocations both in sections and LC_DYSYMTAB"));
+ if (!cSegments)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_OBJECT_FILE);
+
+ switch (uEffFileType)
+ {
+ case MH_OBJECT:
+ case MH_EXECUTE:
+ RTLDRMODMACHO_CHECK_RETURN(!fDySymbolTabWithRelocs || (fOpenFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)),
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "Did not expect relocations in LC_DYSYMTAB (file type %u)", uEffFileType));
+ break;
+
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_KEXT_BUNDLE:
+ RTLDRMODMACHO_CHECK_RETURN(cDySymbolTabs > 0,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "No LC_DYSYMTAB command (file type %u)", uEffFileType));
+ RTLDRMODMACHO_CHECK_RETURN(fDySymbolTabWithRelocs || cSectionsWithRelocs == 0,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "Expected relocations in LC_DYSYMTAB (file type %u)", uEffFileType));
+ break;
+
+ case MH_DSYM:
+ break;
+ }
+
+ /*
+ * Set return values and return.
+ */
+ *pcSegments = cSegments;
+ *pcSections = cSections;
+ *pcbStringPool = (uint32_t)cbStringPool;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Parses the load commands after we've carved out the module instance.
+ *
+ * This fills in the segment table and perhaps some other properties.
+ *
+ * @returns IPRT status code.
+ * @param pThis The module.
+ * @param pbStringPool The string pool
+ * @param cbStringPool The size of the string pool.
+ */
+static int kldrModMachOParseLoadCommands(PRTLDRMODMACHO pThis, char *pbStringPool, uint32_t cbStringPool)
+{
+ union
+ {
+ const uint8_t *pb;
+ const load_command_t *pLoadCmd;
+ const segment_command_32_t *pSeg32;
+ const segment_command_64_t *pSeg64;
+ const symtab_command_t *pSymTab;
+ const uuid_command_t *pUuid;
+ const linkedit_data_command_t *pData;
+ } u;
+ uint32_t cLeft = pThis->Hdr.ncmds;
+ uint32_t cbLeft = pThis->Hdr.sizeofcmds;
+ const uint8_t *pb = pThis->pbLoadCommands;
+ PRTLDRMODMACHOSEG pDstSeg = &pThis->aSegments[0];
+ PRTLDRMODMACHOSECT pSectExtra = pThis->paSections;
+ const uint32_t cSegments = pThis->cSegments;
+ PRTLDRMODMACHOSEG pSegItr;
+ bool fFirstSeg = true;
+ RT_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;
+ uint32_t 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->SegInfo.pszName = pbStringPool; \
+ pDstSeg->SegInfo.cchName = (uint32_t)RTStrNLen(a_achName1, sizeof(a_achName1)); \
+ memcpy(pbStringPool, a_achName1, pDstSeg->SegInfo.cchName); \
+ pbStringPool += pDstSeg->SegInfo.cchName; \
+ if (a_fObjFile) \
+ { /* MH_OBJECT: Add '.sectname' - sections aren't sorted by segments. */ \
+ size_t cchName2 = RTStrNLen(a_achName2, sizeof(a_achName2)); \
+ *pbStringPool++ = '.'; \
+ memcpy(pbStringPool, a_achName2, cchName2); \
+ pbStringPool += cchName2; \
+ pDstSeg->SegInfo.cchName += (uint32_t)cchName2; \
+ } \
+ *pbStringPool++ = '\0'; \
+ pDstSeg->SegInfo.SelFlat = 0; \
+ pDstSeg->SegInfo.Sel16bit = 0; \
+ pDstSeg->SegInfo.fFlags = 0; \
+ pDstSeg->SegInfo.fProt = RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC; /** @todo fixme! */ \
+ pDstSeg->SegInfo.cb = (a_cbSeg); \
+ pDstSeg->SegInfo.Alignment = 1; /* updated while parsing sections. */ \
+ pDstSeg->SegInfo.LinkAddress = (a_SegAddr); \
+ if (a_fFileBits) \
+ { \
+ pDstSeg->SegInfo.offFile = (RTFOFF)((a_offFile) + pThis->offImage); \
+ pDstSeg->SegInfo.cbFile = (RTFOFF)(a_cbFile); \
+ } \
+ else \
+ { \
+ pDstSeg->SegInfo.offFile = -1; \
+ pDstSeg->SegInfo.cbFile = -1; \
+ } \
+ pDstSeg->SegInfo.RVA = (a_SegAddr) - pThis->LinkAddress; \
+ pDstSeg->SegInfo.cbMapped = 0; \
+ \
+ pDstSeg->iOrgSegNo = (uint32_t)(pDstSeg - &pThis->aSegments[0]); \
+ pDstSeg->cSections = 0; \
+ pDstSeg->paSections = pSectExtra; \
+ } while (0)
+
+ /* Closes the new segment - part of NEW_SEGMENT. */
+#define CLOSE_SEGMENT() \
+ do { \
+ pDstSeg->cSections = (uint32_t)(pSectExtra - pDstSeg->paSections); \
+ pDstSeg++; \
+ } while (0)
+
+
+ /* Shared with the 64-bit variant. */
+#define ADD_SEGMENT_AND_ITS_SECTIONS(a_cBits) \
+ do { \
+ bool fAddSegOuter = false; \
+ \
+ /* \
+ * Check that the segment name is unique. We couldn't do that \
+ * in the preparsing stage. \
+ */ \
+ if (pThis->uEffFileType != MH_OBJECT) \
+ for (pSegItr = &pThis->aSegments[0]; pSegItr != pDstSeg; pSegItr++) \
+ if (!strncmp(pSegItr->SegInfo.pszName, pSrcSeg->segname, sizeof(pSrcSeg->segname))) \
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_DUPLICATE_SEGMENT_NAME); \
+ \
+ /* \
+ * Create a new segment, unless we're supposed to skip this one. \
+ */ \
+ if ( pThis->uEffFileType != MH_OBJECT \
+ && (cSectionsLeft == 0 || !(pFirstSect->flags & S_ATTR_DEBUG)) \
+ && strcmp(pSrcSeg->segname, "__DWARF") \
+ && strcmp(pSrcSeg->segname, "__CTF") ) \
+ { \
+ NEW_SEGMENT(a_cBits, pSrcSeg->segname, false /*a_fObjFile*/, "" /*a_achName2*/, \
+ pSrcSeg->vmaddr, pSrcSeg->vmsize, \
+ pSrcSeg->filesize != 0, pSrcSeg->fileoff, pSrcSeg->filesize); \
+ fAddSegOuter = true; \
+ } \
+ \
+ /* \
+ * Convert and parse the sections. \
+ */ \
+ while (cSectionsLeft-- > 0) \
+ { \
+ /* New segment if object file. */ \
+ bool fAddSegInner = false; \
+ if ( pThis->uEffFileType == MH_OBJECT \
+ && !(pSect->flags & S_ATTR_DEBUG) \
+ && strcmp(pSrcSeg->segname, "__DWARF") \
+ && strcmp(pSrcSeg->segname, "__CTF") ) \
+ { \
+ Assert(!fAddSegOuter); \
+ NEW_SEGMENT(a_cBits, pSect->segname, true /*a_fObjFile*/, pSect->sectname, \
+ pSect->addr, pSect->size, \
+ pSect->offset != 0, pSect->offset, pSect->size); \
+ fAddSegInner = true; \
+ } \
+ \
+ /* Section data extract. */ \
+ pSectExtra->cb = pSect->size; \
+ pSectExtra->RVA = pSect->addr - pDstSeg->SegInfo.LinkAddress; \
+ pSectExtra->LinkAddress = pSect->addr; \
+ if (pSect->offset) \
+ pSectExtra->offFile = pSect->offset + pThis->offImage; \
+ else \
+ pSectExtra->offFile = -1; \
+ pSectExtra->cFixups = pSect->nreloc; \
+ pSectExtra->paFixups = NULL; \
+ pSectExtra->pauFixupVirginData = NULL; \
+ if (pSect->nreloc) \
+ pSectExtra->offFixups = pSect->reloff + pThis->offImage; \
+ else \
+ pSectExtra->offFixups = -1; \
+ pSectExtra->fFlags = pSect->flags; \
+ pSectExtra->iSegment = (uint32_t)(pDstSeg - &pThis->aSegments[0]); \
+ pSectExtra->pvMachoSection = pSect; \
+ \
+ /* Update the segment alignment, if we're not skipping it. */ \
+ if ( (fAddSegOuter || fAddSegInner) \
+ && pDstSeg->SegInfo.Alignment < ((RTLDRADDR)1 << pSect->align)) \
+ pDstSeg->SegInfo.Alignment = (RTLDRADDR)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(); \
+ \
+ /* Take down 'execSeg' info for signing */ \
+ if (fFirstSeg) \
+ { \
+ fFirstSeg = false; \
+ pThis->offSeg0ForCodeSign = pSrcSeg->fileoff; \
+ pThis->cbSeg0ForCodeSign = pSrcSeg->filesize; /** @todo file or vm size? */ \
+ pThis->fSeg0ForCodeSign = pSrcSeg->flags; \
+ } \
+ } 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;
+ uint32_t cSectionsLeft = pSrcSeg->nsects;
+
+ ADD_SEGMENT_AND_ITS_SECTIONS(64);
+ break;
+ }
+
+ case LC_SYMTAB:
+ switch (pThis->uEffFileType)
+ {
+ case MH_OBJECT:
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DSYM:
+ case MH_KEXT_BUNDLE:
+ pThis->offSymbols = u.pSymTab->symoff + pThis->offImage;
+ pThis->cSymbols = u.pSymTab->nsyms;
+ pThis->offStrings = u.pSymTab->stroff + pThis->offImage;
+ pThis->cchStrings = u.pSymTab->strsize;
+ break;
+ }
+ break;
+
+ case LC_DYSYMTAB:
+ pThis->pDySymTab = (dysymtab_command_t *)u.pb;
+ break;
+
+ case LC_UUID:
+ memcpy(pThis->abImageUuid, u.pUuid->uuid, sizeof(pThis->abImageUuid));
+ break;
+
+ case LC_CODE_SIGNATURE:
+ pThis->offCodeSignature = u.pData->dataoff;
+ pThis->cbCodeSignature = u.pData->datasize;
+ break;
+
+ default:
+ break;
+ } /* command switch */
+ } /* while more commands */
+
+ Assert(pDstSeg == &pThis->aSegments[cSegments - pThis->fMakeGot]);
+
+ /*
+ * Adjust mapping addresses calculating the image size.
+ */
+ {
+ bool fLoadLinkEdit = RT_BOOL(pThis->fOpenFlags & RTLDR_O_MACHO_LOAD_LINKEDIT);
+ PRTLDRMODMACHOSECT pSectExtraItr;
+ RTLDRADDR uNextRVA = 0;
+ RTLDRADDR cb;
+ uint32_t cSegmentsToAdjust = cSegments - pThis->fMakeGot;
+ uint32_t 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
+ && !strcmp(pThis->aSegments[cSegmentsToAdjust - 1].SegInfo.pszName, "__DWARF"))
+ {
+ cSegmentsToAdjust--;
+ pThis->aSegments[cSegmentsToAdjust].SegInfo.RVA = NIL_RTLDRADDR;
+ pThis->aSegments[cSegmentsToAdjust].SegInfo.cbMapped = NIL_RTLDRADDR;
+ 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 (typcially is, but not always for mach_kernel). */
+ if ( !fLoadLinkEdit
+ && cSegmentsToAdjust > 0
+ && !strcmp(pThis->aSegments[cSegmentsToAdjust - 1].SegInfo.pszName, "__LINKEDIT"))
+ {
+ cSegmentsToAdjust--;
+ pThis->aSegments[cSegmentsToAdjust].SegInfo.RVA = NIL_RTLDRADDR;
+ pThis->aSegments[cSegmentsToAdjust].SegInfo.cbMapped = NIL_RTLDRADDR;
+ continue;
+ }
+ break;
+ }
+
+ /* Adjust RVAs. */
+ c = cSegmentsToAdjust;
+ for (pDstSeg = &pThis->aSegments[0]; c-- > 0; pDstSeg++)
+ {
+ uNextRVA = RTLDR_ALIGN_ADDR(uNextRVA, pDstSeg->SegInfo.Alignment);
+ cb = pDstSeg->SegInfo.RVA - uNextRVA;
+ if (cb >= 0x00100000) /* 1MB */
+ {
+ pDstSeg->SegInfo.RVA = uNextRVA;
+ //pThis->pMod->fFlags |= KLDRMOD_FLAGS_NON_CONTIGUOUS_LINK_ADDRS;
+ }
+ uNextRVA = pDstSeg->SegInfo.RVA + pDstSeg->SegInfo.cb;
+ }
+
+ /* Calculate the cbMapping members. */
+ c = cSegmentsToAdjust;
+ for (pDstSeg = &pThis->aSegments[0]; c-- > 1; pDstSeg++)
+ {
+
+ cb = pDstSeg[1].SegInfo.RVA - pDstSeg->SegInfo.RVA;
+ pDstSeg->SegInfo.cbMapped = (size_t)cb == cb ? (size_t)cb : ~(size_t)0;
+ }
+
+ cb = RTLDR_ALIGN_ADDR(pDstSeg->SegInfo.cb, pDstSeg->SegInfo.Alignment);
+ pDstSeg->SegInfo.cbMapped = (size_t)cb == cb ? (size_t)cb : ~(size_t)0;
+
+ /* Set the image size. */
+ pThis->cbImage = pDstSeg->SegInfo.RVA + cb;
+
+ /* Fixup the section RVAs (internal). */
+ c = cSegmentsToAdjust;
+ uNextRVA = pThis->cbImage;
+ pDstSeg = &pThis->aSegments[0];
+ for (pSectExtraItr = pThis->paSections; pSectExtraItr != pSectExtra; pSectExtraItr++)
+ {
+ if (pSectExtraItr->iSegment < c)
+ pSectExtraItr->RVA += pDstSeg[pSectExtraItr->iSegment].SegInfo.RVA;
+ else
+ {
+ pSectExtraItr->RVA = uNextRVA;
+ uNextRVA += RTLDR_ALIGN_ADDR(pSectExtraItr->cb, 64);
+ }
+ }
+ }
+
+ /*
+ * Make the GOT segment if necessary.
+ */
+ if (pThis->fMakeGot)
+ {
+ uint32_t cbPtr = ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ ? sizeof(uint32_t)
+ : sizeof(uint64_t);
+ uint32_t cbGot = pThis->cSymbols * cbPtr;
+ uint32_t cbJmpStubs;
+
+ pThis->GotRVA = pThis->cbImage;
+
+ if (pThis->cbJmpStub)
+ {
+ cbGot = RT_ALIGN_Z(cbGot, 64);
+ pThis->JmpStubsRVA = pThis->GotRVA + cbGot;
+ cbJmpStubs = pThis->cbJmpStub * pThis->cSymbols;
+ }
+ else
+ {
+ pThis->JmpStubsRVA = NIL_RTLDRADDR;
+ cbJmpStubs = 0;
+ }
+
+ pDstSeg = &pThis->aSegments[cSegments - 1];
+ pDstSeg->SegInfo.pszName = "GOT";
+ pDstSeg->SegInfo.cchName = 3;
+ pDstSeg->SegInfo.SelFlat = 0;
+ pDstSeg->SegInfo.Sel16bit = 0;
+ pDstSeg->SegInfo.fFlags = 0;
+ pDstSeg->SegInfo.fProt = RTMEM_PROT_READ;
+ pDstSeg->SegInfo.cb = cbGot + cbJmpStubs;
+ pDstSeg->SegInfo.Alignment = 64;
+ pDstSeg->SegInfo.LinkAddress = pThis->LinkAddress + pThis->GotRVA;
+ pDstSeg->SegInfo.offFile = -1;
+ pDstSeg->SegInfo.cbFile = -1;
+ pDstSeg->SegInfo.RVA = pThis->GotRVA;
+ pDstSeg->SegInfo.cbMapped = (size_t)RTLDR_ALIGN_ADDR(cbGot + cbJmpStubs, pDstSeg->SegInfo.Alignment);
+
+ pDstSeg->iOrgSegNo = UINT32_MAX;
+ pDstSeg->cSections = 0;
+ pDstSeg->paSections = NULL;
+
+ pThis->cbImage += pDstSeg->SegInfo.cbMapped;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtldrMachO_Close(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ RTLDRMODMACHO_ASSERT(!pThis->pvMapping);
+
+ uint32_t i = pThis->cSegments;
+ while (i-- > 0)
+ {
+ uint32_t j = pThis->aSegments[i].cSections;
+ while (j-- > 0)
+ {
+ RTMemFree(pThis->aSegments[i].paSections[j].paFixups);
+ pThis->aSegments[i].paSections[j].paFixups = NULL;
+ RTMemFree(pThis->aSegments[i].paSections[j].pauFixupVirginData);
+ pThis->aSegments[i].paSections[j].pauFixupVirginData = NULL;
+ }
+ }
+
+ RTMemFree(pThis->pbLoadCommands);
+ pThis->pbLoadCommands = NULL;
+ RTMemFree(pThis->pchStrings);
+ pThis->pchStrings = NULL;
+ RTMemFree(pThis->pvaSymbols);
+ pThis->pvaSymbols = NULL;
+ RTMemFree(pThis->paidxIndirectSymbols);
+ pThis->paidxIndirectSymbols = NULL;
+ RTMemFree(pThis->paRelocations);
+ pThis->paRelocations = NULL;
+ RTMemFree(pThis->pauRelocationsVirginData);
+ pThis->pauRelocationsVirginData = NULL;
+ RTMemFree(pThis->PtrCodeSignature.pb);
+ pThis->PtrCodeSignature.pb = NULL;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Gets the right base address.
+ *
+ * @param pThis The interpreter module instance
+ * @param pBaseAddress The base address, IN & OUT. Optional.
+ */
+static void kldrModMachOAdjustBaseAddress(PRTLDRMODMACHO pThis, PRTLDRADDR pBaseAddress)
+{
+ /*
+ * Adjust the base address.
+ */
+ if (*pBaseAddress == RTLDR_BASEADDRESS_LINK)
+ *pBaseAddress = pThis->LinkAddress;
+}
+
+
+/**
+ * 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 VINF_SUCCESS or VERR_SYMBOL_NOT_FOUND.
+ * @param pThis The interpreter 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(PRTLDRMODMACHO pThis, const char *pchSymbol, size_t cchSymbol,
+ RTLDRADDR BaseAddress, PRTLDRADDR puValue)
+{
+ /*
+ * Match possible name prefixes.
+ */
+ static const struct
+ {
+ const char *pszPrefix;
+ uint32_t cchPrefix;
+ bool fSection;
+ bool fStart;
+ } s_aPrefixes[] =
+ {
+ { "section$start$", (uint8_t)sizeof("section$start$") - 1, true, true },
+ { "section$end$", (uint8_t)sizeof("section$end$") - 1, true, false},
+ { "segment$start$", (uint8_t)sizeof("segment$start$") - 1, false, true },
+ { "segment$end$", (uint8_t)sizeof("segment$end$") - 1, false, false},
+ };
+ size_t cchSectName = 0;
+ const char *pchSectName = "";
+ size_t cchSegName = 0;
+ const char *pchSegName = NULL;
+ uint32_t iPrefix = RT_ELEMENTS(s_aPrefixes) - 1;
+ uint32_t iSeg;
+ RTLDRADDR uValue;
+
+ for (;;)
+ {
+ uint8_t const cchPrefix = s_aPrefixes[iPrefix].cchPrefix;
+ if ( cchSymbol > cchPrefix
+ && strncmp(pchSymbol, s_aPrefixes[iPrefix].pszPrefix, cchPrefix) == 0)
+ {
+ pchSegName = pchSymbol + cchPrefix;
+ cchSegName = cchSymbol - cchPrefix;
+ break;
+ }
+
+ /* next */
+ if (!iPrefix)
+ return VERR_SYMBOL_NOT_FOUND;
+ iPrefix--;
+ }
+
+ /*
+ * Split the remainder into segment and section name, if necessary.
+ */
+ if (s_aPrefixes[iPrefix].fSection)
+ {
+ pchSectName = (const char *)memchr(pchSegName, '$', cchSegName);
+ if (!pchSectName)
+ return VERR_SYMBOL_NOT_FOUND;
+ cchSegName = pchSectName - pchSegName;
+ pchSectName++;
+ cchSectName = cchSymbol - (pchSectName - pchSymbol);
+ }
+
+ /*
+ * Locate the segment.
+ */
+ if (!pThis->cSegments)
+ return VERR_SYMBOL_NOT_FOUND;
+ for (iSeg = 0; iSeg < pThis->cSegments; iSeg++)
+ {
+ if ( pThis->aSegments[iSeg].SegInfo.cchName >= cchSegName
+ && memcmp(pThis->aSegments[iSeg].SegInfo.pszName, pchSegName, cchSegName) == 0)
+ {
+ section_32_t const *pSect;
+ if ( pThis->aSegments[iSeg].SegInfo.cchName == cchSegName
+ && pThis->Hdr.filetype != MH_OBJECT /* Good enough for __DWARF segs in MH_DHSYM, I hope. */)
+ break;
+
+ pSect = (section_32_t *)pThis->aSegments[iSeg].paSections[0].pvMachoSection;
+ if ( pThis->uEffFileType == MH_OBJECT
+ && pThis->aSegments[iSeg].SegInfo.cchName > cchSegName + 1
+ && pThis->aSegments[iSeg].SegInfo.pszName[cchSegName] == '.'
+ && strncmp(&pThis->aSegments[iSeg].SegInfo.pszName[cchSegName + 1], pSect->sectname, sizeof(pSect->sectname)) == 0
+ && pThis->aSegments[iSeg].SegInfo.cchName - cchSegName - 1 <= sizeof(pSect->sectname) )
+ break;
+ }
+ }
+ if (iSeg >= pThis->cSegments)
+ return VERR_SYMBOL_NOT_FOUND;
+
+ if (!s_aPrefixes[iPrefix].fSection)
+ {
+ /*
+ * Calculate the segment start/end address.
+ */
+ uValue = pThis->aSegments[iSeg].SegInfo.RVA;
+ if (!s_aPrefixes[iPrefix].fStart)
+ uValue += pThis->aSegments[iSeg].SegInfo.cb;
+ }
+ else
+ {
+ /*
+ * Locate the section.
+ */
+ uint32_t iSect = pThis->aSegments[iSeg].cSections;
+ if (!iSect)
+ return VERR_SYMBOL_NOT_FOUND;
+ for (;;)
+ {
+ section_32_t *pSect = (section_32_t *)pThis->aSegments[iSeg].paSections[iSect].pvMachoSection;
+ if ( cchSectName <= sizeof(pSect->sectname)
+ && memcmp(pSect->sectname, pchSectName, cchSectName) == 0
+ && ( cchSectName == sizeof(pSect->sectname)
+ || pSect->sectname[cchSectName] == '\0') )
+ break;
+ /* next */
+ if (!iSect)
+ return VERR_SYMBOL_NOT_FOUND;
+ iSect--;
+ }
+
+ uValue = pThis->aSegments[iSeg].paSections[iSect].RVA;
+ if (!s_aPrefixes[iPrefix].fStart)
+ uValue += pThis->aSegments[iSeg].paSections[iSect].cb;
+ }
+
+ /*
+ * Convert from RVA to load address.
+ */
+ uValue += BaseAddress;
+ if (puValue)
+ *puValue = uValue;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnGetSymbolEx}
+ */
+static DECLCALLBACK(int) rtldrMachO_GetSymbolEx(PRTLDRMODINTERNAL pMod, const void *pvBits, RTUINTPTR BaseAddress,
+ uint32_t iOrdinal, const char *pszSymbol, RTUINTPTR *pValue)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ RT_NOREF(pvBits);
+ //RT_NOREF(pszVersion);
+ //RT_NOREF(pfnGetForwarder);
+ //RT_NOREF(pvUser);
+ uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
+ uint32_t *pfKind = &fKind;
+ size_t cchSymbol = pszSymbol ? strlen(pszSymbol) : 0;
+
+ /*
+ * Resolve defaults.
+ */
+ kldrModMachOAdjustBaseAddress(pThis, &BaseAddress);
+
+ /*
+ * Refuse segmented requests for now.
+ */
+ RTLDRMODMACHO_CHECK_RETURN( !pfKind
+ || (*pfKind & RTLDRSYMKIND_REQ_TYPE_MASK) == RTLDRSYMKIND_REQ_FLAT,
+ VERR_LDRMACHO_TODO);
+
+ /*
+ * Take action according to file type.
+ */
+ int rc;
+ if ( pThis->Hdr.filetype == MH_OBJECT
+ || pThis->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
+ || pThis->Hdr.filetype == MH_DYLIB
+ || pThis->Hdr.filetype == MH_BUNDLE
+ || pThis->Hdr.filetype == MH_DSYM
+ || pThis->Hdr.filetype == MH_KEXT_BUNDLE)
+ {
+ rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ rc = kldrModMachODoQuerySymbol32Bit(pThis, (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols,
+ pThis->pchStrings, pThis->cchStrings, BaseAddress, iOrdinal, pszSymbol,
+ (uint32_t)cchSymbol, pValue, pfKind);
+ else
+ rc = kldrModMachODoQuerySymbol64Bit(pThis, (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols,
+ pThis->pchStrings, pThis->cchStrings, BaseAddress, iOrdinal, pszSymbol,
+ (uint32_t)cchSymbol, pValue, 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 == VERR_SYMBOL_NOT_FOUND
+ && cchSymbol > sizeof("section$end$") - 1
+ && ( pszSymbol[0] == 's'
+ || (pszSymbol[1] == 's' && pszSymbol[0] == '_') )
+ && memchr(pszSymbol, '$', cchSymbol) )
+ {
+ if (pszSymbol[0] == '_')
+ rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol + 1, cchSymbol - 1, BaseAddress, pValue);
+ else
+ rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, pValue);
+ }
+ }
+ else
+ rc = VERR_LDRMACHO_TODO;
+
+ return rc;
+}
+
+
+/**
+ * Lookup a symbol in a 32-bit symbol table.
+ *
+ * @returns IPRT status code.
+ * @param pThis
+ * @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(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
+ const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol,
+ const char *pchSymbol, uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind)
+{
+ /*
+ * Find a valid symbol matching the search criteria.
+ */
+ if (iSymbol == UINT32_MAX)
+ {
+ /* simplify validation. */
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (cchStrings <= cchSymbol + 1)
+ return VERR_SYMBOL_NOT_FOUND;
+ cchStrings -= cchSymbol + 1;
+
+ /* external symbols are usually at the end, so search the other way. */
+ for (iSymbol = cSyms - 1; iSymbol != UINT32_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 ((uint32_t)paSyms[iSymbol].n_un.n_strx >= cchStrings)
+ continue;
+ psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
+ if (psz[cchSymbol + 1])
+ continue;
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (*psz != '_' || memcmp(psz + 1, pchSymbol, cchSymbol))
+ continue;
+
+ /* match! */
+ break;
+ }
+ if (iSymbol == UINT32_MAX)
+ return VERR_SYMBOL_NOT_FOUND;
+ }
+ else
+ {
+ if (iSymbol >= cSyms)
+ return VERR_SYMBOL_NOT_FOUND;
+ if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+ return VERR_SYMBOL_NOT_FOUND;
+ if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ return VERR_SYMBOL_NOT_FOUND;
+ }
+
+ /*
+ * Calc the return values.
+ */
+ if (pfKind)
+ {
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ *pfKind = RTLDRSYMKIND_32BIT | RTLDRSYMKIND_NO_TYPE;
+ else
+ *pfKind = RTLDRSYMKIND_64BIT | RTLDRSYMKIND_NO_TYPE;
+ if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
+ *pfKind |= RTLDRSYMKIND_WEAK;
+ }
+
+ switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSect;
+ RTLDRADDR offSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSymbol].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSect = &pThis->paSections[paSyms[iSymbol].n_sect - 1];
+
+ offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
+ RTLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
+ || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
+ && offSect == 0U - pSect->RVA
+ && pThis->uEffFileType != MH_OBJECT),
+ VERR_LDRMACHO_BAD_SYMBOL);
+ if (puValue)
+ *puValue = BaseAddress + pSect->RVA + offSect;
+
+ if ( pfKind
+ && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
+ *pfKind = (*pfKind & ~RTLDRSYMKIND_TYPE_MASK) | RTLDRSYMKIND_CODE;
+ break;
+ }
+
+ case MACHO_N_ABS:
+ if (puValue)
+ *puValue = paSyms[iSymbol].n_value;
+ /*if (pfKind)
+ pfKind |= RTLDRSYMKIND_ABS;*/
+ break;
+
+ case MACHO_N_PBUD:
+ case MACHO_N_INDR:
+ /** @todo implement indirect and prebound symbols. */
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Lookup a symbol in a 64-bit symbol table.
+ *
+ * @returns IPRT status code.
+ * @param pThis
+ * @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(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
+ const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol,
+ const char *pchSymbol, uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind)
+{
+ /*
+ * Find a valid symbol matching the search criteria.
+ */
+ if (iSymbol == UINT32_MAX)
+ {
+ /* simplify validation. */
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (cchStrings <= cchSymbol + 1)
+ return VERR_SYMBOL_NOT_FOUND;
+ cchStrings -= cchSymbol + 1;
+
+ /* external symbols are usually at the end, so search the other way. */
+ for (iSymbol = cSyms - 1; iSymbol != UINT32_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 ((uint32_t)paSyms[iSymbol].n_un.n_strx >= cchStrings)
+ continue;
+ psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
+ if (psz[cchSymbol + 1])
+ continue;
+ if (*psz != '_' || memcmp(psz + 1, pchSymbol, cchSymbol))
+ continue;
+
+ /* match! */
+ break;
+ }
+ if (iSymbol == UINT32_MAX)
+ return VERR_SYMBOL_NOT_FOUND;
+ }
+ else
+ {
+ if (iSymbol >= cSyms)
+ return VERR_SYMBOL_NOT_FOUND;
+ if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+ return VERR_SYMBOL_NOT_FOUND;
+ if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ return VERR_SYMBOL_NOT_FOUND;
+ }
+
+ /*
+ * Calc the return values.
+ */
+ if (pfKind)
+ {
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ *pfKind = RTLDRSYMKIND_32BIT | RTLDRSYMKIND_NO_TYPE;
+ else
+ *pfKind = RTLDRSYMKIND_64BIT | RTLDRSYMKIND_NO_TYPE;
+ if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
+ *pfKind |= RTLDRSYMKIND_WEAK;
+ }
+
+ switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSect;
+ RTLDRADDR offSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSymbol].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSect = &pThis->paSections[paSyms[iSymbol].n_sect - 1];
+
+ offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
+ RTLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
+ || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
+ && offSect == 0U - pSect->RVA
+ && pThis->uEffFileType != MH_OBJECT),
+ VERR_LDRMACHO_BAD_SYMBOL);
+ if (puValue)
+ *puValue = BaseAddress + pSect->RVA + offSect;
+
+ if ( pfKind
+ && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
+ *pfKind = (*pfKind & ~RTLDRSYMKIND_TYPE_MASK) | RTLDRSYMKIND_CODE;
+ break;
+ }
+
+ case MACHO_N_ABS:
+ if (puValue)
+ *puValue = paSyms[iSymbol].n_value;
+ /*if (pfKind)
+ pfKind |= RTLDRSYMKIND_ABS;*/
+ break;
+
+ case MACHO_N_PBUD:
+ case MACHO_N_INDR:
+ /** @todo implement indirect and prebound symbols. */
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnEnumSymbols}
+ */
+static DECLCALLBACK(int) rtldrMachO_EnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits,
+ RTUINTPTR BaseAddress, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ RT_NOREF(pvBits);
+
+ /*
+ * Resolve defaults.
+ */
+ kldrModMachOAdjustBaseAddress(pThis, &BaseAddress);
+
+ /*
+ * Take action according to file type.
+ */
+ int rc;
+ if ( pThis->Hdr.filetype == MH_OBJECT
+ || pThis->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
+ || pThis->Hdr.filetype == MH_DYLIB
+ || pThis->Hdr.filetype == MH_BUNDLE
+ || pThis->Hdr.filetype == MH_DSYM
+ || pThis->Hdr.filetype == MH_KEXT_BUNDLE)
+ {
+ rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ rc = kldrModMachODoEnumSymbols32Bit(pThis, (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols,
+ pThis->pchStrings, pThis->cchStrings, BaseAddress,
+ fFlags, pfnCallback, pvUser);
+ else
+ rc = kldrModMachODoEnumSymbols64Bit(pThis, (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols,
+ pThis->pchStrings, pThis->cchStrings, BaseAddress,
+ fFlags, pfnCallback, pvUser);
+ }
+ }
+ else
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+
+ return rc;
+}
+
+
+/**
+ * Enum a 32-bit symbol table.
+ *
+ * @returns IPRT status code.
+ * @param pThis
+ * @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(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
+ const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
+ uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ const uint32_t fKindBase = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ ? RTLDRSYMKIND_32BIT : RTLDRSYMKIND_64BIT;
+ uint32_t iSym;
+ int rc;
+
+ /*
+ * Iterate the symbol table.
+ */
+ for (iSym = 0; iSym < cSyms; iSym++)
+ {
+ uint32_t fKind;
+ RTLDRADDR uValue;
+ const char *psz;
+ size_t 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 & RTLDR_ENUM_SYMBOL_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 */
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
+ psz = &pchStrings[paSyms[iSym].n_un.n_strx];
+ cch = strlen(psz);
+ if (!cch)
+ psz = NULL;
+
+ /* kind & value */
+ fKind = fKindBase;
+ if (paSyms[iSym].n_desc & N_WEAK_DEF)
+ fKind |= RTLDRSYMKIND_WEAK;
+ switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSym].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
+
+ uValue = paSyms[iSym].n_value - pSect->LinkAddress;
+ RTLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
+ || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
+ && uValue == 0U - pSect->RVA
+ && pThis->uEffFileType != MH_OBJECT),
+ VERR_LDRMACHO_BAD_SYMBOL);
+ uValue += BaseAddress + pSect->RVA;
+
+ if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
+ fKind |= RTLDRSYMKIND_CODE;
+ else
+ fKind |= RTLDRSYMKIND_NO_TYPE;
+ break;
+ }
+
+ case MACHO_N_ABS:
+ uValue = paSyms[iSym].n_value;
+ fKind |= RTLDRSYMKIND_NO_TYPE /*RTLDRSYMKIND_ABS*/;
+ break;
+
+ case MACHO_N_PBUD:
+ case MACHO_N_INDR:
+ /** @todo implement indirect and prebound symbols. */
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ }
+
+ /*
+ * Do callback.
+ */
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (cch > 1 && *psz == '_')
+ psz++;
+ rc = pfnCallback(&pThis->Core, psz, iSym, uValue/*, fKind*/, pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Enum a 64-bit symbol table.
+ *
+ * @returns IPRT status code.
+ * @param pThis
+ * @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(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
+ const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
+ uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ const uint32_t fKindBase = pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE
+ ? RTLDRSYMKIND_64BIT : RTLDRSYMKIND_32BIT;
+ uint32_t iSym;
+ int rc;
+
+ /*
+ * Iterate the symbol table.
+ */
+ for (iSym = 0; iSym < cSyms; iSym++)
+ {
+ uint32_t fKind;
+ RTLDRADDR uValue;
+ const char *psz;
+ size_t 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 & RTLDR_ENUM_SYMBOL_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 */
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
+ psz = &pchStrings[paSyms[iSym].n_un.n_strx];
+ cch = strlen(psz);
+ if (!cch)
+ psz = NULL;
+
+ /* kind & value */
+ fKind = fKindBase;
+ if (paSyms[iSym].n_desc & N_WEAK_DEF)
+ fKind |= RTLDRSYMKIND_WEAK;
+ switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSym].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
+
+ uValue = paSyms[iSym].n_value - pSect->LinkAddress;
+ RTLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
+ || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
+ && uValue == 0U - pSect->RVA
+ && pThis->uEffFileType != MH_OBJECT),
+ VERR_LDRMACHO_BAD_SYMBOL);
+ uValue += BaseAddress + pSect->RVA;
+
+ if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
+ fKind |= RTLDRSYMKIND_CODE;
+ else
+ fKind |= RTLDRSYMKIND_NO_TYPE;
+ break;
+ }
+
+ case MACHO_N_ABS:
+ uValue = paSyms[iSym].n_value;
+ fKind |= RTLDRSYMKIND_NO_TYPE /*RTLDRSYMKIND_ABS*/;
+ break;
+
+ case MACHO_N_PBUD:
+ case MACHO_N_INDR:
+ /** @todo implement indirect and prebound symbols. */
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ }
+
+ /*
+ * Do callback.
+ */
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (cch > 1 && *psz == '_')
+ psz++;
+ rc = pfnCallback(&pThis->Core, psz, iSym, uValue/*, fKind*/, pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+#if 0
+
+/** @copydoc kLdrModGetImport */
+static int kldrModMachOGetImport(PRTLDRMODINTERNAL pMod, const void *pvBits, uint32_t iImport, char *pszName, size_t cchName)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ RT_NOREF(pvBits);
+ RT_NOREF(iImport);
+ RT_NOREF(pszName);
+ RT_NOREF(cchName);
+
+ if (pThis->Hdr.filetype == MH_OBJECT)
+ return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+
+ /* later */
+ return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+}
+
+
+
+/** @copydoc kLdrModNumberOfImports */
+static int32_t kldrModMachONumberOfImports(PRTLDRMODINTERNAL pMod, const void *pvBits)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ RT_NOREF(pvBits);
+
+ if (pThis->Hdr.filetype == MH_OBJECT)
+ return VINF_SUCCESS;
+
+ /* later */
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModGetStackInfo */
+static int kldrModMachOGetStackInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
+{
+ /*PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);*/
+ RT_NOREF(pMod);
+ RT_NOREF(pvBits);
+ RT_NOREF(BaseAddress);
+
+ pStackInfo->Address = NIL_RTLDRADDR;
+ pStackInfo->LinkAddress = NIL_RTLDRADDR;
+ pStackInfo->cbStack = pStackInfo->cbStackThread = 0;
+ /* later */
+
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModQueryMainEntrypoint */
+static int kldrModMachOQueryMainEntrypoint(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PRTLDRADDR pMainEPAddress)
+{
+#if 0
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ int rc;
+
+ /*
+ * Resolve base address alias if any.
+ */
+ rc = kldrModMachOBitsAndBaseAddress(pThis, NULL, &BaseAddress);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Convert the address from the header.
+ */
+ *pMainEPAddress = pThis->Hdrs.OptionalHeader.AddressOfEntryPoint
+ ? BaseAddress + pThis->Hdrs.OptionalHeader.AddressOfEntryPoint
+ : NIL_RTLDRADDR;
+#else
+ *pMainEPAddress = NIL_RTLDRADDR;
+ RT_NOREF(pvBits);
+ RT_NOREF(BaseAddress);
+ RT_NOREF(pMod);
+#endif
+ return VINF_SUCCESS;
+}
+
+#endif
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnEnumDbgInfo}
+ */
+static DECLCALLBACK(int) rtldrMachO_EnumDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, PFNRTLDRENUMDBG pfnCallback, void *pvUser)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ int rc = VINF_SUCCESS;
+ uint32_t iSect;
+ RT_NOREF(pvBits);
+
+ for (iSect = 0; iSect < pThis->cSections; iSect++)
+ {
+ /* (32-bit & 64-bit starts the same way) */
+ section_32_t *pMachOSect = (section_32_t *)pThis->paSections[iSect].pvMachoSection;
+ char szTmp[sizeof(pMachOSect->sectname) + 1];
+
+ if (strcmp(pMachOSect->segname, "__DWARF"))
+ continue;
+
+ memcpy(szTmp, pMachOSect->sectname, sizeof(pMachOSect->sectname));
+ szTmp[sizeof(pMachOSect->sectname)] = '\0';
+
+ RTLDRDBGINFO DbgInfo;
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF;
+ DbgInfo.iDbgInfo = iSect;
+ DbgInfo.LinkAddress = pThis->paSections[iSect].LinkAddress;
+ DbgInfo.cb = pThis->paSections[iSect].cb;
+ DbgInfo.pszExtFile = NULL;
+ DbgInfo.u.Dwarf.pszSection = szTmp;
+ rc = pfnCallback(&pThis->Core, &DbgInfo, pvUser);
+ if (rc != VINF_SUCCESS)
+ break;
+ }
+
+ return rc;
+}
+
+#if 0
+
+/** @copydoc kLdrModHasDbgInfo */
+static int kldrModMachOHasDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits)
+{
+ /*PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);*/
+
+#if 0
+ /*
+ * Base this entirely on the presence of a debug directory.
+ */
+ if ( pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size
+ < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
+ || !pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
+ return KLDR_ERR_NO_DEBUG_INFO;
+ return VINF_SUCCESS;
+#else
+ RT_NOREF(pMod);
+ RT_NOREF(pvBits);
+ return VERR_LDR_NO_DEBUG_INFO;
+#endif
+}
+
+
+/** @copydoc kLdrModMap */
+static int kldrModMachOMap(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ unsigned fFixed;
+ uint32_t i;
+ void *pvBase;
+ int rc;
+
+ if (!pThis->fCanLoad)
+ return VERR_LDRMACHO_TODO;
+
+ /*
+ * Already mapped?
+ */
+ if (pThis->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 *)(uintptr_t)pMod->aSegments[0].LinkAddress;
+ if ((uintptr_t)pvBase != pMod->aSegments[0].LinkAddress)
+ return VERR_LDR_ADDRESS_OVERFLOW;
+ }
+
+ /* try do the prepare */
+ rc = kRdrMap(pMod->pRdr, &pvBase, pMod->cSegments, pMod->aSegments, fFixed);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Update the segments with their map addresses.
+ */
+ for (i = 0; i < pMod->cSegments; i++)
+ {
+ if (pMod->aSegments[i].RVA != NIL_RTLDRADDR)
+ pMod->aSegments[i].MapAddress = (uintptr_t)pvBase + (uintptr_t)pMod->aSegments[i].RVA;
+ }
+ pThis->pvMapping = pvBase;
+
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModUnmap */
+static int kldrModMachOUnmap(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ uint32_t i;
+ int rc;
+
+ /*
+ * Mapped?
+ */
+ if (!pThis->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /*
+ * Try unmap the image.
+ */
+ rc = kRdrUnmap(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Update the segments to reflect that they aren't mapped any longer.
+ */
+ pThis->pvMapping = NULL;
+ for (i = 0; i < pMod->cSegments; i++)
+ pMod->aSegments[i].MapAddress = 0;
+
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModAllocTLS */
+static int kldrModMachOAllocTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+
+ /*
+ * Mapped?
+ */
+ if ( pvMapping == KLDRMOD_INT_MAP
+ && !pThis->pvMapping )
+ return KLDR_ERR_NOT_MAPPED;
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModFreeTLS */
+static void kldrModMachOFreeTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
+{
+ RT_NOREF(pMod);
+ RT_NOREF(pvMapping);
+}
+
+
+
+/** @copydoc kLdrModReload */
+static int kldrModMachOReload(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+
+ /*
+ * Mapped?
+ */
+ if (!pThis->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /* the file provider does it all */
+ return kRdrRefresh(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
+}
+
+
+/** @copydoc kLdrModFixupMapping */
+static int kldrModMachOFixupMapping(PRTLDRMODINTERNAL pMod, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ int rc, rc2;
+
+ /*
+ * Mapped?
+ */
+ if (!pThis->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /*
+ * Before doing anything we'll have to make all pages writable.
+ */
+ rc = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Resolve imports and apply base relocations.
+ */
+ rc = rtldrMachO_RelocateBits(pMod, pThis->pvMapping, (uintptr_t)pThis->pvMapping, pThis->LinkAddress,
+ pfnGetImport, pvUser);
+
+ /*
+ * Restore protection.
+ */
+ rc2 = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */);
+ if (RT_SUCCESS(rc) && RT_FAILURE(rc2)
+ rc = rc2;
+ return rc;
+}
+
+#endif
+
+
+/**
+ * Worker for resolving an undefined 32-bit symbol table entry.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pSym The symbol table entry.
+ * @param BaseAddress The module base address.
+ * @param pfnGetImport The callback for resolving an imported symbol.
+ * @param pvUser User argument to the callback.
+ */
+DECLINLINE(int) rtdlrModMachOHandleUndefinedSymbol32(PRTLDRMODMACHO pThis, macho_nlist_32_t *pSym,
+ RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ RTLDRADDR Value = NIL_RTLDRADDR;
+
+ /** @todo Implement N_REF_TO_WEAK. */
+ RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
+
+ /* Get the symbol name. */
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
+ const char *pszSymbol = &pThis->pchStrings[pSym->n_un.n_strx];
+ size_t cchSymbol = strlen(pszSymbol);
+
+ /* Check for linker defined symbols relating to sections and segments. */
+ int rc;
+ if ( cchSymbol <= sizeof("section$end$") - 1
+ || *pszSymbol != 's'
+ || memchr(pszSymbol, '$', cchSymbol) == NULL)
+ rc = VERR_SYMBOL_NOT_FOUND;
+ else
+ rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
+
+ /* Ask the user for an address to the symbol. */
+ //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (RT_FAILURE_NP(rc))
+ rc = pfnGetImport(&pThis->Core, NULL /*pszModule*/, pszSymbol + (pszSymbol[0] == '_'),
+ UINT32_MAX, &Value/*, &fKind*/, pvUser);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ /* If weak reference we can continue, otherwise fail? */
+ else if (pSym->n_desc & N_WEAK_REF)
+ Value = 0;
+ else
+ return rc;
+
+ /* Update the symbol. */
+ pSym->n_value = (uint32_t)Value;
+ if (pSym->n_value == Value)
+ return VINF_SUCCESS;
+ return VERR_LDR_ADDRESS_OVERFLOW;
+}
+
+
+/**
+ * Worker for resolving an undefined 64-bit symbol table entry.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pSym The symbol table entry.
+ * @param BaseAddress The module base address.
+ * @param pfnGetImport The callback for resolving an imported symbol.
+ * @param pvUser User argument to the callback.
+ */
+DECLINLINE(int) rtdlrModMachOHandleUndefinedSymbol64(PRTLDRMODMACHO pThis, macho_nlist_64_t *pSym,
+ RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ RTLDRADDR Value = NIL_RTLDRADDR;
+
+ /** @todo Implement N_REF_TO_WEAK. */
+ RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
+
+ /* Get the symbol name. */
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
+ const char *pszSymbol = &pThis->pchStrings[pSym->n_un.n_strx];
+ size_t cchSymbol = strlen(pszSymbol);
+
+ /* Check for linker defined symbols relating to sections and segments. */
+ int rc;
+ if ( cchSymbol <= sizeof("section$end$") - 1
+ || *pszSymbol != 's'
+ || memchr(pszSymbol, '$', cchSymbol) == NULL)
+ rc = VERR_SYMBOL_NOT_FOUND;
+ else
+ rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
+
+ /* Ask the user for an address to the symbol. */
+ //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (RT_FAILURE_NP(rc))
+ rc = pfnGetImport(&pThis->Core, NULL /*pszModule*/, pszSymbol + (pszSymbol[0] == '_'),
+ UINT32_MAX, &Value/*, &fKind*/, pvUser);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ /* If weak reference we can continue, otherwise fail? */
+ else if (pSym->n_desc & N_WEAK_REF)
+ Value = 0;
+ else
+ return rc;
+
+ /* Update the symbol. */
+ pSym->n_value = (uint64_t)Value;
+ if (pSym->n_value == Value)
+ return VINF_SUCCESS;
+ return VERR_LDR_ADDRESS_OVERFLOW;
+}
+
+
+/**
+ * MH_OBJECT: Resolves undefined symbols (imports).
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param BaseAddress The module base address.
+ * @param pfnGetImport The callback for resolving an imported symbol.
+ * @param pvUser User argument to the callback.
+ */
+static int kldrModMachOObjDoImports(PRTLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+
+ /*
+ * Ensure that we've got the symbol table.
+ */
+ int rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Iterate the symbol table and resolve undefined symbols.
+ * We currently ignore REFERENCE_TYPE.
+ */
+ const uint32_t cSyms = pThis->cSymbols;
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ macho_nlist_32_t *paSyms = (macho_nlist_32_t *)pThis->pvaSymbols;
+ for (uint32_t 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)
+ {
+ rc = rtdlrModMachOHandleUndefinedSymbol32(pThis, &paSyms[iSym], BaseAddress, pfnGetImport, pvUser);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else if (paSyms[iSym].n_desc & N_WEAK_DEF)
+ {
+ /** @todo implement weak symbols. */
+ /*return VERR_LDRMACHO_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 *)pThis->pvaSymbols;
+ for (uint32_t 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)
+ {
+ rc = rtdlrModMachOHandleUndefinedSymbol64(pThis, &paSyms[iSym], BaseAddress, pfnGetImport, pvUser);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else if (paSyms[iSym].n_desc & N_WEAK_DEF)
+ {
+ /** @todo implement weak symbols. */
+ /*return VERR_LDRMACHO_TODO; - ignored for now. */
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Dylib: Resolves undefined symbols (imports).
+ *
+ * This is conceptually identically to kldrModMachOObjDoImports, only
+ * LC_DYSYMTAB helps us avoid working over the whole symbol table.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param BaseAddress The module base address.
+ * @param pfnGetImport The callback for resolving an imported symbol.
+ * @param pvUser User argument to the callback.
+ */
+static int kldrModMachODylibDoImports(PRTLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ /*
+ * There must be a LC_DYSYMTAB.
+ * We might be lucky, though, and not have any imports.
+ */
+ dysymtab_command_t const *pDySymTab = pThis->pDySymTab;
+ AssertReturn(pDySymTab, VERR_INTERNAL_ERROR_2);
+ if (pDySymTab->nundefsym == 0)
+ return VINF_SUCCESS;
+
+ /*
+ * Ensure that we've got the symbol table.
+ */
+ int rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Iterate the give symbol table section containing undefined symbols and resolve them.
+ */
+ uint32_t const cSyms = pDySymTab->iundefsym + pDySymTab->nundefsym;
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ macho_nlist_32_t *paSyms = (macho_nlist_32_t *)pThis->pvaSymbols;
+ for (uint32_t iSym = pDySymTab->iundefsym; RT_SUCCESS(rc) && iSym < cSyms; iSym++)
+ {
+ AssertContinue((paSyms[iSym].n_type & (MACHO_N_TYPE | MACHO_N_STAB)) == MACHO_N_UNDF);
+ rc = rtdlrModMachOHandleUndefinedSymbol32(pThis, &paSyms[iSym], BaseAddress, pfnGetImport, pvUser);
+ }
+ }
+ 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 *)pThis->pvaSymbols;
+ for (uint32_t iSym = pDySymTab->iundefsym; RT_SUCCESS(rc) && iSym < cSyms; iSym++)
+ {
+ AssertContinue((paSyms[iSym].n_type & (MACHO_N_TYPE | MACHO_N_STAB)) == MACHO_N_UNDF);
+ rc = rtdlrModMachOHandleUndefinedSymbol64(pThis, &paSyms[iSym], BaseAddress, pfnGetImport, pvUser);
+ }
+ }
+
+ return rc;
+}
+
+
+static int kldrModMachODylibDoIndirectSymbols(PRTLDRMODMACHO pThis, void *pvBits, RTLDRADDR offDelta)
+{
+ /*
+ * There must be a LC_DYSYMTAB.
+ * We might be lucky, though, and not have any imports.
+ */
+ dysymtab_command_t const *pDySymTab = pThis->pDySymTab;
+ AssertReturn(pDySymTab, VERR_INTERNAL_ERROR_2);
+ uint32_t const cIndirectSymbols = pDySymTab->nindirectsymb;
+ if (cIndirectSymbols == 0)
+ return VINF_SUCCESS;
+
+ /*
+ * Ensure that we've got the symbol table.
+ */
+ int rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Load the indirect symbol table.
+ */
+ if (!pThis->paidxIndirectSymbols)
+ {
+ uint32_t *paidxIndirectSymbols = (uint32_t *)RTMemAlloc(cIndirectSymbols * sizeof(uint32_t));
+ if (!paidxIndirectSymbols)
+ return VERR_NO_MEMORY;
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paidxIndirectSymbols, cIndirectSymbols * sizeof(uint32_t),
+ pDySymTab->indirectsymboff);
+ if (RT_SUCCESS(rc))
+ pThis->paidxIndirectSymbols = paidxIndirectSymbols;
+ else
+ {
+ RTMemFree(paidxIndirectSymbols);
+ return rc;
+ }
+
+ /* Byte swap if needed. */
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ for (uint32_t i = 0; i < cIndirectSymbols; i++)
+ paidxIndirectSymbols[i] = RT_BSWAP_U32(paidxIndirectSymbols[i]);
+ }
+ uint32_t const *paidxIndirectSymbols = pThis->paidxIndirectSymbols;
+
+ /*
+ * Process the sections using indirect symbols.
+ */
+ const uint32_t cSymbols = pThis->cSymbols;
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ macho_nlist_32_t const *paSymbols = (macho_nlist_32_t *)pThis->pvaSymbols;
+ for (uint32_t iSect = 0; iSect < pThis->cSections; iSect++)
+ {
+ section_32_t const *pSect = (section_32_t const *)pThis->paSections[iSect].pvMachoSection;
+ switch (pSect->flags & SECTION_TYPE)
+ {
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ case S_LAZY_SYMBOL_POINTERS:
+ {
+ uint32_t *pauDstPtrs = (uint32_t *)((uintptr_t)pvBits + (uintptr_t)pThis->paSections[iSect].RVA);
+ uint32_t const cDstPtrs = pThis->paSections[iSect].cb / sizeof(pauDstPtrs[0]);
+ uint32_t const idxSrcSkip = pSect->reserved1;
+ if ((uint64_t)idxSrcSkip + cDstPtrs > cIndirectSymbols)
+ return VERR_BAD_EXE_FORMAT; /// @todo better error code.
+
+ for (uint32_t i = 0; i < cDstPtrs; i++)
+ {
+ uint32_t const idxSym = paidxIndirectSymbols[idxSrcSkip + i];
+ if (idxSym == INDIRECT_SYMBOL_LOCAL)
+ pauDstPtrs[i] += (int32_t)offDelta;
+ else if (idxSym != INDIRECT_SYMBOL_ABS)
+ {
+ AssertMsgReturn(idxSym < cSymbols,
+ ("i=%#x idxSym=%#x cSymbols=%#x iSect=%#x\n", i, idxSym, cSymbols, iSect),
+ VERR_BAD_EXE_FORMAT); /// @todo better error code.
+ pauDstPtrs[i] = paSymbols[idxSym].n_value;
+ }
+ }
+ break;
+ }
+
+ case S_SYMBOL_STUBS:
+ if ( pThis->Core.enmArch == RTLDRARCH_X86_32
+ && (pSect->flags & S_ATTR_SELF_MODIFYING_CODE)
+ && pSect->reserved2 == 5)
+ {
+ uint32_t uDstRva = pThis->paSections[iSect].RVA;
+ uint8_t *pbDst = (uint8_t *)((uintptr_t)pvBits + uDstRva);
+ uint32_t const cDstPtrs = pThis->paSections[iSect].cb / 5;
+ uint32_t const idxSrcSkip = pSect->reserved1;
+ if ((uint64_t)idxSrcSkip + cDstPtrs > cIndirectSymbols)
+ return VERR_BAD_EXE_FORMAT; /// @todo better error code.
+
+ for (uint32_t i = 0; i < cDstPtrs; i++, uDstRva += 5, pbDst += 5)
+ {
+ uint32_t const idxSym = paidxIndirectSymbols[idxSrcSkip + i];
+ if (idxSym != INDIRECT_SYMBOL_ABS && idxSym != INDIRECT_SYMBOL_LOCAL)
+ {
+ AssertMsgReturn(idxSym < cSymbols,
+ ("i=%#x idxSym=%#x cSymbols=%#x iSect=%#x\n", i, idxSym, cSymbols, iSect),
+ VERR_BAD_EXE_FORMAT); /// @todo better error code.
+ pbDst[0] = 0xeb; /* JMP rel32 */
+ uint32_t offDisp = paSymbols[idxSym].n_value - (uint32_t)uDstRva - 5;
+ pbDst[1] = (uint8_t)offDisp;
+ offDisp >>= 8;
+ pbDst[2] = (uint8_t)offDisp;
+ offDisp >>= 8;
+ pbDst[3] = (uint8_t)offDisp;
+ offDisp >>= 8;
+ pbDst[4] = (uint8_t)offDisp;
+ }
+ }
+ break;
+ }
+ break;
+ }
+
+ }
+ }
+ else
+ {
+ /* Exact like for 32-bit, except for 64-bit symbol table, 64-bit addresses and no need to process S_SYMBOL_STUBS. */
+ macho_nlist_64_t const *paSymbols = (macho_nlist_64_t *)pThis->pvaSymbols;
+ for (uint32_t iSect = 0; iSect < pThis->cSections; iSect++)
+ {
+ section_64_t const *pSect = (section_64_t const *)pThis->paSections[iSect].pvMachoSection;
+ switch (pSect->flags & SECTION_TYPE)
+ {
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ case S_LAZY_SYMBOL_POINTERS:
+ {
+ uint64_t *pauDstPtrs = (uint64_t *)((uintptr_t)pvBits + (uintptr_t)pThis->paSections[iSect].RVA);
+ uint32_t const cDstPtrs = pThis->paSections[iSect].cb / sizeof(pauDstPtrs[0]);
+ uint32_t const idxSrcSkip = pSect->reserved1;
+ if ((uint64_t)idxSrcSkip + cDstPtrs > cIndirectSymbols)
+ return VERR_BAD_EXE_FORMAT; /// @todo better error code.
+
+ for (uint32_t i = 0; i < cDstPtrs; i++)
+ {
+ uint32_t const idxSym = paidxIndirectSymbols[idxSrcSkip + i];
+ if (idxSym == INDIRECT_SYMBOL_LOCAL)
+ pauDstPtrs[i] += (int64_t)offDelta;
+ else if (idxSym != INDIRECT_SYMBOL_ABS)
+ {
+ AssertMsgReturn(idxSym < cSymbols,
+ ("i=%#x idxSym=%#x cSymbols=%#x iSect=%#x\n", i, idxSym, cSymbols, iSect),
+ VERR_BAD_EXE_FORMAT); /// @todo better error code.
+ pauDstPtrs[i] = paSymbols[idxSym].n_value;
+ }
+ }
+ break;
+ }
+
+ case S_SYMBOL_STUBS:
+ if ( pThis->Core.enmArch == RTLDRARCH_X86_32
+ && (pSect->flags & S_ATTR_SELF_MODIFYING_CODE)
+ && pSect->reserved2 == 5)
+ return VERR_BAD_EXE_FORMAT;
+ break;
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * MH_OBJECT: Applies base relocations to an (unprotected) image mapping.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pvMapping The mapping to fixup.
+ * @param NewBaseAddress The address to fixup the mapping to.
+ */
+static int kldrModMachOObjDoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress)
+{
+ /*
+ * Ensure that we've got the symbol table.
+ */
+ int rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Iterate over the segments and their sections and apply fixups.
+ */
+ rc = VINF_SUCCESS;
+ for (uint32_t iSeg = 0; RT_SUCCESS(rc) && iSeg < pThis->cSegments; iSeg++)
+ {
+ PRTLDRMODMACHOSEG pSeg = &pThis->aSegments[iSeg];
+ for (uint32_t iSect = 0; iSect < pSeg->cSections; iSect++)
+ {
+ PRTLDRMODMACHOSECT pSect = &pSeg->paSections[iSect];
+
+ /* skip sections without fixups. */
+ if (!pSect->cFixups)
+ continue;
+ AssertReturn(pSect->paFixups, VERR_INTERNAL_ERROR_4);
+ AssertReturn(pSect->pauFixupVirginData, VERR_INTERNAL_ERROR_4);
+
+ /*
+ * Apply the fixups.
+ */
+ uint8_t *pbSectBits = (uint8_t *)pvMapping + (uintptr_t)pSect->RVA;
+ if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE) /** @todo this aint right. */
+ rc = kldrModMachOApplyFixupsGeneric32Bit(pThis, pbSectBits, (size_t)pSect->cb, pSect->RVA, pSect->LinkAddress,
+ pSect->paFixups, pSect->cFixups, pSect->pauFixupVirginData,
+ (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols, NewBaseAddress);
+ else if ( pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
+ && pThis->Hdr.cputype == CPU_TYPE_X86_64)
+ rc = kldrModMachOApplyFixupsAMD64(pThis, pbSectBits, (size_t)pSect->cb, pSect->RVA,
+ pSect->paFixups, pSect->cFixups, pSect->pauFixupVirginData,
+ (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols, NewBaseAddress);
+ else
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Dylib: Applies base relocations to an (unprotected) image mapping.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pvMapping The mapping to fixup.
+ * @param NewBaseAddress The address to fixup the mapping to.
+ */
+static int kldrModMachODylibDoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress)
+{
+ /*
+ * There must be a LC_DYSYMTAB.
+ * We might be lucky, though, and not have any imports.
+ */
+ dysymtab_command_t const *pDySymTab = pThis->pDySymTab;
+ AssertReturn(pDySymTab, VERR_INTERNAL_ERROR_2);
+ uint32_t cRelocations = pDySymTab->nlocrel + pDySymTab->nextrel;
+ if (cRelocations == 0)
+ return VINF_SUCCESS;
+
+ /*
+ * Ensure that we've got the symbol table.
+ */
+ int rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Load the relocations if needed.
+ */
+ macho_relocation_union_t const *paRelocations = pThis->paRelocations;
+ if (!paRelocations)
+ {
+ uint32_t *paRawRelocs = (uint32_t *)RTMemAlloc(cRelocations * sizeof(macho_relocation_union_t));
+ if (!paRawRelocs)
+ return VERR_NO_MEMORY;
+ if (pDySymTab->nextrel)
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paRawRelocs, pDySymTab->nextrel * sizeof(macho_relocation_union_t),
+ pDySymTab->extreloff);
+ if (pDySymTab->nlocrel && RT_SUCCESS(rc))
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader,
+ (uint8_t *)paRawRelocs + pDySymTab->nextrel * sizeof(macho_relocation_union_t),
+ pDySymTab->nlocrel * sizeof(macho_relocation_union_t), pDySymTab->locreloff);
+ if (RT_SUCCESS(rc))
+ pThis->paRelocations = (macho_relocation_union_t *)paRawRelocs;
+ else
+ {
+ RTMemFree(paRawRelocs);
+ return rc;
+ }
+
+ /* Byte swap if needed. */
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ {
+ for (uint32_t i = 0; i < cRelocations; i++)
+ {
+ paRawRelocs[i * 2] = RT_BSWAP_U32(paRawRelocs[i * 2]);
+ paRawRelocs[i * 2 + 1] = RT_BSWAP_U32(paRawRelocs[i * 2 + 1]);
+ }
+ ASMCompilerBarrier();
+ }
+
+ paRelocations = pThis->paRelocations;
+ }
+
+ /*
+ * Apply the fixups.
+ */
+ if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE) /** @todo this aint right. */
+ return kldrModMachOApplyFixupsGeneric32Bit(pThis, (uint8_t *)pvMapping, (size_t)pThis->cbImage, 0, pThis->LinkAddress,
+ paRelocations, cRelocations, pThis->pauRelocationsVirginData,
+ (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols, NewBaseAddress);
+ if ( pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
+ && pThis->Hdr.cputype == CPU_TYPE_X86_64)
+ return kldrModMachOApplyFixupsAMD64(pThis, (uint8_t *)pvMapping, (size_t)pThis->cbImage, 0,
+ paRelocations, cRelocations, pThis->pauRelocationsVirginData,
+ (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols, NewBaseAddress);
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+}
+
+
+/**
+ * Applies generic fixups to a section in an image of the same endian-ness
+ * as the host CPU.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pbBits Pointer to the bits to fix up.
+ * @param cbBits Size of the bits to fix up.
+ * @param uBitsRva The RVA of the bits.
+ * @param uBitsLinkAddr The link address of the bits.
+ * @param paFixups The fixups.
+ * @param cFixups Number of fixups.
+ * @param pauVirginData The virgin data / addends. Parallel to paFixups.
+ * @param paSyms Pointer to the symbol table.
+ * @param cSyms Number of symbols.
+ * @param NewBaseAddress The new base image address.
+ */
+static int kldrModMachOApplyFixupsGeneric32Bit(PRTLDRMODMACHO pThis, uint8_t *pbBits, size_t cbBits, RTLDRADDR uBitsRva,
+ RTLDRADDR uBitsLinkAddr, const macho_relocation_union_t *paFixups,
+ const uint32_t cFixups, PCRTUINT64U const pauVirginData,
+ macho_nlist_32_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
+{
+ /*
+ * Iterate the fixups and apply them.
+ */
+ for (uint32_t iFixup = 0; iFixup < cFixups; iFixup++)
+ {
+ macho_relocation_union_t Fixup = paFixups[iFixup];
+ RTLDRADDR SymAddr = ~(RTLDRADDR)0;
+ RTPTRUNION uFix;
+
+ if (!(Fixup.r.r_address & R_SCATTERED))
+ {
+ /* sanity */
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)Fixup.r.r_address + RT_BIT_32(Fixup.r.r_length) <= cbBits, VERR_LDR_BAD_FIXUP);
+
+ /* Calc the fixup address. */
+ uFix.pv = pbBits + Fixup.r.r_address;
+
+ /*
+ * Calc the symbol value.
+ */
+ /* Calc the linked symbol address / addend. */
+ switch (Fixup.r.r_length)
+ {
+ case 0: SymAddr = (int8_t)pauVirginData[iFixup].au8[0]; break;
+ case 1: SymAddr = (int16_t)pauVirginData[iFixup].au16[0]; break;
+ case 2: SymAddr = (int32_t)pauVirginData[iFixup].au32[0]; break;
+ case 3: SymAddr = (int64_t)pauVirginData[iFixup].u; break;
+ default: RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+ if (Fixup.r.r_pcrel)
+ SymAddr += Fixup.r.r_address + uBitsLinkAddr;
+
+ /* Add symbol / section address. */
+ if (Fixup.r.r_extern)
+ {
+ const macho_nlist_32_t *pSym;
+ if (Fixup.r.r_symbolnum >= cSyms)
+ return VERR_LDR_BAD_FIXUP;
+ pSym = &paSyms[Fixup.r.r_symbolnum];
+
+ if (pSym->n_type & MACHO_N_STAB)
+ return VERR_LDR_BAD_FIXUP;
+
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSymSect = &pThis->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:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
+ }
+ }
+ else if (Fixup.r.r_symbolnum != R_ABS)
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ if (Fixup.r.r_symbolnum > pThis->cSections)
+ return VERR_LDR_BAD_FIXUP;
+ pSymSect = &pThis->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 + uBitsRva + NewBaseAddress;
+ }
+ else
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ uint32_t iSymSect;
+ RTLDRADDR Value;
+
+ /* sanity */
+ RTLDRMODMACHO_ASSERT(Fixup.s.r_scattered);
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)Fixup.s.r_address + RT_BIT_32(Fixup.s.r_length) <= cbBits, VERR_LDR_BAD_FIXUP);
+
+ /* Calc the fixup address. */
+ uFix.pv = pbBits + Fixup.s.r_address;
+
+ /*
+ * Calc the symbol value.
+ */
+ /* The addend is stored in the code. */
+ switch (Fixup.s.r_length)
+ {
+ case 0: SymAddr = (int8_t)pauVirginData[iFixup].au8[0]; break;
+ case 1: SymAddr = (int16_t)pauVirginData[iFixup].au16[0]; break;
+ case 2: SymAddr = (int32_t)pauVirginData[iFixup].au32[0]; break;
+ case 3: SymAddr = (int64_t)pauVirginData[iFixup].u; break;
+ default: RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+ 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 < pThis->cSections; iSymSect++)
+ {
+ RTLDRADDR off = Value - pThis->paSections[iSymSect].LinkAddress;
+ if (off < pThis->paSections[iSymSect].cb)
+ {
+ pSymSect = &pThis->paSections[iSymSect];
+ break;
+ }
+ else if (off == pThis->paSections[iSymSect].cb) /* edge case */
+ pSymSect = &pThis->paSections[iSymSect];
+ }
+ if (!pSymSect)
+ return VERR_LDR_BAD_FIXUP;
+
+ /* Calc the symbol address. */
+ SymAddr += Value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ if (Fixup.s.r_pcrel)
+ SymAddr -= Fixup.s.r_address + uBitsRva + 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 = (uint8_t)SymAddr; break;
+ case 1: *uFix.pu16 = (uint16_t)SymAddr; break;
+ case 2: *uFix.pu32 = (uint32_t)SymAddr; break;
+ case 3: *uFix.pu64 = (uint64_t)SymAddr; break;
+ }
+ }
+ else if (Fixup.r.r_type <= GENERIC_RELOC_LOCAL_SECTDIFF)
+ return VERR_LDRMACHO_UNSUPPORTED_FIXUP_TYPE;
+ else
+ return VERR_LDR_BAD_FIXUP;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Applies AMD64 fixups to a section.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pbBits Pointer to the section bits.
+ * @param cbBits Size of the bits to fix up.
+ * @param uBitsRva The RVA of the bits.
+ * @param paFixups The fixups.
+ * @param cFixups Number of fixups.
+ * @param pauVirginData The virgin data / addends. Parallel to paFixups.
+ * @param paSyms Pointer to the symbol table.
+ * @param cSyms Number of symbols.
+ * @param NewBaseAddress The new base image address.
+ */
+static int kldrModMachOApplyFixupsAMD64(PRTLDRMODMACHO pThis, uint8_t *pbBits, size_t cbBits, RTLDRADDR uBitsRva,
+ const macho_relocation_union_t *paFixups,
+ const uint32_t cFixups, PCRTUINT64U const pauVirginData,
+ macho_nlist_64_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
+{
+ /*
+ * Iterate the fixups and apply them.
+ */
+ for (uint32_t iFixup = 0; iFixup < cFixups; iFixup++)
+ {
+ macho_relocation_union_t Fixup = paFixups[iFixup];
+
+ /* AMD64 doesn't use scattered fixups. */
+ RTLDRMODMACHO_CHECK_RETURN(!(Fixup.r.r_address & R_SCATTERED), VERR_LDR_BAD_FIXUP);
+
+ /* sanity */
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)Fixup.r.r_address + RT_BIT_32(Fixup.r.r_length) <= cbBits, VERR_LDR_BAD_FIXUP);
+
+ /* calc fixup addresses. */
+ RTPTRUNION uFix;
+ uFix.pv = pbBits + Fixup.r.r_address;
+
+ /*
+ * Calc the symbol value.
+ */
+ /* Calc the linked symbol address / addend. */
+ RTLDRADDR SymAddr;
+ switch (Fixup.r.r_length)
+ {
+ case 2: SymAddr = (int32_t)pauVirginData[iFixup].au32[0]; break;
+ case 3: SymAddr = (int64_t)pauVirginData[iFixup].u; break;
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+
+ /* Add symbol / section address. */
+ if (Fixup.r.r_extern)
+ {
+ const macho_nlist_64_t *pSym;
+
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
+ pSym = &paSyms[Fixup.r.r_symbolnum];
+ RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_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:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
+ }
+ SymAddr = sizeof(uint64_t) * Fixup.r.r_symbolnum + pThis->GotRVA + NewBaseAddress;
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_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:
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
+ RT_FALL_THRU();
+ default:
+ {
+ /* Adjust with fixup specific addend and verify unsigned/r_pcrel. */
+ switch (Fixup.r.r_type)
+ {
+ case X86_64_RELOC_UNSIGNED:
+ RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
+ break;
+ case X86_64_RELOC_BRANCH:
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_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:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSymSect = &pThis->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 + uBitsRva + NewBaseAddress
+ - pSym->n_value
+ + UINT64_C(0x80000000)
+ >= UINT64_C(0xffffff20))
+ {
+ RTLDRMODMACHO_CHECK_RETURN(pThis->JmpStubsRVA != NIL_RTLDRADDR, VERR_LDR_ADDRESS_OVERFLOW);
+ SymAddr += pThis->cbJmpStub * Fixup.r.r_symbolnum + pThis->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:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
+ }
+ break;
+ }
+
+ /*
+ * This is a weird customer, it will always be follows by an UNSIGNED fixup.
+ * The value is calculated: target - pair_target.
+ * Note! The linker generally eliminate these when linking modules rather
+ * than objects (-r).
+ */
+ case X86_64_RELOC_SUBTRACTOR:
+ {
+ /* Deal with the SUBTRACT symbol first, by subtracting it from SymAddr. */
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSymSect = &pThis->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:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
+ }
+
+ /* Load the 2nd fixup, check sanity. */
+ iFixup++;
+ RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel && iFixup < cFixups, VERR_LDR_BAD_FIXUP);
+ macho_relocation_info_t const Fixup2 = paFixups[iFixup].r;
+ RTLDRMODMACHO_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,
+ VERR_LDR_BAD_FIXUP);
+
+ if (Fixup2.r_extern)
+ {
+ RTLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
+ pSym = &paSyms[Fixup2.r_symbolnum];
+ RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
+
+ /* Add its value to SymAddr. */
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSymSect = &pThis->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:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
+ }
+ }
+ else if (Fixup2.r_symbolnum != R_ABS)
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
+ pSymSect = &pThis->paSections[Fixup2.r_symbolnum - 1];
+ SymAddr += pSymSect->RVA - pSymSect->LinkAddress + NewBaseAddress;
+ }
+ else
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+ break;
+ }
+ }
+ else
+ {
+ /* verify against fixup type and make adjustments */
+ switch (Fixup.r.r_type)
+ {
+ case X86_64_RELOC_UNSIGNED:
+ RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
+ break;
+ case X86_64_RELOC_BRANCH:
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_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:
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_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:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+ if (Fixup.r.r_symbolnum != R_ABS)
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
+ pSymSect = &pThis->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 + uBitsRva + NewBaseAddress;
+
+ /*
+ * Write back the fixed up value.
+ */
+ switch (Fixup.r.r_length)
+ {
+ case 3:
+ *uFix.pu64 = (uint64_t)SymAddr;
+ break;
+ case 2:
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel || Fixup.r.r_type == X86_64_RELOC_SUBTRACTOR, VERR_LDR_BAD_FIXUP);
+ RTLDRMODMACHO_CHECK_RETURN((int32_t)SymAddr == (int64_t)SymAddr, VERR_LDR_ADDRESS_OVERFLOW);
+ *uFix.pu32 = (uint32_t)SymAddr;
+ break;
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Loads the symbol table (LC_SYMTAB).
+ *
+ * The symbol table is pointed to by RTLDRMODMACHO::pvaSymbols.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ */
+static int kldrModMachOLoadObjSymTab(PRTLDRMODMACHO pThis)
+{
+ int rc = VINF_SUCCESS;
+
+ if ( !pThis->pvaSymbols
+ && pThis->cSymbols)
+ {
+ size_t cbSyms;
+ size_t cbSym;
+ void *pvSyms;
+ void *pvStrings;
+
+ /* sanity */
+ RTLDRMODMACHO_CHECK_RETURN( pThis->offSymbols
+ && (!pThis->cchStrings || pThis->offStrings),
+ VERR_LDRMACHO_BAD_OBJECT_FILE);
+
+ /* allocate */
+ cbSym = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ ? sizeof(macho_nlist_32_t)
+ : sizeof(macho_nlist_64_t);
+ cbSyms = pThis->cSymbols * cbSym;
+ RTLDRMODMACHO_CHECK_RETURN(cbSyms / cbSym == pThis->cSymbols, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
+ rc = VERR_NO_MEMORY;
+ pvSyms = RTMemAlloc(cbSyms);
+ if (pvSyms)
+ {
+ if (pThis->cchStrings)
+ pvStrings = RTMemAlloc(pThis->cchStrings);
+ else
+ pvStrings = RTMemAllocZ(4);
+ if (pvStrings)
+ {
+ /* read */
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvSyms, cbSyms, pThis->offSymbols);
+ if (RT_SUCCESS(rc) && pThis->cchStrings)
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvStrings, pThis->cchStrings, pThis->offStrings);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pvaSymbols = pvSyms;
+ pThis->pchStrings = (char *)pvStrings;
+
+ /* perform endian conversion? */
+ if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ uint32_t cLeft = pThis->cSymbols;
+ macho_nlist_32_t *pSym = (macho_nlist_32_t *)pvSyms;
+ while (cLeft-- > 0)
+ {
+ pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
+ pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
+ pSym->n_value = RT_BSWAP_U32(pSym->n_value);
+ pSym++;
+ }
+ }
+ else if (pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ {
+ uint32_t cLeft = pThis->cSymbols;
+ macho_nlist_64_t *pSym = (macho_nlist_64_t *)pvSyms;
+ while (cLeft-- > 0)
+ {
+ pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
+ pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
+ pSym->n_value = RT_BSWAP_U64(pSym->n_value);
+ pSym++;
+ }
+ }
+
+ return VINF_SUCCESS;
+ }
+ RTMemFree(pvStrings);
+ }
+ RTMemFree(pvSyms);
+ }
+ }
+ else
+ RTLDRMODMACHO_ASSERT(pThis->pchStrings || pThis->Hdr.filetype == MH_DSYM);
+
+ return rc;
+}
+
+
+/**
+ * Loads the fixups at the given address and performs endian
+ * conversion if necessary.
+ *
+ * @returns IPRT status code.
+ * @param pThis 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(PRTLDRMODMACHO pThis, RTFOFF offFixups, uint32_t cFixups, macho_relocation_union_t **ppaFixups)
+{
+ macho_relocation_union_t *paFixups;
+ size_t cbFixups;
+
+ /* allocate the memory. */
+ cbFixups = cFixups * sizeof(*paFixups);
+ RTLDRMODMACHO_CHECK_RETURN(cbFixups / sizeof(*paFixups) == cFixups, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
+ paFixups = (macho_relocation_union_t *)RTMemAlloc(cbFixups);
+ if (!paFixups)
+ return VERR_NO_MEMORY;
+
+ /* read the fixups. */
+ int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paFixups, cbFixups, offFixups);
+ if (RT_SUCCESS(rc))
+ {
+ *ppaFixups = paFixups;
+
+ /* do endian conversion if necessary. */
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ {
+ uint32_t iFixup;
+ for (iFixup = 0; iFixup < cFixups; iFixup++)
+ {
+ uint32_t *pu32 = (uint32_t *)&paFixups[iFixup];
+ pu32[0] = RT_BSWAP_U32(pu32[0]);
+ pu32[1] = RT_BSWAP_U32(pu32[1]);
+ }
+ }
+ }
+ else
+ RTMemFree(paFixups);
+ return rc;
+}
+
+
+/**
+ * Loads virgin data (addends) for an array of fixups.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pbBits The virgin bits to lift the data from
+ * @param cbBits The number of virgin bytes.
+ * @param paFixups The fixups.
+ * @param cFixups Number of fixups
+ * @param pszName Name for logging.
+ * @param ppauVirginData Where to return the virgin data.
+ */
+static int rtldrMachOLoadVirginData(PRTLDRMODMACHO pThis, uint8_t const *pbBits, size_t cbBits,
+ macho_relocation_union_t const *paFixups, uint32_t cFixups, const char *pszName,
+ PRTUINT64U *ppauVirginData)
+{
+ /*
+ * In case we jettisoned the fixups, we will leave virgin data.
+ */
+ if (*ppauVirginData)
+ return VINF_SUCCESS;
+
+#ifdef LOG_ENABLED
+ /*
+ * Ensure that we've got the symbol table if we're logging fixups.
+ */
+ if (LogIs5Enabled())
+ {
+ int rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+#endif
+
+
+ /*
+ * Allocate memory and iterate the fixups to get the data.
+ */
+ PRTUINT64U pauVirginData = *ppauVirginData = (PRTUINT64U)RTMemAllocZ(sizeof(uint64_t) * cFixups);
+ if (pauVirginData)
+ {
+ Log5(("Fixups for %s: (%u)\n", pszName, cFixups));
+ for (uint32_t i = 0; i < cFixups; i++)
+ {
+ uint32_t off;
+ uint32_t cShift;
+ if (!paFixups[i].s.r_scattered)
+ {
+ off = paFixups[i].r.r_address;
+ cShift = paFixups[i].r.r_length;
+ }
+ else
+ {
+ off = paFixups[i].s.r_address;
+ cShift = paFixups[i].s.r_length;
+ }
+ RTLDRMODMACHO_CHECK_RETURN(off + RT_BIT_32(cShift) <= cbBits, VERR_LDR_BAD_FIXUP);
+
+ /** @todo This ASSUMES same endian in the image and on the host. Would need
+ * to check target cpu (pThis->Core.enmArch) endianness against host to get
+ * it right... (outside the loop, obviously) */
+ switch (cShift)
+ {
+ case 3:
+ pauVirginData[i].u = RT_MAKE_U64_FROM_U8(pbBits[off], pbBits[off + 1], pbBits[off + 2], pbBits[off + 3],
+ pbBits[off + 4], pbBits[off + 5], pbBits[off + 6], pbBits[off + 7]);
+ break;
+ case 2:
+ pauVirginData[i].u = (int32_t)RT_MAKE_U32_FROM_U8(pbBits[off], pbBits[off + 1], pbBits[off + 2], pbBits[off + 3]);
+ break;
+ case 1:
+ pauVirginData[i].u = (int16_t)RT_MAKE_U16(pbBits[off], pbBits[off + 1]);
+ break;
+ case 0:
+ pauVirginData[i].u = (int8_t)pbBits[off];
+ break;
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+
+#ifdef LOG_ENABLED
+ if (LogIs5Enabled())
+ {
+ if (!paFixups[i].s.r_scattered)
+ {
+ Log5((" #%06x: %#08x LB %u: t=%#x pc=%u ex=%u sym=%#010x add=%#RX64\n",
+ i, off, RT_BIT_32(cShift), paFixups[i].r.r_type, paFixups[i].r.r_pcrel, paFixups[i].r.r_extern,
+ paFixups[i].r.r_symbolnum, pauVirginData[i].u));
+ if (paFixups[i].r.r_symbolnum < pThis->cSymbols)
+ {
+ if (pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ {
+ macho_nlist_64_t const *pSym = (macho_nlist_64_t const *)pThis->pvaSymbols + paFixups[i].r.r_symbolnum;
+ Log5((" sym: %#04x:%#018RX64 t=%#04x d=%#06x %s\n",
+ pSym->n_sect, pSym->n_value, pSym->n_type, pSym->n_desc,
+ pSym->n_un.n_strx < pThis->cchStrings ? &pThis->pchStrings[pSym->n_un.n_strx] : ""));
+
+ }
+ else
+ {
+ macho_nlist_32_t const *pSym = (macho_nlist_32_t const *)pThis->pvaSymbols + paFixups[i].r.r_symbolnum;
+ Log5((" sym: %#04x:%#010RX32 t=%#04x d=%#06x %s\n",
+ pSym->n_sect, pSym->n_value, pSym->n_type, pSym->n_desc,
+ (uint32_t)pSym->n_un.n_strx < pThis->cchStrings ? &pThis->pchStrings[pSym->n_un.n_strx] : ""));
+ }
+ }
+ }
+ else
+ Log5((" #%06x: %#08x LB %u: t=%#x pc=%u val=%#010x add=%#RX64\n", i, off, RT_BIT_32(cShift),
+ paFixups[i].s.r_type, paFixups[i].s.r_pcrel, paFixups[i].s.r_value, pauVirginData[i].u));
+ }
+#endif
+ }
+ return VINF_SUCCESS;
+ }
+ RT_NOREF(pThis, pszName);
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * MH_OBJECT: Loads fixups and addends for each section.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pbBits The image bits. First time we're called, these are
+ * ASSUMED to be in virgin state and suitable for
+ * saving addends.
+ */
+static int rtldrMachOObjLoadFixupsAndVirginData(PRTLDRMODMACHO pThis, uint8_t const *pbBits)
+{
+ PRTLDRMODMACHOSECT pSect = pThis->paSections;
+ for (uint32_t i = 0; i < pThis->cSections; i++, pSect++)
+ if ( !pSect->paFixups
+ && pSect->cFixups > 0)
+ {
+ /*
+ * Load and endian convert the fixups.
+ */
+ int rc = kldrModMachOLoadFixups(pThis, pSect->offFixups, pSect->cFixups, &pSect->paFixups);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Save virgin data (addends) for each fixup.
+ */
+ rc = rtldrMachOLoadVirginData(pThis, &pbBits[(size_t)pSect->RVA], (size_t)pSect->cb, pSect->paFixups, pSect->cFixups,
+ pThis->aSegments[pSect->iSegment].SegInfo.pszName, &pSect->pauFixupVirginData);
+ if (RT_SUCCESS(rc))
+ continue;
+
+ RTMemFree(pSect->pauFixupVirginData);
+ pSect->pauFixupVirginData = NULL;
+ RTMemFree(pSect->paFixups);
+ pSect->paFixups = NULL;
+ }
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Dylib: Loads fixups and addends.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pbBits The image bits. First time we're called, these are
+ * ASSUMED to be in virgin state and suitable for
+ * saving addends.
+ */
+static int rtldrMachODylibLoadFixupsAndVirginData(PRTLDRMODMACHO pThis, uint8_t const *pbBits)
+{
+ /*
+ * Don't do it again if we already loaded them.
+ */
+ if (pThis->paRelocations)
+ {
+ Assert(pThis->pauRelocationsVirginData);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * There must be a LC_DYSYMTAB. Fixups are optionals.
+ */
+ dysymtab_command_t const *pDySymTab = pThis->pDySymTab;
+ AssertReturn(pDySymTab, VERR_INTERNAL_ERROR_2);
+ uint32_t cRelocations = pDySymTab->nlocrel + pDySymTab->nextrel;
+ if (cRelocations == 0)
+ return VINF_SUCCESS;
+
+ /*
+ * Load fixups.
+ */
+ int rc = VINF_SUCCESS;
+ uint32_t *paRawRelocs = (uint32_t *)RTMemAlloc(cRelocations * sizeof(macho_relocation_union_t));
+ if (paRawRelocs)
+ {
+ pThis->paRelocations = (macho_relocation_union_t *)paRawRelocs;
+
+ if (pDySymTab->nextrel)
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paRawRelocs,
+ pDySymTab->nextrel * sizeof(macho_relocation_union_t), pDySymTab->extreloff);
+ if (pDySymTab->nlocrel && RT_SUCCESS(rc))
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader,
+ (uint8_t *)paRawRelocs + pDySymTab->nextrel * sizeof(macho_relocation_union_t),
+ pDySymTab->nlocrel * sizeof(macho_relocation_union_t), pDySymTab->locreloff);
+ if (RT_SUCCESS(rc))
+ {
+ /* Byte swap if needed. */
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ {
+ for (uint32_t i = 0; i < cRelocations; i++)
+ {
+ paRawRelocs[i * 2] = RT_BSWAP_U32(paRawRelocs[i * 2]);
+ paRawRelocs[i * 2 + 1] = RT_BSWAP_U32(paRawRelocs[i * 2 + 1]);
+ }
+ ASMCompilerBarrier();
+ }
+
+ /*
+ * Load virgin data (addends).
+ */
+ rc = rtldrMachOLoadVirginData(pThis, pbBits, (size_t)pThis->cbImage, pThis->paRelocations, cRelocations,
+ "whole-image", &pThis->pauRelocationsVirginData);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ RTMemFree(pThis->pauRelocationsVirginData);
+ pThis->pauRelocationsVirginData = NULL;
+ }
+ RTMemFree(pThis->paRelocations);
+ pThis->paRelocations = NULL;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+#if 0
+
+/** @copydoc kLdrModCallInit */
+static int kldrModMachOCallInit(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
+{
+ /* later */
+ RT_NOREF(pMod);
+ RT_NOREF(pvMapping);
+ RT_NOREF(uHandle);
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModCallTerm */
+static int kldrModMachOCallTerm(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
+{
+ /* later */
+ RT_NOREF(pMod);
+ RT_NOREF(pvMapping);
+ RT_NOREF(uHandle);
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModCallThread */
+static int kldrModMachOCallThread(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle, unsigned fAttachingOrDetaching)
+{
+ /* Relevant for Mach-O? */
+ RT_NOREF(pMod);
+ RT_NOREF(pvMapping);
+ RT_NOREF(uHandle);
+ RT_NOREF(fAttachingOrDetaching);
+ return VINF_SUCCESS;
+}
+
+#endif
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnGetImageSize}
+ */
+static DECLCALLBACK(size_t) rtldrMachO_GetImageSize(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ return pThis->cbImage;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnGetBits}
+ */
+static DECLCALLBACK(int) rtldrMachO_GetBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR BaseAddress,
+ PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+
+ if (!pThis->fCanLoad)
+ return VERR_LDRMACHO_TODO;
+
+ /*
+ * Zero the entire buffer first to simplify things.
+ */
+ memset(pvBits, 0, (size_t)pThis->cbImage);
+
+ /*
+ * When possible use the segment table to load the data.
+ */
+ for (uint32_t i = 0; i < pThis->cSegments; i++)
+ {
+ /* skip it? */
+ if ( pThis->aSegments[i].SegInfo.cbFile == -1
+ || pThis->aSegments[i].SegInfo.offFile == -1
+ || pThis->aSegments[i].SegInfo.RVA == NIL_RTLDRADDR
+ || pThis->aSegments[i].SegInfo.cbMapped == 0
+ || !pThis->aSegments[i].SegInfo.Alignment)
+ continue;
+ int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader,
+ (uint8_t *)pvBits + pThis->aSegments[i].SegInfo.RVA,
+ pThis->aSegments[i].SegInfo.cbFile,
+ pThis->aSegments[i].SegInfo.offFile);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Perform relocations.
+ */
+ return rtldrMachO_RelocateBits(pMod, pvBits, BaseAddress, pThis->LinkAddress, pfnGetImport, pvUser);
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnRelocate}
+ */
+static DECLCALLBACK(int) rtldrMachO_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
+ RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ int rc;
+
+ /*
+ * Call workers to do the jobs.
+ */
+ if (pThis->Hdr.filetype == MH_OBJECT)
+ {
+ rc = rtldrMachOObjLoadFixupsAndVirginData(pThis, (uint8_t const *)pvBits);
+ if (RT_SUCCESS(rc))
+ rc = kldrModMachOObjDoImports(pThis, NewBaseAddress, pfnGetImport, pvUser);
+ if (RT_SUCCESS(rc))
+ rc = kldrModMachOObjDoFixups(pThis, pvBits, NewBaseAddress);
+
+ }
+ else
+ {
+ rc = rtldrMachODylibLoadFixupsAndVirginData(pThis, (uint8_t const *)pvBits);
+ if (RT_SUCCESS(rc))
+ rc = kldrModMachODylibDoImports(pThis, NewBaseAddress, pfnGetImport, pvUser);
+ if (RT_SUCCESS(rc))
+ rc = kldrModMachODylibDoIndirectSymbols(pThis, pvBits, NewBaseAddress - OldBaseAddress);
+ if (RT_SUCCESS(rc))
+ rc = kldrModMachODylibDoFixups(pThis, pvBits, NewBaseAddress);
+ }
+
+ /*
+ * Construct the global offset table if necessary, it's always the last
+ * segment when present.
+ */
+ if (RT_SUCCESS(rc) && pThis->fMakeGot)
+ rc = kldrModMachOMakeGOT(pThis, 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(PRTLDRMODMACHO pThis, void *pvBits, RTLDRADDR NewBaseAddress)
+{
+ uint32_t iSym = pThis->cSymbols;
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ macho_nlist_32_t const *paSyms = (macho_nlist_32_t const *)pThis->pvaSymbols;
+ uint32_t *paGOT = (uint32_t *)((uint8_t *)pvBits + pThis->GotRVA);
+ while (iSym-- > 0)
+ switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
+ paGOT[iSym] = (uint32_t)(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 *)pThis->pvaSymbols;
+ uint64_t *paGOT = (uint64_t *)((uint8_t *)pvBits + pThis->GotRVA);
+ while (iSym-- > 0)
+ {
+ switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSymSect = &pThis->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 (pThis->JmpStubsRVA != NIL_RTLDRADDR)
+ {
+ iSym = pThis->cSymbols;
+ switch (pThis->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:
+ {
+ uint64_t *paJmps = (uint64_t *)((uint8_t *)pvBits + pThis->JmpStubsRVA);
+ int32_t off;
+ uint64_t u64Tmpl;
+ union
+ {
+ uint8_t ab[8];
+ uint64_t u64;
+ } Tmpl;
+
+ /* create the template. */
+ off = (int32_t)(pThis->GotRVA - (pThis->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:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ }
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnEnumSegments}
+ */
+static DECLCALLBACK(int) rtldrMachO_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ uint32_t const cSegments = pThis->cSegments;
+ for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
+ {
+ int rc = pfnCallback(pMod, &pThis->aSegments[iSeg].SegInfo, pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnLinkAddressToSegOffset}
+ */
+static DECLCALLBACK(int) rtldrMachO_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
+ uint32_t *piSeg, PRTLDRADDR poffSeg)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ uint32_t const cSegments = pThis->cSegments;
+ for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
+ if (pThis->aSegments[iSeg].SegInfo.RVA != NIL_RTLDRADDR)
+ {
+ Assert(pThis->aSegments[iSeg].SegInfo.cbMapped != NIL_RTLDRADDR);
+ RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
+ if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
+ || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
+ {
+ *piSeg = iSeg;
+ *poffSeg = offSeg;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_LDR_INVALID_LINK_ADDRESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnLinkAddressToRva}
+ */
+static DECLCALLBACK(int) rtldrMachO_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ uint32_t const cSegments = pThis->cSegments;
+ for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
+ if (pThis->aSegments[iSeg].SegInfo.RVA != NIL_RTLDRADDR)
+ {
+ Assert(pThis->aSegments[iSeg].SegInfo.cbMapped != NIL_RTLDRADDR);
+ RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
+ if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
+ || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
+ {
+ *pRva = pThis->aSegments[iSeg].SegInfo.RVA + offSeg;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_LDR_INVALID_RVA;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnSegOffsetToRva}
+ */
+static DECLCALLBACK(int) rtldrMachO_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg, PRTLDRADDR pRva)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+
+ if (iSeg >= pThis->cSegments)
+ return VERR_LDR_INVALID_SEG_OFFSET;
+ RTLDRMODMACHOSEG const *pSegment = &pThis->aSegments[iSeg];
+
+ if (pSegment->SegInfo.RVA == NIL_RTLDRADDR)
+ return VERR_LDR_INVALID_SEG_OFFSET;
+
+ if ( offSeg > pSegment->SegInfo.cbMapped
+ && offSeg > pSegment->SegInfo.cb
+ && ( pSegment->SegInfo.cbFile < 0
+ || offSeg > (uint64_t)pSegment->SegInfo.cbFile))
+ return VERR_LDR_INVALID_SEG_OFFSET;
+
+ *pRva = pSegment->SegInfo.RVA + offSeg;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnRvaToSegOffset}
+ */
+static DECLCALLBACK(int) rtldrMachO_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva, uint32_t *piSeg, PRTLDRADDR poffSeg)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ uint32_t const cSegments = pThis->cSegments;
+ for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
+ if (pThis->aSegments[iSeg].SegInfo.RVA != NIL_RTLDRADDR)
+ {
+ Assert(pThis->aSegments[iSeg].SegInfo.cbMapped != NIL_RTLDRADDR);
+ RTLDRADDR offSeg = Rva - pThis->aSegments[iSeg].SegInfo.RVA;
+ if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
+ || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
+ {
+ *piSeg = iSeg;
+ *poffSeg = offSeg;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_LDR_INVALID_RVA;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnReadDbgInfo}
+ */
+static DECLCALLBACK(int) rtldrMachO_ReadDbgInfo(PRTLDRMODINTERNAL pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void *pvBuf)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+
+ /** @todo May have to apply fixups here. */
+ if (iDbgInfo < pThis->cSections)
+ return pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvBuf, cb, off);
+ return VERR_OUT_OF_RANGE;
+}
+
+
+/**
+ * Loads the code signing blob if necessary (RTLDRMODMACHO::PtrCodeSignature).
+ *
+ * @returns IPRT status code.
+ * @param pThis The mach-o instance.
+ */
+static int rtldrMachO_LoadSignatureBlob(PRTLDRMODMACHO pThis)
+{
+ Assert(pThis->cbCodeSignature > 0);
+ if (pThis->PtrCodeSignature.pb != NULL)
+ return VINF_SUCCESS;
+
+ if ( pThis->cbCodeSignature > sizeof(RTCRAPLCSHDR)
+ && pThis->cbCodeSignature <= _1M)
+ {
+ /* Allocate and read. */
+ void *pv = RTMemAllocZ(RT_ALIGN_Z(pThis->cbCodeSignature, 16));
+ AssertReturn(pv, VERR_NO_MEMORY);
+ int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pv, pThis->cbCodeSignature,
+ pThis->offImage + pThis->offCodeSignature);
+ if (RT_SUCCESS(rc))
+ {
+ /* Check blob signature. */
+ PCRTCRAPLCSSUPERBLOB pSuper = (PCRTCRAPLCSSUPERBLOB)pv;
+ if (pSuper->Hdr.uMagic == RTCRAPLCS_MAGIC_EMBEDDED_SIGNATURE)
+ {
+ uint32_t cbHdr = RT_BE2H_U32(pSuper->Hdr.cb);
+ uint32_t cSlots = RT_BE2H_U32(pSuper->cSlots);
+ if ( cbHdr <= pThis->cbCodeSignature
+ && cbHdr > RT_UOFFSETOF(RTCRAPLCSSUPERBLOB, aSlots)
+ && cSlots > 0
+ && cSlots < 128
+ && RT_UOFFSETOF_DYN(RTCRAPLCSSUPERBLOB, aSlots[cSlots]) <= cbHdr)
+ {
+ pThis->PtrCodeSignature.pSuper = pSuper;
+ return VINF_SUCCESS;
+ }
+ rc = VERR_LDRVI_BAD_CERT_HDR_LENGTH;
+ }
+ else
+ rc = VERR_LDRVI_BAD_CERT_HDR_TYPE;
+ }
+ RTMemFree(pv);
+ return rc;
+ }
+ return VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY;
+}
+
+
+/**
+ * Handles a RTLDRPROP_PKCS7_SIGNED_DATA query.
+ */
+static int rtldrMachO_QueryPkcs7SignedData(PRTLDRMODMACHO pThis, void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ int rc = rtldrMachO_LoadSignatureBlob(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Locate the signature slot.
+ */
+ uint32_t iSlot = RT_BE2H_U32(pThis->PtrCodeSignature.pSuper->cSlots);
+ PCRTCRAPLCSBLOBSLOT pSlot = &pThis->PtrCodeSignature.pSuper->aSlots[iSlot];
+ while (iSlot-- > 0)
+ {
+ pSlot--;
+ if (pSlot->uType == RTCRAPLCS_SLOT_SIGNATURE)
+ {
+ /*
+ * Validate the data offset.
+ */
+ uint32_t offData = RT_BE2H_U32(pSlot->offData);
+ if ( offData < pThis->cbCodeSignature - sizeof(RTCRAPLCSHDR)
+ || !(offData & 3) )
+ {
+ /*
+ * The data is prefixed by a header with magic set to blob wrapper.
+ * Check that the size is within the bounds of the code signing blob.
+ */
+ PCRTCRAPLCSHDR pHdr = (PCRTCRAPLCSHDR)&pThis->PtrCodeSignature.pb[offData];
+ if (pHdr->uMagic == RTCRAPLCS_MAGIC_BLOBWRAPPER)
+ {
+ uint32_t cbData = RT_BE2H_U32(pHdr->cb);
+ uint32_t cbMax = pThis->cbCodeSignature - offData ;
+ if ( cbData <= cbMax
+ && cbData > sizeof(RTCRAPLCSHDR))
+ {
+ /*
+ * Copy out the requested data.
+ */
+ *pcbRet = cbData;
+ if (cbData <= cbBuf)
+ {
+ memcpy(pvBuf, pHdr + 1, cbData);
+ return VINF_SUCCESS;
+ }
+ memcpy(pvBuf, pHdr + 1, cbBuf);
+ return VERR_BUFFER_OVERFLOW;
+ }
+ }
+ }
+ return VERR_LDRVI_BAD_CERT_FORMAT;
+ }
+ }
+ rc = VERR_NOT_FOUND;
+ }
+ return rc;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnQueryProp} */
+static DECLCALLBACK(int) rtldrMachO_QueryProp(PRTLDRMODINTERNAL pMod, RTLDRPROP enmProp, void const *pvBits,
+ void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ int rc = VERR_NOT_FOUND;
+ switch (enmProp)
+ {
+ case RTLDRPROP_UUID:
+ Assert(cbBuf >= sizeof(pThis->abImageUuid));
+ if (!ASMMemIsZero(pThis->abImageUuid, sizeof(pThis->abImageUuid)))
+ {
+ *pcbRet = sizeof(pThis->abImageUuid);
+ memcpy(pvBuf, pThis->abImageUuid, sizeof(pThis->abImageUuid));
+ return VINF_SUCCESS;
+ }
+ break;
+
+ case RTLDRPROP_FILE_OFF_HEADER:
+ Assert(cbBuf == sizeof(uint32_t) || cbBuf == sizeof(uint64_t));
+ if (cbBuf == sizeof(uint32_t))
+ *(uint32_t *)pvBuf = pThis->offImage;
+ else
+ *(uint64_t *)pvBuf = pThis->offImage;
+ return VINF_SUCCESS;
+
+ case RTLDRPROP_IS_SIGNED:
+ Assert(cbBuf == sizeof(bool));
+ Assert(*pcbRet == cbBuf);
+ *(bool *)pvBuf = pThis->cbCodeSignature > 0;
+ return VINF_SUCCESS;
+
+ case RTLDRPROP_PKCS7_SIGNED_DATA:
+ if (pThis->cbCodeSignature > 0)
+ return rtldrMachO_QueryPkcs7SignedData(pThis, pvBuf, cbBuf, pcbRet);
+ break;
+
+
+#if 0 /** @todo return LC_ID_DYLIB */
+ case RTLDRPROP_INTERNAL_NAME:
+#endif
+
+ default:
+ break;
+ }
+ NOREF(cbBuf);
+ RT_NOREF_PV(pvBits);
+ return rc;
+}
+
+
+#ifndef IPRT_WITHOUT_LDR_VERIFY
+
+/**
+ * Decodes the signature blob at RTLDRMODMACHO::PtrCodeSignature.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module instance.
+ * @param ppSignature Where to return the decoded signature data.
+ * @param pErrInfo Where to supply extra error details. Optional.
+ */
+static int rtldrMachO_VerifySignatureDecode(PRTLDRMODMACHO pThis, PRTLDRMACHOSIGNATURE *ppSignature, PRTERRINFO pErrInfo)
+{
+ Assert(pThis->PtrCodeSignature.pSuper != NULL);
+
+ /*
+ * Allocate and init decoded signature data structure.
+ */
+ PRTLDRMACHOSIGNATURE pSignature = (PRTLDRMACHOSIGNATURE)RTMemTmpAllocZ(sizeof(*pSignature));
+ *ppSignature = pSignature;
+ if (!pSignature)
+ return VERR_NO_TMP_MEMORY;
+ pSignature->idxPkcs7 = UINT32_MAX;
+
+ /*
+ * Parse the slots, validating the slot headers.
+ */
+ PCRTCRAPLCSSUPERBLOB pSuper = pThis->PtrCodeSignature.pSuper;
+ uint32_t const cSlots = RT_BE2H_U32(pSuper->cSlots);
+ uint32_t const offFirst = RT_UOFFSETOF_DYN(RTCRAPLCSSUPERBLOB, aSlots[cSlots]);
+ uint32_t const cbBlob = RT_BE2H_U32(pSuper->Hdr.cb);
+ for (uint32_t iSlot = 0; iSlot < cSlots; iSlot++)
+ {
+ /*
+ * Check that the data offset is valid. There appears to be no alignment
+ * requirements here, which is a little weird consindering the PPC heritage.
+ */
+ uint32_t const offData = RT_BE2H_U32(pSuper->aSlots[iSlot].offData);
+ if ( offData < offFirst
+ || offData > cbBlob - sizeof(RTCRAPLCSHDR))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u has an invalid data offset: %#x (min %#x, max %#x-4)",
+ iSlot, offData, offFirst, cbBlob);
+ uint32_t const cbMaxData = cbBlob - offData;
+
+ /*
+ * PKCS#7/CMS signature.
+ */
+ if (pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_SIGNATURE)
+ {
+ if (pSignature->idxPkcs7 != UINT32_MAX)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Already have PKCS#7 data in slot %#u", iSlot, pSignature->idxPkcs7);
+ PCRTCRAPLCSHDR pHdr = (PCRTCRAPLCSHDR)&pThis->PtrCodeSignature.pb[offData];
+ if (pHdr->uMagic != RTCRAPLCS_MAGIC_BLOBWRAPPER)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Invalid PKCS#7 wrapper magic: %#x", iSlot, RT_BE2H_U32(pHdr->uMagic));
+ uint32_t const cb = RT_BE2H_U32(pHdr->cb);
+ if (cb > cbMaxData || cb < sizeof(*pHdr) + 2U)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Invalid PKCS#7 size is out of bound: %#x (min %#x, max %#x)",
+ iSlot, cb, sizeof(*pHdr) + 2, cbMaxData);
+ pSignature->idxPkcs7 = iSlot;
+ pSignature->pbPkcs7 = (uint8_t const *)(pHdr + 1);
+ pSignature->cbPkcs7 = cb - sizeof(*pHdr);
+ }
+ /*
+ * Code directories.
+ */
+ else if ( pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_CODEDIRECTORY
+ || ( RT_BE2H_U32(pSuper->aSlots[iSlot].uType) - RT_BE2H_U32_C(RTCRAPLCS_SLOT_ALTERNATE_CODEDIRECTORIES)
+ < RTCRAPLCS_SLOT_ALTERNATE_CODEDIRECTORIES_COUNT))
+ {
+ /* Make sure we don't get too many code directories and that the first one is a regular one. */
+ if (pSignature->cCodeDirs >= RT_ELEMENTS(pSignature->aCodeDirs))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Too many code directory slots (%u found thus far)",
+ iSlot, pSignature->cCodeDirs + 1);
+ if ( pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_CODEDIRECTORY
+ && pSignature->cCodeDirs > 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Already have primary code directory in slot #%u",
+ iSlot, pSignature->aCodeDirs[0].uSlot);
+ if ( pSuper->aSlots[iSlot].uType != RTCRAPLCS_SLOT_CODEDIRECTORY /* lazy bird */
+ && pSignature->cCodeDirs == 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Expected alternative code directory after the primary one", iSlot);
+
+ /* Check data header: */
+ if (cbMaxData < RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, uUnused1))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Insufficient data vailable for code directory (max %#x)", iSlot, cbMaxData);
+
+ PCRTCRAPLCSCODEDIRECTORY pCodeDir = (PCRTCRAPLCSCODEDIRECTORY)&pThis->PtrCodeSignature.pb[offData];
+ if (pCodeDir->Hdr.uMagic != RTCRAPLCS_MAGIC_CODEDIRECTORY)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Invalid code directory magic: %#x", iSlot, RT_BE2H_U32(pCodeDir->Hdr.uMagic));
+ uint32_t const cbCodeDir = RT_BE2H_U32(pCodeDir->Hdr.cb);
+ if (cbCodeDir > cbMaxData || cbCodeDir < RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, offScatter))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Code directory size is out of bound: %#x (min %#x, max %#x)",
+ iSlot, cbCodeDir, RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, offScatter), cbMaxData);
+ pSignature->aCodeDirs[pSignature->cCodeDirs].pCodeDir = pCodeDir;
+ pSignature->aCodeDirs[pSignature->cCodeDirs].cb = cbCodeDir;
+
+ /* Check Version: */
+ uint32_t const uVersion = RT_BE2H_U32(pCodeDir->uVersion);
+ if ( uVersion < RTCRAPLCS_VER_2_0
+ || uVersion >= RT_MAKE_U32(0, 3))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Code directory version is out of bounds: %#07x", iSlot, uVersion);
+ uint32_t cbSelf = uVersion >= RTCRAPLCS_VER_SUPPORTS_EXEC_SEG ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, fExecSeg)
+ : uVersion >= RTCRAPLCS_VER_SUPPORTS_CODE_LIMIT_64 ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, cbCodeLimit64)
+ : uVersion >= RTCRAPLCS_VER_SUPPORTS_TEAMID ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offTeamId)
+ : uVersion >= RTCRAPLCS_VER_SUPPORTS_SCATTER ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offScatter)
+ : RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, uUnused1);
+ if (cbSelf > cbCodeDir)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Code directory size is out of bound: %#x (min %#x, max %#x)",
+ iSlot, cbCodeDir, cbSelf, cbCodeDir);
+
+ /* hash type and size. */
+ uint8_t cbHash;
+ RTDIGESTTYPE enmDigest;
+ switch (pCodeDir->bHashType)
+ {
+ case RTCRAPLCS_HASHTYPE_SHA1:
+ enmDigest = RTDIGESTTYPE_SHA1;
+ cbHash = RTSHA1_HASH_SIZE;
+ break;
+ case RTCRAPLCS_HASHTYPE_SHA256:
+ enmDigest = RTDIGESTTYPE_SHA256;
+ cbHash = RTSHA256_HASH_SIZE;
+ break;
+ case RTCRAPLCS_HASHTYPE_SHA256_TRUNCATED:
+ enmDigest = RTDIGESTTYPE_SHA256;
+ cbHash = RTSHA1_HASH_SIZE; /* truncated to SHA-1 size. */
+ break;
+ case RTCRAPLCS_HASHTYPE_SHA384:
+ enmDigest = RTDIGESTTYPE_SHA384;
+ cbHash = RTSHA384_HASH_SIZE;
+ break;
+ default:
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Slot #%u: Unknown hash type %#x (LB %#x)",
+ iSlot, pCodeDir->bHashType, pCodeDir->cbHash);
+ }
+ pSignature->aCodeDirs[pSignature->cCodeDirs].enmDigest = enmDigest;
+ if (pCodeDir->cbHash != cbHash)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Unexpected hash size for %s: %#x, expected %#x",
+ iSlot, RTCrDigestTypeToName(enmDigest), pCodeDir->cbHash, cbHash);
+
+ /* Hash slot offset and counts. Special slots are counted backwards from offHashSlots. */
+ uint32_t const cSpecialSlots = RT_BE2H_U32(pCodeDir->cSpecialSlots);
+ if (cSpecialSlots > 256)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Too many special slots: %#x", iSlot, cSpecialSlots);
+ uint32_t const cCodeSlots = RT_BE2H_U32(pCodeDir->cCodeSlots);
+ if ( cCodeSlots >= UINT32_MAX / 2
+ || cCodeSlots + cSpecialSlots > (cbCodeDir - cbHash) / cbHash)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Slot #%u: Too many code slots: %#x + %#x (max %#x)",
+ iSlot, cCodeSlots, cSpecialSlots, (cbCodeDir - cbHash) / cbHash);
+ uint32_t const offHashSlots = RT_BE2H_U32(pCodeDir->offHashSlots);
+ if ( offHashSlots > cbCodeDir - cCodeSlots * cbHash
+ || offHashSlots < cbSelf + cSpecialSlots * cbHash)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Code directory hash offset is out of bounds: %#x (min: %#x, max: %#x)",
+ iSlot, offHashSlots, cbSelf + cSpecialSlots * cbHash, cbCodeDir - cCodeSlots * cbHash);
+
+ /* page shift */
+ if (pCodeDir->cPageShift == 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Unsupported page shift of zero in code directory", iSlot);
+ uint32_t cMaxPageShift;
+ if ( pThis->Core.enmArch == RTLDRARCH_AMD64
+ || pThis->Core.enmArch == RTLDRARCH_X86_32
+ || pThis->Core.enmArch == RTLDRARCH_ARM32)
+ cMaxPageShift = 12;
+ else if (pThis->Core.enmArch == RTLDRARCH_ARM64)
+ cMaxPageShift = 16; /* 16KB */
+ else
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Unsupported architecture: %d", pThis->Core.enmArch);
+ if ( pCodeDir->cPageShift < 12 /* */
+ || pCodeDir->cPageShift > cMaxPageShift)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Page shift in code directory is out of range: %d (min: 12, max: %d)",
+ iSlot, pCodeDir->cPageShift, cMaxPageShift);
+
+ /* code limit vs page shift and code hash slots */
+ uint32_t const cbCodeLimit32 = RT_BE2H_U32(pCodeDir->cbCodeLimit32);
+ uint32_t const cExpectedCodeHashes = pCodeDir->cPageShift == 0 ? 1
+ : (cbCodeLimit32 + RT_BIT_32(pCodeDir->cPageShift) - 1) >> pCodeDir->cPageShift;
+ if (cExpectedCodeHashes != cCodeSlots)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Code limit and page shift value does not match code hash slots: cbCodeLimit32=%#x cPageShift=%u -> %#x; cCodeSlots=%#x",
+ iSlot, cbCodeLimit32, pCodeDir->cPageShift, cExpectedCodeHashes, cCodeSlots);
+
+ /* Identifier offset: */
+ if (pCodeDir->offIdentifier)
+ {
+ uint32_t const offIdentifier = RT_BE2H_U32(pCodeDir->offIdentifier);
+ if ( offIdentifier < cbSelf
+ || offIdentifier >= cbCodeDir)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Identifier offset is out of bounds: %#x (min: %#x, max: %#x)",
+ iSlot, offIdentifier, cbSelf, cbCodeDir - 1);
+ int rc = RTStrValidateEncodingEx((char const *)pCodeDir + offIdentifier, cbCodeDir - offIdentifier,
+ RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Malformed identifier string: %Rrc", iSlot, rc);
+ }
+
+ /* Team identifier: */
+ if (cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offTeamId) && pCodeDir->offTeamId)
+ {
+ uint32_t const offTeamId = RT_BE2H_U32(pCodeDir->offTeamId);
+ if ( offTeamId < cbSelf
+ || offTeamId >= cbCodeDir)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Team identifier offset is out of bounds: %#x (min: %#x, max: %#x)",
+ iSlot, offTeamId, cbSelf, cbCodeDir - 1);
+ int rc = RTStrValidateEncodingEx((char const *)pCodeDir + offTeamId, cbCodeDir - offTeamId,
+ RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Malformed team identifier string: %Rrc", iSlot, rc);
+ }
+
+ /* We don't support scatter. */
+ if (cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offScatter) && pCodeDir->offScatter)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Scatter not supported.", iSlot);
+
+ /* We don't really support the 64-bit code limit either: */
+ if ( cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, cbCodeLimit64)
+ && pCodeDir->cbCodeLimit64
+ && RT_BE2H_U64(pCodeDir->cbCodeLimit64) != cbCodeLimit32)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: 64-bit code limit does not match 32-bit: %#RX64 vs %#RX32",
+ iSlot, RT_BE2H_U64(pCodeDir->cbCodeLimit64), cbCodeLimit32);
+
+ /* Check executable segment info if present: */
+ if ( cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, fExecSeg)
+ && ( pThis->offSeg0ForCodeSign != RT_BE2H_U64(pCodeDir->offExecSeg)
+ || pThis->cbSeg0ForCodeSign != RT_BE2H_U64(pCodeDir->cbExecSeg)
+ || pThis->fSeg0ForCodeSign != RT_BE2H_U64(pCodeDir->fExecSeg)) )
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Segment #0 info mismatch: @%#RX64 LB %#RX64 flags=%#RX64; expected @%#RX64 LB %#RX64 flags=%#RX64",
+ iSlot, RT_BE2H_U64(pCodeDir->offExecSeg), RT_BE2H_U64(pCodeDir->cbExecSeg),
+ RT_BE2H_U64(pCodeDir->fExecSeg), pThis->offSeg0ForCodeSign, pThis->cbSeg0ForCodeSign,
+ pThis->fSeg0ForCodeSign);
+
+ /* Check fields that must be zero (don't want anyone to use them to counter changes): */
+ if (pCodeDir->uUnused1 != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Unused field #1 is non-zero: %#x", iSlot, RT_BE2H_U32(pCodeDir->uUnused1));
+ if ( cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, uUnused2)
+ && pCodeDir->uUnused2 != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Unused field #2 is non-zero: %#x", iSlot, RT_BE2H_U32(pCodeDir->uUnused2));
+
+ /** @todo idPlatform values. */
+ /** @todo Check for gaps if we know the version number? Alignment? */
+
+ /* If first code directory, check that the code limit covers the whole image up to the signature data. */
+ if (pSignature->cCodeDirs == 0)
+ {
+ /** @todo verify the that the signature data is at the very end... */
+ if (cbCodeLimit32 != pThis->offCodeSignature)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Unexpected code limit: %#x, expected %#x",
+ iSlot, cbCodeLimit32, pThis->offCodeSignature);
+ }
+ /* Otherwise, check that the code limit matches the previous directories. */
+ else
+ for (uint32_t i = 0; i < pSignature->cCodeDirs; i++)
+ if (pSignature->aCodeDirs[i].pCodeDir->cbCodeLimit32 != pCodeDir->cbCodeLimit32)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Code limit differs from previous directory: %#x, expected %#x",
+ iSlot, cbCodeLimit32, RT_BE2H_U32(pSignature->aCodeDirs[i].pCodeDir->cbCodeLimit32));
+
+ /* Commit the code dir entry: */
+ pSignature->aCodeDirs[pSignature->cCodeDirs++].uSlot = iSlot;
+ }
+ }
+
+ /*
+ * Check that we've got at least one code directory and one PKCS#7 signature.
+ */
+ if (pSignature->cCodeDirs == 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "No code directory slot in the code signature");
+ if (pSignature->idxPkcs7 == UINT32_MAX)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "No PKCS#7 slot in the code signature");
+
+ /*
+ * Decode the PKCS#7 signature.
+ */
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pSignature->pbPkcs7, pSignature->cbPkcs7,
+ pErrInfo, &g_RTAsn1DefaultAllocator, 0, "Mach-O-BLOB");
+ int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &pSignature->ContentInfo, "CI");
+ if (RT_SUCCESS(rc))
+ {
+ if (RTCrPkcs7ContentInfo_IsSignedData(&pSignature->ContentInfo))
+ {
+ pSignature->pSignedData = pSignature->ContentInfo.u.pSignedData;
+
+ /*
+ * Check that the signedData stuff adds up.
+ */
+ if (!strcmp(pSignature->pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID))
+ {
+ rc = RTCrPkcs7SignedData_CheckSanity(pSignature->pSignedData,
+ RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE /** @todo consider not piggy-backing on auth-code */
+ | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
+ | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
+ pErrInfo, "SD");
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID,
+ "Unexpected pSignedData.ContentInfo.ContentType.szObjId value: %s (expected %s)",
+ pSignature->pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID, /** @todo error code*/
+ "PKCS#7 is not 'signedData': %s", pSignature->ContentInfo.ContentType.szObjId);
+ }
+ return rc;
+}
+
+/**
+ * Destroys the decoded signature data structure.
+ *
+ * @param pSignature The decoded signature data. Can be NULL.
+ */
+static void rtldrMachO_VerifySignatureDestroy(PRTLDRMACHOSIGNATURE pSignature)
+{
+ if (pSignature)
+ {
+ RTCrPkcs7ContentInfo_Delete(&pSignature->ContentInfo);
+ RTMemTmpFree(pSignature);
+ }
+}
+
+
+/**
+ * Worker for rtldrMachO_VerifySignatureValidatePkcs7Hashes that handles plists
+ * with code directory hashes inside them.
+ *
+ * It is assumed that these plist files was invented to handle alternative code
+ * directories.
+ *
+ * @note Putting an XML plist into the authenticated attribute list was
+ * probably not such a great idea, given all the optional and
+ * adjustable white-space padding. We should probably validate
+ * everything very strictly, limiting the elements, require certain
+ * attribute lists and even have strict expectations about the
+ * white-space, but right now let just make sure it's xml and get the
+ * data in the cdhashes array.
+ *
+ * @todo The code here is a little braindead and bulky. It should be
+ * possible to describe the expected XML structure using a tables.
+ */
+static int rtldrMachO_VerifySignatureValidateCdHashesPlist(PRTLDRMACHOSIGNATURE pSignature, char *pszPlist,
+ uint8_t *pbHash, uint32_t cbHash, PRTERRINFO pErrInfo)
+{
+ const char * const pszStart = pszPlist;
+#define CHECK_ISTR_AND_SKIP_OR_RETURN(a_szLead) \
+ do { \
+ if (!RTStrNICmp(pszPlist, RT_STR_TUPLE(a_szLead))) \
+ pszPlist += sizeof(a_szLead) - 1; \
+ else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
+ "Expected '%s' found '%.16s...' at %#zu in plist", a_szLead, pszPlist, pszPlist - pszStart); \
+ } while (0)
+#define CHECK_STR_AND_SKIP_OR_RETURN(a_szLead) \
+ do { \
+ if (!RTStrNCmp(pszPlist, RT_STR_TUPLE(a_szLead))) \
+ pszPlist += sizeof(a_szLead) - 1; \
+ else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
+ "Expected '%s' found '%.16s...' at %#zu in plist", a_szLead, pszPlist, pszPlist - pszStart); \
+ } while (0)
+#define SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN() \
+ do { /* currently only permitting spaces, tabs and newline, following char must be '<'. */ \
+ char chMacro; \
+ while ((chMacro = *pszPlist) == ' ' || chMacro == '\n' || chMacro == '\t') \
+ pszPlist++; \
+ if (chMacro == '<') { /* likely */ } \
+ else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
+ "Expected '<' found '%.16s...' at %#zu in plist", pszPlist, pszPlist - pszStart); \
+ } while (0)
+#define SKIP_SPACE_BEFORE_VALUE() \
+ do { /* currently only permitting spaces, tabs and newline. */ \
+ char chMacro; \
+ while ((chMacro = *pszPlist) == ' ' || chMacro == '\n' || chMacro == '\t') \
+ pszPlist++; \
+ } while (0)
+#define SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN() \
+ do { /* currently only permitting a single space */ \
+ if (pszPlist[0] == ' ' && pszPlist[1] != ' ') \
+ pszPlist++; \
+ else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
+ "Expected ' ' found '%.16s...' at %#zu in plist", pszPlist, pszPlist - pszStart); \
+ } while (0)
+
+ /* Example:
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>cdhashes</key>
+ <array>
+ <data>
+ hul2SSkDQFRXbGlt3AmCp25MU0Y=
+ </data>
+ <data>
+ N0kvxg0CJBNuZTq135PntAaRczw=
+ </data>
+ </array>
+</dict>
+</plist>
+ */
+
+ /* <?xml version="1.0" encoding="UTF-8"?> */
+ CHECK_STR_AND_SKIP_OR_RETURN("<?xml");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("version=\"1.0\"");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("encoding=\"UTF-8\"");
+ CHECK_STR_AND_SKIP_OR_RETURN("?>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /* <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> */
+ CHECK_STR_AND_SKIP_OR_RETURN("<!DOCTYPE");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("plist");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("PUBLIC");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("\"-//Apple//DTD PLIST 1.0//EN\"");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");
+ CHECK_STR_AND_SKIP_OR_RETURN(">");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /* <plist version="1.0"> */
+ CHECK_STR_AND_SKIP_OR_RETURN("<plist");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("version=\"1.0\"");
+ CHECK_STR_AND_SKIP_OR_RETURN(">");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /* <dict> */
+ CHECK_STR_AND_SKIP_OR_RETURN("<dict>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /* <key>cdhashes</key> */
+ CHECK_STR_AND_SKIP_OR_RETURN("<key>cdhashes</key>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /* <array> */
+ CHECK_STR_AND_SKIP_OR_RETURN("<array>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /*
+ * Repeated: <data>hul2SSkDQFRXbGlt3AmCp25MU0Y=</data>
+ */
+ uint32_t iCodeDir = 0;
+ for (;;)
+ {
+ /* Decode the binary data (base64) and skip it. */
+ CHECK_STR_AND_SKIP_OR_RETURN("<data>");
+ SKIP_SPACE_BEFORE_VALUE();
+
+ char ch;
+ size_t cchBase64 = 0;
+ while (RT_C_IS_ALNUM(ch = pszPlist[cchBase64]) || ch == '+' || ch == '/' || ch == '=')
+ cchBase64++;
+ size_t cbActualHash = cbHash;
+ char *pszEnd = NULL;
+ int rc = RTBase64DecodeEx(pszPlist, cchBase64, pbHash, cbHash, &cbActualHash, &pszEnd);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Failed to decode hash #%u in authenticated plist attribute: %Rrc (%.*s)",
+ iCodeDir, rc, cchBase64, pszPlist);
+ pszPlist += cchBase64;
+ AssertReturn(pszPlist == pszEnd, VERR_INTERNAL_ERROR_2);
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /* The binary hash data must be exactly the size of SHA1, larger
+ hash like SHA-256 and SHA-384 are truncated for some reason. */
+ if (cbActualHash != RTSHA1_HASH_SIZE)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Hash #%u in authenticated plist attribute has the wrong length: %u, exepcted %u",
+ iCodeDir, cbActualHash, RTSHA1_HASH_SIZE);
+
+ /* Skip closing tag. */
+ CHECK_STR_AND_SKIP_OR_RETURN("</data>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+
+ /* Calculate the hash and compare. */
+ RTCRDIGEST hDigest;
+ rc = RTCrDigestCreateByType(&hDigest, pSignature->aCodeDirs[iCodeDir].enmDigest);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrDigestUpdate(hDigest, pSignature->aCodeDirs[iCodeDir].pCodeDir, pSignature->aCodeDirs[iCodeDir].cb);
+ if (RT_SUCCESS(rc))
+ {
+ if (memcmp(pbHash, RTCrDigestGetHash(hDigest), cbActualHash) == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_IMAGE_HASH_MISMATCH,
+ "Code directory #%u hash mismatch (plist):\n"
+ "signed: %.*Rhxs\n"
+ "our: %.*Rhxs\n",
+ iCodeDir, cbActualHash, pbHash,
+ RTCrDigestGetHashSize(hDigest), RTCrDigestGetHash(hDigest));
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTCrDigestUpdate failed: %Rrc", rc);
+ RTCrDigestRelease(hDigest);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "Failed to create a digest of type %u verifying code dir #%u: %Rrc",
+ pSignature->aCodeDirs[iCodeDir].enmDigest, iCodeDir, rc);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Advance.
+ */
+ iCodeDir++;
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+ if (RTStrNCmp(pszPlist, RT_STR_TUPLE("<data>")) == 0)
+ {
+ if (iCodeDir >= pSignature->cCodeDirs)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Authenticated plist attribute has too many code directories (%u in blob)",
+ pSignature->cCodeDirs);
+ }
+ else if (iCodeDir == pSignature->cCodeDirs)
+ break;
+ else
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Authenticated plist attribute does not include all code directors: %u out of %u",
+ iCodeDir, pSignature->cCodeDirs);
+ }
+
+ /*</array>*/
+ CHECK_STR_AND_SKIP_OR_RETURN("</array>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /*</dict>*/
+ CHECK_STR_AND_SKIP_OR_RETURN("</dict>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /*</plist>*/
+ CHECK_STR_AND_SKIP_OR_RETURN("</plist>");
+ SKIP_SPACE_BEFORE_VALUE();
+
+ if (*pszPlist == '\0')
+ return VINF_SUCCESS;
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Authenticated plist attribute has unexpected trailing content: %.32s", pszPlist);
+}
+
+
+/**
+ * Verifies the code directory hashes embedded in the PKCS\#7 data.
+ *
+ * @returns IPRT status code.
+ * @param pSignature The decoded signature data.
+ * @param pErrInfo Where to supply extra error details. Optional.
+ */
+static int rtldrMachO_VerifySignatureValidatePkcs7Hashes(PRTLDRMACHOSIGNATURE pSignature, PRTERRINFO pErrInfo)
+{
+ /*
+ * Look thru the authenticated attributes in the signer info array.
+ */
+ PRTCRPKCS7SIGNEDDATA pSignedData = pSignature->pSignedData;
+ for (uint32_t iSignerInfo = 0; iSignerInfo < pSignedData->SignerInfos.cItems; iSignerInfo++)
+ {
+ PCRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[iSignerInfo];
+ bool fMsgDigest = false;
+ bool fPlist = false;
+ for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->AuthenticatedAttributes.cItems; iAttrib++)
+ {
+ PCRTCRPKCS7ATTRIBUTE pAttrib = pSignerInfo->AuthenticatedAttributes.papItems[iAttrib];
+ if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_MESSAGE_DIGEST_OID) == 0)
+ {
+ /*
+ * Validate the message digest while we're here.
+ */
+ AssertReturn(pAttrib->uValues.pOctetStrings && pAttrib->uValues.pOctetStrings->cItems == 1, VERR_INTERNAL_ERROR_5);
+
+ RTCRDIGEST hDigest;
+ int rc = RTCrDigestCreateByObjId(&hDigest, &pSignerInfo->DigestAlgorithm.Algorithm);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrDigestUpdate(hDigest, pSignature->aCodeDirs[0].pCodeDir, pSignature->aCodeDirs[0].cb);
+ if (RT_SUCCESS(rc))
+ {
+ if (!RTCrDigestMatch(hDigest,
+ pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv,
+ pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb))
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MESSAGE_DIGEST_ATTRIB_MISMATCH,
+ "Authenticated message-digest attribute mismatch:\n"
+ "signed: %.*Rhxs\n"
+ "our: %.*Rhxs\n",
+ pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb,
+ pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv,
+ RTCrDigestGetHashSize(hDigest), RTCrDigestGetHash(hDigest));
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTCrDigestUpdate failed: %Rrc", rc);
+ RTCrDigestRelease(hDigest);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "Failed to create a digest for OID %s: %Rrc",
+ pSignerInfo->DigestAlgorithm.Algorithm.szObjId, rc);
+ if (RT_FAILURE(rc))
+ return rc;
+ fMsgDigest = true;
+ }
+ else if (pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_APPLE_MULTI_CD_PLIST)
+ {
+ /*
+ * An XML (better be) property list with code directory hashes in it.
+ */
+ if (!pAttrib->uValues.pOctetStrings || pAttrib->uValues.pOctetStrings->cItems != 1)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Bad authenticated plist attribute");
+
+ uint32_t cch = pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb;
+ char const *pch = pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pch;
+ int rc = RTStrValidateEncodingEx(pch, cch, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Authenticated plist attribute is not valid UTF-8: %Rrc", rc);
+ uint32_t const cchMin = sizeof("<?xml?><plist><dict><key>cdhashes</key><array><data>hul2SSkDQFRXbGlt3AmCp25MU0Y=</data></array></dict></plist>") - 1;
+ if (cch < cchMin)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Authenticated plist attribute is too short: %#x, min: %#x", cch, cchMin);
+ if (cch > _64K)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Authenticated plist attribute is too long: %#x, max: 64KB", cch);
+
+ /* Copy the plist into a buffer and zero terminate it. Also allocate room for decoding a hash. */
+ const uint32_t cbMaxHash = 128;
+ char *pszTmp = (char *)RTMemTmpAlloc(cbMaxHash + cch + 3);
+ if (!pszTmp)
+ return VERR_NO_TMP_MEMORY;
+ pszTmp[cbMaxHash + cch] = '\0';
+ pszTmp[cbMaxHash + cch + 1] = '\0';
+ pszTmp[cbMaxHash + cch + 2] = '\0';
+ rc = rtldrMachO_VerifySignatureValidateCdHashesPlist(pSignature, (char *)memcpy(pszTmp + cbMaxHash, pch, cch),
+ (uint8_t *)pszTmp, cbMaxHash, pErrInfo);
+ RTMemTmpFree(pszTmp);
+ if (RT_FAILURE(rc))
+ return rc;
+ fPlist = true;
+ }
+ }
+ if (!fMsgDigest && pSignature->cCodeDirs > 1)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Missing authenticated message-digest attribute");
+ if (!fPlist && pSignature->cCodeDirs > 1)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Missing authenticated code directory hash plist attribute");
+ }
+ if (pSignedData->SignerInfos.cItems < 1)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "PKCS#7 signed data contains no signatures");
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Verifies the page hashes of the given code directory.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module instance.
+ * @param pEntry The data entry for the code directory to validate.
+ * @param pbBuf Read buffer.
+ * @param cbBuf Buffer size.
+ * @param pErrInfo Where to supply extra error details. Optional.
+ */
+static int rtldrMachO_VerifySignatureValidateCodeDir(PRTLDRMODMACHO pThis, PRTLDRMACHCODEDIR pEntry,
+ uint8_t *pbBuf, uint32_t cbBuf, PRTERRINFO pErrInfo)
+{
+ RTCRDIGEST hDigest;
+ int rc = RTCrDigestCreateByType(&hDigest, pEntry->enmDigest);
+ if (RT_SUCCESS(rc))
+ {
+ PCRTCRAPLCSCODEDIRECTORY pCodeDir = pEntry->pCodeDir;
+ PRTLDRREADER const pRdr = pThis->Core.pReader;
+ uint32_t cbCodeLimit = RT_BE2H_U32(pCodeDir->cbCodeLimit32);
+ uint32_t const cbPage = RT_BIT_32(pCodeDir->cPageShift);
+ uint32_t const cHashes = RT_BE2H_U32(pCodeDir->cCodeSlots);
+ uint8_t const cbHash = pCodeDir->cbHash;
+ uint8_t const *pbHash = (uint8_t const *)pCodeDir + RT_BE2H_U32(pCodeDir->offHashSlots);
+ RTFOFF offFile = pThis->offImage;
+ if ( RT_BE2H_U32(pCodeDir->uVersion) < RTCRAPLCS_VER_SUPPORTS_SCATTER
+ || pCodeDir->offScatter == 0)
+ {
+ /*
+ * Work the image in linear fashion.
+ */
+ for (uint32_t iHash = 0; iHash < cHashes; iHash++, pbHash += cbHash, cbCodeLimit -= cbPage)
+ {
+ RTFOFF const offPage = offFile;
+
+ /*
+ * Read and digest the data for the current hash page.
+ */
+ rc = RTCrDigestReset(hDigest);
+ AssertRCBreak(rc);
+ Assert(cbCodeLimit > cbPage || iHash + 1 == cHashes);
+ uint32_t cbLeft = iHash + 1 < cHashes ? cbPage : cbCodeLimit;
+ while (cbLeft > 0)
+ {
+ uint32_t const cbToRead = RT_MIN(cbBuf, cbLeft);
+ rc = pRdr->pfnRead(pRdr, pbBuf, cbToRead, offFile);
+ AssertRCBreak(rc);
+
+ rc = RTCrDigestUpdate(hDigest, pbBuf, cbToRead);
+ AssertRCBreak(rc);
+
+ offFile += cbToRead;
+ cbLeft -= cbToRead;
+ }
+ AssertRCBreak(rc);
+ rc = RTCrDigestFinal(hDigest, NULL, 0);
+ AssertRCBreak(rc);
+
+ /*
+ * Compare it.
+ * Note! Don't use RTCrDigestMatch here as there is a truncated SHA-256 variant.
+ */
+ if (memcmp(pbHash, RTCrDigestGetHash(hDigest), cbHash) != 0)
+ {
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_MISMATCH,
+ "Hash #%u (@%RX64 LB %#x) mismatch in code dir #%u: %.*Rhxs, expected %.*Rhxs",
+ iHash, offPage, cbPage, pEntry->uSlot, (int)cbHash, pbHash,
+ (int)cbHash, RTCrDigestGetHash(hDigest));
+ break;
+ }
+
+ }
+ }
+ /*
+ * Work the image in scattered fashion.
+ */
+ else
+ rc = VERR_INTERNAL_ERROR_4;
+
+ RTCrDigestRelease(hDigest);
+ }
+ return rc;
+}
+
+
+/**
+ * Verifies the page hashes of all the code directories
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module instance.
+ * @param pSignature The decoded signature data.
+ * @param pErrInfo Where to supply extra error details. Optional.
+ */
+static int rtldrMachO_VerifySignatureValidateCodeDirs(PRTLDRMODMACHO pThis, PRTLDRMACHOSIGNATURE pSignature, PRTERRINFO pErrInfo)
+{
+ void *pvBuf = RTMemTmpAllocZ(_4K);
+ if (pvBuf)
+ {
+ int rc = VERR_INTERNAL_ERROR_3;
+ for (uint32_t i = 0; i < pSignature->cCodeDirs; i++)
+ {
+ rc = rtldrMachO_VerifySignatureValidateCodeDir(pThis, &pSignature->aCodeDirs[i], (uint8_t *)pvBuf, _4K, pErrInfo);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ RTMemTmpFree(pvBuf);
+ return rc;
+ }
+ return VERR_NO_TMP_MEMORY;
+}
+
+#endif /* !IPRT_WITHOUT_LDR_VERIFY*/
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnVerifySignature}
+ */
+static DECLCALLBACK(int)
+rtldrMachO_VerifySignature(PRTLDRMODINTERNAL pMod, PFNRTLDRVALIDATESIGNEDDATA pfnCallback, void *pvUser, PRTERRINFO pErrInfo)
+{
+#ifndef IPRT_WITHOUT_LDR_VERIFY
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ int rc = rtldrMachO_LoadSignatureBlob(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ PRTLDRMACHOSIGNATURE pSignature = NULL;
+ rc = rtldrMachO_VerifySignatureDecode(pThis, &pSignature, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtldrMachO_VerifySignatureValidatePkcs7Hashes(pSignature, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtldrMachO_VerifySignatureValidateCodeDirs(pThis, pSignature, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Finally, let the caller verify the certificate chain for the PKCS#7 bit.
+ */
+ RTLDRSIGNATUREINFO Info;
+ Info.iSignature = 0;
+ Info.cSignatures = 1;
+ Info.enmType = RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA;
+ Info.pvSignature = &pSignature->ContentInfo;
+ Info.cbSignature = sizeof(pSignature->ContentInfo);
+ Info.pvExternalData = pSignature->aCodeDirs[0].pCodeDir;
+ Info.cbExternalData = pSignature->aCodeDirs[0].cb;
+ rc = pfnCallback(&pThis->Core, &Info, pErrInfo, pvUser);
+ }
+ }
+ }
+ rtldrMachO_VerifySignatureDestroy(pSignature);
+ }
+ return rc;
+#else
+ RT_NOREF_PV(pMod); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(pvUser); RT_NOREF_PV(pErrInfo);
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+/**
+ * Operations for a Mach-O module interpreter.
+ */
+static const RTLDROPS s_rtldrMachOOps=
+{
+ "mach-o",
+ rtldrMachO_Close,
+ NULL,
+ NULL /*pfnDone*/,
+ rtldrMachO_EnumSymbols,
+ /* ext */
+ rtldrMachO_GetImageSize,
+ rtldrMachO_GetBits,
+ rtldrMachO_RelocateBits,
+ rtldrMachO_GetSymbolEx,
+ NULL /*pfnQueryForwarderInfo*/,
+ rtldrMachO_EnumDbgInfo,
+ rtldrMachO_EnumSegments,
+ rtldrMachO_LinkAddressToSegOffset,
+ rtldrMachO_LinkAddressToRva,
+ rtldrMachO_SegOffsetToRva,
+ rtldrMachO_RvaToSegOffset,
+ rtldrMachO_ReadDbgInfo,
+ rtldrMachO_QueryProp,
+ rtldrMachO_VerifySignature,
+ NULL /*pfnHashImage*/,
+ NULL /*pfnUnwindFrame*/,
+ 42
+};
+
+
+/**
+ * Handles opening Mach-O images (non-fat).
+ */
+DECLHIDDEN(int) rtldrMachOOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offImage,
+ PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
+{
+
+ /*
+ * Create the instance data and do a minimal header validation.
+ */
+ PRTLDRMODMACHO pThis = NULL;
+ int rc = kldrModMachODoCreate(pReader, offImage, fFlags, &pThis, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Match up against the requested CPU architecture.
+ */
+ if ( enmArch == RTLDRARCH_WHATEVER
+ || pThis->Core.enmArch == enmArch)
+ {
+ pThis->Core.pOps = &s_rtldrMachOOps;
+ pThis->Core.u32Magic = RTLDRMOD_MAGIC;
+ *phLdrMod = &pThis->Core;
+ return VINF_SUCCESS;
+ }
+ rc = VERR_LDR_ARCH_MISMATCH;
+ }
+ if (pThis)
+ {
+ RTMemFree(pThis->pbLoadCommands);
+ RTMemFree(pThis);
+ }
+ return rc;
+
+}
+
+
+/**
+ * Handles opening FAT Mach-O image.
+ */
+DECLHIDDEN(int) rtldrFatOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
+{
+ fat_header_t FatHdr;
+ int rc = pReader->pfnRead(pReader, &FatHdr, sizeof(FatHdr), 0);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
+
+ if (FatHdr.magic == IMAGE_FAT_SIGNATURE)
+ { /* likely */ }
+ else if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
+ FatHdr.nfat_arch = RT_BSWAP_U32(FatHdr.nfat_arch);
+ else
+ return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "magic=%#x", FatHdr.magic);
+ if (FatHdr.nfat_arch < 64)
+ return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "Bad nfat_arch value: %#x", FatHdr.nfat_arch);
+
+ uint32_t offEntry = sizeof(FatHdr);
+ for (uint32_t i = 0; i < FatHdr.nfat_arch; i++, offEntry += sizeof(fat_arch_t))
+ {
+ fat_arch_t FatEntry;
+ rc = pReader->pfnRead(pReader, &FatEntry, sizeof(FatEntry), offEntry);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
+ if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
+ {
+ FatEntry.cputype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cputype);
+ //FatEntry.cpusubtype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cpusubtype);
+ FatEntry.offset = RT_BSWAP_U32(FatEntry.offset);
+ //FatEntry.size = RT_BSWAP_U32(FatEntry.size);
+ //FatEntry.align = RT_BSWAP_U32(FatEntry.align);
+ }
+
+ /*
+ * Match enmArch.
+ */
+ bool fMatch = false;
+ switch (enmArch)
+ {
+ case RTLDRARCH_WHATEVER:
+ fMatch = true;
+ break;
+
+ case RTLDRARCH_X86_32:
+ fMatch = FatEntry.cputype == CPU_TYPE_X86;
+ break;
+
+ case RTLDRARCH_AMD64:
+ fMatch = FatEntry.cputype == CPU_TYPE_X86_64;
+ break;
+
+ case RTLDRARCH_ARM32:
+ fMatch = FatEntry.cputype == CPU_TYPE_ARM32;
+ break;
+
+ case RTLDRARCH_ARM64:
+ fMatch = FatEntry.cputype == CPU_TYPE_ARM64;
+ break;
+
+ case RTLDRARCH_X86_16:
+ fMatch = false;
+ break;
+
+ case RTLDRARCH_INVALID:
+ case RTLDRARCH_HOST:
+ case RTLDRARCH_END:
+ case RTLDRARCH_32BIT_HACK:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+ if (fMatch)
+ return rtldrMachOOpen(pReader, fFlags, enmArch, FatEntry.offset, phLdrMod, pErrInfo);
+ }
+
+ return VERR_LDR_ARCH_MISMATCH;
+
+}
+