Index: trunk/src/VBox/Runtime/common/ldr/ldrLX.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/ldr/ldrLX.cpp	(revision 74636)
+++ trunk/src/VBox/Runtime/common/ldr/ldrLX.cpp	(revision 74636)
@@ -0,0 +1,2702 @@
+/* $Id$ */
+/** @file
+ * kLdr - The Module Interpreter for the Linear eXecutable (LX) Format.
+ */
+
+/*
+ * Copyright (c) 2006-2007 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                                                               *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+#include <k/kLdrFmts/lx.h>
+
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+/** @def KLDRMODLX_STRICT
+ * Define KLDRMODLX_STRICT to enabled strict checks in KLDRMODLX. */
+#define KLDRMODLX_STRICT 1
+
+/** @def KLDRMODLX_ASSERT
+ * Assert that an expression is true when KLDR_STRICT is defined.
+ */
+#ifdef KLDRMODLX_STRICT
+# define KLDRMODLX_ASSERT(expr)  kHlpAssert(expr)
+#else
+# define KLDRMODLX_ASSERT(expr)  do {} while (0)
+#endif
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+/**
+ * Instance data for the LX module interpreter.
+ */
+typedef struct KLDRMODLX
+{
+    /** Pointer to the module. (Follows the section table.) */
+    PKLDRMOD                pMod;
+    /** Pointer to the user mapping. */
+    const void             *pvMapping;
+    /** The size of the mapped LX image. */
+    KSIZE                   cbMapped;
+    /** Reserved flags. */
+    KU32                    f32Reserved;
+
+    /** The offset of the LX header. */
+    KLDRFOFF                offHdr;
+    /** Copy of the LX header. */
+    struct e32_exe          Hdr;
+
+    /** Pointer to the loader section.
+     * Allocated together with this strcture. */
+    const KU8              *pbLoaderSection;
+    /** Pointer to the last byte in the loader section. */
+    const KU8              *pbLoaderSectionLast;
+    /** Pointer to the object table in the loader section. */
+    const struct o32_obj   *paObjs;
+    /** Pointer to the object page map table in the loader section. */
+    const struct o32_map   *paPageMappings;
+    /** Pointer to the resource table in the loader section. */
+    const struct rsrc32    *paRsrcs;
+    /** Pointer to the resident name table in the loader section. */
+    const KU8              *pbResNameTab;
+    /** Pointer to the entry table in the loader section. */
+    const KU8              *pbEntryTab;
+
+    /** Pointer to the non-resident name table. */
+    KU8                    *pbNonResNameTab;
+    /** Pointer to the last byte in the non-resident name table. */
+    const KU8              *pbNonResNameTabLast;
+
+    /** Pointer to the fixup section. */
+    KU8                    *pbFixupSection;
+    /** Pointer to the last byte in the fixup section. */
+    const KU8              *pbFixupSectionLast;
+    /** Pointer to the fixup page table within pvFixupSection. */
+    const KU32             *paoffPageFixups;
+    /** Pointer to the fixup record table within pvFixupSection. */
+    const KU8              *pbFixupRecs;
+    /** Pointer to the import module name table within pvFixupSection. */
+    const KU8              *pbImportMods;
+    /** Pointer to the import module name table within pvFixupSection. */
+    const KU8              *pbImportProcs;
+} KLDRMODLX, *PKLDRMODLX;
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+static int kldrModLXHasDbgInfo(PKLDRMOD pMod, const void *pvBits);
+static int kldrModLXRelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress,
+                                 PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);
+static int kldrModLXDoCreate(PKRDR pRdr, KLDRFOFF offNewHdr, PKLDRMODLX *ppModLX);
+static const KU8 *kldrModLXDoNameTableLookupByOrdinal(const KU8 *pbNameTable, KSSIZE cbNameTable, KU32 iOrdinal);
+static int kldrModLXDoNameLookup(PKLDRMODLX pModLX, const char *pchSymbol, KSIZE cchSymbol, KU32 *piSymbol);
+static const KU8 *kldrModLXDoNameTableLookupByName(const KU8 *pbNameTable, KSSIZE cbNameTable,
+                                                       const char *pchSymbol, KSIZE cchSymbol);
+static int kldrModLXDoLoadBits(PKLDRMODLX pModLX, void *pvBits);
+static int kldrModLXDoIterDataUnpacking(KU8 *pbDst, const KU8 *pbSrc, int cbSrc);
+static int kldrModLXDoIterData2Unpacking(KU8 *pbDst, const KU8 *pbSrc, int cbSrc);
+static void kLdrModLXMemCopyW(KU8 *pbDst, const KU8 *pbSrc, int cb);
+static int kldrModLXDoProtect(PKLDRMODLX pModLX, void *pvBits, unsigned fUnprotectOrProtect);
+static int kldrModLXDoCallDLL(PKLDRMODLX pModLX, void *pvMapping, unsigned uOp, KUPTR uHandle);
+static int kldrModLXDoForwarderQuery(PKLDRMODLX pModLX, const struct e32_entry *pEntry,
+                                     PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind);
+static int kldrModLXDoLoadFixupSection(PKLDRMODLX pModLX);
+static KI32 kldrModLXDoCall(KUPTR uEntrypoint, KUPTR uHandle, KU32 uOp, void *pvReserved);
+static int kldrModLXDoReloc(KU8 *pbPage, int off, KLDRADDR PageAddress, const struct r32_rlc *prlc,
+                            int iSelector, KLDRADDR uValue, KU32 fKind);
+
+
+/**
+ * Create a loader module instance interpreting the executable image found
+ * in the specified file provider instance.
+ *
+ * @returns 0 on success and *ppMod pointing to a module instance.
+ *          On failure, a non-zero OS specific error code is returned.
+ * @param   pOps            Pointer to the registered method table.
+ * @param   pRdr            The file provider instance to use.
+ * @param   fFlags          Flags, MBZ.
+ * @param   enmCpuArch      The desired CPU architecture. KCPUARCH_UNKNOWN means
+ *                          anything goes, but with a preference for the current
+ *                          host architecture.
+ * @param   offNewHdr       The offset of the new header in MZ files. -1 if not found.
+ * @param   ppMod           Where to store the module instance pointer.
+ */
+static int kldrModLXCreate(PCKLDRMODOPS pOps, PKRDR pRdr, KU32 fFlags, KCPUARCH enmCpuArch, KLDRFOFF offNewHdr, PPKLDRMOD ppMod)
+{
+    PKLDRMODLX pModLX;
+    int rc;
+    K_NOREF(fFlags);
+
+    /*
+     * Create the instance data and do a minimal header validation.
+     */
+    rc = kldrModLXDoCreate(pRdr, offNewHdr, &pModLX);
+    if (!rc)
+    {
+        /*
+         * Match up against the requested CPU architecture.
+         */
+        if (    enmCpuArch == KCPUARCH_UNKNOWN
+            ||  pModLX->pMod->enmArch == enmCpuArch)
+        {
+            pModLX->pMod->pOps = pOps;
+            pModLX->pMod->u32Magic = KLDRMOD_MAGIC;
+            *ppMod = pModLX->pMod;
+            return 0;
+        }
+        rc = KLDR_ERR_CPU_ARCH_MISMATCH;
+    }
+    kHlpFree(pModLX);
+    return rc;
+}
+
+
+/**
+ * Separate function for reading creating the LX module instance to
+ * simplify cleanup on failure.
+ */
+static int kldrModLXDoCreate(PKRDR pRdr, KLDRFOFF offNewHdr, PKLDRMODLX *ppModLX)
+{
+    struct e32_exe Hdr;
+    PKLDRMODLX pModLX;
+    PKLDRMOD pMod;
+    KSIZE cb;
+    KSIZE cchFilename;
+    KSIZE offLdrStuff;
+    KU32 off, offEnd;
+    KU32 i;
+    int rc;
+    int fCanOptimizeMapping;
+    KU32 NextRVA;
+    *ppModLX = NULL;
+
+    /*
+     * Read the signature and file header.
+     */
+    rc = kRdrRead(pRdr, &Hdr, sizeof(Hdr), offNewHdr > 0 ? offNewHdr : 0);
+    if (rc)
+        return rc;
+    if (    Hdr.e32_magic[0] != E32MAGIC1
+        ||  Hdr.e32_magic[1] != E32MAGIC2)
+        return KLDR_ERR_UNKNOWN_FORMAT;
+
+    /* We're not interested in anything but x86 images. */
+    if (    Hdr.e32_level != E32LEVEL
+        ||  Hdr.e32_border != E32LEBO
+        ||  Hdr.e32_worder != E32LEWO
+        ||  Hdr.e32_cpu < E32CPU286
+        ||  Hdr.e32_cpu > E32CPU486
+        ||  Hdr.e32_pagesize != OBJPAGELEN
+        )
+        return KLDR_ERR_LX_BAD_HEADER;
+
+    /* Some rough sanity checks. */
+    offEnd = kRdrSize(pRdr) >= (KLDRFOFF)~(KU32)16 ? ~(KU32)16 : (KU32)kRdrSize(pRdr);
+    if (    Hdr.e32_itermap > offEnd
+        ||  Hdr.e32_datapage > offEnd
+        ||  Hdr.e32_nrestab > offEnd
+        ||  Hdr.e32_nrestab + Hdr.e32_cbnrestab > offEnd
+        ||  Hdr.e32_ldrsize > offEnd - offNewHdr - sizeof(Hdr)
+        ||  Hdr.e32_fixupsize > offEnd - offNewHdr - sizeof(Hdr)
+        ||  Hdr.e32_fixupsize + Hdr.e32_ldrsize > offEnd - offNewHdr - sizeof(Hdr))
+        return KLDR_ERR_LX_BAD_HEADER;
+
+    /* Verify the loader section. */
+    offEnd = Hdr.e32_objtab + Hdr.e32_ldrsize;
+    if (Hdr.e32_objtab < sizeof(Hdr))
+        return KLDR_ERR_LX_BAD_LOADER_SECTION;
+    off = Hdr.e32_objtab + sizeof(struct o32_obj) * Hdr.e32_objcnt;
+    if (off > offEnd)
+        return KLDR_ERR_LX_BAD_LOADER_SECTION;
+    if (    Hdr.e32_objmap
+        &&  (Hdr.e32_objmap < off || Hdr.e32_objmap > offEnd))
+        return KLDR_ERR_LX_BAD_LOADER_SECTION;
+    if (    Hdr.e32_rsrccnt
+        && (   Hdr.e32_rsrctab < off
+            || Hdr.e32_rsrctab > offEnd
+            || Hdr.e32_rsrctab + sizeof(struct rsrc32) * Hdr.e32_rsrccnt > offEnd))
+        return KLDR_ERR_LX_BAD_LOADER_SECTION;
+    if (    Hdr.e32_restab
+        &&  (Hdr.e32_restab < off || Hdr.e32_restab > offEnd - 2))
+        return KLDR_ERR_LX_BAD_LOADER_SECTION;
+    if (    Hdr.e32_enttab
+        &&  (Hdr.e32_enttab < off || Hdr.e32_enttab >= offEnd))
+        return KLDR_ERR_LX_BAD_LOADER_SECTION;
+    if (    Hdr.e32_dircnt
+        && (Hdr.e32_dirtab < off || Hdr.e32_dirtab > offEnd - 2))
+        return KLDR_ERR_LX_BAD_LOADER_SECTION;
+
+    /* Verify the fixup section. */
+    off = offEnd;
+    offEnd = off + Hdr.e32_fixupsize;
+    if (    Hdr.e32_fpagetab
+        &&  (Hdr.e32_fpagetab < off || Hdr.e32_fpagetab > offEnd))
+    {
+        /*
+         * wlink mixes the fixup section and the loader section.
+         */
+        off = Hdr.e32_fpagetab;
+        offEnd = off + Hdr.e32_fixupsize;
+        Hdr.e32_ldrsize = off - Hdr.e32_objtab;
+    }
+    if (    Hdr.e32_frectab
+        &&  (Hdr.e32_frectab < off || Hdr.e32_frectab > offEnd))
+        return KLDR_ERR_LX_BAD_FIXUP_SECTION;
+    if (    Hdr.e32_impmod
+        &&  (Hdr.e32_impmod < off || Hdr.e32_impmod > offEnd || Hdr.e32_impmod + Hdr.e32_impmodcnt > offEnd))
+        return KLDR_ERR_LX_BAD_FIXUP_SECTION;
+    if (    Hdr.e32_impproc
+        &&  (Hdr.e32_impproc < off || Hdr.e32_impproc > offEnd))
+        return KLDR_ERR_LX_BAD_FIXUP_SECTION;
+
+    /*
+     * Calc the instance size, allocate and initialize it.
+     */
+    cchFilename = kHlpStrLen(kRdrName(pRdr));
+    cb = K_ALIGN_Z(sizeof(KLDRMODLX), 8)
+       + K_ALIGN_Z(K_OFFSETOF(KLDRMOD, aSegments[Hdr.e32_objcnt + 1]), 8)
+       + K_ALIGN_Z(cchFilename + 1, 8);
+    offLdrStuff = cb;
+    cb += Hdr.e32_ldrsize + 2; /* +2 for two extra zeros. */
+    pModLX = (PKLDRMODLX)kHlpAlloc(cb);
+    if (!pModLX)
+        return KERR_NO_MEMORY;
+    *ppModLX = pModLX;
+
+    /* KLDRMOD */
+    pMod = (PKLDRMOD)((KU8 *)pModLX + K_ALIGN_Z(sizeof(KLDRMODLX), 8));
+    pMod->pvData = pModLX;
+    pMod->pRdr = pRdr;
+    pMod->pOps = NULL;      /* set upon success. */
+    pMod->cSegments = Hdr.e32_objcnt;
+    pMod->cchFilename = (KU32)cchFilename;
+    pMod->pszFilename = (char *)K_ALIGN_P(&pMod->aSegments[pMod->cSegments], 8);
+    kHlpMemCopy((char *)pMod->pszFilename, kRdrName(pRdr), cchFilename + 1);
+    pMod->pszName = NULL; /* finalized further down */
+    pMod->cchName = 0;
+    pMod->fFlags = 0;
+    switch (Hdr.e32_cpu)
+    {
+        case E32CPU286:
+            pMod->enmCpu = KCPU_I80286;
+            pMod->enmArch = KCPUARCH_X86_16;
+            break;
+        case E32CPU386:
+            pMod->enmCpu = KCPU_I386;
+            pMod->enmArch = KCPUARCH_X86_32;
+            break;
+        case E32CPU486:
+            pMod->enmCpu = KCPU_I486;
+            pMod->enmArch = KCPUARCH_X86_32;
+            break;
+    }
+    pMod->enmEndian = KLDRENDIAN_LITTLE;
+    pMod->enmFmt = KLDRFMT_LX;
+    switch (Hdr.e32_mflags & E32MODMASK)
+    {
+        case E32MODEXE:
+            pMod->enmType = !(Hdr.e32_mflags & E32NOINTFIX)
+                ? KLDRTYPE_EXECUTABLE_RELOCATABLE
+                : KLDRTYPE_EXECUTABLE_FIXED;
+            break;
+
+        case E32MODDLL:
+        case E32PROTDLL:
+        case E32MODPROTDLL:
+            pMod->enmType = !(Hdr.e32_mflags & E32SYSDLL)
+                ? KLDRTYPE_SHARED_LIBRARY_RELOCATABLE
+                : KLDRTYPE_SHARED_LIBRARY_FIXED;
+            break;
+
+        case E32MODPDEV:
+        case E32MODVDEV:
+            pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE;
+            break;
+    }
+    pMod->u32Magic = 0;     /* set upon success. */
+
+    /* KLDRMODLX */
+    pModLX->pMod = pMod;
+    pModLX->pvMapping = 0;
+    pModLX->cbMapped = 0;
+    pModLX->f32Reserved = 0;
+
+    pModLX->offHdr = offNewHdr >= 0 ? offNewHdr : 0;
+    kHlpMemCopy(&pModLX->Hdr, &Hdr, sizeof(Hdr));
+
+    pModLX->pbLoaderSection = (uint8_t *)pModLX + offLdrStuff;
+    pModLX->pbLoaderSectionLast = pModLX->pbLoaderSection + pModLX->Hdr.e32_ldrsize - 1;
+    pModLX->paObjs = NULL;
+    pModLX->paPageMappings = NULL;
+    pModLX->paRsrcs = NULL;
+    pModLX->pbResNameTab = NULL;
+    pModLX->pbEntryTab = NULL;
+
+    pModLX->pbNonResNameTab = NULL;
+    pModLX->pbNonResNameTabLast = NULL;
+
+    pModLX->pbFixupSection = NULL;
+    pModLX->pbFixupSectionLast = NULL;
+    pModLX->paoffPageFixups = NULL;
+    pModLX->pbFixupRecs = NULL;
+    pModLX->pbImportMods = NULL;
+    pModLX->pbImportProcs = NULL;
+
+    /*
+     * Read the loader data.
+     */
+    rc = kRdrRead(pRdr, (void *)pModLX->pbLoaderSection, pModLX->Hdr.e32_ldrsize, pModLX->Hdr.e32_objtab + pModLX->offHdr);
+    if (rc)
+        return rc;
+    ((KU8 *)pModLX->pbLoaderSectionLast)[1] = 0;
+    ((KU8 *)pModLX->pbLoaderSectionLast)[2] = 0;
+    if (pModLX->Hdr.e32_objcnt)
+        pModLX->paObjs = (const struct o32_obj *)pModLX->pbLoaderSection;
+    if (pModLX->Hdr.e32_objmap)
+        pModLX->paPageMappings = (const struct o32_map *)(pModLX->pbLoaderSection + pModLX->Hdr.e32_objmap - pModLX->Hdr.e32_objtab);
+    if (pModLX->Hdr.e32_rsrccnt)
+        pModLX->paRsrcs = (const struct rsrc32 *)(pModLX->pbLoaderSection + pModLX->Hdr.e32_rsrctab - pModLX->Hdr.e32_objtab);
+    if (pModLX->Hdr.e32_restab)
+        pModLX->pbResNameTab = pModLX->pbLoaderSection + pModLX->Hdr.e32_restab - pModLX->Hdr.e32_objtab;
+    if (pModLX->Hdr.e32_enttab)
+        pModLX->pbEntryTab = pModLX->pbLoaderSection + pModLX->Hdr.e32_enttab - pModLX->Hdr.e32_objtab;
+
+    /*
+     * Get the soname from the resident name table.
+     * Very convenient that it's the 0 ordinal, because then we get a
+     * free string terminator.
+     * (The table entry consists of a pascal string followed by a 16-bit ordinal.)
+     */
+    if (pModLX->pbResNameTab)
+        pMod->pszName = (const char *)kldrModLXDoNameTableLookupByOrdinal(pModLX->pbResNameTab,
+                                                                          pModLX->pbLoaderSectionLast - pModLX->pbResNameTab + 1,
+                                                                          0);
+    if (!pMod->pszName)
+        return KLDR_ERR_LX_NO_SONAME;
+    pMod->cchName = *(const KU8 *)pMod->pszName++;
+    if (pMod->cchName != kHlpStrLen(pMod->pszName))
+        return KLDR_ERR_LX_BAD_SONAME;
+
+    /*
+     * Quick validation of the object table.
+     */
+    cb = 0;
+    for (i = 0; i < pMod->cSegments; i++)
+    {
+        if (pModLX->paObjs[i].o32_base & (OBJPAGELEN - 1))
+            return KLDR_ERR_LX_BAD_OBJECT_TABLE;
+        if (pModLX->paObjs[i].o32_base + pModLX->paObjs[i].o32_size <= pModLX->paObjs[i].o32_base)
+            return KLDR_ERR_LX_BAD_OBJECT_TABLE;
+        if (pModLX->paObjs[i].o32_mapsize > (pModLX->paObjs[i].o32_size + (OBJPAGELEN - 1)))
+            return KLDR_ERR_LX_BAD_OBJECT_TABLE;
+        if (    pModLX->paObjs[i].o32_mapsize
+            &&  (   (KU8 *)&pModLX->paPageMappings[pModLX->paObjs[i].o32_pagemap] > pModLX->pbLoaderSectionLast
+                 || (KU8 *)&pModLX->paPageMappings[pModLX->paObjs[i].o32_pagemap + pModLX->paObjs[i].o32_mapsize]
+                     > pModLX->pbLoaderSectionLast))
+            return KLDR_ERR_LX_BAD_OBJECT_TABLE;
+        if (i > 0 && !(pModLX->paObjs[i].o32_flags & OBJRSRC))
+        {
+            if (pModLX->paObjs[i].o32_base <= pModLX->paObjs[i - 1].o32_base)
+                return KLDR_ERR_LX_BAD_OBJECT_TABLE;
+            if (pModLX->paObjs[i].o32_base < pModLX->paObjs[i - 1].o32_base + pModLX->paObjs[i - 1].o32_mapsize)
+                return KLDR_ERR_LX_BAD_OBJECT_TABLE;
+        }
+    }
+
+    /*
+     * Check if we can optimize the mapping by using a different
+     * object alignment. The linker typically uses 64KB alignment,
+     * we can easily get away with page alignment in most cases.
+     */
+    fCanOptimizeMapping = !(Hdr.e32_mflags & (E32NOINTFIX | E32SYSDLL));
+    NextRVA = 0;
+
+    /*
+     * Setup the KLDRMOD segment array.
+     */
+    for (i = 0; i < pMod->cSegments; i++)
+    {
+        /* unused */
+        pMod->aSegments[i].pvUser = NULL;
+        pMod->aSegments[i].MapAddress = 0;
+        pMod->aSegments[i].pchName = NULL;
+        pMod->aSegments[i].cchName = 0;
+        pMod->aSegments[i].offFile = -1;
+        pMod->aSegments[i].cbFile = -1;
+        pMod->aSegments[i].SelFlat = 0;
+        pMod->aSegments[i].Sel16bit = 0;
+
+        /* flags */
+        pMod->aSegments[i].fFlags = 0;
+        if (pModLX->paObjs[i].o32_flags & OBJBIGDEF)
+            pMod->aSegments[i].fFlags = KLDRSEG_FLAG_16BIT;
+        if (pModLX->paObjs[i].o32_flags & OBJALIAS16)
+            pMod->aSegments[i].fFlags = KLDRSEG_FLAG_OS2_ALIAS16;
+        if (pModLX->paObjs[i].o32_flags & OBJCONFORM)
+            pMod->aSegments[i].fFlags = KLDRSEG_FLAG_OS2_CONFORM;
+        if (pModLX->paObjs[i].o32_flags & OBJIOPL)
+            pMod->aSegments[i].fFlags = KLDRSEG_FLAG_OS2_IOPL;
+
+        /* size and addresses */
+        pMod->aSegments[i].Alignment   = OBJPAGELEN;
+        pMod->aSegments[i].cb          = pModLX->paObjs[i].o32_size;
+        pMod->aSegments[i].LinkAddress = pModLX->paObjs[i].o32_base;
+        pMod->aSegments[i].RVA         = NextRVA;
+        if (    fCanOptimizeMapping
+            ||  i + 1 >= pMod->cSegments
+            ||  (pModLX->paObjs[i].o32_flags & OBJRSRC)
+            ||  (pModLX->paObjs[i + 1].o32_flags & OBJRSRC))
+            pMod->aSegments[i].cbMapped = K_ALIGN_Z(pModLX->paObjs[i].o32_size, OBJPAGELEN);
+        else
+            pMod->aSegments[i].cbMapped = pModLX->paObjs[i + 1].o32_base - pModLX->paObjs[i].o32_base;
+        NextRVA += (KU32)pMod->aSegments[i].cbMapped;
+
+        /* protection */
+        switch (  pModLX->paObjs[i].o32_flags
+                & (OBJSHARED | OBJREAD | OBJWRITE | OBJEXEC))
+        {
+            case 0:
+            case OBJSHARED:
+                pMod->aSegments[i].enmProt = KPROT_NOACCESS;
+                break;
+            case OBJREAD:
+            case OBJREAD | OBJSHARED:
+                pMod->aSegments[i].enmProt = KPROT_READONLY;
+                break;
+            case OBJWRITE:
+            case OBJWRITE | OBJREAD:
+                pMod->aSegments[i].enmProt = KPROT_WRITECOPY;
+                break;
+            case OBJWRITE | OBJSHARED:
+            case OBJWRITE | OBJSHARED | OBJREAD:
+                pMod->aSegments[i].enmProt = KPROT_READWRITE;
+                break;
+            case OBJEXEC:
+            case OBJEXEC | OBJSHARED:
+                pMod->aSegments[i].enmProt = KPROT_EXECUTE;
+                break;
+            case OBJEXEC | OBJREAD:
+            case OBJEXEC | OBJREAD | OBJSHARED:
+                pMod->aSegments[i].enmProt = KPROT_EXECUTE_READ;
+                break;
+            case OBJEXEC | OBJWRITE:
+            case OBJEXEC | OBJWRITE | OBJREAD:
+                pMod->aSegments[i].enmProt = KPROT_EXECUTE_WRITECOPY;
+                break;
+            case OBJEXEC | OBJWRITE | OBJSHARED:
+            case OBJEXEC | OBJWRITE | OBJSHARED | OBJREAD:
+                pMod->aSegments[i].enmProt = KPROT_EXECUTE_READWRITE;
+                break;
+        }
+        if ((pModLX->paObjs[i].o32_flags & (OBJREAD | OBJWRITE | OBJEXEC | OBJRSRC)) == OBJRSRC)
+            pMod->aSegments[i].enmProt = KPROT_READONLY;
+        /*pMod->aSegments[i].f16bit = !(pModLX->paObjs[i].o32_flags & OBJBIGDEF)
+        pMod->aSegments[i].fIOPL = !(pModLX->paObjs[i].o32_flags & OBJIOPL)
+        pMod->aSegments[i].fConforming = !(pModLX->paObjs[i].o32_flags & OBJCONFORM) */
+    }
+
+    /* set the mapping size */
+    pModLX->cbMapped = NextRVA;
+
+    /*
+     * We're done.
+     */
+    *ppModLX = pModLX;
+    return 0;
+}
+
+
+/** @copydoc KLDRMODOPS::pfnDestroy */
+static int kldrModLXDestroy(PKLDRMOD pMod)
+{
+    PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+    int rc = 0;
+    KLDRMODLX_ASSERT(!pModLX->pvMapping);
+
+    if (pMod->pRdr)
+    {
+        rc = kRdrClose(pMod->pRdr);
+        pMod->pRdr = NULL;
+    }
+    if (pModLX->pbNonResNameTab)
+    {
+        kHlpFree(pModLX->pbNonResNameTab);
+        pModLX->pbNonResNameTab = NULL;
+    }
+    if (pModLX->pbFixupSection)
+    {
+        kHlpFree(pModLX->pbFixupSection);
+        pModLX->pbFixupSection = NULL;
+    }
+    pMod->u32Magic = 0;
+    pMod->pOps = NULL;
+    kHlpFree(pModLX);
+    return rc;
+}
+
+
+/**
+ * Resolved base address aliases.
+ *
+ * @param   pModLX          The interpreter module instance
+ * @param   pBaseAddress    The base address, IN & OUT.
+ */
+static void kldrModLXResolveBaseAddress(PKLDRMODLX pModLX, PKLDRADDR pBaseAddress)
+{
+    if (*pBaseAddress == KLDRMOD_BASEADDRESS_MAP)
+        *pBaseAddress = pModLX->pMod->aSegments[0].MapAddress;
+    else if (*pBaseAddress == KLDRMOD_BASEADDRESS_LINK)
+        *pBaseAddress = pModLX->pMod->aSegments[0].LinkAddress;
+}
+
+
+/** @copydoc kLdrModQuerySymbol */
+static int kldrModLXQuerySymbol(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, KU32 iSymbol,
+                                const char *pchSymbol, KSIZE cchSymbol, const char *pszVersion,
+                                PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind)
+{
+    PKLDRMODLX                  pModLX = (PKLDRMODLX)pMod->pvData;
+    KU32                        iOrdinal;
+    int                         rc;
+    const struct b32_bundle     *pBundle;
+    K_NOREF(pvBits);
+    K_NOREF(pszVersion);
+
+    /*
+     * Give up at once if there is no entry table.
+     */
+    if (!pModLX->Hdr.e32_enttab)
+        return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+    /*
+     * Translate the symbol name into an ordinal.
+     */
+    if (pchSymbol)
+    {
+        rc = kldrModLXDoNameLookup(pModLX, pchSymbol, cchSymbol, &iSymbol);
+        if (rc)
+            return rc;
+    }
+
+    /*
+     * Iterate the entry table.
+     * (The entry table is made up of bundles of similar exports.)
+     */
+    iOrdinal = 1;
+    pBundle = (const struct b32_bundle *)pModLX->pbEntryTab;
+    while (pBundle->b32_cnt && iOrdinal <= iSymbol)
+    {
+        static const KSIZE s_cbEntry[] = { 0, 3, 5, 5, 7 };
+
+        /*
+         * Check for a hit first.
+         */
+        iOrdinal += pBundle->b32_cnt;
+        if (iSymbol < iOrdinal)
+        {
+            KU32 offObject;
+            const struct e32_entry *pEntry = (const struct e32_entry *)((KUPTR)(pBundle + 1)
+                                                                        +   (iSymbol - (iOrdinal - pBundle->b32_cnt))
+                                                                          * s_cbEntry[pBundle->b32_type]);
+
+            /*
+             * Calculate the return address.
+             */
+            kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
+            switch (pBundle->b32_type)
+            {
+                /* empty bundles are place holders unused ordinal ranges. */
+                case EMPTY:
+                    return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+                /* e32_flags + a 16-bit offset. */
+                case ENTRY16:
+                    offObject = pEntry->e32_variant.e32_offset.offset16;
+                    if (pfKind)
+                        *pfKind = KLDRSYMKIND_16BIT | KLDRSYMKIND_NO_TYPE;
+                    break;
+
+                /* e32_flags + a 16-bit offset + a 16-bit callgate selector. */
+                case GATE16:
+                    offObject = pEntry->e32_variant.e32_callgate.offset;
+                    if (pfKind)
+                        *pfKind = KLDRSYMKIND_16BIT | KLDRSYMKIND_CODE;
+                    break;
+
+                /* e32_flags + a 32-bit offset. */
+                case ENTRY32:
+                    offObject = pEntry->e32_variant.e32_offset.offset32;
+                    if (pfKind)
+                        *pfKind = KLDRSYMKIND_32BIT;
+                    break;
+
+                /* e32_flags + 16-bit import module ordinal + a 32-bit procname or ordinal. */
+                case ENTRYFWD:
+                    return kldrModLXDoForwarderQuery(pModLX, pEntry, pfnGetForwarder, pvUser, puValue, pfKind);
+
+                default:
+                    /* anyone actually using TYPEINFO will end up here. */
+                    KLDRMODLX_ASSERT(!"Bad bundle type");
+                    return KLDR_ERR_LX_BAD_BUNDLE;
+            }
+
+            /*
+             * Validate the object number and calc the return address.
+             */
+            if (    pBundle->b32_obj <= 0
+                ||  pBundle->b32_obj > pMod->cSegments)
+                return KLDR_ERR_LX_BAD_BUNDLE;
+            if (puValue)
+                *puValue = BaseAddress
+                         + offObject
+                         + pMod->aSegments[pBundle->b32_obj - 1].RVA;
+            return 0;
+        }
+
+        /*
+         * Skip the bundle.
+         */
+        if (pBundle->b32_type > ENTRYFWD)
+        {
+            KLDRMODLX_ASSERT(!"Bad type"); /** @todo figure out TYPEINFO. */
+            return KLDR_ERR_LX_BAD_BUNDLE;
+        }
+        if (pBundle->b32_type == 0)
+            pBundle = (const struct b32_bundle *)((const KU8 *)pBundle + 2);
+        else
+            pBundle = (const struct b32_bundle *)((const KU8 *)(pBundle + 1) + s_cbEntry[pBundle->b32_type] * pBundle->b32_cnt);
+    }
+
+    return KLDR_ERR_SYMBOL_NOT_FOUND;
+}
+
+
+/**
+ * Do name lookup.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param   pModLX      The module to lookup the symbol in.
+ * @param   pchSymbol   The symbol to lookup.
+ * @param   cchSymbol   The symbol name length.
+ * @param   piSymbol    Where to store the symbol ordinal.
+ */
+static int kldrModLXDoNameLookup(PKLDRMODLX pModLX, const char *pchSymbol, KSIZE cchSymbol, KU32 *piSymbol)
+{
+
+    /*
+     * First do a hash table lookup.
+     */
+    /** @todo hash name table for speed. */
+
+    /*
+     * Search the name tables.
+     */
+    const KU8 *pbName = kldrModLXDoNameTableLookupByName(pModLX->pbResNameTab,
+                                                         pModLX->pbLoaderSectionLast - pModLX->pbResNameTab + 1,
+                                                         pchSymbol, cchSymbol);
+    if (!pbName)
+    {
+        if (!pModLX->pbNonResNameTab)
+        {
+            /* lazy load it */
+            /** @todo non-resident name table. */
+        }
+        if (pModLX->pbNonResNameTab)
+            pbName = kldrModLXDoNameTableLookupByName(pModLX->pbResNameTab,
+                                                      pModLX->pbNonResNameTabLast - pModLX->pbResNameTab + 1,
+                                                      pchSymbol, cchSymbol);
+    }
+    if (!pbName)
+        return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+    *piSymbol = *(const KU16 *)(pbName + 1 + *pbName);
+    return 0;
+}
+
+
+#if 0
+/**
+ * Hash a symbol using the algorithm from sdbm.
+ *
+ * The following was is the documenation of the orignal sdbm functions:
+ *
+ * This algorithm was created for sdbm (a public-domain reimplementation of
+ * ndbm) database library. it was found to do well in scrambling bits,
+ * causing better distribution of the keys and fewer splits. it also happens
+ * to be a good general hashing function with good distribution. the actual
+ * function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
+ * is the faster version used in gawk. [there is even a faster, duff-device
+ * version] the magic constant 65599 was picked out of thin air while
+ * experimenting with different constants, and turns out to be a prime.
+ * this is one of the algorithms used in berkeley db (see sleepycat) and
+ * elsewhere.
+ */
+static KU32 kldrModLXDoHash(const char *pchSymbol, KU8 cchSymbol)
+{
+    KU32 hash = 0;
+    int ch;
+
+    while (     cchSymbol-- > 0
+           &&   (ch = *(unsigned const char *)pchSymbol++))
+        hash = ch + (hash << 6) + (hash << 16) - hash;
+
+    return hash;
+}
+#endif
+
+
+/**
+ * Lookup a name table entry by name.
+ *
+ * @returns Pointer to the name table entry if found.
+ * @returns NULL if not found.
+ * @param   pbNameTable     Pointer to the name table that should be searched.
+ * @param   cbNameTable     The size of the name table.
+ * @param   pchSymbol       The name of the symbol we're looking for.
+ * @param   cchSymbol       The length of the symbol name.
+ */
+static const KU8 *kldrModLXDoNameTableLookupByName(const KU8 *pbNameTable, KSSIZE cbNameTable,
+                                                   const char *pchSymbol, KSIZE cchSymbol)
+{
+    /*
+     * Determin the namelength up front so we can skip anything which doesn't matches the length.
+     */
+    KU8 cbSymbol8Bit = (KU8)cchSymbol;
+    if (cbSymbol8Bit != cchSymbol)
+        return NULL; /* too long. */
+
+    /*
+     * Walk the name table.
+     */
+    while (*pbNameTable != 0 && cbNameTable > 0)
+    {
+        const KU8 cbName = *pbNameTable;
+
+        cbNameTable -= cbName + 1 + 2;
+        if (cbNameTable < 0)
+            break;
+
+        if (    cbName == cbSymbol8Bit
+            &&  !kHlpMemComp(pbNameTable + 1, pchSymbol, cbName))
+            return pbNameTable;
+
+        /* next entry */
+        pbNameTable += cbName + 1 + 2;
+    }
+
+    return NULL;
+}
+
+
+/**
+ * Deal with a forwarder entry.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param   pModLX          The PE module interpreter instance.
+ * @param   pEntry          The forwarder entry.
+ * @param   pfnGetForwarder The callback for resolving forwarder symbols. (optional)
+ * @param   pvUser          The user argument for the callback.
+ * @param   puValue         Where to put the value. (optional)
+ * @param   pfKind          Where to put the symbol kind. (optional)
+ */
+static int kldrModLXDoForwarderQuery(PKLDRMODLX pModLX, const struct e32_entry *pEntry,
+                                     PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind)
+{
+    int rc;
+    KU32 iSymbol;
+    const char *pchSymbol;
+    KU8 cchSymbol;
+
+    if (!pfnGetForwarder)
+        return KLDR_ERR_FORWARDER_SYMBOL;
+
+    /*
+     * Validate the entry import module ordinal.
+     */
+    if (    !pEntry->e32_variant.e32_fwd.modord
+        ||  pEntry->e32_variant.e32_fwd.modord > pModLX->Hdr.e32_impmodcnt)
+        return KLDR_ERR_LX_BAD_FORWARDER;
+
+    /*
+     * Figure out the parameters.
+     */
+    if (pEntry->e32_flags & FWD_ORDINAL)
+    {
+        iSymbol = pEntry->e32_variant.e32_fwd.value;
+        pchSymbol = NULL;                   /* no symbol name. */
+        cchSymbol = 0;
+    }
+    else
+    {
+        const KU8 *pbName;
+
+        /* load the fixup section if necessary. */
+        if (!pModLX->pbImportProcs)
+        {
+            rc = kldrModLXDoLoadFixupSection(pModLX);
+            if (rc)
+                return rc;
+        }
+
+        /* Make name pointer. */
+        pbName = pModLX->pbImportProcs + pEntry->e32_variant.e32_fwd.value;
+        if (    pbName >= pModLX->pbFixupSectionLast
+            ||  pbName < pModLX->pbFixupSection
+            || !*pbName)
+            return KLDR_ERR_LX_BAD_FORWARDER;
+
+
+        /* check for '#' name. */
+        if (pbName[1] == '#')
+        {
+            KU8         cbLeft = *pbName;
+            const KU8  *pb = pbName + 1;
+            unsigned    uBase;
+
+            /* base detection */
+            uBase = 10;
+            if (    cbLeft > 1
+                &&  pb[1] == '0'
+                &&  (pb[2] == 'x' || pb[2] == 'X'))
+            {
+                uBase = 16;
+                pb += 2;
+                cbLeft -= 2;
+            }
+
+            /* ascii to integer */
+            iSymbol = 0;
+            while (cbLeft-- > 0)
+            {
+                /* convert char to digit. */
+                unsigned uDigit = *pb++;
+                if (uDigit >= '0' && uDigit <= '9')
+                    uDigit -= '0';
+                else if (uDigit >= 'a' && uDigit <= 'z')
+                    uDigit -= 'a' + 10;
+                else if (uDigit >= 'A' && uDigit <= 'Z')
+                    uDigit -= 'A' + 10;
+                else if (!uDigit)
+                    break;
+                else
+                    return KLDR_ERR_LX_BAD_FORWARDER;
+                if (uDigit >= uBase)
+                    return KLDR_ERR_LX_BAD_FORWARDER;
+
+                /* insert the digit */
+                iSymbol *= uBase;
+                iSymbol += uDigit;
+            }
+            if (!iSymbol)
+                return KLDR_ERR_LX_BAD_FORWARDER;
+
+            pchSymbol = NULL;               /* no symbol name. */
+            cchSymbol = 0;
+        }
+        else
+        {
+            pchSymbol = (char *)pbName + 1;
+            cchSymbol = *pbName;
+            iSymbol = NIL_KLDRMOD_SYM_ORDINAL;
+        }
+    }
+
+    /*
+     * Resolve the forwarder.
+     */
+    rc = pfnGetForwarder(pModLX->pMod, pEntry->e32_variant.e32_fwd.modord - 1, iSymbol, pchSymbol, cchSymbol, NULL, puValue, pfKind, pvUser);
+    if (!rc && pfKind)
+        *pfKind |= KLDRSYMKIND_FORWARDER;
+    return rc;
+}
+
+
+/**
+ * Loads the fixup section from the executable image.
+ *
+ * The fixup section isn't loaded until it's accessed. It's also freed by kLdrModDone().
+ *
+ * @returns 0 on success, non-zero kLdr or native status code on failure.
+ * @param   pModLX          The PE module interpreter instance.
+ */
+static int kldrModLXDoLoadFixupSection(PKLDRMODLX pModLX)
+{
+    int rc;
+    KU32 off;
+    void *pv;
+
+    pv = kHlpAlloc(pModLX->Hdr.e32_fixupsize);
+    if (!pv)
+        return KERR_NO_MEMORY;
+
+    off = pModLX->Hdr.e32_objtab + pModLX->Hdr.e32_ldrsize;
+    rc = kRdrRead(pModLX->pMod->pRdr, pv, pModLX->Hdr.e32_fixupsize,
+                     off + pModLX->offHdr);
+    if (!rc)
+    {
+        pModLX->pbFixupSection = pv;
+        pModLX->pbFixupSectionLast = pModLX->pbFixupSection + pModLX->Hdr.e32_fixupsize;
+        KLDRMODLX_ASSERT(!pModLX->paoffPageFixups);
+        if (pModLX->Hdr.e32_fpagetab)
+            pModLX->paoffPageFixups = (const KU32 *)(pModLX->pbFixupSection + pModLX->Hdr.e32_fpagetab - off);
+        KLDRMODLX_ASSERT(!pModLX->pbFixupRecs);
+        if (pModLX->Hdr.e32_frectab)
+            pModLX->pbFixupRecs = pModLX->pbFixupSection + pModLX->Hdr.e32_frectab - off;
+        KLDRMODLX_ASSERT(!pModLX->pbImportMods);
+        if (pModLX->Hdr.e32_impmod)
+            pModLX->pbImportMods = pModLX->pbFixupSection + pModLX->Hdr.e32_impmod - off;
+        KLDRMODLX_ASSERT(!pModLX->pbImportProcs);
+        if (pModLX->Hdr.e32_impproc)
+            pModLX->pbImportProcs = pModLX->pbFixupSection + pModLX->Hdr.e32_impproc - off;
+    }
+    else
+        kHlpFree(pv);
+    return rc;
+}
+
+
+/** @copydoc kLdrModEnumSymbols */
+static int kldrModLXEnumSymbols(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress,
+                                KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser)
+{
+    PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+    const struct b32_bundle *pBundle;
+    KU32 iOrdinal;
+    int rc = 0;
+    K_NOREF(pvBits);
+    K_NOREF(fFlags);
+
+    kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
+
+    /*
+     * Enumerate the entry table.
+     * (The entry table is made up of bundles of similar exports.)
+     */
+    iOrdinal = 1;
+    pBundle = (const struct b32_bundle *)pModLX->pbEntryTab;
+    while (pBundle->b32_cnt && iOrdinal)
+    {
+        static const KSIZE s_cbEntry[] = { 0, 3, 5, 5, 7 };
+
+        /*
+         * Enum the entries in the bundle.
+         */
+        if (pBundle->b32_type != EMPTY)
+        {
+            const struct e32_entry *pEntry;
+            KSIZE cbEntry;
+            KLDRADDR BundleRVA;
+            unsigned cLeft;
+
+
+            /* Validate the bundle. */
+            switch (pBundle->b32_type)
+            {
+                case ENTRY16:
+                case GATE16:
+                case ENTRY32:
+                    if (    pBundle->b32_obj <= 0
+                        ||  pBundle->b32_obj > pMod->cSegments)
+                        return KLDR_ERR_LX_BAD_BUNDLE;
+                    BundleRVA = pMod->aSegments[pBundle->b32_obj - 1].RVA;
+                    break;
+
+                case ENTRYFWD:
+                    BundleRVA = 0;
+                    break;
+
+                default:
+                    /* anyone actually using TYPEINFO will end up here. */
+                    KLDRMODLX_ASSERT(!"Bad bundle type");
+                    return KLDR_ERR_LX_BAD_BUNDLE;
+            }
+
+            /* iterate the bundle entries. */
+            cbEntry = s_cbEntry[pBundle->b32_type];
+            pEntry = (const struct e32_entry *)(pBundle + 1);
+            cLeft = pBundle->b32_cnt;
+            while (cLeft-- > 0)
+            {
+                KLDRADDR uValue;
+                KU32 fKind;
+                int fFoundName;
+                const KU8 *pbName;
+
+                /*
+                 * Calc the symbol value and kind.
+                 */
+                switch (pBundle->b32_type)
+                {
+                    /* e32_flags + a 16-bit offset. */
+                    case ENTRY16:
+                        uValue = BaseAddress + BundleRVA + pEntry->e32_variant.e32_offset.offset16;
+                        fKind = KLDRSYMKIND_16BIT | KLDRSYMKIND_NO_TYPE;
+                        break;
+
+                    /* e32_flags + a 16-bit offset + a 16-bit callgate selector. */
+                    case GATE16:
+                        uValue = BaseAddress + BundleRVA + pEntry->e32_variant.e32_callgate.offset;
+                        fKind = KLDRSYMKIND_16BIT | KLDRSYMKIND_CODE;
+                        break;
+
+                    /* e32_flags + a 32-bit offset. */
+                    case ENTRY32:
+                        uValue = BaseAddress + BundleRVA + pEntry->e32_variant.e32_offset.offset32;
+                        fKind = KLDRSYMKIND_32BIT;
+                        break;
+
+                    /* e32_flags + 16-bit import module ordinal + a 32-bit procname or ordinal. */
+                    case ENTRYFWD:
+                        uValue = 0; /** @todo implement enumeration of forwarders properly. */
+                        fKind = KLDRSYMKIND_FORWARDER;
+                        break;
+
+                    default: /* shut up gcc. */
+                        uValue = 0;
+                        fKind = KLDRSYMKIND_NO_BIT | KLDRSYMKIND_NO_TYPE;
+                        break;
+                }
+
+                /*
+                 * Any symbol names?
+                 */
+                fFoundName = 0;
+
+                /* resident name table. */
+                pbName = pModLX->pbResNameTab;
+                if (pbName)
+                {
+                    do
+                    {
+                        pbName = kldrModLXDoNameTableLookupByOrdinal(pbName, pModLX->pbLoaderSectionLast - pbName + 1, iOrdinal);
+                        if (!pbName)
+                            break;
+                        fFoundName = 1;
+                        rc = pfnCallback(pMod, iOrdinal, (const char *)pbName + 1, *pbName, NULL, uValue, fKind, pvUser);
+                        if (rc)
+                            return rc;
+
+                        /* skip to the next entry */
+                        pbName += 1 + *pbName + 2;
+                    } while (pbName < pModLX->pbLoaderSectionLast);
+                }
+
+                /* resident name table. */
+                pbName = pModLX->pbNonResNameTab;
+                /** @todo lazy load the non-resident name table. */
+                if (pbName)
+                {
+                    do
+                    {
+                        pbName = kldrModLXDoNameTableLookupByOrdinal(pbName, pModLX->pbNonResNameTabLast - pbName + 1, iOrdinal);
+                        if (!pbName)
+                            break;
+                        fFoundName = 1;
+                        rc = pfnCallback(pMod, iOrdinal, (const char *)pbName + 1, *pbName, NULL, uValue, fKind, pvUser);
+                        if (rc)
+                            return rc;
+
+                        /* skip to the next entry */
+                        pbName += 1 + *pbName + 2;
+                    } while (pbName < pModLX->pbLoaderSectionLast);
+                }
+
+                /*
+                 * If no names, call once with the ordinal only.
+                 */
+                if (!fFoundName)
+                {
+                    rc = pfnCallback(pMod, iOrdinal, NULL, 0, NULL, uValue, fKind, pvUser);
+                    if (rc)
+                        return rc;
+                }
+
+                /* next */
+                iOrdinal++;
+                pEntry = (const struct e32_entry *)((KUPTR)pEntry + cbEntry);
+            }
+        }
+
+        /*
+         * The next bundle.
+         */
+        if (pBundle->b32_type > ENTRYFWD)
+        {
+            KLDRMODLX_ASSERT(!"Bad type"); /** @todo figure out TYPEINFO. */
+            return KLDR_ERR_LX_BAD_BUNDLE;
+        }
+        if (pBundle->b32_type == 0)
+            pBundle = (const struct b32_bundle *)((const KU8 *)pBundle + 2);
+        else
+            pBundle = (const struct b32_bundle *)((const KU8 *)(pBundle + 1) + s_cbEntry[pBundle->b32_type] * pBundle->b32_cnt);
+    }
+
+    return 0;
+}
+
+
+/**
+ * Lookup a name table entry by ordinal.
+ *
+ * @returns Pointer to the name table entry if found.
+ * @returns NULL if not found.
+ * @param   pbNameTable Pointer to the name table that should be searched.
+ * @param   cbNameTable The size of the name table.
+ * @param   iOrdinal    The ordinal to search for.
+ */
+static const KU8 *kldrModLXDoNameTableLookupByOrdinal(const KU8 *pbNameTable, KSSIZE cbNameTable, KU32 iOrdinal)
+{
+    while (*pbNameTable != 0 && cbNameTable > 0)
+    {
+        const KU8   cbName = *pbNameTable;
+        KU32        iName;
+
+        cbNameTable -= cbName + 1 + 2;
+        if (cbNameTable < 0)
+            break;
+
+        iName = *(pbNameTable + cbName + 1)
+              | ((unsigned)*(pbNameTable + cbName + 2) << 8);
+        if (iName == iOrdinal)
+            return pbNameTable;
+
+        /* next entry */
+        pbNameTable += cbName + 1 + 2;
+    }
+
+    return NULL;
+}
+
+
+/** @copydoc kLdrModGetImport */
+static int kldrModLXGetImport(PKLDRMOD pMod, const void *pvBits, KU32 iImport, char *pszName, KSIZE cchName)
+{
+    PKLDRMODLX  pModLX = (PKLDRMODLX)pMod->pvData;
+    const KU8  *pb;
+    int         rc;
+    K_NOREF(pvBits);
+
+    /*
+     * Validate
+     */
+    if (iImport >= pModLX->Hdr.e32_impmodcnt)
+        return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+
+    /*
+     * Lazy loading the fixup section.
+     */
+    if (!pModLX->pbImportMods)
+    {
+        rc = kldrModLXDoLoadFixupSection(pModLX);
+        if (rc)
+            return rc;
+    }
+
+    /*
+     * Iterate the module import table until we reach the requested import ordinal.
+     */
+    pb = pModLX->pbImportMods;
+    while (iImport-- > 0)
+        pb += *pb + 1;
+
+    /*
+     * Copy out the result.
+     */
+    if (*pb < cchName)
+    {
+        kHlpMemCopy(pszName, pb + 1, *pb);
+        pszName[*pb] = '\0';
+        rc = 0;
+    }
+    else
+    {
+        kHlpMemCopy(pszName, pb + 1, cchName);
+        if (cchName)
+            pszName[cchName - 1] = '\0';
+        rc = KERR_BUFFER_OVERFLOW;
+    }
+
+    return rc;
+}
+
+
+/** @copydoc kLdrModNumberOfImports */
+static KI32 kldrModLXNumberOfImports(PKLDRMOD pMod, const void *pvBits)
+{
+    PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+    K_NOREF(pvBits);
+    return pModLX->Hdr.e32_impmodcnt;
+}
+
+
+/** @copydoc kLdrModGetStackInfo */
+static int kldrModLXGetStackInfo(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
+{
+    PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+    const KU32 i = pModLX->Hdr.e32_stackobj;
+    K_NOREF(pvBits);
+
+    if (    i
+        &&  i <= pMod->cSegments
+        &&  pModLX->Hdr.e32_esp <= pMod->aSegments[i - 1].LinkAddress + pMod->aSegments[i - 1].cb
+        &&  pModLX->Hdr.e32_stacksize
+        &&  pModLX->Hdr.e32_esp - pModLX->Hdr.e32_stacksize >= pMod->aSegments[i - 1].LinkAddress)
+    {
+
+        kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
+        pStackInfo->LinkAddress = pModLX->Hdr.e32_esp - pModLX->Hdr.e32_stacksize;
+        pStackInfo->Address = BaseAddress
+                            + pMod->aSegments[i - 1].RVA
+                            + pModLX->Hdr.e32_esp - pModLX->Hdr.e32_stacksize - pMod->aSegments[i - 1].LinkAddress;
+    }
+    else
+    {
+        pStackInfo->Address = NIL_KLDRADDR;
+        pStackInfo->LinkAddress = NIL_KLDRADDR;
+    }
+    pStackInfo->cbStack = pModLX->Hdr.e32_stacksize;
+    pStackInfo->cbStackThread = 0;
+
+    return 0;
+}
+
+
+/** @copydoc kLdrModQueryMainEntrypoint */
+static int kldrModLXQueryMainEntrypoint(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRADDR pMainEPAddress)
+{
+    PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+    K_NOREF(pvBits);
+
+    /*
+     * Convert the address from the header.
+     */
+    kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
+    *pMainEPAddress = pModLX->Hdr.e32_startobj
+                   && pModLX->Hdr.e32_startobj <= pMod->cSegments
+                   && pModLX->Hdr.e32_eip < pMod->aSegments[pModLX->Hdr.e32_startobj - 1].cb
+        ? BaseAddress + pMod->aSegments[pModLX->Hdr.e32_startobj - 1].RVA + pModLX->Hdr.e32_eip
+        : NIL_KLDRADDR;
+    return 0;
+}
+
+
+/** @copydoc kLdrModEnumDbgInfo */
+static int kldrModLXEnumDbgInfo(PKLDRMOD pMod, const void *pvBits, PFNKLDRENUMDBG pfnCallback, void *pvUser)
+{
+    /*PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;*/
+    K_NOREF(pfnCallback);
+    K_NOREF(pvUser);
+
+    /*
+     * Quit immediately if no debug info.
+     */
+    if (kldrModLXHasDbgInfo(pMod, pvBits))
+        return 0;
+#if 0
+    /*
+     * Read the debug info and look for familiar magics and structures.
+     */
+    /** @todo */
+#endif
+
+    return 0;
+}
+
+
+/** @copydoc kLdrModHasDbgInfo */
+static int kldrModLXHasDbgInfo(PKLDRMOD pMod, const void *pvBits)
+{
+    PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+    K_NOREF(pvBits);
+
+    /*
+     * Don't curretnly bother with linkers which doesn't advertise it in the header.
+     */
+    if (    !pModLX->Hdr.e32_debuginfo
+        ||  !pModLX->Hdr.e32_debuglen)
+        return KLDR_ERR_NO_DEBUG_INFO;
+    return 0;
+}
+
+
+/** @copydoc kLdrModMap */
+static int kldrModLXMap(PKLDRMOD pMod)
+{
+    PKLDRMODLX  pModLX = (PKLDRMODLX)pMod->pvData;
+    unsigned    fFixed;
+    void       *pvBase;
+    int         rc;
+
+    /*
+     * Already mapped?
+     */
+    if (pModLX->pvMapping)
+        return KLDR_ERR_ALREADY_MAPPED;
+
+    /*
+     * Allocate memory for it.
+     */
+    /* fixed image? */
+    fFixed = pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
+          || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
+    if (!fFixed)
+        pvBase = NULL;
+    else
+    {
+        pvBase = (void *)(KUPTR)pMod->aSegments[0].LinkAddress;
+        if ((KUPTR)pvBase != pMod->aSegments[0].LinkAddress)
+            return KLDR_ERR_ADDRESS_OVERFLOW;
+    }
+    rc = kHlpPageAlloc(&pvBase, pModLX->cbMapped, KPROT_EXECUTE_READWRITE, fFixed);
+    if (rc)
+        return rc;
+
+    /*
+     * Load the bits, apply page protection, and update the segment table.
+     */
+    rc = kldrModLXDoLoadBits(pModLX, pvBase);
+    if (!rc)
+        rc = kldrModLXDoProtect(pModLX, pvBase, 0 /* protect */);
+    if (!rc)
+    {
+        KU32 i;
+        for (i = 0; i < pMod->cSegments; i++)
+        {
+            if (pMod->aSegments[i].RVA != NIL_KLDRADDR)
+                pMod->aSegments[i].MapAddress = (KUPTR)pvBase + (KUPTR)pMod->aSegments[i].RVA;
+        }
+        pModLX->pvMapping = pvBase;
+    }
+    else
+        kHlpPageFree(pvBase, pModLX->cbMapped);
+    return rc;
+}
+
+
+/**
+ * Loads the LX pages into the specified memory mapping.
+ *
+ * @returns 0 on success.
+ * @returns non-zero kLdr or OS status code on failure.
+ *
+ * @param   pModLX  The LX module interpreter instance.
+ * @param   pvBits  Where to load the bits.
+ */
+static int kldrModLXDoLoadBits(PKLDRMODLX pModLX, void *pvBits)
+{
+    const PKRDR pRdr = pModLX->pMod->pRdr;
+    KU8 *pbTmpPage = NULL;
+    int rc = 0;
+    KU32 i;
+
+    /*
+     * Iterate the segments.
+     */
+    for (i = 0; i < pModLX->Hdr.e32_objcnt; i++)
+    {
+        const struct o32_obj * const pObj = &pModLX->paObjs[i];
+        const KU32      cPages = (KU32)(pModLX->pMod->aSegments[i].cbMapped / OBJPAGELEN);
+        KU32            iPage;
+        KU8            *pbPage = (KU8 *)pvBits + (KUPTR)pModLX->pMod->aSegments[i].RVA;
+
+        /*
+         * Iterate the page map pages.
+         */
+        for (iPage = 0; !rc && iPage < pObj->o32_mapsize; iPage++, pbPage += OBJPAGELEN)
+        {
+            const struct o32_map *pMap = &pModLX->paPageMappings[iPage + pObj->o32_pagemap - 1];
+            switch (pMap->o32_pageflags)
+            {
+                case VALID:
+                    if (pMap->o32_pagesize == OBJPAGELEN)
+                        rc = kRdrRead(pRdr, pbPage, OBJPAGELEN,
+                                         pModLX->Hdr.e32_datapage + (pMap->o32_pagedataoffset << pModLX->Hdr.e32_pageshift));
+                    else if (pMap->o32_pagesize < OBJPAGELEN)
+                    {
+                        rc = kRdrRead(pRdr, pbPage, pMap->o32_pagesize,
+                                         pModLX->Hdr.e32_datapage + (pMap->o32_pagedataoffset << pModLX->Hdr.e32_pageshift));
+                        kHlpMemSet(pbPage + pMap->o32_pagesize, 0, OBJPAGELEN - pMap->o32_pagesize);
+                    }
+                    else
+                        rc = KLDR_ERR_LX_BAD_PAGE_MAP;
+                    break;
+
+                case ITERDATA:
+                case ITERDATA2:
+                    /* make sure we've got a temp page .*/
+                    if (!pbTmpPage)
+                    {
+                        pbTmpPage = kHlpAlloc(OBJPAGELEN + 256);
+                        if (!pbTmpPage)
+                            break;
+                    }
+                    /* validate the size. */
+                    if (pMap->o32_pagesize > OBJPAGELEN + 252)
+                    {
+                        rc = KLDR_ERR_LX_BAD_PAGE_MAP;
+                        break;
+                    }
+
+                    /* read it and ensure 4 extra zero bytes. */
+                    rc = kRdrRead(pRdr, pbTmpPage, pMap->o32_pagesize,
+                                     pModLX->Hdr.e32_datapage + (pMap->o32_pagedataoffset << pModLX->Hdr.e32_pageshift));
+                    if (rc)
+                        break;
+                    kHlpMemSet(pbTmpPage + pMap->o32_pagesize, 0, 4);
+
+                    /* unpack it into the image page. */
+                    if (pMap->o32_pageflags == ITERDATA2)
+                        rc = kldrModLXDoIterData2Unpacking(pbPage, pbTmpPage, pMap->o32_pagesize);
+                    else
+                        rc = kldrModLXDoIterDataUnpacking(pbPage, pbTmpPage, pMap->o32_pagesize);
+                    break;
+
+                case INVALID: /* we're probably not dealing correctly with INVALID pages... */
+                case ZEROED:
+                    kHlpMemSet(pbPage, 0, OBJPAGELEN);
+                    break;
+
+                case RANGE:
+                    KLDRMODLX_ASSERT(!"RANGE");
+                    /* Falls through. */
+                default:
+                    rc = KLDR_ERR_LX_BAD_PAGE_MAP;
+                    break;
+            }
+        }
+        if (rc)
+            break;
+
+        /*
+         * Zero the remaining pages.
+         */
+        if (iPage < cPages)
+            kHlpMemSet(pbPage, 0, (cPages - iPage) * OBJPAGELEN);
+    }
+
+    if (pbTmpPage)
+        kHlpFree(pbTmpPage);
+    return rc;
+}
+
+
+/**
+ * Unpacks iterdata (aka EXEPACK).
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param   pbDst       Where to put the uncompressed data. (Assumes OBJPAGELEN size.)
+ * @param   pbSrc       The compressed source data.
+ * @param   cbSrc       The file size of the compressed data. The source buffer
+ *                      contains 4 additional zero bytes.
+ */
+static int kldrModLXDoIterDataUnpacking(KU8 *pbDst, const KU8 *pbSrc, int cbSrc)
+{
+    const struct LX_Iter   *pIter = (const struct LX_Iter *)pbSrc;
+    int                     cbDst = OBJPAGELEN;
+
+    /* Validate size of data. */
+    if (cbSrc >= (int)OBJPAGELEN - 2)
+        return KLDR_ERR_LX_BAD_ITERDATA;
+
+    /*
+     * Expand the page.
+     */
+    while (cbSrc > 0 && pIter->LX_nIter)
+    {
+        if (pIter->LX_nBytes == 1)
+        {
+            /*
+             * Special case - one databyte.
+             */
+            cbDst -= pIter->LX_nIter;
+            if (cbDst < 0)
+                return KLDR_ERR_LX_BAD_ITERDATA;
+
+            cbSrc -= 4 + 1;
+            if (cbSrc < -4)
+                return KLDR_ERR_LX_BAD_ITERDATA;
+
+            kHlpMemSet(pbDst, pIter->LX_Iterdata, pIter->LX_nIter);
+            pbDst += pIter->LX_nIter;
+            pIter++;
+        }
+        else
+        {
+            /*
+             * General.
+             */
+            int i;
+
+            cbDst -= pIter->LX_nIter * pIter->LX_nBytes;
+            if (cbDst < 0)
+                return KLDR_ERR_LX_BAD_ITERDATA;
+
+            cbSrc -= 4 + pIter->LX_nBytes;
+            if (cbSrc < -4)
+                return KLDR_ERR_LX_BAD_ITERDATA;
+
+            for (i = pIter->LX_nIter; i > 0; i--, pbDst += pIter->LX_nBytes)
+                kHlpMemCopy(pbDst, &pIter->LX_Iterdata, pIter->LX_nBytes);
+            pIter   = (struct LX_Iter *)((char*)pIter + 4 + pIter->LX_nBytes);
+        }
+    }
+
+    /*
+     * Zero remainder of the page.
+     */
+    if (cbDst > 0)
+        kHlpMemSet(pbDst, 0, cbDst);
+
+    return 0;
+}
+
+
+/**
+ * Unpacks iterdata (aka EXEPACK).
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param   pbDst       Where to put the uncompressed data. (Assumes OBJPAGELEN size.)
+ * @param   pbSrc       The compressed source data.
+ * @param   cbSrc       The file size of the compressed data. The source buffer
+ *                      contains 4 additional zero bytes.
+ */
+static int kldrModLXDoIterData2Unpacking(KU8 *pbDst, const KU8 *pbSrc, int cbSrc)
+{
+    int cbDst = OBJPAGELEN;
+
+    while (cbSrc > 0)
+    {
+        /*
+         * Bit 0 and 1 is the encoding type.
+         */
+        switch (*pbSrc & 0x03)
+        {
+            /*
+             *
+             *  0  1  2  3  4  5  6  7
+             *  type  |              |
+             *        ----------------
+             *             cb         <cb bytes of data>
+             *
+             * Bits 2-7 is, if not zero, the length of an uncompressed run
+             * starting at the following byte.
+             *
+             *  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
+             *  type  |              |  |                    | |                     |
+             *        ----------------  ---------------------- -----------------------
+             *             zero                 cb                 char to multiply
+             *
+             * If the bits are zero, the following two bytes describes a 1 byte interation
+             * run. First byte is count, second is the byte to copy. A count of zero is
+             * means end of data, and we simply stops. In that case the rest of the data
+             * should be zero.
+             */
+            case 0:
+            {
+                if (*pbSrc)
+                {
+                    const int cb = *pbSrc >> 2;
+                    cbDst -= cb;
+                    if (cbDst < 0)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    cbSrc -= cb + 1;
+                    if (cbSrc < 0)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    kHlpMemCopy(pbDst, ++pbSrc, cb);
+                    pbDst += cb;
+                    pbSrc += cb;
+                }
+                else if (cbSrc < 2)
+                    return KLDR_ERR_LX_BAD_ITERDATA2;
+                else
+                {
+                    const int cb = pbSrc[1];
+                    if (!cb)
+                        goto l_endloop;
+                    cbDst -= cb;
+                    if (cbDst < 0)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    cbSrc -= 3;
+                    if (cbSrc < 0)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    kHlpMemSet(pbDst, pbSrc[2], cb);
+                    pbDst += cb;
+                    pbSrc += 3;
+                }
+                break;
+            }
+
+
+            /*
+             *  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
+             *  type  |  |  |     |  |                       |
+             *        ----  -------  -------------------------
+             *        cb1   cb2 - 3          offset            <cb1 bytes of data>
+             *
+             * Two bytes layed out as described above, followed by cb1 bytes of data to be copied.
+             * The cb2(+3) and offset describes an amount of data to be copied from the expanded
+             * data relative to the current position. The data copied as you would expect it to be.
+             */
+            case 1:
+            {
+                cbSrc -= 2;
+                if (cbSrc < 0)
+                    return KLDR_ERR_LX_BAD_ITERDATA2;
+                else
+                {
+                    const unsigned  off = ((unsigned)pbSrc[1] << 1) | (*pbSrc >> 7);
+                    const int       cb1 = (*pbSrc >> 2) & 3;
+                    const int       cb2 = ((*pbSrc >> 4) & 7) + 3;
+
+                    pbSrc += 2;
+                    cbSrc -= cb1;
+                    if (cbSrc < 0)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    cbDst -= cb1;
+                    if (cbDst < 0)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    kHlpMemCopy(pbDst, pbSrc, cb1);
+                    pbDst += cb1;
+                    pbSrc += cb1;
+
+                    if (off > OBJPAGELEN - (unsigned)cbDst)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    cbDst -= cb2;
+                    if (cbDst < 0)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    kHlpMemMove(pbDst, pbDst - off, cb2);
+                    pbDst += cb2;
+                }
+                break;
+            }
+
+
+            /*
+             *  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
+             *  type  |  |  |                                |
+             *        ----  ----------------------------------
+             *       cb-3               offset
+             *
+             * Two bytes layed out as described above.
+             * The cb(+3) and offset describes an amount of data to be copied from the expanded
+             * data relative to the current position.
+             *
+             * If offset == 1 the data is not copied as expected, but in the memcpyw manner.
+             */
+            case 2:
+            {
+                cbSrc -= 2;
+                if (cbSrc < 0)
+                    return KLDR_ERR_LX_BAD_ITERDATA2;
+                else
+                {
+                    const unsigned  off = ((unsigned)pbSrc[1] << 4) | (*pbSrc >> 4);
+                    const int       cb = ((*pbSrc >> 2) & 3) + 3;
+
+                    pbSrc += 2;
+                    if (off > OBJPAGELEN - (unsigned)cbDst)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    cbDst -= cb;
+                    if (cbDst < 0)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    kLdrModLXMemCopyW(pbDst, pbDst - off, cb);
+                    pbDst += cb;
+                }
+                break;
+            }
+
+
+            /*
+             *  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
+             *  type  |        |  |              |  |                                |
+             *        ----------  ----------------  ----------------------------------
+             *           cb1            cb2                      offset                <cb1 bytes of data>
+             *
+             * Three bytes layed out as described above, followed by cb1 bytes of data to be copied.
+             * The cb2 and offset describes an amount of data to be copied from the expanded
+             * data relative to the current position.
+             *
+             * If offset == 1 the data is not copied as expected, but in the memcpyw manner.
+             */
+            case 3:
+            {
+                cbSrc -= 3;
+                if (cbSrc < 0)
+                    return KLDR_ERR_LX_BAD_ITERDATA2;
+                else
+                {
+                    const int       cb1 = (*pbSrc >> 2) & 0xf;
+                    const int       cb2 = ((pbSrc[1] & 0xf) << 2) | (*pbSrc >> 6);
+                    const unsigned  off = ((unsigned)pbSrc[2] << 4) | (pbSrc[1] >> 4);
+
+                    pbSrc += 3;
+                    cbSrc -= cb1;
+                    if (cbSrc < 0)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    cbDst -= cb1;
+                    if (cbDst < 0)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    kHlpMemCopy(pbDst, pbSrc, cb1);
+                    pbDst += cb1;
+                    pbSrc += cb1;
+
+                    if (off > OBJPAGELEN - (unsigned)cbDst)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    cbDst -= cb2;
+                    if (cbDst < 0)
+                        return KLDR_ERR_LX_BAD_ITERDATA2;
+                    kLdrModLXMemCopyW(pbDst, pbDst - off, cb2);
+                    pbDst += cb2;
+                }
+                break;
+            }
+        } /* type switch. */
+    } /* unpack loop */
+
+l_endloop:
+
+
+    /*
+     * Zero remainder of the page.
+     */
+    if (cbDst > 0)
+        kHlpMemSet(pbDst, 0, cbDst);
+
+    return 0;
+}
+
+
+/**
+ * Special memcpy employed by the iterdata2 algorithm.
+ *
+ * Emulate a 16-bit memcpy (copying 16-bit at a time) and the effects this
+ * has if src is very close to the destination.
+ *
+ * @param   pbDst   Destination pointer.
+ * @param   pbSrc   Source pointer. Will always be <= pbDst.
+ * @param   cb      Amount of data to be copied.
+ * @remark  This assumes that unaligned word and dword access is fine.
+ */
+static void kLdrModLXMemCopyW(KU8 *pbDst, const KU8 *pbSrc, int cb)
+{
+    switch (pbDst - pbSrc)
+    {
+        case 0:
+        case 1:
+        case 2:
+        case 3:
+            /* 16-bit copy (unaligned) */
+            if (cb & 1)
+                *pbDst++ = *pbSrc++;
+            for (cb >>= 1; cb > 0; cb--, pbDst += 2, pbSrc += 2)
+                *(KU16 *)pbDst = *(const KU16 *)pbSrc;
+            break;
+
+        default:
+            /* 32-bit copy (unaligned) */
+            if (cb & 1)
+                *pbDst++ = *pbSrc++;
+            if (cb & 2)
+            {
+                *(KU16 *)pbDst = *(const KU16 *)pbSrc;
+                pbDst += 2;
+                pbSrc += 2;
+            }
+            for (cb >>= 2; cb > 0; cb--, pbDst += 4, pbSrc += 4)
+                *(KU32 *)pbDst = *(const KU32 *)pbSrc;
+            break;
+    }
+}
+
+
+/**
+ * Unprotects or protects the specified image mapping.
+ *
+ * @returns 0 on success.
+ * @returns non-zero kLdr or OS status code on failure.
+ *
+ * @param   pModLX  The LX module interpreter instance.
+ * @param   pvBits  The mapping to protect.
+ * @param   UnprotectOrProtect  If 1 unprotect (i.e. make all writable), otherwise
+ *          protect according to the object table.
+ */
+static int kldrModLXDoProtect(PKLDRMODLX pModLX, void *pvBits, unsigned fUnprotectOrProtect)
+{
+    KU32 i;
+    PKLDRMOD pMod = pModLX->pMod;
+
+    /*
+     * Change object protection.
+     */
+    for (i = 0; i < pMod->cSegments; i++)
+    {
+        int rc;
+        void *pv;
+        KPROT enmProt;
+
+        /* calc new protection. */
+        enmProt = pMod->aSegments[i].enmProt;
+        if (fUnprotectOrProtect)
+        {
+            switch (enmProt)
+            {
+                case KPROT_NOACCESS:
+                case KPROT_READONLY:
+                case KPROT_READWRITE:
+                case KPROT_WRITECOPY:
+                    enmProt = KPROT_READWRITE;
+                    break;
+                case KPROT_EXECUTE:
+                case KPROT_EXECUTE_READ:
+                case KPROT_EXECUTE_READWRITE:
+                case KPROT_EXECUTE_WRITECOPY:
+                    enmProt = KPROT_EXECUTE_READWRITE;
+                    break;
+                default:
+                    KLDRMODLX_ASSERT(!"bad enmProt");
+                    return -1;
+            }
+        }
+        else
+        {
+            /* copy on write -> normal write. */
+            if (enmProt == KPROT_EXECUTE_WRITECOPY)
+                enmProt = KPROT_EXECUTE_READWRITE;
+            else if (enmProt == KPROT_WRITECOPY)
+                enmProt = KPROT_READWRITE;
+        }
+
+
+        /* calc the address and set page protection. */
+        pv = (KU8 *)pvBits + pMod->aSegments[i].RVA;
+
+        rc = kHlpPageProtect(pv, pMod->aSegments[i].cbMapped, enmProt);
+        if (rc)
+            break;
+
+        /** @todo the gap page should be marked NOACCESS! */
+    }
+
+    return 0;
+}
+
+
+/** @copydoc kLdrModUnmap */
+static int kldrModLXUnmap(PKLDRMOD pMod)
+{
+    PKLDRMODLX  pModLX = (PKLDRMODLX)pMod->pvData;
+    KU32        i;
+    int         rc;
+
+    /*
+     * Mapped?
+     */
+    if (!pModLX->pvMapping)
+        return KLDR_ERR_NOT_MAPPED;
+
+    /*
+     * Free the mapping and update the segments.
+     */
+    rc = kHlpPageFree((void *)pModLX->pvMapping, pModLX->cbMapped);
+    KLDRMODLX_ASSERT(!rc);
+    pModLX->pvMapping = NULL;
+
+    for (i = 0; i < pMod->cSegments; i++)
+        pMod->aSegments[i].MapAddress = 0;
+
+    return rc;
+}
+
+
+/** @copydoc kLdrModAllocTLS */
+static int kldrModLXAllocTLS(PKLDRMOD pMod, void *pvMapping)
+{
+    PKLDRMODLX  pModLX = (PKLDRMODLX)pMod->pvData;
+
+    /* no tls, just do the error checking. */
+    if (   pvMapping == KLDRMOD_INT_MAP
+        && pModLX->pvMapping)
+        return KLDR_ERR_NOT_MAPPED;
+    return 0;
+}
+
+
+/** @copydoc kLdrModFreeTLS */
+static void kldrModLXFreeTLS(PKLDRMOD pMod, void *pvMapping)
+{
+    /* no tls. */
+    K_NOREF(pMod);
+    K_NOREF(pvMapping);
+
+}
+
+
+/** @copydoc kLdrModReload */
+static int kldrModLXReload(PKLDRMOD pMod)
+{
+    PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+    int rc, rc2;
+
+    /*
+     * Mapped?
+     */
+    if (!pModLX->pvMapping)
+        return KLDR_ERR_NOT_MAPPED;
+
+    /*
+     * Before doing anything we'll have to make all pages writable.
+     */
+    rc = kldrModLXDoProtect(pModLX, (void *)pModLX->pvMapping, 1 /* unprotect */);
+    if (rc)
+        return rc;
+
+    /*
+     * Load the bits again.
+     */
+    rc = kldrModLXDoLoadBits(pModLX, (void *)pModLX->pvMapping);
+
+    /*
+     * Restore protection.
+     */
+    rc2 = kldrModLXDoProtect(pModLX, (void *)pModLX->pvMapping, 0 /* protect */);
+    if (!rc && rc2)
+        rc = rc2;
+    return rc;
+}
+
+
+/** @copydoc kLdrModFixupMapping */
+static int kldrModLXFixupMapping(PKLDRMOD pMod, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+    PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+    int rc, rc2;
+
+    /*
+     * Mapped?
+     */
+    if (!pModLX->pvMapping)
+        return KLDR_ERR_NOT_MAPPED;
+
+    /*
+     * Before doing anything we'll have to make all pages writable.
+     */
+    rc = kldrModLXDoProtect(pModLX, (void *)pModLX->pvMapping, 1 /* unprotect */);
+    if (rc)
+        return rc;
+
+    /*
+     * Apply fixups and resolve imports.
+     */
+    rc = kldrModLXRelocateBits(pMod, (void *)pModLX->pvMapping, (KUPTR)pModLX->pvMapping,
+                               pMod->aSegments[0].LinkAddress, pfnGetImport, pvUser);
+
+    /*
+     * Restore protection.
+     */
+    rc2 = kldrModLXDoProtect(pModLX, (void *)pModLX->pvMapping, 0 /* protect */);
+    if (!rc && rc2)
+        rc = rc2;
+    return rc;
+}
+
+
+/** @copydoc kLdrModCallInit */
+static int kldrModLXCallInit(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+    PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+    int rc;
+
+    /*
+     * Mapped?
+     */
+    if (pvMapping == KLDRMOD_INT_MAP)
+    {
+        pvMapping = (void *)pModLX->pvMapping;
+        if (!pvMapping)
+            return KLDR_ERR_NOT_MAPPED;
+    }
+
+    /*
+     * Do TLS callbacks first and then call the init/term function if it's a DLL.
+     */
+    if ((pModLX->Hdr.e32_mflags & E32MODMASK) == E32MODDLL)
+        rc = kldrModLXDoCallDLL(pModLX, pvMapping, 0 /* attach */, uHandle);
+    else
+        rc = 0;
+    return rc;
+}
+
+
+/**
+ * Call the DLL entrypoint.
+ *
+ * @returns 0 on success.
+ * @returns KLDR_ERR_MODULE_INIT_FAILED  or KLDR_ERR_THREAD_ATTACH_FAILED on failure.
+ * @param   pModLX          The LX module interpreter instance.
+ * @param   pvMapping       The module mapping to use (resolved).
+ * @param   uOp             The operation (DLL_*).
+ * @param   uHandle         The module handle to present.
+ */
+static int kldrModLXDoCallDLL(PKLDRMODLX pModLX, void *pvMapping, unsigned uOp, KUPTR uHandle)
+{
+    int rc;
+
+    /*
+     * If no entrypoint there isn't anything to be done.
+     */
+    if (    !pModLX->Hdr.e32_startobj
+        ||  pModLX->Hdr.e32_startobj > pModLX->Hdr.e32_objcnt)
+        return 0;
+
+    /*
+     * Invoke the entrypoint and convert the boolean result to a kLdr status code.
+     */
+    rc = kldrModLXDoCall((KUPTR)pvMapping
+                         + (KUPTR)pModLX->pMod->aSegments[pModLX->Hdr.e32_startobj - 1].RVA
+                         + pModLX->Hdr.e32_eip,
+                         uHandle, uOp, NULL);
+    if (rc)
+        rc = 0;
+    else if (uOp == 0 /* attach */)
+        rc = KLDR_ERR_MODULE_INIT_FAILED;
+    else /* detach: ignore failures */
+        rc = 0;
+    return rc;
+}
+
+
+/**
+ * Do a 3 parameter callback.
+ *
+ * @returns 32-bit callback return.
+ * @param   uEntrypoint     The address of the function to be called.
+ * @param   uHandle         The first argument, the module handle.
+ * @param   uOp             The second argumnet, the reason we're calling.
+ * @param   pvReserved      The third argument, reserved argument. (figure this one out)
+ */
+static KI32 kldrModLXDoCall(KUPTR uEntrypoint, KUPTR uHandle, KU32 uOp, void *pvReserved)
+{
+#if defined(__X86__) || defined(__i386__) || defined(_M_IX86)
+    KI32 rc;
+/** @todo try/except */
+
+    /*
+     * Paranoia.
+     */
+# ifdef __GNUC__
+    __asm__ __volatile__(
+        "pushl  %2\n\t"
+        "pushl  %1\n\t"
+        "pushl  %0\n\t"
+        "lea   12(%%esp), %2\n\t"
+        "call  *%3\n\t"
+        "movl   %2, %%esp\n\t"
+        : "=a" (rc)
+        : "d" (uOp),
+          "S" (0),
+          "c" (uEntrypoint),
+          "0" (uHandle));
+# elif defined(_MSC_VER)
+    __asm {
+        mov     eax, [uHandle]
+        mov     edx, [uOp]
+        mov     ecx, 0
+        mov     ebx, [uEntrypoint]
+        push    edi
+        mov     edi, esp
+        push    ecx
+        push    edx
+        push    eax
+        call    ebx
+        mov     esp, edi
+        pop     edi
+        mov     [rc], eax
+    }
+# else
+#  error "port me!"
+# endif
+    K_NOREF(pvReserved);
+    return rc;
+
+#else
+    K_NOREF(uEntrypoint);
+    K_NOREF(uHandle);
+    K_NOREF(uOp);
+    K_NOREF(pvReserved);
+    return KCPU_ERR_ARCH_CPU_NOT_COMPATIBLE;
+#endif
+}
+
+
+/** @copydoc kLdrModCallTerm */
+static int kldrModLXCallTerm(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+    PKLDRMODLX  pModLX = (PKLDRMODLX)pMod->pvData;
+
+    /*
+     * Mapped?
+     */
+    if (pvMapping == KLDRMOD_INT_MAP)
+    {
+        pvMapping = (void *)pModLX->pvMapping;
+        if (!pvMapping)
+            return KLDR_ERR_NOT_MAPPED;
+    }
+
+    /*
+     * Do the call.
+     */
+    if ((pModLX->Hdr.e32_mflags & E32MODMASK) == E32MODDLL)
+        kldrModLXDoCallDLL(pModLX, pvMapping, 1 /* detach */, uHandle);
+
+    return 0;
+}
+
+
+/** @copydoc kLdrModCallThread */
+static int kldrModLXCallThread(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle, unsigned fAttachingOrDetaching)
+{
+    /* no thread attach/detach callout. */
+    K_NOREF(pMod);
+    K_NOREF(pvMapping);
+    K_NOREF(uHandle);
+    K_NOREF(fAttachingOrDetaching);
+    return 0;
+}
+
+
+/** @copydoc kLdrModSize */
+static KLDRADDR kldrModLXSize(PKLDRMOD pMod)
+{
+    PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+    return pModLX->cbMapped;
+}
+
+
+/** @copydoc kLdrModGetBits */
+static int kldrModLXGetBits(PKLDRMOD pMod, void *pvBits, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+    PKLDRMODLX  pModLX = (PKLDRMODLX)pMod->pvData;
+    int         rc;
+
+    /*
+     * Load the image bits.
+     */
+    rc = kldrModLXDoLoadBits(pModLX, pvBits);
+    if (rc)
+        return rc;
+
+    /*
+     * Perform relocations.
+     */
+    return kldrModLXRelocateBits(pMod, pvBits, BaseAddress, pMod->aSegments[0].LinkAddress, pfnGetImport, pvUser);
+
+}
+
+
+/** @copydoc kLdrModRelocateBits */
+static int kldrModLXRelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress,
+                                 PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+    PKLDRMODLX pModLX = (PKLDRMODLX)pMod->pvData;
+    KU32 iSeg;
+    int rc;
+
+    /*
+     * Do we need to to *anything*?
+     */
+    if (    NewBaseAddress == OldBaseAddress
+        &&  NewBaseAddress == pModLX->paObjs[0].o32_base
+        &&  !pModLX->Hdr.e32_impmodcnt)
+        return 0;
+
+    /*
+     * Load the fixup section.
+     */
+    if (!pModLX->pbFixupSection)
+    {
+        rc = kldrModLXDoLoadFixupSection(pModLX);
+        if (rc)
+            return rc;
+    }
+
+    /*
+     * Iterate the segments.
+     */
+    for (iSeg = 0; iSeg < pModLX->Hdr.e32_objcnt; iSeg++)
+    {
+        const struct o32_obj * const pObj = &pModLX->paObjs[iSeg];
+        KLDRADDR        PageAddress = NewBaseAddress + pModLX->pMod->aSegments[iSeg].RVA;
+        KU32            iPage;
+        KU8            *pbPage = (KU8 *)pvBits + (KUPTR)pModLX->pMod->aSegments[iSeg].RVA;
+
+        /*
+         * Iterate the page map pages.
+         */
+        for (iPage = 0, rc = 0; !rc && iPage < pObj->o32_mapsize; iPage++, pbPage += OBJPAGELEN, PageAddress += OBJPAGELEN)
+        {
+            const KU8 * const   pbFixupRecEnd = pModLX->pbFixupRecs + pModLX->paoffPageFixups[iPage + pObj->o32_pagemap];
+            const KU8          *pb            = pModLX->pbFixupRecs + pModLX->paoffPageFixups[iPage + pObj->o32_pagemap - 1];
+            KLDRADDR            uValue        = NIL_KLDRADDR;
+            KU32                fKind         = 0;
+            int                 iSelector;
+
+            /* sanity */
+            if (pbFixupRecEnd < pb)
+                return KLDR_ERR_BAD_FIXUP;
+            if (pbFixupRecEnd - 1 > pModLX->pbFixupSectionLast)
+                return KLDR_ERR_BAD_FIXUP;
+            if (pb < pModLX->pbFixupSection)
+                return KLDR_ERR_BAD_FIXUP;
+
+            /*
+             * Iterate the fixup record.
+             */
+            while (pb < pbFixupRecEnd)
+            {
+                union _rel
+                {
+                    const KU8 *             pb;
+                    const struct r32_rlc   *prlc;
+                } u;
+
+                u.pb = pb;
+                pb += 3 + (u.prlc->nr_stype & NRCHAIN ? 0 : 1); /* place pch at the 4th member. */
+
+                /*
+                 * Figure out the target.
+                 */
+                switch (u.prlc->nr_flags & NRRTYP)
+                {
+                    /*
+                     * Internal fixup.
+                     */
+                    case NRRINT:
+                    {
+                        KU16 iTrgObject;
+                        KU32 offTrgObject;
+
+                        /* the object */
+                        if (u.prlc->nr_flags & NR16OBJMOD)
+                        {
+                            iTrgObject = *(const KU16 *)pb;
+                            pb += 2;
+                        }
+                        else
+                            iTrgObject = *pb++;
+                        iTrgObject--;
+                        if (iTrgObject >= pModLX->Hdr.e32_objcnt)
+                            return KLDR_ERR_BAD_FIXUP;
+
+                        /* the target */
+                        if ((u.prlc->nr_stype & NRSRCMASK) != NRSSEG)
+                        {
+                            if (u.prlc->nr_flags & NR32BITOFF)
+                            {
+                                offTrgObject = *(const KU32 *)pb;
+                                pb += 4;
+                            }
+                            else
+                            {
+                                offTrgObject = *(const KU16 *)pb;
+                                pb += 2;
+                            }
+
+                            /* calculate the symbol info. */
+                            uValue = offTrgObject + NewBaseAddress + pMod->aSegments[iTrgObject].RVA;
+                        }
+                        else
+                            uValue = NewBaseAddress + pMod->aSegments[iTrgObject].RVA;
+                        if (    (u.prlc->nr_stype & NRALIAS)
+                            ||  (pMod->aSegments[iTrgObject].fFlags & KLDRSEG_FLAG_16BIT))
+                            iSelector = pMod->aSegments[iTrgObject].Sel16bit;
+                        else
+                            iSelector = pMod->aSegments[iTrgObject].SelFlat;
+                        fKind = 0;
+                        break;
+                    }
+
+                    /*
+                     * Import by symbol ordinal.
+                     */
+                    case NRRORD:
+                    {
+                        KU16 iModule;
+                        KU32 iSymbol;
+
+                        /* the module ordinal */
+                        if (u.prlc->nr_flags & NR16OBJMOD)
+                        {
+                            iModule = *(const KU16 *)pb;
+                            pb += 2;
+                        }
+                        else
+                            iModule = *pb++;
+                        iModule--;
+                        if (iModule >= pModLX->Hdr.e32_impmodcnt)
+                            return KLDR_ERR_BAD_FIXUP;
+#if 1
+                        if (u.prlc->nr_flags & NRICHAIN)
+                            return KLDR_ERR_BAD_FIXUP;
+#endif
+
+                        /* . */
+                        if (u.prlc->nr_flags & NR32BITOFF)
+                        {
+                            iSymbol = *(const KU32 *)pb;
+                            pb += 4;
+                        }
+                        else if (!(u.prlc->nr_flags & NR8BITORD))
+                        {
+                            iSymbol = *(const KU16 *)pb;
+                            pb += 2;
+                        }
+                        else
+                            iSymbol = *pb++;
+
+                        /* resolve it. */
+                        rc = pfnGetImport(pMod, iModule, iSymbol, NULL, 0, NULL, &uValue, &fKind, pvUser);
+                        if (rc)
+                            return rc;
+                        iSelector = -1;
+                        break;
+                    }
+
+                    /*
+                     * Import by symbol name.
+                     */
+                    case NRRNAM:
+                    {
+                        KU32 iModule;
+                        KU16 offSymbol;
+                        const KU8 *pbSymbol;
+
+                        /* the module ordinal */
+                        if (u.prlc->nr_flags & NR16OBJMOD)
+                        {
+                            iModule = *(const KU16 *)pb;
+                            pb += 2;
+                        }
+                        else
+                            iModule = *pb++;
+                        iModule--;
+                        if (iModule >= pModLX->Hdr.e32_impmodcnt)
+                            return KLDR_ERR_BAD_FIXUP;
+#if 1
+                        if (u.prlc->nr_flags & NRICHAIN)
+                            return KLDR_ERR_BAD_FIXUP;
+#endif
+
+                        /* . */
+                        if (u.prlc->nr_flags & NR32BITOFF)
+                        {
+                            offSymbol = *(const KU32 *)pb;
+                            pb += 4;
+                        }
+                        else if (!(u.prlc->nr_flags & NR8BITORD))
+                        {
+                            offSymbol = *(const KU16 *)pb;
+                            pb += 2;
+                        }
+                        else
+                            offSymbol = *pb++;
+                        pbSymbol = pModLX->pbImportProcs + offSymbol;
+                        if (    pbSymbol < pModLX->pbImportProcs
+                            ||  pbSymbol > pModLX->pbFixupSectionLast)
+                            return KLDR_ERR_BAD_FIXUP;
+
+                        /* resolve it. */
+                        rc = pfnGetImport(pMod, iModule, NIL_KLDRMOD_SYM_ORDINAL, (const char *)pbSymbol + 1, *pbSymbol, NULL,
+                                          &uValue, &fKind, pvUser);
+                        if (rc)
+                            return rc;
+                        iSelector = -1;
+                        break;
+                    }
+
+                    case NRRENT:
+                        KLDRMODLX_ASSERT(!"NRRENT");
+                        /* Falls through. */
+                    default:
+                        iSelector = -1;
+                        break;
+                }
+
+                /* addend */
+                if (u.prlc->nr_flags & NRADD)
+                {
+                    if (u.prlc->nr_flags & NR32BITADD)
+                    {
+                        uValue += *(const KU32 *)pb;
+                        pb += 4;
+                    }
+                    else
+                    {
+                        uValue += *(const KU16 *)pb;
+                        pb += 2;
+                    }
+                }
+
+
+                /*
+                 * Deal with the 'source' (i.e. the place that should be modified - very logical).
+                 */
+                if (!(u.prlc->nr_stype & NRCHAIN))
+                {
+                    int off = u.prlc->r32_soff;
+
+                    /* common / simple */
+                    if (    (u.prlc->nr_stype & NRSRCMASK) == NROFF32
+                        &&  off >= 0
+                        &&  off <= (int)OBJPAGELEN - 4)
+                        *(KU32 *)&pbPage[off] = (KU32)uValue;
+                    else if (    (u.prlc->nr_stype & NRSRCMASK) == NRSOFF32
+                            &&  off >= 0
+                            &&  off <= (int)OBJPAGELEN - 4)
+                        *(KU32 *)&pbPage[off] = (KU32)(uValue - (PageAddress + off + 4));
+                    else
+                    {
+                        /* generic */
+                        rc = kldrModLXDoReloc(pbPage, off, PageAddress, u.prlc, iSelector, uValue, fKind);
+                        if (rc)
+                            return rc;
+                    }
+                }
+                else if (!(u.prlc->nr_flags & NRICHAIN))
+                {
+                    const KI16 *poffSrc = (const KI16 *)pb;
+                    KU8 c = u.pb[2];
+
+                    /* common / simple */
+                    if ((u.prlc->nr_stype & NRSRCMASK) == NROFF32)
+                    {
+                        while (c-- > 0)
+                        {
+                            int off = *poffSrc++;
+                            if (off >= 0 && off <= (int)OBJPAGELEN - 4)
+                                *(KU32 *)&pbPage[off] = (KU32)uValue;
+                            else
+                            {
+                                rc = kldrModLXDoReloc(pbPage, off, PageAddress, u.prlc, iSelector, uValue, fKind);
+                                if (rc)
+                                    return rc;
+                            }
+                        }
+                    }
+                    else if ((u.prlc->nr_stype & NRSRCMASK) == NRSOFF32)
+                    {
+                        while (c-- > 0)
+                        {
+                            int off = *poffSrc++;
+                            if (off >= 0 && off <= (int)OBJPAGELEN - 4)
+                                *(KU32 *)&pbPage[off] = (KU32)(uValue - (PageAddress + off + 4));
+                            else
+                            {
+                                rc = kldrModLXDoReloc(pbPage, off, PageAddress, u.prlc, iSelector, uValue, fKind);
+                                if (rc)
+                                    return rc;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        while (c-- > 0)
+                        {
+                            rc = kldrModLXDoReloc(pbPage, *poffSrc++, PageAddress, u.prlc, iSelector, uValue, fKind);
+                            if (rc)
+                                return rc;
+                        }
+                    }
+                    pb = (const KU8 *)poffSrc;
+                }
+                else
+                {
+                    /* This is a pain because it will require virgin pages on a relocation. */
+                    KLDRMODLX_ASSERT(!"NRICHAIN");
+                    return KLDR_ERR_LX_NRICHAIN_NOT_SUPPORTED;
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+/**
+ * Applies the relocation to one 'source' in a page.
+ *
+ * This takes care of the more esotic case while the common cases
+ * are dealt with seperately.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param   pbPage      The page in which to apply the fixup.
+ * @param   off         Page relative offset of where to apply the offset.
+ * @param   uValue      The target value.
+ * @param   fKind       The target kind.
+ */
+static int kldrModLXDoReloc(KU8 *pbPage, int off, KLDRADDR PageAddress, const struct r32_rlc *prlc,
+                            int iSelector, KLDRADDR uValue, KU32 fKind)
+{
+#pragma pack(1) /* just to be sure */
+    union
+    {
+        KU8         ab[6];
+        KU32        off32;
+        KU16        off16;
+        KU8         off8;
+        struct
+        {
+            KU16    off;
+            KU16    Sel;
+        }           Far16;
+        struct
+        {
+            KU32    off;
+            KU16    Sel;
+        }           Far32;
+    }               uData;
+#pragma pack()
+    const KU8      *pbSrc;
+    KU8            *pbDst;
+    KU8             cb;
+
+    K_NOREF(fKind);
+
+    /*
+     * Compose the fixup data.
+     */
+    switch (prlc->nr_stype & NRSRCMASK)
+    {
+        case NRSBYT:
+            uData.off8 = (KU8)uValue;
+            cb = 1;
+            break;
+        case NRSSEG:
+            if (iSelector == -1)
+            {
+                /* fixme */
+            }
+            uData.off16 = iSelector;
+            cb = 2;
+            break;
+        case NRSPTR:
+            if (iSelector == -1)
+            {
+                /* fixme */
+            }
+            uData.Far16.off = (KU16)uValue;
+            uData.Far16.Sel = iSelector;
+            cb = 4;
+            break;
+        case NRSOFF:
+            uData.off16 = (KU16)uValue;
+            cb = 2;
+            break;
+        case NRPTR48:
+            if (iSelector == -1)
+            {
+                /* fixme */
+            }
+            uData.Far32.off = (KU32)uValue;
+            uData.Far32.Sel = iSelector;
+            cb = 6;
+            break;
+        case NROFF32:
+            uData.off32 = (KU32)uValue;
+            cb = 4;
+            break;
+        case NRSOFF32:
+            uData.off32 = (KU32)(uValue - (PageAddress + off + 4));
+            cb = 4;
+            break;
+        default:
+            return KLDR_ERR_LX_BAD_FIXUP_SECTION; /** @todo fix error, add more checks! */
+    }
+
+    /*
+     * Apply it. This is sloooow...
+     */
+    pbSrc = &uData.ab[0];
+    pbDst = pbPage + off;
+    while (cb-- > 0)
+    {
+        if (off > (int)OBJPAGELEN)
+            break;
+        if (off >= 0)
+            *pbDst = *pbSrc;
+        pbSrc++;
+        pbDst++;
+    }
+
+    return 0;
+}
+
+
+/**
+ * The LX module interpreter method table.
+ */
+KLDRMODOPS g_kLdrModLXOps =
+{
+    "LX",
+    NULL,
+    kldrModLXCreate,
+    kldrModLXDestroy,
+    kldrModLXQuerySymbol,
+    kldrModLXEnumSymbols,
+    kldrModLXGetImport,
+    kldrModLXNumberOfImports,
+    NULL /* can execute one is optional */,
+    kldrModLXGetStackInfo,
+    kldrModLXQueryMainEntrypoint,
+    NULL /* pfnQueryImageUuid */,
+    NULL /* fixme */,
+    NULL /* fixme */,
+    kldrModLXEnumDbgInfo,
+    kldrModLXHasDbgInfo,
+    kldrModLXMap,
+    kldrModLXUnmap,
+    kldrModLXAllocTLS,
+    kldrModLXFreeTLS,
+    kldrModLXReload,
+    kldrModLXFixupMapping,
+    kldrModLXCallInit,
+    kldrModLXCallTerm,
+    kldrModLXCallThread,
+    kldrModLXSize,
+    kldrModLXGetBits,
+    kldrModLXRelocateBits,
+    NULL /* fixme: pfnMostlyDone */,
+    42 /* the end */
+};
+
Index: trunk/src/VBox/Runtime/common/ldr/ldrMachO.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/ldr/ldrMachO.cpp	(revision 74636)
+++ trunk/src/VBox/Runtime/common/ldr/ldrMachO.cpp	(revision 74636)
@@ -0,0 +1,3729 @@
+/* $Id$ */
+/** @file
+ * kLdr - The Module Interpreter for the MACH-O format.
+ */
+
+/*
+ * 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                                                               *
+*******************************************************************************/
+#include <k/kLdr.h>
+#include "kLdrInternal.h"
+#include <k/kLdrFmts/mach-o.h>
+
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+/** @def KLDRMODMACHO_STRICT
+ * Define KLDRMODMACHO_STRICT to enabled strict checks in KLDRMODMACHO. */
+#define KLDRMODMACHO_STRICT 1
+
+/** @def KLDRMODMACHO_ASSERT
+ * Assert that an expression is true when KLDR_STRICT is defined.
+ */
+#ifdef KLDRMODMACHO_STRICT
+# define KLDRMODMACHO_ASSERT(expr)  kHlpAssert(expr)
+#else
+# define KLDRMODMACHO_ASSERT(expr)  do {} while (0)
+#endif
+
+/** @def KLDRMODMACHO_CHECK_RETURN
+ * Checks that an expression is true and return if it isn't.
+ * This is a debug aid.
+ */
+#ifdef KLDRMODMACHO_STRICT2
+# define KLDRMODMACHO_CHECK_RETURN(expr, rc)  kHlpAssertReturn(expr, rc)
+#else
+# define KLDRMODMACHO_CHECK_RETURN(expr, rc)  do { if (!(expr)) { return (rc); } } while (0)
+#endif
+
+/** @def KLDRMODMACHO_CHECK_RETURN
+ * Checks that an expression is true and return if it isn't.
+ * This is a debug aid.
+ */
+#ifdef KLDRMODMACHO_STRICT2
+# define KLDRMODMACHO_FAILED_RETURN(rc)  kHlpAssertFailedReturn(rc)
+#else
+# define KLDRMODMACHO_FAILED_RETURN(rc)  return (rc)
+#endif
+
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+/**
+ * Mach-O section details.
+ */
+typedef struct KLDRMODMACHOSECT
+{
+    /** The size of the section (in bytes). */
+    KLDRSIZE                cb;
+    /** The link address of this section. */
+    KLDRADDR                LinkAddress;
+    /** The RVA of this section. */
+    KLDRADDR                RVA;
+    /** The file offset of this section.
+     * This is -1 if the section doesn't have a file backing. */
+    KLDRFOFF                offFile;
+    /** The number of fixups. */
+    KU32                    cFixups;
+    /** The array of fixups. (lazy loaded) */
+    macho_relocation_info_t *paFixups;
+    /** The file offset of the fixups for this section.
+     * This is -1 if the section doesn't have any fixups. */
+    KLDRFOFF                offFixups;
+    /** Mach-O section flags. */
+    KU32                    fFlags;
+    /** kLdr segment index. */
+    KU32                    iSegment;
+    /** Pointer to the Mach-O section structure. */
+    void                   *pvMachoSection;
+} KLDRMODMACHOSECT, *PKLDRMODMACHOSECT;
+
+/**
+ * Extra per-segment info.
+ *
+ * This is corresponds to a kLdr segment, not a Mach-O segment!
+ */
+typedef struct KLDRMODMACHOSEG
+{
+    /** The orignal segment number (in case we had to resort it). */
+    KU32                    iOrgSegNo;
+    /** The number of sections in the segment. */
+    KU32                    cSections;
+    /** Pointer to the sections belonging to this segment.
+     * The array resides in the big memory chunk allocated for
+     * the module handle, so it doesn't need freeing. */
+    PKLDRMODMACHOSECT       paSections;
+
+} KLDRMODMACHOSEG, *PKLDRMODMACHOSEG;
+
+/**
+ * Instance data for the Mach-O MH_OBJECT module interpreter.
+ * @todo interpret the other MH_* formats.
+ */
+typedef struct KLDRMODMACHO
+{
+    /** Pointer to the module. (Follows the section table.) */
+    PKLDRMOD                pMod;
+    /** Pointer to the RDR file mapping of the raw file bits. NULL if not mapped. */
+    const void             *pvBits;
+    /** Pointer to the user mapping. */
+    void                   *pvMapping;
+    /** The module open flags. */
+    KU32                    fOpenFlags;
+
+    /** The offset of the image. (FAT fun.) */
+    KLDRFOFF                offImage;
+    /** The link address. */
+    KLDRADDR                LinkAddress;
+    /** The size of the mapped image. */
+    KLDRADDR                cbImage;
+    /** Whether we're capable of loading the image. */
+    KBOOL                   fCanLoad;
+    /** Whether we're creating a global offset table segment.
+     * This dependes on the cputype and image type. */
+    KBOOL                   fMakeGot;
+    /** The size of a indirect GOT jump stub entry.
+     * This is 0 if not needed. */
+    KU8                     cbJmpStub;
+    /** Effective file type.  If the original was a MH_OBJECT file, the
+     * corresponding MH_DSYM needs the segment translation of a MH_OBJECT too.
+     * The MH_DSYM normally has a separate __DWARF segment, but this is
+     * automatically skipped during the transation. */
+    KU8                     uEffFileType;
+    /** Pointer to the load commands. (endian converted) */
+    KU8                    *pbLoadCommands;
+    /** The Mach-O header. (endian converted)
+     * @remark The reserved field is only valid for real 64-bit headers. */
+    mach_header_64_t        Hdr;
+
+    /** The offset of the symbol table. */
+    KLDRFOFF                offSymbols;
+    /** The number of symbols. */
+    KU32                    cSymbols;
+    /** The pointer to the loaded symbol table. */
+    void                   *pvaSymbols;
+    /** The offset of the string table. */
+    KLDRFOFF                offStrings;
+    /** The size of the of the string table. */
+    KU32                    cchStrings;
+    /** Pointer to the loaded string table. */
+    char                   *pchStrings;
+
+    /** The image UUID, all zeros if not found. */
+    KU8                     abImageUuid[16];
+
+    /** The RVA of the Global Offset Table. */
+    KLDRADDR                GotRVA;
+    /** The RVA of the indirect GOT jump stubs.  */
+    KLDRADDR                JmpStubsRVA;
+
+    /** The number of sections. */
+    KU32                    cSections;
+    /** Pointer to the section array running in parallel to the Mach-O one. */
+    PKLDRMODMACHOSECT       paSections;
+
+    /** Array of segments parallel to the one in KLDRMOD. */
+    KLDRMODMACHOSEG         aSegments[1];
+} KLDRMODMACHO, *PKLDRMODMACHO;
+
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+#if 0
+static KI32 kldrModMachONumberOfImports(PKLDRMOD pMod, const void *pvBits);
+#endif
+static int kldrModMachORelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress,
+                                    PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);
+
+static int  kldrModMachODoCreate(PKRDR pRdr, KLDRFOFF offImage, KU32 fOpenFlags, PKLDRMODMACHO *ppMod);
+static int  kldrModMachOPreParseLoadCommands(KU8 *pbLoadCommands, const mach_header_32_t *pHdr, PKRDR pRdr, KLDRFOFF offImage,
+                                             KU32 fOpenFlags, KU32 *pcSegments, KU32 *pcSections, KU32 *pcbStringPool,
+                                             PKBOOL pfCanLoad, PKLDRADDR pLinkAddress, KU8 *puEffFileType);
+static int  kldrModMachOParseLoadCommands(PKLDRMODMACHO pModMachO, char *pbStringPool, KU32 cbStringPool);
+static int  kldrModMachOAdjustBaseAddress(PKLDRMODMACHO pModMachO, PKLDRADDR pBaseAddress);
+
+/*static int  kldrModMachOLoadLoadCommands(PKLDRMODMACHO pModMachO);*/
+static int  kldrModMachOLoadObjSymTab(PKLDRMODMACHO pModMachO);
+static int  kldrModMachOLoadFixups(PKLDRMODMACHO pModMachO, KLDRFOFF offFixups, KU32 cFixups, macho_relocation_info_t **ppaFixups);
+static int  kldrModMachOMapVirginBits(PKLDRMODMACHO pModMachO);
+
+static int  kldrModMachODoQuerySymbol32Bit(PKLDRMODMACHO pModMachO, const macho_nlist_32_t *paSyms, KU32 cSyms, const char *pchStrings,
+                                           KU32 cchStrings, KLDRADDR BaseAddress, KU32 iSymbol, const char *pchSymbol,
+                                           KU32 cchSymbol, PKLDRADDR puValue, KU32 *pfKind);
+static int  kldrModMachODoQuerySymbol64Bit(PKLDRMODMACHO pModMachO, const macho_nlist_64_t *paSyms, KU32 cSyms, const char *pchStrings,
+                                           KU32 cchStrings, KLDRADDR BaseAddress, KU32 iSymbol, const char *pchSymbol,
+                                           KU32 cchSymbol, PKLDRADDR puValue, KU32 *pfKind);
+static int  kldrModMachODoEnumSymbols32Bit(PKLDRMODMACHO pModMachO, const macho_nlist_32_t *paSyms, KU32 cSyms,
+                                           const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress,
+                                           KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser);
+static int  kldrModMachODoEnumSymbols64Bit(PKLDRMODMACHO pModMachO, const macho_nlist_64_t *paSyms, KU32 cSyms,
+                                           const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress,
+                                           KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser);
+static int  kldrModMachOObjDoImports(PKLDRMODMACHO pModMachO, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);
+static int  kldrModMachOObjDoFixups(PKLDRMODMACHO pModMachO, void *pvMapping, KLDRADDR NewBaseAddress);
+static int  kldrModMachOFixupSectionGeneric32Bit(PKLDRMODMACHO pModMachO, KU8 *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
+                                                 macho_nlist_32_t *paSyms, KU32 cSyms, KLDRADDR NewBaseAddress);
+static int  kldrModMachOFixupSectionAMD64(PKLDRMODMACHO pModMachO, KU8 *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
+                                          macho_nlist_64_t *paSyms, KU32 cSyms, KLDRADDR NewBaseAddress);
+
+static int  kldrModMachOMakeGOT(PKLDRMODMACHO pModMachO, void *pvBits, KLDRADDR NewBaseAddress);
+
+/*static int  kldrModMachODoFixups(PKLDRMODMACHO pModMachO, void *pvMapping, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress);
+static int  kldrModMachODoImports(PKLDRMODMACHO pModMachO, void *pvMapping, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);*/
+
+
+/**
+ * Create a loader module instance interpreting the executable image found
+ * in the specified file provider instance.
+ *
+ * @returns 0 on success and *ppMod pointing to a module instance.
+ *          On failure, a non-zero OS specific error code is returned.
+ * @param   pOps            Pointer to the registered method table.
+ * @param   pRdr            The file provider instance to use.
+ * @param   fFlags          Flags, MBZ.
+ * @param   enmCpuArch      The desired CPU architecture. KCPUARCH_UNKNOWN means
+ *                          anything goes, but with a preference for the current
+ *                          host architecture.
+ * @param   offNewHdr       The offset of the new header in MZ files. -1 if not found.
+ * @param   ppMod           Where to store the module instance pointer.
+ */
+static int kldrModMachOCreate(PCKLDRMODOPS pOps, PKRDR pRdr, KU32 fFlags, KCPUARCH enmCpuArch, KLDRFOFF offNewHdr, PPKLDRMOD ppMod)
+{
+    PKLDRMODMACHO pModMachO;
+    int rc;
+
+    /*
+     * Create the instance data and do a minimal header validation.
+     */
+    rc = kldrModMachODoCreate(pRdr, offNewHdr == -1 ? 0 : offNewHdr, fFlags, &pModMachO);
+    if (!rc)
+    {
+
+        /*
+         * Match up against the requested CPU architecture.
+         */
+        if (    enmCpuArch == KCPUARCH_UNKNOWN
+            ||  pModMachO->pMod->enmArch == enmCpuArch)
+        {
+            pModMachO->pMod->pOps = pOps;
+            pModMachO->pMod->u32Magic = KLDRMOD_MAGIC;
+            *ppMod = pModMachO->pMod;
+            return 0;
+        }
+        rc = KLDR_ERR_CPU_ARCH_MISMATCH;
+    }
+    if (pModMachO)
+    {
+        kHlpFree(pModMachO->pbLoadCommands);
+        kHlpFree(pModMachO);
+    }
+    return rc;
+}
+
+
+/**
+ * Separate function for reading creating the Mach-O module instance to
+ * simplify cleanup on failure.
+ */
+static int kldrModMachODoCreate(PKRDR pRdr, KLDRFOFF offImage, KU32 fOpenFlags, PKLDRMODMACHO *ppModMachO)
+{
+    union
+    {
+        mach_header_32_t    Hdr32;
+        mach_header_64_t    Hdr64;
+    } s;
+    PKLDRMODMACHO pModMachO;
+    PKLDRMOD pMod;
+    KU8 *pbLoadCommands;
+    KU32 cSegments = 0; /* (MSC maybe used uninitialized) */
+    KU32 cSections = 0; /* (MSC maybe used uninitialized) */
+    KU32 cbStringPool = 0; /* (MSC maybe used uninitialized) */
+    KSIZE cchFilename;
+    KSIZE cb;
+    KBOOL fMakeGot;
+    KBOOL fCanLoad = K_TRUE;
+    KLDRADDR LinkAddress = NIL_KLDRADDR; /* (MSC maybe used uninitialized) */
+    KU8 cbJmpStub;
+    KU8 uEffFileType = 0; /* (MSC maybe used uninitialized) */
+    int rc;
+    *ppModMachO = NULL;
+
+    kHlpAssert(&s.Hdr32.magic == &s.Hdr64.magic);
+    kHlpAssert(&s.Hdr32.flags == &s.Hdr64.flags);
+
+    /*
+     * Read the Mach-O header.
+     */
+    rc = kRdrRead(pRdr, &s, sizeof(s), offImage);
+    if (rc)
+        return rc;
+    if (    s.Hdr32.magic != IMAGE_MACHO32_SIGNATURE
+        &&  s.Hdr32.magic != IMAGE_MACHO64_SIGNATURE
+        )
+    {
+        if (    s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE
+            ||  s.Hdr32.magic == IMAGE_MACHO64_SIGNATURE_OE)
+            return KLDR_ERR_MACHO_OTHER_ENDIAN_NOT_SUPPORTED;
+        return KLDR_ERR_UNKNOWN_FORMAT;
+    }
+
+    /* sanity checks. */
+    if (    s.Hdr32.sizeofcmds > kRdrSize(pRdr) - sizeof(mach_header_32_t)
+        ||  s.Hdr32.sizeofcmds < sizeof(load_command_t) * s.Hdr32.ncmds
+        ||  (s.Hdr32.flags & ~MH_VALID_FLAGS))
+        return KLDR_ERR_MACHO_BAD_HEADER;
+    switch (s.Hdr32.cputype)
+    {
+        case CPU_TYPE_X86:
+            fMakeGot = K_FALSE;
+            cbJmpStub = 0;
+            break;
+        case CPU_TYPE_X86_64:
+            fMakeGot = s.Hdr32.filetype == MH_OBJECT;
+            cbJmpStub = fMakeGot ? 8 : 0;
+            break;
+        default:
+            return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE;
+    }
+    if (   s.Hdr32.filetype != MH_OBJECT
+        && s.Hdr32.filetype != MH_EXECUTE
+        && s.Hdr32.filetype != MH_DYLIB
+        && s.Hdr32.filetype != MH_BUNDLE
+        && s.Hdr32.filetype != MH_DSYM
+        && s.Hdr32.filetype != MH_KEXT_BUNDLE)
+        return KLDR_ERR_MACHO_UNSUPPORTED_FILE_TYPE;
+
+    /*
+     * Read and pre-parse the load commands to figure out how many segments we'll be needing.
+     */
+    pbLoadCommands = kHlpAlloc(s.Hdr32.sizeofcmds);
+    if (!pbLoadCommands)
+        return KERR_NO_MEMORY;
+    rc = kRdrRead(pRdr, pbLoadCommands, s.Hdr32.sizeofcmds,
+                     s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE
+                  || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE
+                  ? sizeof(mach_header_32_t) + offImage
+                  : sizeof(mach_header_64_t) + offImage);
+    if (!rc)
+        rc = kldrModMachOPreParseLoadCommands(pbLoadCommands, &s.Hdr32, pRdr, offImage, fOpenFlags,
+                                              &cSegments, &cSections, &cbStringPool, &fCanLoad, &LinkAddress, &uEffFileType);
+    if (rc)
+    {
+        kHlpFree(pbLoadCommands);
+        return rc;
+    }
+    cSegments += fMakeGot;
+
+
+    /*
+     * Calc the instance size, allocate and initialize it.
+     */
+    cchFilename = kHlpStrLen(kRdrName(pRdr));
+    cb = K_ALIGN_Z(  K_OFFSETOF(KLDRMODMACHO, aSegments[cSegments])
+                      + sizeof(KLDRMODMACHOSECT) * cSections, 16)
+       + K_OFFSETOF(KLDRMOD, aSegments[cSegments])
+       + cchFilename + 1
+       + cbStringPool;
+    pModMachO = (PKLDRMODMACHO)kHlpAlloc(cb);
+    if (!pModMachO)
+        return KERR_NO_MEMORY;
+    *ppModMachO = pModMachO;
+    pModMachO->pbLoadCommands = pbLoadCommands;
+    pModMachO->offImage = offImage;
+
+    /* KLDRMOD */
+    pMod = (PKLDRMOD)((KU8 *)pModMachO + K_ALIGN_Z(  K_OFFSETOF(KLDRMODMACHO, aSegments[cSegments])
+                                                      + sizeof(KLDRMODMACHOSECT) * cSections, 16));
+    pMod->pvData = pModMachO;
+    pMod->pRdr = pRdr;
+    pMod->pOps = NULL;      /* set upon success. */
+    pMod->cSegments = cSegments;
+    pMod->cchFilename = (KU32)cchFilename;
+    pMod->pszFilename = (char *)&pMod->aSegments[pMod->cSegments];
+    kHlpMemCopy((char *)pMod->pszFilename, kRdrName(pRdr), cchFilename + 1);
+    pMod->pszName = kHlpGetFilename(pMod->pszFilename);
+    pMod->cchName = (KU32)(cchFilename - (pMod->pszName - pMod->pszFilename));
+    pMod->fFlags = 0;
+    switch (s.Hdr32.cputype)
+    {
+        case CPU_TYPE_X86:
+            pMod->enmArch = KCPUARCH_X86_32;
+            pMod->enmEndian = KLDRENDIAN_LITTLE;
+            switch (s.Hdr32.cpusubtype)
+            {
+                case CPU_SUBTYPE_I386_ALL: /* == CPU_SUBTYPE_386 */
+                    pMod->enmCpu = KCPU_X86_32_BLEND;
+                    break;
+                case CPU_SUBTYPE_486:
+                    pMod->enmCpu = KCPU_I486;
+                    break;
+                case CPU_SUBTYPE_486SX:
+                    pMod->enmCpu = KCPU_I486SX;
+                    break;
+                case CPU_SUBTYPE_PENT: /* == CPU_SUBTYPE_586 */
+                    pMod->enmCpu = KCPU_I586;
+                    break;
+                case CPU_SUBTYPE_PENTPRO:
+                case CPU_SUBTYPE_PENTII_M3:
+                case CPU_SUBTYPE_PENTII_M5:
+                case CPU_SUBTYPE_CELERON:
+                case CPU_SUBTYPE_CELERON_MOBILE:
+                case CPU_SUBTYPE_PENTIUM_3:
+                case CPU_SUBTYPE_PENTIUM_3_M:
+                case CPU_SUBTYPE_PENTIUM_3_XEON:
+                    pMod->enmCpu = KCPU_I686;
+                    break;
+                case CPU_SUBTYPE_PENTIUM_M:
+                case CPU_SUBTYPE_PENTIUM_4:
+                case CPU_SUBTYPE_PENTIUM_4_M:
+                case CPU_SUBTYPE_XEON:
+                case CPU_SUBTYPE_XEON_MP:
+                    pMod->enmCpu = KCPU_P4;
+                    break;
+
+                default:
+                    /* Hack for kextutil output. */
+                    if (   s.Hdr32.cpusubtype == 0
+                        && s.Hdr32.filetype   == MH_OBJECT)
+                        break;
+                    return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE;
+            }
+            break;
+
+        case CPU_TYPE_X86_64:
+            pMod->enmArch = KCPUARCH_AMD64;
+            pMod->enmEndian = KLDRENDIAN_LITTLE;
+            switch (s.Hdr32.cpusubtype & ~CPU_SUBTYPE_MASK)
+            {
+                case CPU_SUBTYPE_X86_64_ALL:        pMod->enmCpu = KCPU_AMD64_BLEND; break;
+                default:
+                    return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE;
+            }
+            break;
+
+        default:
+            return KLDR_ERR_MACHO_UNSUPPORTED_MACHINE;
+    }
+
+    pMod->enmFmt = KLDRFMT_MACHO;
+    switch (s.Hdr32.filetype)
+    {
+        case MH_OBJECT:     pMod->enmType = KLDRTYPE_OBJECT; break;
+        case MH_EXECUTE:    pMod->enmType = KLDRTYPE_EXECUTABLE_FIXED; break;
+        case MH_DYLIB:      pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
+        case MH_BUNDLE:     pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
+        case MH_KEXT_BUNDLE:pMod->enmType = KLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
+        case MH_DSYM:       pMod->enmType = KLDRTYPE_DEBUG_INFO; break;
+        default:
+            return KLDR_ERR_MACHO_UNSUPPORTED_FILE_TYPE;
+    }
+    pMod->u32Magic = 0;     /* set upon success. */
+
+    /* KLDRMODMACHO */
+    pModMachO->pMod = pMod;
+    pModMachO->pvBits = NULL;
+    pModMachO->pvMapping = NULL;
+    pModMachO->fOpenFlags = fOpenFlags;
+    pModMachO->Hdr = s.Hdr64;
+    if (    s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE
+        ||  s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE)
+        pModMachO->Hdr.reserved = 0;
+    pModMachO->LinkAddress = LinkAddress;
+    pModMachO->cbImage = 0;
+    pModMachO->fCanLoad = fCanLoad;
+    pModMachO->fMakeGot = fMakeGot;
+    pModMachO->cbJmpStub = cbJmpStub;
+    pModMachO->uEffFileType = uEffFileType;
+    pModMachO->offSymbols = 0;
+    pModMachO->cSymbols = 0;
+    pModMachO->pvaSymbols = NULL;
+    pModMachO->offStrings = 0;
+    pModMachO->cchStrings = 0;
+    pModMachO->pchStrings = NULL;
+    kHlpMemSet(pModMachO->abImageUuid, 0, sizeof(pModMachO->abImageUuid));
+    pModMachO->GotRVA = NIL_KLDRADDR;
+    pModMachO->JmpStubsRVA = NIL_KLDRADDR;
+    pModMachO->cSections = cSections;
+    pModMachO->paSections = (PKLDRMODMACHOSECT)&pModMachO->aSegments[pModMachO->pMod->cSegments];
+
+    /*
+     * Setup the KLDRMOD segment array.
+     */
+    rc = kldrModMachOParseLoadCommands(pModMachO, (char *)pMod->pszFilename + pMod->cchFilename + 1, cbStringPool);
+    if (rc)
+        return rc;
+
+    /*
+     * We're done.
+     */
+    return 0;
+}
+
+
+/**
+ * Converts, validates and preparses the load commands before we carve
+ * out the module instance.
+ *
+ * The conversion that's preformed is format endian to host endian.  The
+ * preparsing has to do with segment counting, section counting and string pool
+ * sizing.
+ *
+ * Segment are created in two different ways, depending on the file type.
+ *
+ * For object files there is only one segment command without a given segment
+ * name. The sections inside that segment have different segment names and are
+ * not sorted by their segname attribute.  We create one segment for each
+ * section, with the segment name being 'segname.sectname' in order to hopefully
+ * keep the names unique.  Debug sections does not get segments.
+ *
+ * For non-object files, one kLdr segment is created for each Mach-O segment.
+ * Debug segments is not exposed by kLdr via the kLdr segment table, but via the
+ * debug enumeration callback API.
+ *
+ * @returns 0 on success.
+ * @returns KLDR_ERR_MACHO_* on failure.
+ * @param   pbLoadCommands  The load commands to parse.
+ * @param   pHdr            The header.
+ * @param   pRdr            The file reader.
+ * @param   offImage        The image header (FAT fun).
+ * @param   pcSegments      Where to store the segment count.
+ * @param   pcSegments      Where to store the section count.
+ * @param   pcbStringPool   Where to store the string pool size.
+ * @param   pfCanLoad       Where to store the can-load-image indicator.
+ * @param   pLinkAddress    Where to store the image link address (i.e. the
+ *                          lowest segment address).
+ */
+static int  kldrModMachOPreParseLoadCommands(KU8 *pbLoadCommands, const mach_header_32_t *pHdr, PKRDR pRdr, KLDRFOFF offImage,
+                                             KU32 fOpenFlags, KU32 *pcSegments, KU32 *pcSections, KU32 *pcbStringPool,
+                                             PKBOOL pfCanLoad, PKLDRADDR pLinkAddress, KU8 *puEffFileType)
+{
+    union
+    {
+        KU8                  *pb;
+        load_command_t       *pLoadCmd;
+        segment_command_32_t *pSeg32;
+        segment_command_64_t *pSeg64;
+        thread_command_t     *pThread;
+        symtab_command_t     *pSymTab;
+        uuid_command_t       *pUuid;
+    } u;
+    const KU64 cbFile = kRdrSize(pRdr) - offImage;
+    KU32 cSegments = 0;
+    KU32 cSections = 0;
+    KSIZE cbStringPool = 0;
+    KU32 cLeft = pHdr->ncmds;
+    KU32 cbLeft = pHdr->sizeofcmds;
+    KU8 *pb = pbLoadCommands;
+    int cSegmentCommands = 0;
+    int cSymbolTabs = 0;
+    int fConvertEndian = pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+                      || pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE;
+    KU8 uEffFileType = *puEffFileType = pHdr->filetype;
+
+    *pcSegments = 0;
+    *pcSections = 0;
+    *pcbStringPool = 0;
+    *pfCanLoad = K_TRUE;
+    *pLinkAddress = ~(KLDRADDR)0;
+
+    while (cLeft-- > 0)
+    {
+        u.pb = pb;
+
+        /*
+         * Convert and validate command header.
+         */
+        KLDRMODMACHO_CHECK_RETURN(cbLeft >= sizeof(load_command_t), KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+        if (fConvertEndian)
+        {
+            u.pLoadCmd->cmd = K_E2E_U32(u.pLoadCmd->cmd);
+            u.pLoadCmd->cmdsize = K_E2E_U32(u.pLoadCmd->cmdsize);
+        }
+        KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize <= cbLeft, KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+        cbLeft -= u.pLoadCmd->cmdsize;
+        pb += u.pLoadCmd->cmdsize;
+
+        /*
+         * Convert endian if needed, parse and validate the command.
+         */
+        switch (u.pLoadCmd->cmd)
+        {
+            case LC_SEGMENT_32:
+            {
+                segment_command_32_t *pSrcSeg = (segment_command_32_t *)u.pLoadCmd;
+                section_32_t   *pFirstSect    = (section_32_t *)(pSrcSeg + 1);
+                section_32_t   *pSect         = pFirstSect;
+                KU32            cSectionsLeft = pSrcSeg->nsects;
+                KU64            offSect       = 0;
+
+                /* Convert and verify the segment. */
+                KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_32_t), KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+                KLDRMODMACHO_CHECK_RETURN(   pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+                                          || pHdr->magic == IMAGE_MACHO32_SIGNATURE, KLDR_ERR_MACHO_BIT_MIX);
+                if (fConvertEndian)
+                {
+                    pSrcSeg->vmaddr   = K_E2E_U32(pSrcSeg->vmaddr);
+                    pSrcSeg->vmsize   = K_E2E_U32(pSrcSeg->vmsize);
+                    pSrcSeg->fileoff  = K_E2E_U32(pSrcSeg->fileoff);
+                    pSrcSeg->filesize = K_E2E_U32(pSrcSeg->filesize);
+                    pSrcSeg->maxprot  = K_E2E_U32(pSrcSeg->maxprot);
+                    pSrcSeg->initprot = K_E2E_U32(pSrcSeg->initprot);
+                    pSrcSeg->nsects   = K_E2E_U32(pSrcSeg->nsects);
+                    pSrcSeg->flags    = K_E2E_U32(pSrcSeg->flags);
+                }
+
+                /* Validation code shared with the 64-bit variant. */
+                #define VALIDATE_AND_ADD_SEGMENT(a_cBits) \
+                do { \
+                    KBOOL fSkipSeg = !kHlpStrComp(pSrcSeg->segname, "__DWARF")   /* Note: Not for non-object files. */ \
+                                  || (   !kHlpStrComp(pSrcSeg->segname, "__CTF") /* Their CTF tool did/does weird things, */ \
+                                      && pSrcSeg->vmsize == 0)                   /* overlapping vmaddr and zero vmsize. */ \
+                                  || (cSectionsLeft > 0 && (pFirstSect->flags & S_ATTR_DEBUG)); \
+                    \
+                    /* MH_DSYM files for MH_OBJECT files must have MH_OBJECT segment translation. */ \
+                    if (   uEffFileType == MH_DSYM \
+                        && cSegmentCommands == 0 \
+                        && pSrcSeg->segname[0] == '\0') \
+                        *puEffFileType = uEffFileType = MH_OBJECT; \
+                    \
+                    KLDRMODMACHO_CHECK_RETURN(   pSrcSeg->filesize == 0 \
+                                              || (   pSrcSeg->fileoff <= cbFile \
+                                                  && (KU64)pSrcSeg->fileoff + pSrcSeg->filesize <= cbFile), \
+                                              KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \
+                    KLDRMODMACHO_CHECK_RETURN(   pSrcSeg->filesize <= pSrcSeg->vmsize \
+                                              || (fSkipSeg && !kHlpStrComp(pSrcSeg->segname, "__CTF") /* see above */), \
+                                              KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \
+                    KLDRMODMACHO_CHECK_RETURN(!(~pSrcSeg->maxprot & pSrcSeg->initprot), \
+                                              KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \
+                    KLDRMODMACHO_CHECK_RETURN(!(pSrcSeg->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1)), \
+                                              KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \
+                    KLDRMODMACHO_CHECK_RETURN(   pSrcSeg->nsects * sizeof(section_##a_cBits##_t) \
+                                              <= u.pLoadCmd->cmdsize - sizeof(segment_command_##a_cBits##_t), \
+                                              KLDR_ERR_MACHO_BAD_LOAD_COMMAND); \
+                    KLDRMODMACHO_CHECK_RETURN(   uEffFileType != MH_OBJECT \
+                                              || cSegmentCommands == 0 \
+                                              || (   cSegmentCommands == 1 \
+                                                  && uEffFileType == MH_OBJECT \
+                                                  && pHdr->filetype == MH_DSYM \
+                                                  && fSkipSeg), \
+                                              KLDR_ERR_MACHO_BAD_OBJECT_FILE); \
+                    cSegmentCommands++; \
+                    \
+                    /* Add the segment, if not object file. */ \
+                    if (!fSkipSeg && uEffFileType != MH_OBJECT) \
+                    { \
+                        cbStringPool += kHlpStrNLen(&pSrcSeg->segname[0], sizeof(pSrcSeg->segname)) + 1; \
+                        cSegments++; \
+                        if (cSegments == 1) /* The link address is set by the first segment. */  \
+                            *pLinkAddress = pSrcSeg->vmaddr; \
+                    } \
+                } while (0)
+
+                VALIDATE_AND_ADD_SEGMENT(32);
+
+                /*
+                 * Convert, validate and parse the sections.
+                 */
+                cSectionsLeft = pSrcSeg->nsects;
+                pFirstSect = pSect = (section_32_t *)(pSrcSeg + 1);
+                while (cSectionsLeft-- > 0)
+                {
+                    if (fConvertEndian)
+                    {
+                        pSect->addr      = K_E2E_U32(pSect->addr);
+                        pSect->size      = K_E2E_U32(pSect->size);
+                        pSect->offset    = K_E2E_U32(pSect->offset);
+                        pSect->align     = K_E2E_U32(pSect->align);
+                        pSect->reloff    = K_E2E_U32(pSect->reloff);
+                        pSect->nreloc    = K_E2E_U32(pSect->nreloc);
+                        pSect->flags     = K_E2E_U32(pSect->flags);
+                        pSect->reserved1 = K_E2E_U32(pSect->reserved1);
+                        pSect->reserved2 = K_E2E_U32(pSect->reserved2);
+                    }
+
+                    /* Validation code shared with the 64-bit variant. */
+                    #define VALIDATE_AND_ADD_SECTION(a_cBits) \
+                    do { \
+                        int fFileBits; \
+                        \
+                        /* validate */ \
+                        if (uEffFileType != MH_OBJECT) \
+                            KLDRMODMACHO_CHECK_RETURN(!kHlpStrComp(pSect->segname, pSrcSeg->segname),\
+                                                      KLDR_ERR_MACHO_BAD_SECTION); \
+                        \
+                        switch (pSect->flags & SECTION_TYPE) \
+                        { \
+                            case S_ZEROFILL: \
+                                KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \
+                                KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \
+                                fFileBits = 0; \
+                                break; \
+                            case S_REGULAR: \
+                            case S_CSTRING_LITERALS: \
+                            case S_COALESCED: \
+                            case S_4BYTE_LITERALS: \
+                            case S_8BYTE_LITERALS: \
+                            case S_16BYTE_LITERALS: \
+                                KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \
+                                KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \
+                                fFileBits = 1; \
+                                break; \
+                            \
+                            case S_SYMBOL_STUBS: \
+                                KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \
+                                /* reserved2 == stub size. 0 has been seen (corecrypto.kext) */ \
+                                KLDRMODMACHO_CHECK_RETURN(pSect->reserved2 < 64, KLDR_ERR_MACHO_BAD_SECTION); \
+                                fFileBits = 1; \
+                                break; \
+                            \
+                            case S_NON_LAZY_SYMBOL_POINTERS: \
+                            case S_LAZY_SYMBOL_POINTERS: \
+                            case S_LAZY_DYLIB_SYMBOL_POINTERS: \
+                                /* (reserved 1 = is indirect symbol table index) */ \
+                                KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \
+                                *pfCanLoad = K_FALSE; \
+                                fFileBits = -1; /* __DATA.__got in the 64-bit mach_kernel has bits, any things without bits? */ \
+                                break; \
+                            \
+                            case S_MOD_INIT_FUNC_POINTERS: \
+                                /** @todo this requires a query API or flag... (e.g. C++ constructors) */ \
+                                KLDRMODMACHO_CHECK_RETURN(fOpenFlags & KLDRMOD_OPEN_FLAGS_FOR_INFO, \
+                                                          KLDR_ERR_MACHO_UNSUPPORTED_INIT_SECTION); \
+                                /* Falls through. */ \
+                            case S_MOD_TERM_FUNC_POINTERS: \
+                                /** @todo this requires a query API or flag... (e.g. C++ destructors) */ \
+                                KLDRMODMACHO_CHECK_RETURN(fOpenFlags & KLDRMOD_OPEN_FLAGS_FOR_INFO, \
+                                                          KLDR_ERR_MACHO_UNSUPPORTED_TERM_SECTION); \
+                                KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \
+                                KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \
+                                fFileBits = 1; \
+                                break; /* ignored */ \
+                            \
+                            case S_LITERAL_POINTERS: \
+                            case S_DTRACE_DOF: \
+                                KLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, KLDR_ERR_MACHO_BAD_SECTION); \
+                                KLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, KLDR_ERR_MACHO_BAD_SECTION); \
+                                fFileBits = 1; \
+                                break; \
+                            \
+                            case S_INTERPOSING: \
+                            case S_GB_ZEROFILL: \
+                                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNSUPPORTED_SECTION); \
+                            \
+                            default: \
+                                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNKNOWN_SECTION); \
+                        } \
+                        KLDRMODMACHO_CHECK_RETURN(!(pSect->flags & ~(  S_ATTR_PURE_INSTRUCTIONS | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS \
+                                                                     | S_ATTR_NO_DEAD_STRIP | S_ATTR_LIVE_SUPPORT | S_ATTR_SELF_MODIFYING_CODE \
+                                                                     | S_ATTR_DEBUG | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_EXT_RELOC \
+                                                                     | S_ATTR_LOC_RELOC | SECTION_TYPE)), \
+                                                  KLDR_ERR_MACHO_BAD_SECTION); \
+                        KLDRMODMACHO_CHECK_RETURN((pSect->flags & S_ATTR_DEBUG) == (pFirstSect->flags & S_ATTR_DEBUG), \
+                                                  KLDR_ERR_MACHO_MIXED_DEBUG_SECTION_FLAGS); \
+                        \
+                        KLDRMODMACHO_CHECK_RETURN(pSect->addr - pSrcSeg->vmaddr <= pSrcSeg->vmsize, \
+                                                  KLDR_ERR_MACHO_BAD_SECTION); \
+                        KLDRMODMACHO_CHECK_RETURN(   pSect->addr - pSrcSeg->vmaddr + pSect->size <= pSrcSeg->vmsize \
+                                                  || !kHlpStrComp(pSrcSeg->segname, "__CTF") /* see above */, \
+                                                  KLDR_ERR_MACHO_BAD_SECTION); \
+                        KLDRMODMACHO_CHECK_RETURN(pSect->align < 31, \
+                                                  KLDR_ERR_MACHO_BAD_SECTION); \
+                        /* Workaround for buggy ld64 (or as, llvm, ++) that produces a misaligned __TEXT.__unwind_info. */ \
+                        /* Seen: pSect->align = 4, pSect->addr = 0x5ebe14.  Just adjust the alignment down. */ \
+                        if (   ((K_BIT32(pSect->align) - KU32_C(1)) & pSect->addr) \
+                            && pSect->align == 4 \
+                            && kHlpStrComp(pSect->sectname, "__unwind_info") == 0) \
+                            pSect->align = 2; \
+                        KLDRMODMACHO_CHECK_RETURN(!((K_BIT32(pSect->align) - KU32_C(1)) & pSect->addr), \
+                                                  KLDR_ERR_MACHO_BAD_SECTION); \
+                        KLDRMODMACHO_CHECK_RETURN(!((K_BIT32(pSect->align) - KU32_C(1)) & pSrcSeg->vmaddr), \
+                                                  KLDR_ERR_MACHO_BAD_SECTION); \
+                        \
+                        /* Adjust the section offset before we check file offset. */ \
+                        offSect = (offSect + K_BIT64(pSect->align) - KU64_C(1)) & ~(K_BIT64(pSect->align) - KU64_C(1)); \
+                        if (pSect->addr) \
+                        { \
+                            KLDRMODMACHO_CHECK_RETURN(offSect <= pSect->addr - pSrcSeg->vmaddr, KLDR_ERR_MACHO_BAD_SECTION); \
+                            if (offSect < pSect->addr - pSrcSeg->vmaddr) \
+                                offSect = pSect->addr - pSrcSeg->vmaddr; \
+                        } \
+                        \
+                        if (fFileBits && pSect->offset == 0 && pSrcSeg->fileoff == 0 && pHdr->filetype == MH_DSYM) \
+                            fFileBits = 0; \
+                        if (fFileBits) \
+                        { \
+                            if (uEffFileType != MH_OBJECT) \
+                            { \
+                                KLDRMODMACHO_CHECK_RETURN(pSect->offset == pSrcSeg->fileoff + offSect, \
+                                                          KLDR_ERR_MACHO_NON_CONT_SEG_BITS); \
+                                KLDRMODMACHO_CHECK_RETURN(pSect->offset - pSrcSeg->fileoff <= pSrcSeg->filesize, \
+                                                          KLDR_ERR_MACHO_BAD_SECTION); \
+                            } \
+                            KLDRMODMACHO_CHECK_RETURN(pSect->offset <= cbFile, \
+                                                      KLDR_ERR_MACHO_BAD_SECTION); \
+                            KLDRMODMACHO_CHECK_RETURN((KU64)pSect->offset + pSect->size <= cbFile, \
+                                                      KLDR_ERR_MACHO_BAD_SECTION); \
+                        } \
+                        else \
+                            KLDRMODMACHO_CHECK_RETURN(pSect->offset == 0, KLDR_ERR_MACHO_BAD_SECTION); \
+                        \
+                        if (!pSect->nreloc) \
+                            KLDRMODMACHO_CHECK_RETURN(!pSect->reloff, \
+                                                      KLDR_ERR_MACHO_BAD_SECTION); \
+                        else \
+                        { \
+                            KLDRMODMACHO_CHECK_RETURN(pSect->reloff <= cbFile, \
+                                                      KLDR_ERR_MACHO_BAD_SECTION); \
+                            KLDRMODMACHO_CHECK_RETURN(     (KU64)pSect->reloff \
+                                                         + (KLDRFOFF)pSect->nreloc * sizeof(macho_relocation_info_t) \
+                                                      <= cbFile, \
+                                                      KLDR_ERR_MACHO_BAD_SECTION); \
+                        } \
+                        \
+                        /* Validate against file type (pointless?) and count the section, for object files add segment. */ \
+                        switch (uEffFileType) \
+                        { \
+                            case MH_OBJECT: \
+                                if (   !(pSect->flags & S_ATTR_DEBUG) \
+                                    && kHlpStrComp(pSect->segname, "__DWARF")) \
+                                { \
+                                    cbStringPool += kHlpStrNLen(&pSect->segname[0], sizeof(pSect->segname)) + 1; \
+                                    cbStringPool += kHlpStrNLen(&pSect->sectname[0], sizeof(pSect->sectname)) + 1; \
+                                    cSegments++; \
+                                    if (cSegments == 1) /* The link address is set by the first segment. */  \
+                                        *pLinkAddress = pSect->addr; \
+                                } \
+                                /* Falls through. */ \
+                            case MH_EXECUTE: \
+                            case MH_DYLIB: \
+                            case MH_BUNDLE: \
+                            case MH_DSYM: \
+                            case MH_KEXT_BUNDLE: \
+                                cSections++; \
+                                break; \
+                            default: \
+                                KLDRMODMACHO_FAILED_RETURN(KERR_INVALID_PARAMETER); \
+                        } \
+                        \
+                        /* Advance the section offset, since we're also aligning it. */ \
+                        offSect += pSect->size; \
+                    } while (0) /* VALIDATE_AND_ADD_SECTION */
+
+                    VALIDATE_AND_ADD_SECTION(32);
+
+                    /* next */
+                    pSect++;
+                }
+                break;
+            }
+
+            case LC_SEGMENT_64:
+            {
+                segment_command_64_t *pSrcSeg = (segment_command_64_t *)u.pLoadCmd;
+                section_64_t   *pFirstSect    = (section_64_t *)(pSrcSeg + 1);
+                section_64_t   *pSect         = pFirstSect;
+                KU32            cSectionsLeft = pSrcSeg->nsects;
+                KU64            offSect       = 0;
+
+                /* Convert and verify the segment. */
+                KLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_64_t), KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+                KLDRMODMACHO_CHECK_RETURN(   pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE
+                                          || pHdr->magic == IMAGE_MACHO64_SIGNATURE, KLDR_ERR_MACHO_BIT_MIX);
+                if (fConvertEndian)
+                {
+                    pSrcSeg->vmaddr   = K_E2E_U64(pSrcSeg->vmaddr);
+                    pSrcSeg->vmsize   = K_E2E_U64(pSrcSeg->vmsize);
+                    pSrcSeg->fileoff  = K_E2E_U64(pSrcSeg->fileoff);
+                    pSrcSeg->filesize = K_E2E_U64(pSrcSeg->filesize);
+                    pSrcSeg->maxprot  = K_E2E_U32(pSrcSeg->maxprot);
+                    pSrcSeg->initprot = K_E2E_U32(pSrcSeg->initprot);
+                    pSrcSeg->nsects   = K_E2E_U32(pSrcSeg->nsects);
+                    pSrcSeg->flags    = K_E2E_U32(pSrcSeg->flags);
+                }
+
+                VALIDATE_AND_ADD_SEGMENT(64);
+
+                /*
+                 * Convert, validate and parse the sections.
+                 */
+                while (cSectionsLeft-- > 0)
+                {
+                    if (fConvertEndian)
+                    {
+                        pSect->addr      = K_E2E_U64(pSect->addr);
+                        pSect->size      = K_E2E_U64(pSect->size);
+                        pSect->offset    = K_E2E_U32(pSect->offset);
+                        pSect->align     = K_E2E_U32(pSect->align);
+                        pSect->reloff    = K_E2E_U32(pSect->reloff);
+                        pSect->nreloc    = K_E2E_U32(pSect->nreloc);
+                        pSect->flags     = K_E2E_U32(pSect->flags);
+                        pSect->reserved1 = K_E2E_U32(pSect->reserved1);
+                        pSect->reserved2 = K_E2E_U32(pSect->reserved2);
+                    }
+
+                    VALIDATE_AND_ADD_SECTION(64);
+
+                    /* next */
+                    pSect++;
+                }
+                break;
+            } /* LC_SEGMENT_64 */
+
+
+            case LC_SYMTAB:
+            {
+                KSIZE cbSym;
+                if (fConvertEndian)
+                {
+                    u.pSymTab->symoff  = K_E2E_U32(u.pSymTab->symoff);
+                    u.pSymTab->nsyms   = K_E2E_U32(u.pSymTab->nsyms);
+                    u.pSymTab->stroff  = K_E2E_U32(u.pSymTab->stroff);
+                    u.pSymTab->strsize = K_E2E_U32(u.pSymTab->strsize);
+                }
+
+                /* verify */
+                cbSym = pHdr->magic == IMAGE_MACHO32_SIGNATURE
+                     || pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+                      ? sizeof(macho_nlist_32_t)
+                      : sizeof(macho_nlist_64_t);
+                if (    u.pSymTab->symoff >= cbFile
+                    ||  (KU64)u.pSymTab->symoff + u.pSymTab->nsyms * cbSym > cbFile)
+                    KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+                if (    u.pSymTab->stroff >= cbFile
+                    ||  (KU64)u.pSymTab->stroff + u.pSymTab->strsize > cbFile)
+                    KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+
+                /* only one string in objects, please. */
+                cSymbolTabs++;
+                if (    uEffFileType == MH_OBJECT
+                    &&  cSymbolTabs != 1)
+                    KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_OBJECT_FILE);
+                break;
+            }
+
+            case LC_DYSYMTAB:
+                /** @todo deal with this! */
+                break;
+
+            case LC_THREAD:
+            case LC_UNIXTHREAD:
+            {
+                KU32 *pu32 = (KU32 *)(u.pb + sizeof(load_command_t));
+                KU32 cItemsLeft = (u.pThread->cmdsize - sizeof(load_command_t)) / sizeof(KU32);
+                while (cItemsLeft)
+                {
+                    /* convert & verify header items ([0] == flavor, [1] == KU32 count). */
+                    if (cItemsLeft < 2)
+                        KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+                    if (fConvertEndian)
+                    {
+                        pu32[0] = K_E2E_U32(pu32[0]);
+                        pu32[1] = K_E2E_U32(pu32[1]);
+                    }
+                    if (pu32[1] + 2 > cItemsLeft)
+                        KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+
+                    /* convert & verify according to flavor. */
+                    switch (pu32[0])
+                    {
+                        /** @todo */
+                        default:
+                            break;
+                    }
+
+                    /* next */
+                    cItemsLeft -= pu32[1] + 2;
+                    pu32 += pu32[1] + 2;
+                }
+                break;
+            }
+
+            case LC_UUID:
+                if (u.pUuid->cmdsize != sizeof(uuid_command_t))
+                    KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+                /** @todo Check anything here need converting? */
+                break;
+
+            case LC_CODE_SIGNATURE:
+                if (u.pUuid->cmdsize != sizeof(linkedit_data_command_t))
+                    KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+                break;
+
+            case LC_VERSION_MIN_MACOSX:
+            case LC_VERSION_MIN_IPHONEOS:
+                if (u.pUuid->cmdsize != sizeof(version_min_command_t))
+                    KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+                break;
+
+            case LC_SOURCE_VERSION:     /* Harmless. It just gives a clue regarding the source code revision/version. */
+            case LC_DATA_IN_CODE:       /* Ignore */
+            case LC_DYLIB_CODE_SIGN_DRS:/* Ignore */
+                /** @todo valid command size. */
+                break;
+
+            case LC_FUNCTION_STARTS:    /** @todo dylib++ */
+                /* Ignore for now. */
+                break;
+            case LC_ID_DYLIB:           /** @todo dylib */
+            case LC_LOAD_DYLIB:         /** @todo dylib */
+            case LC_LOAD_DYLINKER:      /** @todo dylib */
+            case LC_TWOLEVEL_HINTS:     /** @todo dylib */
+            case LC_LOAD_WEAK_DYLIB:    /** @todo dylib */
+            case LC_ID_DYLINKER:        /** @todo dylib */
+            case LC_RPATH:              /** @todo dylib */
+            case LC_SEGMENT_SPLIT_INFO: /** @todo dylib++ */
+            case LC_REEXPORT_DYLIB:     /** @todo dylib */
+            case LC_DYLD_INFO:          /** @todo dylib */
+            case LC_DYLD_INFO_ONLY:     /** @todo dylib */
+            case LC_LOAD_UPWARD_DYLIB:  /** @todo dylib */
+            case LC_DYLD_ENVIRONMENT:   /** @todo dylib */
+            case LC_MAIN: /** @todo parse this and find and entry point or smth. */
+                /** @todo valid command size. */
+                if (!(fOpenFlags & KLDRMOD_OPEN_FLAGS_FOR_INFO))
+                    KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNSUPPORTED_LOAD_COMMAND);
+                *pfCanLoad = K_FALSE;
+                break;
+
+            case LC_LOADFVMLIB:
+            case LC_IDFVMLIB:
+            case LC_IDENT:
+            case LC_FVMFILE:
+            case LC_PREPAGE:
+            case LC_PREBOUND_DYLIB:
+            case LC_ROUTINES:
+            case LC_ROUTINES_64:
+            case LC_SUB_FRAMEWORK:
+            case LC_SUB_UMBRELLA:
+            case LC_SUB_CLIENT:
+            case LC_SUB_LIBRARY:
+            case LC_PREBIND_CKSUM:
+            case LC_SYMSEG:
+                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNSUPPORTED_LOAD_COMMAND);
+
+            default:
+                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_UNKNOWN_LOAD_COMMAND);
+        }
+    }
+
+    /* be strict. */
+    if (cbLeft)
+        KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_LOAD_COMMAND);
+
+    switch (uEffFileType)
+    {
+        case MH_OBJECT:
+        case MH_EXECUTE:
+        case MH_DYLIB:
+        case MH_BUNDLE:
+        case MH_DSYM:
+        case MH_KEXT_BUNDLE:
+            if (!cSegments)
+                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_OBJECT_FILE);
+            break;
+    }
+
+    *pcSegments = cSegments;
+    *pcSections = cSections;
+    *pcbStringPool = (KU32)cbStringPool;
+
+    return 0;
+}
+
+
+/**
+ * Parses the load commands after we've carved out the module instance.
+ *
+ * This fills in the segment table and perhaps some other properties.
+ *
+ * @returns 0 on success.
+ * @returns KLDR_ERR_MACHO_* on failure.
+ * @param   pModMachO       The module.
+ * @param   pbStringPool    The string pool
+ * @param   cbStringPool    The size of the string pool.
+ */
+static int  kldrModMachOParseLoadCommands(PKLDRMODMACHO pModMachO, char *pbStringPool, KU32 cbStringPool)
+{
+    union
+    {
+        const KU8                  *pb;
+        const load_command_t       *pLoadCmd;
+        const segment_command_32_t *pSeg32;
+        const segment_command_64_t *pSeg64;
+        const symtab_command_t     *pSymTab;
+        const uuid_command_t       *pUuid;
+    } u;
+    KU32 cLeft = pModMachO->Hdr.ncmds;
+    KU32 cbLeft = pModMachO->Hdr.sizeofcmds;
+    const KU8 *pb = pModMachO->pbLoadCommands;
+    PKLDRSEG pDstSeg = &pModMachO->pMod->aSegments[0];
+    PKLDRMODMACHOSEG pSegExtra = &pModMachO->aSegments[0];
+    PKLDRMODMACHOSECT pSectExtra = pModMachO->paSections;
+    const KU32 cSegments = pModMachO->pMod->cSegments;
+    PKLDRSEG pSegItr;
+    K_NOREF(cbStringPool);
+
+    while (cLeft-- > 0)
+    {
+        u.pb = pb;
+        cbLeft -= u.pLoadCmd->cmdsize;
+        pb += u.pLoadCmd->cmdsize;
+
+        /*
+         * Convert endian if needed, parse and validate the command.
+         */
+        switch (u.pLoadCmd->cmd)
+        {
+            case LC_SEGMENT_32:
+            {
+                const segment_command_32_t *pSrcSeg = (const segment_command_32_t *)u.pLoadCmd;
+                section_32_t   *pFirstSect    = (section_32_t *)(pSrcSeg + 1);
+                section_32_t   *pSect         = pFirstSect;
+                KU32            cSectionsLeft = pSrcSeg->nsects;
+
+                /* Adds a segment, used by the macro below and thus shared with the 64-bit segment variant. */
+                #define NEW_SEGMENT(a_cBits, a_achName1, a_fObjFile, a_achName2, a_SegAddr, a_cbSeg, a_fFileBits, a_offFile, a_cbFile) \
+                do { \
+                    pDstSeg->pvUser = NULL; \
+                    pDstSeg->pchName = pbStringPool; \
+                    pDstSeg->cchName = (KU32)kHlpStrNLen(a_achName1, sizeof(a_achName1)); \
+                    kHlpMemCopy(pbStringPool, a_achName1, pDstSeg->cchName); \
+                    pbStringPool += pDstSeg->cchName; \
+                    if (a_fObjFile) \
+                    {   /* MH_OBJECT: Add '.sectname' - sections aren't sorted by segments. */ \
+                        KSIZE cchName2 = kHlpStrNLen(a_achName2, sizeof(a_achName2)); \
+                        *pbStringPool++ = '.'; \
+                        kHlpMemCopy(pbStringPool, a_achName2, cchName2); \
+                        pbStringPool += cchName2; \
+                        pDstSeg->cchName += (KU32)cchName2; \
+                    } \
+                    *pbStringPool++ = '\0'; \
+                    pDstSeg->SelFlat = 0; \
+                    pDstSeg->Sel16bit = 0; \
+                    pDstSeg->fFlags = 0; \
+                    pDstSeg->enmProt = KPROT_EXECUTE_WRITECOPY; /** @todo fixme! */ \
+                    pDstSeg->cb = (a_cbSeg); \
+                    pDstSeg->Alignment = 1; /* updated while parsing sections. */ \
+                    pDstSeg->LinkAddress = (a_SegAddr); \
+                    if (a_fFileBits) \
+                    { \
+                        pDstSeg->offFile = (KLDRFOFF)((a_offFile) + pModMachO->offImage); \
+                        pDstSeg->cbFile  = (KLDRFOFF)(a_cbFile); \
+                    } \
+                    else \
+                    { \
+                        pDstSeg->offFile = -1; \
+                        pDstSeg->cbFile  = -1; \
+                    } \
+                    pDstSeg->RVA = (a_SegAddr) - pModMachO->LinkAddress; \
+                    pDstSeg->cbMapped = 0; \
+                    pDstSeg->MapAddress = 0; \
+                    \
+                    pSegExtra->iOrgSegNo = (KU32)(pSegExtra - &pModMachO->aSegments[0]); \
+                    pSegExtra->cSections = 0; \
+                    pSegExtra->paSections = pSectExtra; \
+                } while (0)
+
+                /* Closes the new segment - part of NEW_SEGMENT. */
+                #define CLOSE_SEGMENT() \
+                do { \
+                    pSegExtra->cSections = (KU32)(pSectExtra - pSegExtra->paSections); \
+                    pSegExtra++; \
+                    pDstSeg++; \
+                } while (0)
+
+
+                /* Shared with the 64-bit variant. */
+                #define ADD_SEGMENT_AND_ITS_SECTIONS(a_cBits) \
+                do { \
+                    KBOOL fAddSegOuter = K_FALSE; \
+                    \
+                    /* \
+                     * Check that the segment name is unique.  We couldn't do that \
+                     * in the preparsing stage. \
+                     */ \
+                    if (pModMachO->uEffFileType != MH_OBJECT) \
+                        for (pSegItr = &pModMachO->pMod->aSegments[0]; pSegItr != pDstSeg; pSegItr++) \
+                            if (!kHlpStrNComp(pSegItr->pchName, pSrcSeg->segname, sizeof(pSrcSeg->segname))) \
+                                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_DUPLICATE_SEGMENT_NAME); \
+                    \
+                    /* \
+                     * Create a new segment, unless we're supposed to skip this one. \
+                     */ \
+                    if (   pModMachO->uEffFileType != MH_OBJECT \
+                        && (cSectionsLeft == 0 || !(pFirstSect->flags & S_ATTR_DEBUG)) \
+                        && kHlpStrComp(pSrcSeg->segname, "__DWARF") \
+                        && kHlpStrComp(pSrcSeg->segname, "__CTF") ) \
+                    { \
+                        NEW_SEGMENT(a_cBits, pSrcSeg->segname, K_FALSE /*a_fObjFile*/, 0 /*a_achName2*/, \
+                                    pSrcSeg->vmaddr, pSrcSeg->vmsize, \
+                                    pSrcSeg->filesize != 0, pSrcSeg->fileoff, pSrcSeg->filesize); \
+                        fAddSegOuter = K_TRUE; \
+                    } \
+                    \
+                    /* \
+                     * Convert and parse the sections. \
+                     */ \
+                    while (cSectionsLeft-- > 0) \
+                    { \
+                        /* New segment if object file. */ \
+                        KBOOL fAddSegInner = K_FALSE; \
+                        if (   pModMachO->uEffFileType == MH_OBJECT \
+                            && !(pSect->flags & S_ATTR_DEBUG) \
+                            && kHlpStrComp(pSrcSeg->segname, "__DWARF") \
+                            && kHlpStrComp(pSrcSeg->segname, "__CTF") ) \
+                        { \
+                            kHlpAssert(!fAddSegOuter); \
+                            NEW_SEGMENT(a_cBits, pSect->segname, K_TRUE /*a_fObjFile*/, pSect->sectname, \
+                                        pSect->addr, pSect->size, \
+                                        pSect->offset != 0, pSect->offset, pSect->size); \
+                            fAddSegInner = K_TRUE; \
+                        } \
+                        \
+                        /* Section data extract. */ \
+                        pSectExtra->cb = pSect->size; \
+                        pSectExtra->RVA = pSect->addr - pDstSeg->LinkAddress; \
+                        pSectExtra->LinkAddress = pSect->addr; \
+                        if (pSect->offset) \
+                            pSectExtra->offFile = pSect->offset + pModMachO->offImage; \
+                        else \
+                            pSectExtra->offFile = -1; \
+                        pSectExtra->cFixups = pSect->nreloc; \
+                        pSectExtra->paFixups = NULL; \
+                        if (pSect->nreloc) \
+                            pSectExtra->offFixups = pSect->reloff + pModMachO->offImage; \
+                        else \
+                            pSectExtra->offFixups = -1; \
+                        pSectExtra->fFlags = pSect->flags; \
+                        pSectExtra->iSegment = (KU32)(pSegExtra - &pModMachO->aSegments[0]); \
+                        pSectExtra->pvMachoSection = pSect; \
+                        \
+                        /* Update the segment alignment, if we're not skipping it. */ \
+                        if (   (fAddSegOuter || fAddSegInner) \
+                            && pDstSeg->Alignment < ((KLDRADDR)1 << pSect->align)) \
+                            pDstSeg->Alignment = (KLDRADDR)1 << pSect->align; \
+                        \
+                        /* Next section, and if object file next segment. */ \
+                        pSectExtra++; \
+                        pSect++; \
+                        if (fAddSegInner) \
+                            CLOSE_SEGMENT(); \
+                    } \
+                    \
+                    /* Close the segment and advance. */ \
+                    if (fAddSegOuter) \
+                        CLOSE_SEGMENT(); \
+                } while (0) /* ADD_SEGMENT_AND_ITS_SECTIONS */
+
+                ADD_SEGMENT_AND_ITS_SECTIONS(32);
+                break;
+            }
+
+            case LC_SEGMENT_64:
+            {
+                const segment_command_64_t *pSrcSeg = (const segment_command_64_t *)u.pLoadCmd;
+                section_64_t   *pFirstSect    = (section_64_t *)(pSrcSeg + 1);
+                section_64_t   *pSect         = pFirstSect;
+                KU32            cSectionsLeft = pSrcSeg->nsects;
+
+                ADD_SEGMENT_AND_ITS_SECTIONS(64);
+                break;
+            }
+
+            case LC_SYMTAB:
+                switch (pModMachO->uEffFileType)
+                {
+                    case MH_OBJECT:
+                    case MH_EXECUTE:
+                    case MH_DYLIB: /** @todo ??? */
+                    case MH_BUNDLE:  /** @todo ??? */
+                    case MH_DSYM:
+                    case MH_KEXT_BUNDLE:
+                        pModMachO->offSymbols = u.pSymTab->symoff + pModMachO->offImage;
+                        pModMachO->cSymbols = u.pSymTab->nsyms;
+                        pModMachO->offStrings = u.pSymTab->stroff + pModMachO->offImage;
+                        pModMachO->cchStrings = u.pSymTab->strsize;
+                        break;
+                }
+                break;
+
+            case LC_UUID:
+                kHlpMemCopy(pModMachO->abImageUuid, u.pUuid->uuid, sizeof(pModMachO->abImageUuid));
+                break;
+
+            default:
+                break;
+        } /* command switch */
+    } /* while more commands */
+
+    kHlpAssert(pDstSeg == &pModMachO->pMod->aSegments[cSegments - pModMachO->fMakeGot]);
+
+    /*
+     * Adjust mapping addresses calculating the image size.
+     */
+    {
+        KBOOL               fLoadLinkEdit = K_FALSE;
+        PKLDRMODMACHOSECT   pSectExtraItr;
+        KLDRADDR            uNextRVA = 0;
+        KLDRADDR            cb;
+        KU32                cSegmentsToAdjust = cSegments - pModMachO->fMakeGot;
+        KU32                c;
+
+        for (;;)
+        {
+            /* Check if there is __DWARF segment at the end and make sure it's left
+               out of the RVA negotiations and image loading. */
+            if (   cSegmentsToAdjust > 0
+                && !kHlpStrComp(pModMachO->pMod->aSegments[cSegmentsToAdjust - 1].pchName, "__DWARF"))
+            {
+                cSegmentsToAdjust--;
+                pModMachO->pMod->aSegments[cSegmentsToAdjust].RVA = NIL_KLDRADDR;
+                pModMachO->pMod->aSegments[cSegmentsToAdjust].cbMapped = 0;
+                continue;
+            }
+
+            /* If we're skipping the __LINKEDIT segment, check for it and adjust
+               the number of segments we'll be messing with here.  ASSUMES it's
+               last (by now anyway). */
+            if (   !fLoadLinkEdit
+                && cSegmentsToAdjust > 0
+                && !kHlpStrComp(pModMachO->pMod->aSegments[cSegmentsToAdjust - 1].pchName, "__LINKEDIT"))
+            {
+                cSegmentsToAdjust--;
+                pModMachO->pMod->aSegments[cSegmentsToAdjust].RVA = NIL_KLDRADDR;
+                pModMachO->pMod->aSegments[cSegmentsToAdjust].cbMapped = 0;
+                continue;
+            }
+            break;
+        }
+
+        /* Adjust RVAs. */
+        c = cSegmentsToAdjust;
+        for (pDstSeg = &pModMachO->pMod->aSegments[0]; c-- > 0; pDstSeg++)
+        {
+            cb = pDstSeg->RVA - uNextRVA;
+            if (cb >= 0x00100000) /* 1MB */
+            {
+                pDstSeg->RVA = uNextRVA;
+                pModMachO->pMod->fFlags |= KLDRMOD_FLAGS_NON_CONTIGUOUS_LINK_ADDRS;
+            }
+            uNextRVA = pDstSeg->RVA + KLDR_ALIGN_ADDR(pDstSeg->cb, pDstSeg->Alignment);
+        }
+
+        /* Calculate the cbMapping members. */
+        c = cSegmentsToAdjust;
+        for (pDstSeg = &pModMachO->pMod->aSegments[0]; c-- > 1; pDstSeg++)
+        {
+
+            cb = pDstSeg[1].RVA - pDstSeg->RVA;
+            pDstSeg->cbMapped = (KSIZE)cb == cb ? (KSIZE)cb : KSIZE_MAX;
+        }
+
+        cb = KLDR_ALIGN_ADDR(pDstSeg->cb, pDstSeg->Alignment);
+        pDstSeg->cbMapped = (KSIZE)cb == cb ? (KSIZE)cb : KSIZE_MAX;
+
+        /* Set the image size. */
+        pModMachO->cbImage = pDstSeg->RVA + cb;
+
+        /* Fixup the section RVAs (internal). */
+        c        = cSegmentsToAdjust;
+        uNextRVA = pModMachO->cbImage;
+        pDstSeg  = &pModMachO->pMod->aSegments[0];
+        for (pSectExtraItr = pModMachO->paSections; pSectExtraItr != pSectExtra; pSectExtraItr++)
+        {
+            if (pSectExtraItr->iSegment < c)
+                pSectExtraItr->RVA += pDstSeg[pSectExtraItr->iSegment].RVA;
+            else
+            {
+                pSectExtraItr->RVA = uNextRVA;
+                uNextRVA += KLDR_ALIGN_ADDR(pSectExtraItr->cb, 64);
+            }
+        }
+    }
+
+    /*
+     * Make the GOT segment if necessary.
+     */
+    if (pModMachO->fMakeGot)
+    {
+        KU32 cbPtr = (   pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+                      || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+                   ? sizeof(KU32)
+                   : sizeof(KU64);
+        KU32 cbGot = pModMachO->cSymbols * cbPtr;
+        KU32 cbJmpStubs;
+
+        pModMachO->GotRVA = pModMachO->cbImage;
+
+        if (pModMachO->cbJmpStub)
+        {
+            cbGot = K_ALIGN_Z(cbGot, 64);
+            pModMachO->JmpStubsRVA = pModMachO->GotRVA + cbGot;
+            cbJmpStubs = pModMachO->cbJmpStub * pModMachO->cSymbols;
+        }
+        else
+        {
+            pModMachO->JmpStubsRVA = NIL_KLDRADDR;
+            cbJmpStubs = 0;
+        }
+
+        pDstSeg = &pModMachO->pMod->aSegments[cSegments - 1];
+        pDstSeg->pvUser = NULL;
+        pDstSeg->pchName = "GOT";
+        pDstSeg->cchName = 3;
+        pDstSeg->SelFlat = 0;
+        pDstSeg->Sel16bit = 0;
+        pDstSeg->fFlags = 0;
+        pDstSeg->enmProt = KPROT_READONLY;
+        pDstSeg->cb = cbGot + cbJmpStubs;
+        pDstSeg->Alignment = 64;
+        pDstSeg->LinkAddress = pModMachO->LinkAddress + pModMachO->GotRVA;
+        pDstSeg->offFile = -1;
+        pDstSeg->cbFile  = -1;
+        pDstSeg->RVA = pModMachO->GotRVA;
+        pDstSeg->cbMapped = (KSIZE)KLDR_ALIGN_ADDR(cbGot + cbJmpStubs, pDstSeg->Alignment);
+        pDstSeg->MapAddress = 0;
+
+        pSegExtra->iOrgSegNo = KU32_MAX;
+        pSegExtra->cSections = 0;
+        pSegExtra->paSections = NULL;
+
+        pModMachO->cbImage += pDstSeg->cbMapped;
+    }
+
+    return 0;
+}
+
+
+/** @copydoc KLDRMODOPS::pfnDestroy */
+static int kldrModMachODestroy(PKLDRMOD pMod)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    int rc = 0;
+    KU32 i, j;
+    KLDRMODMACHO_ASSERT(!pModMachO->pvMapping);
+
+    i = pMod->cSegments;
+    while (i-- > 0)
+    {
+        j = pModMachO->aSegments[i].cSections;
+        while (j-- > 0)
+        {
+            kHlpFree(pModMachO->aSegments[i].paSections[j].paFixups);
+            pModMachO->aSegments[i].paSections[j].paFixups = NULL;
+        }
+    }
+
+    if (pMod->pRdr)
+    {
+        rc = kRdrClose(pMod->pRdr);
+        pMod->pRdr = NULL;
+    }
+    pMod->u32Magic = 0;
+    pMod->pOps = NULL;
+    kHlpFree(pModMachO->pbLoadCommands);
+    pModMachO->pbLoadCommands = NULL;
+    kHlpFree(pModMachO->pchStrings);
+    pModMachO->pchStrings = NULL;
+    kHlpFree(pModMachO->pvaSymbols);
+    pModMachO->pvaSymbols = NULL;
+    kHlpFree(pModMachO);
+    return rc;
+}
+
+
+/**
+ * Gets the right base address.
+ *
+ * @returns 0 on success.
+ * @returns A non-zero status code if the BaseAddress isn't right.
+ * @param   pModMachO       The interpreter module instance
+ * @param   pBaseAddress    The base address, IN & OUT. Optional.
+ */
+static int kldrModMachOAdjustBaseAddress(PKLDRMODMACHO pModMachO, PKLDRADDR pBaseAddress)
+{
+    /*
+     * Adjust the base address.
+     */
+    if (*pBaseAddress == KLDRMOD_BASEADDRESS_MAP)
+        *pBaseAddress = pModMachO->pMod->aSegments[0].MapAddress;
+    else if (*pBaseAddress == KLDRMOD_BASEADDRESS_LINK)
+        *pBaseAddress = pModMachO->LinkAddress;
+
+    return 0;
+}
+
+
+/**
+ * Resolves a linker generated symbol.
+ *
+ * The Apple linker generates symbols indicating the start and end of sections
+ * and segments.  This function checks for these and returns the right value.
+ *
+ * @returns 0 or KLDR_ERR_SYMBOL_NOT_FOUND.
+ * @param   pModMachO           The interpreter module instance.
+ * @param   pMod                The generic module instance.
+ * @param   pchSymbol           The symbol.
+ * @param   cchSymbol           The length of the symbol.
+ * @param   BaseAddress         The base address to apply when calculating the
+ *                              value.
+ * @param   puValue             Where to return the symbol value.
+ */
+static int kldrModMachOQueryLinkerSymbol(PKLDRMODMACHO pModMachO, PKLDRMOD pMod, const char *pchSymbol, KSIZE cchSymbol,
+                                         KLDRADDR BaseAddress, PKLDRADDR puValue)
+{
+    /*
+     * Match possible name prefixes.
+     */
+    static const struct
+    {
+        const char *pszPrefix;
+        KU8         cchPrefix;
+        KBOOL       fSection;
+        KBOOL       fStart;
+    }   s_aPrefixes[] =
+    {
+        { "section$start$",  (KU8)sizeof("section$start$") - 1,   K_TRUE,  K_TRUE },
+        { "section$end$",    (KU8)sizeof("section$end$") - 1,     K_TRUE,  K_FALSE},
+        { "segment$start$",  (KU8)sizeof("segment$start$") - 1,   K_FALSE, K_TRUE },
+        { "segment$end$",    (KU8)sizeof("segment$end$") - 1,     K_FALSE, K_FALSE},
+    };
+    KSIZE       cchSectName = 0;
+    const char *pchSectName = "";
+    KSIZE       cchSegName  = 0;
+    const char *pchSegName  = NULL;
+    KU32        iPrefix     = K_ELEMENTS(s_aPrefixes) - 1;
+    KU32        iSeg;
+    KLDRADDR    uValue;
+
+    for (;;)
+    {
+        KU8 const cchPrefix = s_aPrefixes[iPrefix].cchPrefix;
+        if (   cchSymbol > cchPrefix
+            && kHlpStrNComp(pchSymbol, s_aPrefixes[iPrefix].pszPrefix, cchPrefix) == 0)
+        {
+            pchSegName = pchSymbol + cchPrefix;
+            cchSegName = cchSymbol - cchPrefix;
+            break;
+        }
+
+        /* next */
+        if (!iPrefix)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+        iPrefix--;
+    }
+
+    /*
+     * Split the remainder into segment and section name, if necessary.
+     */
+    if (s_aPrefixes[iPrefix].fSection)
+    {
+        pchSectName = kHlpMemChr(pchSegName, '$', cchSegName);
+        if (!pchSectName)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+        cchSegName  = pchSectName - pchSegName;
+        pchSectName++;
+        cchSectName = cchSymbol - (pchSectName - pchSymbol);
+    }
+
+    /*
+     * Locate the segment.
+     */
+    if (!pMod->cSegments)
+        return KLDR_ERR_SYMBOL_NOT_FOUND;
+    for (iSeg = 0; iSeg < pMod->cSegments; iSeg++)
+    {
+        if (   pMod->aSegments[iSeg].cchName >= cchSegName
+            && kHlpMemComp(pMod->aSegments[iSeg].pchName, pchSegName, cchSegName) == 0)
+        {
+            section_32_t const *pSect;
+            if (   pMod->aSegments[iSeg].cchName == cchSegName
+                && pModMachO->Hdr.filetype != MH_OBJECT /* Good enough for __DWARF segs in MH_DHSYM, I hope. */)
+                break;
+
+            pSect = (section_32_t *)pModMachO->aSegments[iSeg].paSections[0].pvMachoSection;
+            if (   pModMachO->uEffFileType == MH_OBJECT
+                && pMod->aSegments[iSeg].cchName > cchSegName + 1
+                && pMod->aSegments[iSeg].pchName[cchSegName] == '.'
+                && kHlpStrNComp(&pMod->aSegments[iSeg].pchName[cchSegName + 1], pSect->sectname, sizeof(pSect->sectname)) == 0
+                && pMod->aSegments[iSeg].cchName - cchSegName - 1 <= sizeof(pSect->sectname) )
+                break;
+        }
+    }
+    if (iSeg >= pMod->cSegments)
+        return KLDR_ERR_SYMBOL_NOT_FOUND;
+
+    if (!s_aPrefixes[iPrefix].fSection)
+    {
+        /*
+         * Calculate the segment start/end address.
+         */
+        uValue = pMod->aSegments[iSeg].RVA;
+        if (!s_aPrefixes[iPrefix].fStart)
+            uValue += pMod->aSegments[iSeg].cb;
+    }
+    else
+    {
+        /*
+         * Locate the section.
+         */
+        KU32 iSect = pModMachO->aSegments[iSeg].cSections;
+        if (!iSect)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+        for (;;)
+        {
+            section_32_t *pSect = (section_32_t *)pModMachO->aSegments[iSeg].paSections[iSect].pvMachoSection;
+            if (   cchSectName <= sizeof(pSect->sectname)
+                && kHlpMemComp(pSect->sectname, pchSectName, cchSectName) == 0
+                && (   cchSectName == sizeof(pSect->sectname)
+                    || pSect->sectname[cchSectName] == '\0') )
+                break;
+            /* next */
+            if (!iSect)
+                return KLDR_ERR_SYMBOL_NOT_FOUND;
+            iSect--;
+        }
+
+        uValue = pModMachO->aSegments[iSeg].paSections[iSect].RVA;
+        if (!s_aPrefixes[iPrefix].fStart)
+            uValue += pModMachO->aSegments[iSeg].paSections[iSect].cb;
+    }
+
+    /*
+     * Convert from RVA to load address.
+     */
+    uValue += BaseAddress;
+    if (puValue)
+        *puValue = uValue;
+
+    return 0;
+}
+
+
+/** @copydoc kLdrModQuerySymbol */
+static int kldrModMachOQuerySymbol(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, KU32 iSymbol,
+                                   const char *pchSymbol, KSIZE cchSymbol, const char *pszVersion,
+                                   PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, KU32 *pfKind)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    int rc;
+    K_NOREF(pvBits);
+    K_NOREF(pszVersion);
+    K_NOREF(pfnGetForwarder);
+    K_NOREF(pvUser);
+
+    /*
+     * Resolve defaults.
+     */
+    rc  = kldrModMachOAdjustBaseAddress(pModMachO, &BaseAddress);
+    if (rc)
+        return rc;
+
+    /*
+     * Refuse segmented requests for now.
+     */
+    KLDRMODMACHO_CHECK_RETURN(   !pfKind
+                              || (*pfKind & KLDRSYMKIND_REQ_TYPE_MASK) == KLDRSYMKIND_REQ_FLAT,
+                              KLDR_ERR_TODO);
+
+    /*
+     * Take action according to file type.
+     */
+    if (   pModMachO->Hdr.filetype == MH_OBJECT
+        || pModMachO->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
+        || pModMachO->Hdr.filetype == MH_DYLIB
+        || pModMachO->Hdr.filetype == MH_BUNDLE
+        || pModMachO->Hdr.filetype == MH_DSYM
+        || pModMachO->Hdr.filetype == MH_KEXT_BUNDLE)
+    {
+        rc = kldrModMachOLoadObjSymTab(pModMachO);
+        if (!rc)
+        {
+            if (    pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+                ||  pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+                rc = kldrModMachODoQuerySymbol32Bit(pModMachO, (macho_nlist_32_t *)pModMachO->pvaSymbols, pModMachO->cSymbols,
+                                                    pModMachO->pchStrings, pModMachO->cchStrings, BaseAddress, iSymbol, pchSymbol,
+                                                    (KU32)cchSymbol, puValue, pfKind);
+            else
+                rc = kldrModMachODoQuerySymbol64Bit(pModMachO, (macho_nlist_64_t *)pModMachO->pvaSymbols, pModMachO->cSymbols,
+                                                    pModMachO->pchStrings, pModMachO->cchStrings, BaseAddress, iSymbol, pchSymbol,
+                                                    (KU32)cchSymbol, puValue, pfKind);
+        }
+
+        /*
+         * Check for link-editor generated symbols and supply what we can.
+         *
+         * As small service to clients that insists on adding a '_' prefix
+         * before querying symbols, we will ignore the prefix.
+         */
+        if (  rc == KLDR_ERR_SYMBOL_NOT_FOUND
+            && cchSymbol > sizeof("section$end$") - 1
+            && (    pchSymbol[0] == 's'
+                || (pchSymbol[1] == 's' && pchSymbol[0] == '_') )
+            && kHlpMemChr(pchSymbol, '$', cchSymbol) )
+        {
+            if (pchSymbol[0] == '_')
+                rc = kldrModMachOQueryLinkerSymbol(pModMachO, pMod, pchSymbol + 1, cchSymbol - 1, BaseAddress, puValue);
+            else
+                rc = kldrModMachOQueryLinkerSymbol(pModMachO, pMod, pchSymbol, cchSymbol, BaseAddress, puValue);
+        }
+    }
+    else
+        rc = KLDR_ERR_TODO;
+
+    return rc;
+}
+
+
+/**
+ * Lookup a symbol in a 32-bit symbol table.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param   pModMachO
+ * @param   paSyms      Pointer to the symbol table.
+ * @param   cSyms       Number of symbols in the table.
+ * @param   pchStrings  Pointer to the string table.
+ * @param   cchStrings  Size of the string table.
+ * @param   BaseAddress Adjusted base address, see kLdrModQuerySymbol.
+ * @param   iSymbol     See kLdrModQuerySymbol.
+ * @param   pchSymbol   See kLdrModQuerySymbol.
+ * @param   cchSymbol   See kLdrModQuerySymbol.
+ * @param   puValue     See kLdrModQuerySymbol.
+ * @param   pfKind      See kLdrModQuerySymbol.
+ */
+static int kldrModMachODoQuerySymbol32Bit(PKLDRMODMACHO pModMachO, const macho_nlist_32_t *paSyms, KU32 cSyms,
+                                          const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress, KU32 iSymbol,
+                                          const char *pchSymbol, KU32 cchSymbol, PKLDRADDR puValue, KU32 *pfKind)
+{
+    /*
+     * Find a valid symbol matching the search criteria.
+     */
+    if (iSymbol == NIL_KLDRMOD_SYM_ORDINAL)
+    {
+        /* simplify validation. */
+        if (cchStrings <= cchSymbol)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+        cchStrings -= cchSymbol;
+
+        /* external symbols are usually at the end, so search the other way. */
+        for (iSymbol = cSyms - 1; iSymbol != KU32_MAX; iSymbol--)
+        {
+            const char *psz;
+
+            /* Skip irrellevant and non-public symbols. */
+            if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+                continue;
+            if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+                continue;
+            if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
+                continue;
+            if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
+                continue;
+
+            /* get name */
+            if (!paSyms[iSymbol].n_un.n_strx)
+                continue;
+            if ((KU32)paSyms[iSymbol].n_un.n_strx >= cchStrings)
+                continue;
+            psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
+            if (psz[cchSymbol])
+                continue;
+            if (kHlpMemComp(psz, pchSymbol, cchSymbol))
+                continue;
+
+            /* match! */
+            break;
+        }
+        if (iSymbol == KU32_MAX)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+    }
+    else
+    {
+        if (iSymbol >= cSyms)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+        if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+        if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+    }
+
+    /*
+     * Calc the return values.
+     */
+    if (pfKind)
+    {
+        if (    pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+            ||  pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+            *pfKind = KLDRSYMKIND_32BIT | KLDRSYMKIND_NO_TYPE;
+        else
+            *pfKind = KLDRSYMKIND_64BIT | KLDRSYMKIND_NO_TYPE;
+        if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
+            *pfKind |= KLDRSYMKIND_WEAK;
+    }
+
+    switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
+    {
+        case MACHO_N_SECT:
+        {
+            PKLDRMODMACHOSECT pSect;
+            KLDRADDR offSect;
+            KLDRMODMACHO_CHECK_RETURN((KU32)(paSyms[iSymbol].n_sect - 1) < pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+            pSect = &pModMachO->paSections[paSyms[iSymbol].n_sect - 1];
+
+            offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
+            KLDRMODMACHO_CHECK_RETURN(   offSect <= pSect->cb
+                                      || (   paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
+                                          && offSect == 0U - pSect->RVA
+                                          && pModMachO->uEffFileType != MH_OBJECT),
+                                      KLDR_ERR_MACHO_BAD_SYMBOL);
+            if (puValue)
+                *puValue = BaseAddress + pSect->RVA + offSect;
+
+            if (    pfKind
+                &&  (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
+                *pfKind = (*pfKind & ~KLDRSYMKIND_TYPE_MASK) | KLDRSYMKIND_CODE;
+            break;
+        }
+
+        case MACHO_N_ABS:
+            if (puValue)
+                *puValue = paSyms[iSymbol].n_value;
+            /*if (pfKind)
+                pfKind |= KLDRSYMKIND_ABS;*/
+            break;
+
+        case MACHO_N_PBUD:
+        case MACHO_N_INDR:
+            /** @todo implement indirect and prebound symbols. */
+        default:
+            KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+    }
+
+    return 0;
+}
+
+
+/**
+ * Lookup a symbol in a 64-bit symbol table.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param   pModMachO
+ * @param   paSyms      Pointer to the symbol table.
+ * @param   cSyms       Number of symbols in the table.
+ * @param   pchStrings  Pointer to the string table.
+ * @param   cchStrings  Size of the string table.
+ * @param   BaseAddress Adjusted base address, see kLdrModQuerySymbol.
+ * @param   iSymbol     See kLdrModQuerySymbol.
+ * @param   pchSymbol   See kLdrModQuerySymbol.
+ * @param   cchSymbol   See kLdrModQuerySymbol.
+ * @param   puValue     See kLdrModQuerySymbol.
+ * @param   pfKind      See kLdrModQuerySymbol.
+ */
+static int kldrModMachODoQuerySymbol64Bit(PKLDRMODMACHO pModMachO, const macho_nlist_64_t *paSyms, KU32 cSyms,
+                                          const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress, KU32 iSymbol,
+                                          const char *pchSymbol, KU32 cchSymbol, PKLDRADDR puValue, KU32 *pfKind)
+{
+    /*
+     * Find a valid symbol matching the search criteria.
+     */
+    if (iSymbol == NIL_KLDRMOD_SYM_ORDINAL)
+    {
+        /* simplify validation. */
+        if (cchStrings <= cchSymbol)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+        cchStrings -= cchSymbol;
+
+        /* external symbols are usually at the end, so search the other way. */
+        for (iSymbol = cSyms - 1; iSymbol != KU32_MAX; iSymbol--)
+        {
+            const char *psz;
+
+            /* Skip irrellevant and non-public symbols. */
+            if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+                continue;
+            if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+                continue;
+            if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
+                continue;
+            if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
+                continue;
+
+            /* get name */
+            if (!paSyms[iSymbol].n_un.n_strx)
+                continue;
+            if ((KU32)paSyms[iSymbol].n_un.n_strx >= cchStrings)
+                continue;
+            psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
+            if (psz[cchSymbol])
+                continue;
+            if (kHlpMemComp(psz, pchSymbol, cchSymbol))
+                continue;
+
+            /* match! */
+            break;
+        }
+        if (iSymbol == KU32_MAX)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+    }
+    else
+    {
+        if (iSymbol >= cSyms)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+        if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+        if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+            return KLDR_ERR_SYMBOL_NOT_FOUND;
+    }
+
+    /*
+     * Calc the return values.
+     */
+    if (pfKind)
+    {
+        if (    pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+            ||  pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+            *pfKind = KLDRSYMKIND_32BIT | KLDRSYMKIND_NO_TYPE;
+        else
+            *pfKind = KLDRSYMKIND_64BIT | KLDRSYMKIND_NO_TYPE;
+        if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
+            *pfKind |= KLDRSYMKIND_WEAK;
+    }
+
+    switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
+    {
+        case MACHO_N_SECT:
+        {
+            PKLDRMODMACHOSECT pSect;
+            KLDRADDR offSect;
+            KLDRMODMACHO_CHECK_RETURN((KU32)(paSyms[iSymbol].n_sect - 1) < pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+            pSect = &pModMachO->paSections[paSyms[iSymbol].n_sect - 1];
+
+            offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
+            KLDRMODMACHO_CHECK_RETURN(   offSect <= pSect->cb
+                                      || (   paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
+                                          && offSect == 0U - pSect->RVA
+                                          && pModMachO->uEffFileType != MH_OBJECT),
+                                      KLDR_ERR_MACHO_BAD_SYMBOL);
+            if (puValue)
+                *puValue = BaseAddress + pSect->RVA + offSect;
+
+            if (    pfKind
+                &&  (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
+                *pfKind = (*pfKind & ~KLDRSYMKIND_TYPE_MASK) | KLDRSYMKIND_CODE;
+            break;
+        }
+
+        case MACHO_N_ABS:
+            if (puValue)
+                *puValue = paSyms[iSymbol].n_value;
+            /*if (pfKind)
+                pfKind |= KLDRSYMKIND_ABS;*/
+            break;
+
+        case MACHO_N_PBUD:
+        case MACHO_N_INDR:
+            /** @todo implement indirect and prebound symbols. */
+        default:
+            KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+    }
+
+    return 0;
+}
+
+
+/** @copydoc kLdrModEnumSymbols */
+static int kldrModMachOEnumSymbols(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress,
+                                   KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    int rc;
+    K_NOREF(pvBits);
+
+    /*
+     * Resolve defaults.
+     */
+    rc  = kldrModMachOAdjustBaseAddress(pModMachO, &BaseAddress);
+    if (rc)
+        return rc;
+
+    /*
+     * Take action according to file type.
+     */
+    if (   pModMachO->Hdr.filetype == MH_OBJECT
+        || pModMachO->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
+        || pModMachO->Hdr.filetype == MH_DYLIB
+        || pModMachO->Hdr.filetype == MH_BUNDLE
+        || pModMachO->Hdr.filetype == MH_DSYM
+        || pModMachO->Hdr.filetype == MH_KEXT_BUNDLE)
+    {
+        rc = kldrModMachOLoadObjSymTab(pModMachO);
+        if (!rc)
+        {
+            if (    pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+                ||  pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+                rc = kldrModMachODoEnumSymbols32Bit(pModMachO, (macho_nlist_32_t *)pModMachO->pvaSymbols, pModMachO->cSymbols,
+                                                    pModMachO->pchStrings, pModMachO->cchStrings, BaseAddress,
+                                                    fFlags, pfnCallback, pvUser);
+            else
+                rc = kldrModMachODoEnumSymbols64Bit(pModMachO, (macho_nlist_64_t *)pModMachO->pvaSymbols, pModMachO->cSymbols,
+                                                    pModMachO->pchStrings, pModMachO->cchStrings, BaseAddress,
+                                                    fFlags, pfnCallback, pvUser);
+        }
+    }
+    else
+        KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+
+    return rc;
+}
+
+
+/**
+ * Enum a 32-bit symbol table.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param   pModMachO
+ * @param   paSyms      Pointer to the symbol table.
+ * @param   cSyms       Number of symbols in the table.
+ * @param   pchStrings  Pointer to the string table.
+ * @param   cchStrings  Size of the string table.
+ * @param   BaseAddress Adjusted base address, see kLdrModEnumSymbols.
+ * @param   fFlags      See kLdrModEnumSymbols.
+ * @param   pfnCallback See kLdrModEnumSymbols.
+ * @param   pvUser      See kLdrModEnumSymbols.
+ */
+static int kldrModMachODoEnumSymbols32Bit(PKLDRMODMACHO pModMachO, const macho_nlist_32_t *paSyms, KU32 cSyms,
+                                          const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress,
+                                          KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser)
+{
+    const KU32 fKindBase =    pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+                           || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+                         ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT;
+    KU32 iSym;
+    int rc;
+
+    /*
+     * Iterate the symbol table.
+     */
+    for (iSym = 0; iSym < cSyms; iSym++)
+    {
+        KU32 fKind;
+        KLDRADDR uValue;
+        const char *psz;
+        KSIZE cch;
+
+        /* Skip debug symbols and undefined symbols. */
+        if (paSyms[iSym].n_type & MACHO_N_STAB)
+            continue;
+        if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+            continue;
+
+        /* Skip non-public symbols unless they are requested explicitly. */
+        if (!(fFlags & KLDRMOD_ENUM_SYMS_FLAGS_ALL))
+        {
+            if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
+                continue;
+            if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
+                continue;
+            if (!paSyms[iSym].n_un.n_strx)
+                continue;
+        }
+
+        /*
+         * Gather symbol info
+         */
+
+        /* name */
+        KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_un.n_strx < cchStrings, KLDR_ERR_MACHO_BAD_SYMBOL);
+        psz = &pchStrings[paSyms[iSym].n_un.n_strx];
+        cch = kHlpStrLen(psz);
+        if (!cch)
+            psz = NULL;
+
+        /* kind & value */
+        fKind = fKindBase;
+        if (paSyms[iSym].n_desc & N_WEAK_DEF)
+            fKind |= KLDRSYMKIND_WEAK;
+        switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+        {
+            case MACHO_N_SECT:
+            {
+                PKLDRMODMACHOSECT pSect;
+                KLDRMODMACHO_CHECK_RETURN((KU32)(paSyms[iSym].n_sect - 1) < pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+                pSect = &pModMachO->paSections[paSyms[iSym].n_sect - 1];
+
+                uValue = paSyms[iSym].n_value - pSect->LinkAddress;
+                KLDRMODMACHO_CHECK_RETURN(   uValue <= pSect->cb
+                                          || (   paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
+                                              && uValue == 0U - pSect->RVA
+                                              && pModMachO->uEffFileType != MH_OBJECT),
+                                          KLDR_ERR_MACHO_BAD_SYMBOL);
+                uValue += BaseAddress + pSect->RVA;
+
+                if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
+                    fKind |= KLDRSYMKIND_CODE;
+                else
+                    fKind |= KLDRSYMKIND_NO_TYPE;
+                break;
+            }
+
+            case MACHO_N_ABS:
+                uValue = paSyms[iSym].n_value;
+                fKind |= KLDRSYMKIND_NO_TYPE /*KLDRSYMKIND_ABS*/;
+                break;
+
+            case MACHO_N_PBUD:
+            case MACHO_N_INDR:
+                /** @todo implement indirect and prebound symbols. */
+            default:
+                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+        }
+
+        /*
+         * Do callback.
+         */
+        rc = pfnCallback(pModMachO->pMod, iSym, psz, cch, NULL, uValue, fKind, pvUser);
+        if (rc)
+            return rc;
+    }
+    return 0;
+}
+
+
+/**
+ * Enum a 64-bit symbol table.
+ *
+ * @returns See kLdrModQuerySymbol.
+ * @param   pModMachO
+ * @param   paSyms      Pointer to the symbol table.
+ * @param   cSyms       Number of symbols in the table.
+ * @param   pchStrings  Pointer to the string table.
+ * @param   cchStrings  Size of the string table.
+ * @param   BaseAddress Adjusted base address, see kLdrModEnumSymbols.
+ * @param   fFlags      See kLdrModEnumSymbols.
+ * @param   pfnCallback See kLdrModEnumSymbols.
+ * @param   pvUser      See kLdrModEnumSymbols.
+ */
+static int kldrModMachODoEnumSymbols64Bit(PKLDRMODMACHO pModMachO, const macho_nlist_64_t *paSyms, KU32 cSyms,
+                                          const char *pchStrings, KU32 cchStrings, KLDRADDR BaseAddress,
+                                          KU32 fFlags, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser)
+{
+    const KU32 fKindBase =    pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE
+                           || pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE
+                         ? KLDRSYMKIND_64BIT : KLDRSYMKIND_32BIT;
+    KU32 iSym;
+    int rc;
+
+    /*
+     * Iterate the symbol table.
+     */
+    for (iSym = 0; iSym < cSyms; iSym++)
+    {
+        KU32 fKind;
+        KLDRADDR uValue;
+        const char *psz;
+        KSIZE cch;
+
+        /* Skip debug symbols and undefined symbols. */
+        if (paSyms[iSym].n_type & MACHO_N_STAB)
+            continue;
+        if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+            continue;
+
+        /* Skip non-public symbols unless they are requested explicitly. */
+        if (!(fFlags & KLDRMOD_ENUM_SYMS_FLAGS_ALL))
+        {
+            if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
+                continue;
+            if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
+                continue;
+            if (!paSyms[iSym].n_un.n_strx)
+                continue;
+        }
+
+        /*
+         * Gather symbol info
+         */
+
+        /* name */
+        KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_un.n_strx < cchStrings, KLDR_ERR_MACHO_BAD_SYMBOL);
+        psz = &pchStrings[paSyms[iSym].n_un.n_strx];
+        cch = kHlpStrLen(psz);
+        if (!cch)
+            psz = NULL;
+
+        /* kind & value */
+        fKind = fKindBase;
+        if (paSyms[iSym].n_desc & N_WEAK_DEF)
+            fKind |= KLDRSYMKIND_WEAK;
+        switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+        {
+            case MACHO_N_SECT:
+            {
+                PKLDRMODMACHOSECT pSect;
+                KLDRMODMACHO_CHECK_RETURN((KU32)(paSyms[iSym].n_sect - 1) < pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+                pSect = &pModMachO->paSections[paSyms[iSym].n_sect - 1];
+
+                uValue = paSyms[iSym].n_value - pSect->LinkAddress;
+                KLDRMODMACHO_CHECK_RETURN(   uValue <= pSect->cb
+                                          || (   paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
+                                              && uValue == 0U - pSect->RVA
+                                              && pModMachO->uEffFileType != MH_OBJECT),
+                                          KLDR_ERR_MACHO_BAD_SYMBOL);
+                uValue += BaseAddress + pSect->RVA;
+
+                if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
+                    fKind |= KLDRSYMKIND_CODE;
+                else
+                    fKind |= KLDRSYMKIND_NO_TYPE;
+                break;
+            }
+
+            case MACHO_N_ABS:
+                uValue = paSyms[iSym].n_value;
+                fKind |= KLDRSYMKIND_NO_TYPE /*KLDRSYMKIND_ABS*/;
+                break;
+
+            case MACHO_N_PBUD:
+            case MACHO_N_INDR:
+                /** @todo implement indirect and prebound symbols. */
+            default:
+                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+        }
+
+        /*
+         * Do callback.
+         */
+        rc = pfnCallback(pModMachO->pMod, iSym, psz, cch, NULL, uValue, fKind, pvUser);
+        if (rc)
+            return rc;
+    }
+    return 0;
+}
+
+
+/** @copydoc kLdrModGetImport */
+static int kldrModMachOGetImport(PKLDRMOD pMod, const void *pvBits, KU32 iImport, char *pszName, KSIZE cchName)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    K_NOREF(pvBits);
+    K_NOREF(iImport);
+    K_NOREF(pszName);
+    K_NOREF(cchName);
+
+    if (pModMachO->Hdr.filetype == MH_OBJECT)
+        return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+
+    /* later */
+    return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+}
+
+
+/** @copydoc kLdrModNumberOfImports */
+static KI32 kldrModMachONumberOfImports(PKLDRMOD pMod, const void *pvBits)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    K_NOREF(pvBits);
+
+    if (pModMachO->Hdr.filetype == MH_OBJECT)
+        return 0;
+
+    /* later */
+    return 0;
+}
+
+
+/** @copydoc kLdrModGetStackInfo */
+static int kldrModMachOGetStackInfo(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
+{
+    /*PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;*/
+    K_NOREF(pMod);
+    K_NOREF(pvBits);
+    K_NOREF(BaseAddress);
+
+    pStackInfo->Address = NIL_KLDRADDR;
+    pStackInfo->LinkAddress = NIL_KLDRADDR;
+    pStackInfo->cbStack = pStackInfo->cbStackThread = 0;
+    /* later */
+
+    return 0;
+}
+
+
+/** @copydoc kLdrModQueryMainEntrypoint */
+static int kldrModMachOQueryMainEntrypoint(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRADDR pMainEPAddress)
+{
+#if 0
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    int rc;
+
+    /*
+     * Resolve base address alias if any.
+     */
+    rc = kldrModMachOBitsAndBaseAddress(pModMachO, NULL, &BaseAddress);
+    if (rc)
+        return rc;
+
+    /*
+     * Convert the address from the header.
+     */
+    *pMainEPAddress = pModMachO->Hdrs.OptionalHeader.AddressOfEntryPoint
+        ? BaseAddress + pModMachO->Hdrs.OptionalHeader.AddressOfEntryPoint
+        : NIL_KLDRADDR;
+#else
+    *pMainEPAddress = NIL_KLDRADDR;
+    K_NOREF(pvBits);
+    K_NOREF(BaseAddress);
+    K_NOREF(pMod);
+#endif
+    return 0;
+}
+
+
+/** @copydoc kLdrModQueryImageUuid */
+static int kldrModMachOQueryImageUuid(PKLDRMOD pMod, const void *pvBits, void *pvUuid, KSIZE cbUuid)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    K_NOREF(pvBits);
+
+    kHlpMemSet(pvUuid, 0, cbUuid);
+    if (kHlpMemComp(pvUuid, pModMachO->abImageUuid, sizeof(pModMachO->abImageUuid)) == 0)
+        return KLDR_ERR_NO_IMAGE_UUID;
+
+    kHlpMemCopy(pvUuid, pModMachO->abImageUuid, sizeof(pModMachO->abImageUuid));
+    return 0;
+}
+
+
+/** @copydoc kLdrModEnumDbgInfo */
+static int kldrModMachOEnumDbgInfo(PKLDRMOD pMod, const void *pvBits, PFNKLDRENUMDBG pfnCallback, void *pvUser)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    int rc = 0;
+    KU32 iSect;
+    K_NOREF(pvBits);
+
+    for (iSect = 0; iSect < pModMachO->cSections; iSect++)
+    {
+        section_32_t *pMachOSect = pModMachO->paSections[iSect].pvMachoSection; /* (32-bit & 64-bit starts the same way) */
+        char          szTmp[sizeof(pMachOSect->sectname) + 1];
+
+        if (kHlpStrComp(pMachOSect->segname, "__DWARF"))
+            continue;
+
+        kHlpMemCopy(szTmp, pMachOSect->sectname, sizeof(pMachOSect->sectname));
+        szTmp[sizeof(pMachOSect->sectname)] = '\0';
+
+        rc = pfnCallback(pMod, iSect, KLDRDBGINFOTYPE_DWARF, 0, 0, szTmp,
+                         pModMachO->paSections[iSect].offFile,
+                         pModMachO->paSections[iSect].LinkAddress,
+                         pModMachO->paSections[iSect].cb,
+                         NULL, pvUser);
+        if (rc != 0)
+            break;
+    }
+
+    return rc;
+}
+
+
+/** @copydoc kLdrModHasDbgInfo */
+static int kldrModMachOHasDbgInfo(PKLDRMOD pMod, const void *pvBits)
+{
+    /*PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;*/
+
+#if 0
+    /*
+     * Base this entirely on the presence of a debug directory.
+     */
+    if (    pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size
+            < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
+        ||  !pModMachO->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
+        return KLDR_ERR_NO_DEBUG_INFO;
+    return 0;
+#else
+    K_NOREF(pMod);
+    K_NOREF(pvBits);
+    return KLDR_ERR_NO_DEBUG_INFO;
+#endif
+}
+
+
+/** @copydoc kLdrModMap */
+static int kldrModMachOMap(PKLDRMOD pMod)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    unsigned fFixed;
+    KU32 i;
+    void *pvBase;
+    int rc;
+
+    if (!pModMachO->fCanLoad)
+        return KLDR_ERR_TODO;
+
+    /*
+     * Already mapped?
+     */
+    if (pModMachO->pvMapping)
+        return KLDR_ERR_ALREADY_MAPPED;
+
+    /*
+     * Map it.
+     */
+    /* fixed image? */
+    fFixed = pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
+          || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
+    if (!fFixed)
+        pvBase = NULL;
+    else
+    {
+        pvBase = (void *)(KUPTR)pMod->aSegments[0].LinkAddress;
+        if ((KUPTR)pvBase != pMod->aSegments[0].LinkAddress)
+            return KLDR_ERR_ADDRESS_OVERFLOW;
+    }
+
+    /* try do the prepare */
+    rc = kRdrMap(pMod->pRdr, &pvBase, pMod->cSegments, pMod->aSegments, fFixed);
+    if (rc)
+        return rc;
+
+    /*
+     * Update the segments with their map addresses.
+     */
+    for (i = 0; i < pMod->cSegments; i++)
+    {
+        if (pMod->aSegments[i].RVA != NIL_KLDRADDR)
+            pMod->aSegments[i].MapAddress = (KUPTR)pvBase + (KUPTR)pMod->aSegments[i].RVA;
+    }
+    pModMachO->pvMapping = pvBase;
+
+    return 0;
+}
+
+
+/** @copydoc kLdrModUnmap */
+static int kldrModMachOUnmap(PKLDRMOD pMod)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    KU32 i;
+    int rc;
+
+    /*
+     * Mapped?
+     */
+    if (!pModMachO->pvMapping)
+        return KLDR_ERR_NOT_MAPPED;
+
+    /*
+     * Try unmap the image.
+     */
+    rc = kRdrUnmap(pMod->pRdr, pModMachO->pvMapping, pMod->cSegments, pMod->aSegments);
+    if (rc)
+        return rc;
+
+    /*
+     * Update the segments to reflect that they aren't mapped any longer.
+     */
+    pModMachO->pvMapping = NULL;
+    for (i = 0; i < pMod->cSegments; i++)
+        pMod->aSegments[i].MapAddress = 0;
+
+    return 0;
+}
+
+
+/** @copydoc kLdrModAllocTLS */
+static int kldrModMachOAllocTLS(PKLDRMOD pMod, void *pvMapping)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+
+    /*
+     * Mapped?
+     */
+    if (   pvMapping == KLDRMOD_INT_MAP
+        && !pModMachO->pvMapping )
+        return KLDR_ERR_NOT_MAPPED;
+    return 0;
+}
+
+
+/** @copydoc kLdrModFreeTLS */
+static void kldrModMachOFreeTLS(PKLDRMOD pMod, void *pvMapping)
+{
+    K_NOREF(pMod);
+    K_NOREF(pvMapping);
+}
+
+
+/** @copydoc kLdrModReload */
+static int kldrModMachOReload(PKLDRMOD pMod)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+
+    /*
+     * Mapped?
+     */
+    if (!pModMachO->pvMapping)
+        return KLDR_ERR_NOT_MAPPED;
+
+    /* the file provider does it all */
+    return kRdrRefresh(pMod->pRdr, pModMachO->pvMapping, pMod->cSegments, pMod->aSegments);
+}
+
+
+/** @copydoc kLdrModFixupMapping */
+static int kldrModMachOFixupMapping(PKLDRMOD pMod, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    int rc, rc2;
+
+    /*
+     * Mapped?
+     */
+    if (!pModMachO->pvMapping)
+        return KLDR_ERR_NOT_MAPPED;
+
+    /*
+     * Before doing anything we'll have to make all pages writable.
+     */
+    rc = kRdrProtect(pMod->pRdr, pModMachO->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */);
+    if (rc)
+        return rc;
+
+    /*
+     * Resolve imports and apply base relocations.
+     */
+    rc = kldrModMachORelocateBits(pMod, pModMachO->pvMapping, (KUPTR)pModMachO->pvMapping, pModMachO->LinkAddress,
+                                  pfnGetImport, pvUser);
+
+    /*
+     * Restore protection.
+     */
+    rc2 = kRdrProtect(pMod->pRdr, pModMachO->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */);
+    if (!rc && rc2)
+        rc = rc2;
+    return rc;
+}
+
+
+/**
+ * MH_OBJECT: Resolves undefined symbols (imports).
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param   pModMachO       The Mach-O module interpreter instance.
+ * @param   pfnGetImport    The callback for resolving an imported symbol.
+ * @param   pvUser          User argument to the callback.
+ */
+static int  kldrModMachOObjDoImports(PKLDRMODMACHO pModMachO, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+    const KU32 cSyms = pModMachO->cSymbols;
+    KU32 iSym;
+    int rc;
+
+    /*
+     * Ensure that we've got the symbol table and section fixups handy.
+     */
+    rc = kldrModMachOLoadObjSymTab(pModMachO);
+    if (rc)
+        return rc;
+
+    /*
+     * Iterate the symbol table and resolve undefined symbols.
+     * We currently ignore REFERENCE_TYPE.
+     */
+    if (    pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+        ||  pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+    {
+        macho_nlist_32_t *paSyms = (macho_nlist_32_t *)pModMachO->pvaSymbols;
+        for (iSym = 0; iSym < cSyms; iSym++)
+        {
+            /* skip stabs */
+            if (paSyms[iSym].n_type & MACHO_N_STAB)
+                continue;
+
+            if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+            {
+                const char *pszSymbol;
+                KSIZE cchSymbol;
+                KU32 fKind = KLDRSYMKIND_REQ_FLAT;
+                KLDRADDR Value = NIL_KLDRADDR;
+
+                /** @todo Implement N_REF_TO_WEAK. */
+                KLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), KLDR_ERR_TODO);
+
+                /* Get the symbol name. */
+                KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_un.n_strx < pModMachO->cchStrings, KLDR_ERR_MACHO_BAD_SYMBOL);
+                pszSymbol = &pModMachO->pchStrings[paSyms[iSym].n_un.n_strx];
+                cchSymbol = kHlpStrLen(pszSymbol);
+
+                /* Check for linker defined symbols relating to sections and segments. */
+                if (   cchSymbol > sizeof("section$end$") - 1
+                    && *pszSymbol == 's'
+                    && kHlpMemChr(pszSymbol, '$', cchSymbol))
+                    rc = kldrModMachOQueryLinkerSymbol(pModMachO, pModMachO->pMod, pszSymbol, cchSymbol, BaseAddress, &Value);
+                else
+                    rc = KLDR_ERR_SYMBOL_NOT_FOUND;
+
+                /* Ask the user for an address to the symbol. */
+                if (rc)
+                    rc = pfnGetImport(pModMachO->pMod, NIL_KLDRMOD_IMPORT, iSym, pszSymbol, cchSymbol, NULL,
+                                      &Value, &fKind, pvUser);
+                if (rc)
+                {
+                    /* weak reference? */
+                    if (!(paSyms[iSym].n_desc & N_WEAK_REF))
+                        break;
+                    Value = 0;
+                }
+
+                /* Update the symbol. */
+                paSyms[iSym].n_value = (KU32)Value;
+                if (paSyms[iSym].n_value != Value)
+                {
+                    rc = KLDR_ERR_ADDRESS_OVERFLOW;
+                    break;
+                }
+            }
+            else if (paSyms[iSym].n_desc & N_WEAK_DEF)
+            {
+                /** @todo implement weak symbols. */
+                /*return KLDR_ERR_TODO; - ignored for now. */
+            }
+        }
+    }
+    else
+    {
+        /* (Identical to the 32-bit code, just different paSym type. (and n_strx is unsigned)) */
+        macho_nlist_64_t *paSyms = (macho_nlist_64_t *)pModMachO->pvaSymbols;
+        for (iSym = 0; iSym < cSyms; iSym++)
+        {
+            /* skip stabs */
+            if (paSyms[iSym].n_type & MACHO_N_STAB)
+                continue;
+
+            if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+            {
+                const char *pszSymbol;
+                KSIZE cchSymbol;
+                KU32 fKind = KLDRSYMKIND_REQ_FLAT;
+                KLDRADDR Value = NIL_KLDRADDR;
+
+                /** @todo Implement N_REF_TO_WEAK. */
+                KLDRMODMACHO_CHECK_RETURN(!(paSyms[iSym].n_desc & N_REF_TO_WEAK), KLDR_ERR_TODO);
+
+                 /* Get the symbol name. */
+                KLDRMODMACHO_CHECK_RETURN(paSyms[iSym].n_un.n_strx < pModMachO->cchStrings, KLDR_ERR_MACHO_BAD_SYMBOL);
+                pszSymbol = &pModMachO->pchStrings[paSyms[iSym].n_un.n_strx];
+                cchSymbol = kHlpStrLen(pszSymbol);
+
+                /* Check for linker defined symbols relating to sections and segments. */
+                if (   cchSymbol > sizeof("section$end$") - 1
+                    && *pszSymbol == 's'
+                    && kHlpMemChr(pszSymbol, '$', cchSymbol))
+                    rc = kldrModMachOQueryLinkerSymbol(pModMachO, pModMachO->pMod, pszSymbol, cchSymbol, BaseAddress, &Value);
+                else
+                    rc = KLDR_ERR_SYMBOL_NOT_FOUND;
+
+                /* Ask the user for an address to the symbol. */
+                if (rc)
+                    rc = pfnGetImport(pModMachO->pMod, NIL_KLDRMOD_IMPORT, iSym, pszSymbol, cchSymbol, NULL,
+                                      &Value, &fKind, pvUser);
+                if (rc)
+                {
+                    /* weak reference? */
+                    if (!(paSyms[iSym].n_desc & N_WEAK_REF))
+                        break;
+                    Value = 0;
+                }
+
+                /* Update the symbol. */
+                paSyms[iSym].n_value = Value;
+                if (paSyms[iSym].n_value != Value)
+                {
+                    rc = KLDR_ERR_ADDRESS_OVERFLOW;
+                    break;
+                }
+            }
+            else if (paSyms[iSym].n_desc & N_WEAK_DEF)
+            {
+                /** @todo implement weak symbols. */
+                /*return KLDR_ERR_TODO; - ignored for now. */
+            }
+        }
+    }
+
+    return rc;
+}
+
+
+/**
+ * MH_OBJECT: Applies base relocations to a (unprotected) image mapping.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param   pModMachO       The Mach-O module interpreter instance.
+ * @param   pvMapping       The mapping to fixup.
+ * @param   NewBaseAddress  The address to fixup the mapping to.
+ * @param   OldBaseAddress  The address the mapping is currently fixed up to.
+ */
+static int  kldrModMachOObjDoFixups(PKLDRMODMACHO pModMachO, void *pvMapping, KLDRADDR NewBaseAddress)
+{
+    KU32 iSeg;
+    int rc;
+
+
+    /*
+     * Ensure that we've got the symbol table and section fixups handy.
+     */
+    rc = kldrModMachOLoadObjSymTab(pModMachO);
+    if (rc)
+        return rc;
+
+    /*
+     * Iterate over the segments and their sections and apply fixups.
+     */
+    for (iSeg = rc = 0; !rc && iSeg < pModMachO->pMod->cSegments; iSeg++)
+    {
+        PKLDRMODMACHOSEG pSeg = &pModMachO->aSegments[iSeg];
+        KU32 iSect;
+
+        for (iSect = 0; iSect < pSeg->cSections; iSect++)
+        {
+            PKLDRMODMACHOSECT pSect = &pSeg->paSections[iSect];
+            KU8 *pbSectBits;
+
+            /* skip sections without fixups. */
+            if (!pSect->cFixups)
+                continue;
+
+            /* lazy load (and endian convert) the fixups. */
+            if (!pSect->paFixups)
+            {
+                rc = kldrModMachOLoadFixups(pModMachO, pSect->offFixups, pSect->cFixups, &pSect->paFixups);
+                if (rc)
+                    break;
+            }
+
+            /*
+             * Apply the fixups.
+             */
+            pbSectBits = (KU8 *)pvMapping + (KUPTR)pSect->RVA;
+            if (pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE) /** @todo this aint right. */
+                rc = kldrModMachOFixupSectionGeneric32Bit(pModMachO, pbSectBits, pSect,
+                                                          (macho_nlist_32_t *)pModMachO->pvaSymbols,
+                                                          pModMachO->cSymbols, NewBaseAddress);
+            else if (   pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE
+                     && pModMachO->Hdr.cputype == CPU_TYPE_X86_64)
+                rc = kldrModMachOFixupSectionAMD64(pModMachO, pbSectBits, pSect,
+                                                   (macho_nlist_64_t *)pModMachO->pvaSymbols,
+                                                   pModMachO->cSymbols, NewBaseAddress);
+            else
+                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+            if (rc)
+                break;
+        }
+    }
+
+    return rc;
+}
+
+
+/**
+ * Applies generic fixups to a section in an image of the same endian-ness
+ * as the host CPU.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param   pModMachO       The Mach-O module interpreter instance.
+ * @param   pbSectBits      Pointer to the section bits.
+ * @param   pFixupSect      The section being fixed up.
+ * @param   NewBaseAddress  The new base image address.
+ */
+static int  kldrModMachOFixupSectionGeneric32Bit(PKLDRMODMACHO pModMachO, KU8 *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
+                                                 macho_nlist_32_t *paSyms, KU32 cSyms, KLDRADDR NewBaseAddress)
+{
+    const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
+    const KU32 cFixups = pFixupSect->cFixups;
+    KSIZE cbSectBits = (KSIZE)pFixupSect->cb;
+    const KU8 *pbSectVirginBits;
+    KU32 iFixup;
+    KLDRPU uFixVirgin;
+    KLDRPU uFix;
+    KLDRADDR SymAddr = ~(KLDRADDR)0;
+    int rc;
+
+    /*
+     * Find the virgin bits.
+     */
+    if (pFixupSect->offFile != -1)
+    {
+        rc = kldrModMachOMapVirginBits(pModMachO);
+        if (rc)
+            return rc;
+        pbSectVirginBits = (const KU8 *)pModMachO->pvBits + pFixupSect->offFile;
+    }
+    else
+        pbSectVirginBits = NULL;
+
+    /*
+     * Iterate the fixups and apply them.
+     */
+    for (iFixup = 0; iFixup < cFixups; iFixup++)
+    {
+        union
+        {
+            macho_relocation_info_t     r;
+            scattered_relocation_info_t s;
+        } Fixup;
+        Fixup.r = paFixups[iFixup];
+
+        if (!(Fixup.r.r_address & R_SCATTERED))
+        {
+            /* sanity */
+            if ((KU32)Fixup.r.r_address >= cbSectBits)
+                return KLDR_ERR_BAD_FIXUP;
+
+            /* calc fixup addresses. */
+            uFix.pv = pbSectBits + Fixup.r.r_address;
+            uFixVirgin.pv = pbSectVirginBits ? (KU8 *)pbSectVirginBits + Fixup.r.r_address : 0;
+
+            /*
+             * Calc the symbol value.
+             */
+            /* Calc the linked symbol address / addend. */
+            switch (Fixup.r.r_length)
+            {
+                /** @todo Deal with unaligned accesses on non x86 platforms. */
+                case 0: SymAddr = *uFixVirgin.pi8; break;
+                case 1: SymAddr = *uFixVirgin.pi16; break;
+                case 2: SymAddr = *uFixVirgin.pi32; break;
+                case 3: SymAddr = *uFixVirgin.pi64; break;
+            }
+            if (Fixup.r.r_pcrel)
+                SymAddr += Fixup.r.r_address + pFixupSect->LinkAddress;
+
+            /* Add symbol / section address. */
+            if (Fixup.r.r_extern)
+            {
+                const macho_nlist_32_t *pSym;
+                if (Fixup.r.r_symbolnum >= cSyms)
+                    return KLDR_ERR_BAD_FIXUP;
+                pSym = &paSyms[Fixup.r.r_symbolnum];
+
+                if (pSym->n_type & MACHO_N_STAB)
+                    return KLDR_ERR_BAD_FIXUP;
+
+                switch (pSym->n_type & MACHO_N_TYPE)
+                {
+                    case MACHO_N_SECT:
+                    {
+                        PKLDRMODMACHOSECT pSymSect;
+                        KLDRMODMACHO_CHECK_RETURN((KU32)pSym->n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+                        pSymSect = &pModMachO->paSections[pSym->n_sect - 1];
+
+                        SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+                        break;
+                    }
+
+                    case MACHO_N_UNDF:
+                    case MACHO_N_ABS:
+                        SymAddr += pSym->n_value;
+                        break;
+
+                    case MACHO_N_INDR:
+                    case MACHO_N_PBUD:
+                        KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+                    default:
+                        KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL);
+                }
+            }
+            else if (Fixup.r.r_symbolnum != R_ABS)
+            {
+                PKLDRMODMACHOSECT pSymSect;
+                if (Fixup.r.r_symbolnum > pModMachO->cSections)
+                    return KLDR_ERR_BAD_FIXUP;
+                pSymSect = &pModMachO->paSections[Fixup.r.r_symbolnum - 1];
+
+                SymAddr -= pSymSect->LinkAddress;
+                SymAddr += pSymSect->RVA + NewBaseAddress;
+            }
+
+            /* adjust for PC relative */
+            if (Fixup.r.r_pcrel)
+                SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
+        }
+        else
+        {
+            PKLDRMODMACHOSECT pSymSect;
+            KU32 iSymSect;
+            KLDRADDR Value;
+
+            /* sanity */
+            KLDRMODMACHO_ASSERT(Fixup.s.r_scattered);
+            if ((KU32)Fixup.s.r_address >= cbSectBits)
+                return KLDR_ERR_BAD_FIXUP;
+
+            /* calc fixup addresses. */
+            uFix.pv = pbSectBits + Fixup.s.r_address;
+            uFixVirgin.pv = pbSectVirginBits ? (KU8 *)pbSectVirginBits + Fixup.s.r_address : 0;
+
+            /*
+             * Calc the symbol value.
+             */
+            /* The addend is stored in the code. */
+            switch (Fixup.s.r_length)
+            {
+                case 0: SymAddr = *uFixVirgin.pi8; break;
+                case 1: SymAddr = *uFixVirgin.pi16; break;
+                case 2: SymAddr = *uFixVirgin.pi32; break;
+                case 3: SymAddr = *uFixVirgin.pi64; break;
+            }
+            if (Fixup.s.r_pcrel)
+                SymAddr += Fixup.s.r_address;
+            Value = Fixup.s.r_value;
+            SymAddr -= Value;                   /* (-> addend only) */
+
+            /* Find the section number from the r_value. */
+            pSymSect = NULL;
+            for (iSymSect = 0; iSymSect < pModMachO->cSections; iSymSect++)
+            {
+                KLDRADDR off = Value - pModMachO->paSections[iSymSect].LinkAddress;
+                if (off < pModMachO->paSections[iSymSect].cb)
+                {
+                    pSymSect = &pModMachO->paSections[iSymSect];
+                    break;
+                }
+                else if (off == pModMachO->paSections[iSymSect].cb) /* edge case */
+                    pSymSect = &pModMachO->paSections[iSymSect];
+            }
+            if (!pSymSect)
+                return KLDR_ERR_BAD_FIXUP;
+
+            /* Calc the symbol address. */
+            SymAddr += Value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+            if (Fixup.s.r_pcrel)
+                SymAddr -= Fixup.s.r_address + pFixupSect->RVA + NewBaseAddress;
+
+            Fixup.r.r_length = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_length;
+            Fixup.r.r_type   = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_type;
+        }
+
+        /*
+         * Write back the fixed up value.
+         */
+        if (Fixup.r.r_type == GENERIC_RELOC_VANILLA)
+        {
+            switch (Fixup.r.r_length)
+            {
+                case 0: *uFix.pu8  = (KU8)SymAddr; break;
+                case 1: *uFix.pu16 = (KU16)SymAddr; break;
+                case 2: *uFix.pu32 = (KU32)SymAddr; break;
+                case 3: *uFix.pu64 = (KU64)SymAddr; break;
+            }
+        }
+        else if (Fixup.r.r_type <= GENERIC_RELOC_LOCAL_SECTDIFF)
+            return KLDR_ERR_MACHO_UNSUPPORTED_FIXUP_TYPE;
+        else
+            return KLDR_ERR_BAD_FIXUP;
+    }
+
+    return 0;
+}
+
+
+/**
+ * Applies AMD64 fixups to a section.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param   pModMachO       The Mach-O module interpreter instance.
+ * @param   pbSectBits      Pointer to the section bits.
+ * @param   pFixupSect      The section being fixed up.
+ * @param   NewBaseAddress  The new base image address.
+ */
+static int  kldrModMachOFixupSectionAMD64(PKLDRMODMACHO pModMachO, KU8 *pbSectBits, PKLDRMODMACHOSECT pFixupSect,
+                                          macho_nlist_64_t *paSyms, KU32 cSyms, KLDRADDR NewBaseAddress)
+{
+    const macho_relocation_info_t *paFixups = pFixupSect->paFixups;
+    const KU32 cFixups = pFixupSect->cFixups;
+    KSIZE cbSectBits = (KSIZE)pFixupSect->cb;
+    const KU8 *pbSectVirginBits;
+    KU32 iFixup;
+    KLDRPU uFixVirgin;
+    KLDRPU uFix;
+    KLDRADDR SymAddr;
+    int rc;
+
+    /*
+     * Find the virgin bits.
+     */
+    if (pFixupSect->offFile != -1)
+    {
+        rc = kldrModMachOMapVirginBits(pModMachO);
+        if (rc)
+            return rc;
+        pbSectVirginBits = (const KU8 *)pModMachO->pvBits + pFixupSect->offFile;
+    }
+    else
+        pbSectVirginBits = NULL;
+
+    /*
+     * Iterate the fixups and apply them.
+     */
+    for (iFixup = 0; iFixup < cFixups; iFixup++)
+    {
+        union
+        {
+            macho_relocation_info_t     r;
+            scattered_relocation_info_t s;
+        } Fixup;
+        Fixup.r = paFixups[iFixup];
+
+        /* AMD64 doesn't use scattered fixups. */
+        KLDRMODMACHO_CHECK_RETURN(!(Fixup.r.r_address & R_SCATTERED), KLDR_ERR_BAD_FIXUP);
+
+        /* sanity */
+        KLDRMODMACHO_CHECK_RETURN((KU32)Fixup.r.r_address < cbSectBits, KLDR_ERR_BAD_FIXUP);
+
+        /* calc fixup addresses. */
+        uFix.pv = pbSectBits + Fixup.r.r_address;
+        uFixVirgin.pv = pbSectVirginBits ? (KU8 *)pbSectVirginBits + Fixup.r.r_address : 0;
+
+        /*
+         * Calc the symbol value.
+         */
+        /* Calc the linked symbol address / addend. */
+        switch (Fixup.r.r_length)
+        {
+            /** @todo Deal with unaligned accesses on non x86 platforms. */
+            case 2: SymAddr = *uFixVirgin.pi32; break;
+            case 3: SymAddr = *uFixVirgin.pi64; break;
+            default:
+                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP);
+        }
+
+        /* Add symbol / section address. */
+        if (Fixup.r.r_extern)
+        {
+            const macho_nlist_64_t *pSym;
+
+            KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum < cSyms, KLDR_ERR_BAD_FIXUP);
+            pSym = &paSyms[Fixup.r.r_symbolnum];
+            KLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), KLDR_ERR_BAD_FIXUP);
+
+            switch (Fixup.r.r_type)
+            {
+                /* GOT references just needs to have their symbol verified.
+                   Later, we'll optimize GOT building here using a parallel sym->got array. */
+                case X86_64_RELOC_GOT_LOAD:
+                case X86_64_RELOC_GOT:
+                    switch (pSym->n_type & MACHO_N_TYPE)
+                    {
+                        case MACHO_N_SECT:
+                        case MACHO_N_UNDF:
+                        case MACHO_N_ABS:
+                            break;
+                        case MACHO_N_INDR:
+                        case MACHO_N_PBUD:
+                            KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+                        default:
+                            KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL);
+                    }
+                    SymAddr = sizeof(KU64) * Fixup.r.r_symbolnum + pModMachO->GotRVA + NewBaseAddress;
+                    KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, KLDR_ERR_BAD_FIXUP);
+                    SymAddr -= 4;
+                    break;
+
+                /* Verify the r_pcrel field for signed fixups on the way into the default case. */
+                case X86_64_RELOC_BRANCH:
+                case X86_64_RELOC_SIGNED:
+                case X86_64_RELOC_SIGNED_1:
+                case X86_64_RELOC_SIGNED_2:
+                case X86_64_RELOC_SIGNED_4:
+                    KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP);
+                    /* Falls through. */
+                default:
+                {
+                    /* Adjust with fixup specific addend and vierfy unsigned/r_pcrel. */
+                    switch (Fixup.r.r_type)
+                    {
+                        case X86_64_RELOC_UNSIGNED:
+                            KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP);
+                            break;
+                        case X86_64_RELOC_BRANCH:
+                            KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, KLDR_ERR_BAD_FIXUP);
+                            SymAddr -= 4;
+                            break;
+                        case X86_64_RELOC_SIGNED:
+                        case X86_64_RELOC_SIGNED_1:
+                        case X86_64_RELOC_SIGNED_2:
+                        case X86_64_RELOC_SIGNED_4:
+                            SymAddr -= 4;
+                            break;
+                        default:
+                            KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP);
+                    }
+
+                    switch (pSym->n_type & MACHO_N_TYPE)
+                    {
+                        case MACHO_N_SECT:
+                        {
+                            PKLDRMODMACHOSECT pSymSect;
+                            KLDRMODMACHO_CHECK_RETURN((KU32)pSym->n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+                            pSymSect = &pModMachO->paSections[pSym->n_sect - 1];
+                            SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+                            break;
+                        }
+
+                        case MACHO_N_UNDF:
+                            /* branch to an external symbol may have to take a short detour. */
+                            if (   Fixup.r.r_type == X86_64_RELOC_BRANCH
+                                &&       SymAddr + Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress
+                                       - pSym->n_value
+                                       + KU64_C(0x80000000)
+                                    >= KU64_C(0xffffff20))
+                                SymAddr += pModMachO->cbJmpStub * Fixup.r.r_symbolnum + pModMachO->JmpStubsRVA + NewBaseAddress;
+                            else
+                                SymAddr += pSym->n_value;
+                            break;
+
+                        case MACHO_N_ABS:
+                            SymAddr += pSym->n_value;
+                            break;
+
+                        case MACHO_N_INDR:
+                        case MACHO_N_PBUD:
+                            KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+                        default:
+                            KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL);
+                    }
+                    break;
+                }
+
+                /*
+                 * This is a weird customer, it will always be follows by an UNSIGNED fixup.
+                 */
+                case X86_64_RELOC_SUBTRACTOR:
+                {
+                    macho_relocation_info_t Fixup2;
+
+                    /* Deal with the SUBTRACT symbol first, by subtracting it from SymAddr. */
+                    switch (pSym->n_type & MACHO_N_TYPE)
+                    {
+                        case MACHO_N_SECT:
+                        {
+                            PKLDRMODMACHOSECT pSymSect;
+                            KLDRMODMACHO_CHECK_RETURN((KU32)pSym->n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+                            pSymSect = &pModMachO->paSections[pSym->n_sect - 1];
+                            SymAddr -= pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+                            break;
+                        }
+
+                        case MACHO_N_UNDF:
+                        case MACHO_N_ABS:
+                            SymAddr -= pSym->n_value;
+                            break;
+
+                        case MACHO_N_INDR:
+                        case MACHO_N_PBUD:
+                            KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+                        default:
+                            KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL);
+                    }
+
+                    /* Load the 2nd fixup, check sanity. */
+                    iFixup++;
+                    KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel && iFixup < cFixups, KLDR_ERR_BAD_FIXUP);
+                    Fixup2 = paFixups[iFixup];
+                    KLDRMODMACHO_CHECK_RETURN(   Fixup2.r_address == Fixup.r.r_address
+                                              && Fixup2.r_length == Fixup.r.r_length
+                                              && Fixup2.r_type == X86_64_RELOC_UNSIGNED
+                                              && !Fixup2.r_pcrel
+                                              && Fixup2.r_symbolnum < cSyms,
+                                              KLDR_ERR_BAD_FIXUP);
+
+                    if (Fixup2.r_extern)
+                    {
+                        KLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum < cSyms, KLDR_ERR_BAD_FIXUP);
+                        pSym = &paSyms[Fixup2.r_symbolnum];
+                        KLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), KLDR_ERR_BAD_FIXUP);
+
+                        /* Add it's value to SymAddr. */
+                        switch (pSym->n_type & MACHO_N_TYPE)
+                        {
+                            case MACHO_N_SECT:
+                            {
+                                PKLDRMODMACHOSECT pSymSect;
+                                KLDRMODMACHO_CHECK_RETURN((KU32)pSym->n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+                                pSymSect = &pModMachO->paSections[pSym->n_sect - 1];
+                                SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+                                break;
+                            }
+
+                            case MACHO_N_UNDF:
+                            case MACHO_N_ABS:
+                                SymAddr += pSym->n_value;
+                                break;
+
+                            case MACHO_N_INDR:
+                            case MACHO_N_PBUD:
+                                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+                            default:
+                                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_MACHO_BAD_SYMBOL);
+                        }
+                    }
+                    else if (Fixup2.r_symbolnum != R_ABS)
+                    {
+                        PKLDRMODMACHOSECT pSymSect;
+                        KLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum <= pModMachO->cSections, KLDR_ERR_BAD_FIXUP);
+                        pSymSect = &pModMachO->paSections[Fixup2.r_symbolnum - 1];
+                        SymAddr += pSymSect->RVA + NewBaseAddress;
+                    }
+                    else
+                        KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP);
+                }
+                break;
+            }
+        }
+        else
+        {
+            /* verify against fixup type and make adjustments */
+            switch (Fixup.r.r_type)
+            {
+                case X86_64_RELOC_UNSIGNED:
+                    KLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP);
+                    break;
+                case X86_64_RELOC_BRANCH:
+                    KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP);
+                    SymAddr += 4; /* dunno what the assmbler/linker really is doing here... */
+                    break;
+                case X86_64_RELOC_SIGNED:
+                case X86_64_RELOC_SIGNED_1:
+                case X86_64_RELOC_SIGNED_2:
+                case X86_64_RELOC_SIGNED_4:
+                    KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, KLDR_ERR_BAD_FIXUP);
+                    break;
+                /*case X86_64_RELOC_GOT_LOAD:*/
+                /*case X86_64_RELOC_GOT: */
+                /*case X86_64_RELOC_SUBTRACTOR: - must be r_extern=1 says as. */
+                default:
+                    KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP);
+            }
+            if (Fixup.r.r_symbolnum != R_ABS)
+            {
+                PKLDRMODMACHOSECT pSymSect;
+                KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum <= pModMachO->cSections, KLDR_ERR_BAD_FIXUP);
+                pSymSect = &pModMachO->paSections[Fixup.r.r_symbolnum - 1];
+
+                SymAddr -= pSymSect->LinkAddress;
+                SymAddr += pSymSect->RVA + NewBaseAddress;
+                if (Fixup.r.r_pcrel)
+                    SymAddr += Fixup.r.r_address;
+            }
+        }
+
+        /* adjust for PC relative */
+        if (Fixup.r.r_pcrel)
+            SymAddr -= Fixup.r.r_address + pFixupSect->RVA + NewBaseAddress;
+
+        /*
+         * Write back the fixed up value.
+         */
+        switch (Fixup.r.r_length)
+        {
+            case 3:
+                *uFix.pu64 = (KU64)SymAddr;
+                break;
+            case 2:
+                KLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel || Fixup.r.r_type == X86_64_RELOC_SUBTRACTOR, KLDR_ERR_BAD_FIXUP);
+                KLDRMODMACHO_CHECK_RETURN((KI32)SymAddr == (KI64)SymAddr, KLDR_ERR_ADDRESS_OVERFLOW);
+                *uFix.pu32 = (KU32)SymAddr;
+                break;
+            default:
+                KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_BAD_FIXUP);
+        }
+    }
+
+    return 0;
+}
+
+
+/**
+ * Loads the symbol table for a MH_OBJECT file.
+ *
+ * The symbol table is pointed to by KLDRMODMACHO::pvaSymbols.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param   pModMachO       The Mach-O module interpreter instance.
+ */
+static int  kldrModMachOLoadObjSymTab(PKLDRMODMACHO pModMachO)
+{
+    int rc = 0;
+
+    if (    !pModMachO->pvaSymbols
+        &&  pModMachO->cSymbols)
+    {
+        KSIZE cbSyms;
+        KSIZE cbSym;
+        void *pvSyms;
+        void *pvStrings;
+
+        /* sanity */
+        KLDRMODMACHO_CHECK_RETURN(   pModMachO->offSymbols
+                                  && (!pModMachO->cchStrings || pModMachO->offStrings),
+                                  KLDR_ERR_MACHO_BAD_OBJECT_FILE);
+
+        /* allocate */
+        cbSym = pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+             || pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+             ? sizeof(macho_nlist_32_t)
+             : sizeof(macho_nlist_64_t);
+        cbSyms = pModMachO->cSymbols * cbSym;
+        KLDRMODMACHO_CHECK_RETURN(cbSyms / cbSym == pModMachO->cSymbols, KLDR_ERR_SIZE_OVERFLOW);
+        rc = KERR_NO_MEMORY;
+        pvSyms = kHlpAlloc(cbSyms);
+        if (pvSyms)
+        {
+            if (pModMachO->cchStrings)
+                pvStrings = kHlpAlloc(pModMachO->cchStrings);
+            else
+                pvStrings = kHlpAllocZ(4);
+            if (pvStrings)
+            {
+                /* read */
+                rc = kRdrRead(pModMachO->pMod->pRdr, pvSyms, cbSyms, pModMachO->offSymbols);
+                if (!rc && pModMachO->cchStrings)
+                    rc = kRdrRead(pModMachO->pMod->pRdr, pvStrings, pModMachO->cchStrings, pModMachO->offStrings);
+                if (!rc)
+                {
+                    pModMachO->pvaSymbols = pvSyms;
+                    pModMachO->pchStrings = (char *)pvStrings;
+
+                    /* perform endian conversion? */
+                    if (pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+                    {
+                        KU32 cLeft = pModMachO->cSymbols;
+                        macho_nlist_32_t *pSym = (macho_nlist_32_t *)pvSyms;
+                        while (cLeft-- > 0)
+                        {
+                            pSym->n_un.n_strx = K_E2E_U32(pSym->n_un.n_strx);
+                            pSym->n_desc = (KI16)K_E2E_U16(pSym->n_desc);
+                            pSym->n_value = K_E2E_U32(pSym->n_value);
+                            pSym++;
+                        }
+                    }
+                    else if (pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+                    {
+                        KU32 cLeft = pModMachO->cSymbols;
+                        macho_nlist_64_t *pSym = (macho_nlist_64_t *)pvSyms;
+                        while (cLeft-- > 0)
+                        {
+                            pSym->n_un.n_strx = K_E2E_U32(pSym->n_un.n_strx);
+                            pSym->n_desc = (KI16)K_E2E_U16(pSym->n_desc);
+                            pSym->n_value = K_E2E_U64(pSym->n_value);
+                            pSym++;
+                        }
+                    }
+
+                    return 0;
+                }
+                kHlpFree(pvStrings);
+            }
+            kHlpFree(pvSyms);
+        }
+    }
+    else
+        KLDRMODMACHO_ASSERT(pModMachO->pchStrings || pModMachO->Hdr.filetype == MH_DSYM);
+
+    return rc;
+}
+
+
+/**
+ * Loads the fixups at the given address and performs endian
+ * conversion if necessary.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param   pModMachO       The Mach-O module interpreter instance.
+ * @param   offFixups       The file offset of the fixups.
+ * @param   cFixups         The number of fixups to load.
+ * @param   ppaFixups       Where to put the pointer to the allocated fixup array.
+ */
+static int  kldrModMachOLoadFixups(PKLDRMODMACHO pModMachO, KLDRFOFF offFixups, KU32 cFixups, macho_relocation_info_t **ppaFixups)
+{
+    macho_relocation_info_t *paFixups;
+    KSIZE cbFixups;
+    int rc;
+
+    /* allocate the memory. */
+    cbFixups = cFixups * sizeof(*paFixups);
+    KLDRMODMACHO_CHECK_RETURN(cbFixups / sizeof(*paFixups) == cFixups, KLDR_ERR_SIZE_OVERFLOW);
+    paFixups = (macho_relocation_info_t *)kHlpAlloc(cbFixups);
+    if (!paFixups)
+        return KERR_NO_MEMORY;
+
+    /* read the fixups. */
+    rc = kRdrRead(pModMachO->pMod->pRdr, paFixups, cbFixups, offFixups);
+    if (!rc)
+    {
+        *ppaFixups = paFixups;
+
+        /* do endian conversion if necessary. */
+        if (    pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+            ||  pModMachO->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+        {
+            KU32 iFixup;
+            for (iFixup = 0; iFixup < cFixups; iFixup++)
+            {
+                KU32 *pu32 = (KU32 *)&paFixups[iFixup];
+                pu32[0] = K_E2E_U32(pu32[0]);
+                pu32[1] = K_E2E_U32(pu32[1]);
+            }
+        }
+    }
+    else
+        kHlpFree(paFixups);
+    return rc;
+}
+
+
+/**
+ * Maps the virgin file bits into memory if not already done.
+ *
+ * @returns 0 on success, non-zero kLdr status code on failure.
+ * @param   pModMachO       The Mach-O module interpreter instance.
+ */
+static int kldrModMachOMapVirginBits(PKLDRMODMACHO pModMachO)
+{
+    int rc = 0;
+    if (!pModMachO->pvBits)
+        rc = kRdrAllMap(pModMachO->pMod->pRdr, &pModMachO->pvBits);
+    return rc;
+}
+
+
+/** @copydoc kLdrModCallInit */
+static int kldrModMachOCallInit(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+    /* later */
+    K_NOREF(pMod);
+    K_NOREF(pvMapping);
+    K_NOREF(uHandle);
+    return 0;
+}
+
+
+/** @copydoc kLdrModCallTerm */
+static int kldrModMachOCallTerm(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle)
+{
+    /* later */
+    K_NOREF(pMod);
+    K_NOREF(pvMapping);
+    K_NOREF(uHandle);
+    return 0;
+}
+
+
+/** @copydoc kLdrModCallThread */
+static int kldrModMachOCallThread(PKLDRMOD pMod, void *pvMapping, KUPTR uHandle, unsigned fAttachingOrDetaching)
+{
+    /* Relevant for Mach-O? */
+    K_NOREF(pMod);
+    K_NOREF(pvMapping);
+    K_NOREF(uHandle);
+    K_NOREF(fAttachingOrDetaching);
+    return 0;
+}
+
+
+/** @copydoc kLdrModSize */
+static KLDRADDR kldrModMachOSize(PKLDRMOD pMod)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    return pModMachO->cbImage;
+}
+
+
+/** @copydoc kLdrModGetBits */
+static int kldrModMachOGetBits(PKLDRMOD pMod, void *pvBits, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+    PKLDRMODMACHO  pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    KU32        i;
+    int         rc;
+
+    if (!pModMachO->fCanLoad)
+        return KLDR_ERR_TODO;
+
+    /*
+     * Zero the entire buffer first to simplify things.
+     */
+    kHlpMemSet(pvBits, 0, (KSIZE)pModMachO->cbImage);
+
+    /*
+     * When possible use the segment table to load the data.
+     */
+    for (i = 0; i < pMod->cSegments; i++)
+    {
+        /* skip it? */
+        if (    pMod->aSegments[i].cbFile == -1
+            ||  pMod->aSegments[i].offFile == -1
+            ||  pMod->aSegments[i].LinkAddress == NIL_KLDRADDR
+            ||  !pMod->aSegments[i].Alignment)
+            continue;
+        rc = kRdrRead(pMod->pRdr,
+                      (KU8 *)pvBits + pMod->aSegments[i].RVA,
+                      pMod->aSegments[i].cbFile,
+                      pMod->aSegments[i].offFile);
+        if (rc)
+            return rc;
+    }
+
+    /*
+     * Perform relocations.
+     */
+    return kldrModMachORelocateBits(pMod, pvBits, BaseAddress, pModMachO->LinkAddress, pfnGetImport, pvUser);
+}
+
+
+/** @copydoc kLdrModRelocateBits */
+static int kldrModMachORelocateBits(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress,
+                                    PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser)
+{
+    PKLDRMODMACHO pModMachO = (PKLDRMODMACHO)pMod->pvData;
+    int rc;
+    K_NOREF(OldBaseAddress);
+
+    /*
+     * Call workers to do the jobs.
+     */
+    if (pModMachO->Hdr.filetype == MH_OBJECT)
+    {
+        rc = kldrModMachOObjDoImports(pModMachO, NewBaseAddress, pfnGetImport, pvUser);
+        if (!rc)
+            rc = kldrModMachOObjDoFixups(pModMachO, pvBits, NewBaseAddress);
+
+    }
+    else
+        rc = KLDR_ERR_TODO;
+    /*{
+        rc = kldrModMachODoFixups(pModMachO, pvBits, NewBaseAddress, OldBaseAddress, pfnGetImport, pvUser);
+        if (!rc)
+            rc = kldrModMachODoImports(pModMachO, pvBits, pfnGetImport, pvUser);
+    }*/
+
+    /*
+     * Construct the global offset table if necessary, it's always the last
+     * segment when present.
+     */
+    if (!rc && pModMachO->fMakeGot)
+        rc = kldrModMachOMakeGOT(pModMachO, pvBits, NewBaseAddress);
+
+    return rc;
+}
+
+
+/**
+ * Builds the GOT.
+ *
+ * Assumes the symbol table has all external symbols resolved correctly and that
+ * the bits has been cleared up front.
+ */
+static int kldrModMachOMakeGOT(PKLDRMODMACHO pModMachO, void *pvBits, KLDRADDR NewBaseAddress)
+{
+    KU32  iSym = pModMachO->cSymbols;
+    if (    pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+        ||  pModMachO->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+    {
+        macho_nlist_32_t const *paSyms = (macho_nlist_32_t const *)pModMachO->pvaSymbols;
+        KU32 *paGOT = (KU32 *)((KU8 *)pvBits + pModMachO->GotRVA);
+        while (iSym-- > 0)
+            switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+            {
+                case MACHO_N_SECT:
+                {
+                    PKLDRMODMACHOSECT pSymSect;
+                    KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+                    pSymSect = &pModMachO->paSections[paSyms[iSym].n_sect - 1];
+                    paGOT[iSym] = (KU32)(paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress);
+                    break;
+                }
+
+                case MACHO_N_UNDF:
+                case MACHO_N_ABS:
+                    paGOT[iSym] = paSyms[iSym].n_value;
+                    break;
+            }
+    }
+    else
+    {
+        macho_nlist_64_t const *paSyms = (macho_nlist_64_t const *)pModMachO->pvaSymbols;
+        KU64 *paGOT = (KU64 *)((KU8 *)pvBits + pModMachO->GotRVA);
+        while (iSym-- > 0)
+        {
+            switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+            {
+                case MACHO_N_SECT:
+                {
+                    PKLDRMODMACHOSECT pSymSect;
+                    KLDRMODMACHO_CHECK_RETURN((KU32)paSyms[iSym].n_sect - 1 <= pModMachO->cSections, KLDR_ERR_MACHO_BAD_SYMBOL);
+                    pSymSect = &pModMachO->paSections[paSyms[iSym].n_sect - 1];
+                    paGOT[iSym] = paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+                    break;
+                }
+
+                case MACHO_N_UNDF:
+                case MACHO_N_ABS:
+                    paGOT[iSym] = paSyms[iSym].n_value;
+                    break;
+            }
+        }
+
+        if (pModMachO->JmpStubsRVA != NIL_KLDRADDR)
+        {
+            iSym = pModMachO->cSymbols;
+            switch (pModMachO->Hdr.cputype)
+            {
+                /*
+                 * AMD64 is simple since the GOT and the indirect jmps are parallel
+                 * arrays with entries of the same size. The relative offset will
+                 * be the the same for each entry, kind of nice. :-)
+                 */
+                case CPU_TYPE_X86_64:
+                {
+                    KU64   *paJmps = (KU64 *)((KU8 *)pvBits + pModMachO->JmpStubsRVA);
+                    KI32    off;
+                    KU64    u64Tmpl;
+                    union
+                    {
+                        KU8     ab[8];
+                        KU64    u64;
+                    }       Tmpl;
+
+                    /* create the template. */
+                    off = (KI32)(pModMachO->GotRVA - (pModMachO->JmpStubsRVA + 6));
+                    Tmpl.ab[0] = 0xff; /* jmp [GOT-entry wrt RIP] */
+                    Tmpl.ab[1] = 0x25;
+                    Tmpl.ab[2] =  off        & 0xff;
+                    Tmpl.ab[3] = (off >>  8) & 0xff;
+                    Tmpl.ab[4] = (off >> 16) & 0xff;
+                    Tmpl.ab[5] = (off >> 24) & 0xff;
+                    Tmpl.ab[6] = 0xcc;
+                    Tmpl.ab[7] = 0xcc;
+                    u64Tmpl = Tmpl.u64;
+
+                    /* copy the template to every jmp table entry. */
+                    while (iSym-- > 0)
+                        paJmps[iSym] = u64Tmpl;
+                    break;
+                }
+
+                default:
+                    KLDRMODMACHO_FAILED_RETURN(KLDR_ERR_TODO);
+            }
+        }
+    }
+    return 0;
+}
+
+
+/**
+ * The Mach-O module interpreter method table.
+ */
+KLDRMODOPS g_kLdrModMachOOps =
+{
+    "Mach-O",
+    NULL,
+    kldrModMachOCreate,
+    kldrModMachODestroy,
+    kldrModMachOQuerySymbol,
+    kldrModMachOEnumSymbols,
+    kldrModMachOGetImport,
+    kldrModMachONumberOfImports,
+    NULL /* can execute one is optional */,
+    kldrModMachOGetStackInfo,
+    kldrModMachOQueryMainEntrypoint,
+    kldrModMachOQueryImageUuid,
+    NULL,
+    NULL,
+    kldrModMachOEnumDbgInfo,
+    kldrModMachOHasDbgInfo,
+    kldrModMachOMap,
+    kldrModMachOUnmap,
+    kldrModMachOAllocTLS,
+    kldrModMachOFreeTLS,
+    kldrModMachOReload,
+    kldrModMachOFixupMapping,
+    kldrModMachOCallInit,
+    kldrModMachOCallTerm,
+    kldrModMachOCallThread,
+    kldrModMachOSize,
+    kldrModMachOGetBits,
+    kldrModMachORelocateBits,
+    NULL, /** @todo mostly done */
+    42 /* the end */
+};
+
