summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/ldr
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/common/ldr
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/common/ldr')
-rw-r--r--src/VBox/Runtime/common/ldr/Makefile.kup0
-rw-r--r--src/VBox/Runtime/common/ldr/ldr.cpp186
-rw-r--r--src/VBox/Runtime/common/ldr/ldrELF.cpp381
-rw-r--r--src/VBox/Runtime/common/ldr/ldrELFRelocatable.cpp.h3163
-rw-r--r--src/VBox/Runtime/common/ldr/ldrEx.cpp773
-rw-r--r--src/VBox/Runtime/common/ldr/ldrFile.cpp317
-rw-r--r--src/VBox/Runtime/common/ldr/ldrLX.cpp3121
-rw-r--r--src/VBox/Runtime/common/ldr/ldrMachO.cpp5734
-rw-r--r--src/VBox/Runtime/common/ldr/ldrMemory.cpp336
-rw-r--r--src/VBox/Runtime/common/ldr/ldrNative.cpp336
-rw-r--r--src/VBox/Runtime/common/ldr/ldrPE.cpp5194
-rw-r--r--src/VBox/Runtime/common/ldr/ldrVfsFile.cpp293
12 files changed, 19834 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/ldr/Makefile.kup b/src/VBox/Runtime/common/ldr/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/Makefile.kup
diff --git a/src/VBox/Runtime/common/ldr/ldr.cpp b/src/VBox/Runtime/common/ldr/ldr.cpp
new file mode 100644
index 00000000..0d697974
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/ldr.cpp
@@ -0,0 +1,186 @@
+/* $Id: ldr.cpp $ */
+/** @file
+ * IPRT - Binary Image Loader.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include "internal/ldr.h"
+
+
+RTDECL(int) RTLdrGetSymbol(RTLDRMOD hLdrMod, const char *pszSymbol, void **ppvValue)
+{
+ LogFlow(("RTLdrGetSymbol: hLdrMod=%RTldrm pszSymbol=%p:{%s} ppvValue=%p\n",
+ hLdrMod, pszSymbol, pszSymbol, ppvValue));
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ AssertMsgReturn(pszSymbol, ("pszSymbol=%p\n", pszSymbol), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppvValue, VERR_INVALID_POINTER);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ //AssertMsgReturn(pMod->eState == LDR_STATE_OPENED, ("eState=%d\n", pMod->eState), VERR_WRONG_ORDER);
+
+ /*
+ * Do it.
+ */
+ int rc;
+ if (pMod->pOps->pfnGetSymbol)
+ rc = pMod->pOps->pfnGetSymbol(pMod, pszSymbol, ppvValue);
+ else
+ {
+ RTUINTPTR Value = 0;
+ rc = pMod->pOps->pfnGetSymbolEx(pMod, NULL, 0, UINT32_MAX, pszSymbol, &Value);
+ if (RT_SUCCESS(rc))
+ {
+ *ppvValue = (void *)(uintptr_t)Value;
+ if ((uintptr_t)*ppvValue != Value)
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+ LogFlow(("RTLdrGetSymbol: return %Rrc *ppvValue=%p\n", rc, *ppvValue));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrGetSymbol);
+
+
+RTDECL(PFNRT) RTLdrGetFunction(RTLDRMOD hLdrMod, const char *pszSymbol)
+{
+ PFNRT pfn;
+ int rc = RTLdrGetSymbol(hLdrMod, pszSymbol, (void **)&pfn);
+ if (RT_SUCCESS(rc))
+ return pfn;
+ return NULL;
+}
+RT_EXPORT_SYMBOL(RTLdrGetFunction);
+
+
+RTDECL(RTLDRFMT) RTLdrGetFormat(RTLDRMOD hLdrMod)
+{
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), RTLDRFMT_INVALID);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ return pMod->enmFormat;
+}
+RT_EXPORT_SYMBOL(RTLdrGetFormat);
+
+
+RTDECL(RTLDRTYPE) RTLdrGetType(RTLDRMOD hLdrMod)
+{
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), RTLDRTYPE_INVALID);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ return pMod->enmType;
+}
+RT_EXPORT_SYMBOL(RTLdrGetType);
+
+
+RTDECL(RTLDRENDIAN) RTLdrGetEndian(RTLDRMOD hLdrMod)
+{
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), RTLDRENDIAN_INVALID);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ return pMod->enmEndian;
+}
+RT_EXPORT_SYMBOL(RTLdrGetEndian);
+
+
+RTDECL(RTLDRARCH) RTLdrGetArch(RTLDRMOD hLdrMod)
+{
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), RTLDRARCH_INVALID);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ return pMod->enmArch;
+}
+RT_EXPORT_SYMBOL(RTLdrGetArch);
+
+
+RTDECL(int) RTLdrClose(RTLDRMOD hLdrMod)
+{
+ LogFlow(("RTLdrClose: hLdrMod=%RTldrm\n", hLdrMod));
+
+ /*
+ * Validate input.
+ */
+ if (hLdrMod == NIL_RTLDRMOD)
+ return VINF_SUCCESS;
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ //AssertMsgReturn(pMod->eState == LDR_STATE_OPENED, ("eState=%d\n", pMod->eState), VERR_WRONG_ORDER);
+
+ /*
+ * Do it.
+ */
+ int rc = pMod->pOps->pfnClose(pMod);
+ AssertRC(rc);
+ pMod->eState = LDR_STATE_INVALID;
+ pMod->u32Magic++;
+ if (pMod->pReader)
+ {
+ rc = pMod->pReader->pfnDestroy(pMod->pReader);
+ AssertRC(rc);
+ pMod->pReader = NULL;
+ }
+ RTMemFree(pMod);
+
+ LogFlow(("RTLdrClose: returns VINF_SUCCESS\n"));
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTLdrClose);
+
+
+RTDECL(RTLDRARCH) RTLdrGetHostArch(void)
+{
+#if defined(RT_ARCH_AMD64)
+ RTLDRARCH enmArch = RTLDRARCH_AMD64;
+#elif defined(RT_ARCH_X86)
+ RTLDRARCH enmArch = RTLDRARCH_X86_32;
+#elif defined(RT_ARCH_ARM) || defined(RT_ARCH_ARM32)
+ RTLDRARCH enmArch = RTLDRARCH_ARM32;
+#elif defined(RT_ARCH_ARM64)
+ RTLDRARCH enmArch = RTLDRARCH_ARM64;
+#else
+ RTLDRARCH enmArch = RTLDRARCH_WHATEVER;
+#endif
+ return enmArch;
+}
+RT_EXPORT_SYMBOL(RTLdrGetHostArch);
+
diff --git a/src/VBox/Runtime/common/ldr/ldrELF.cpp b/src/VBox/Runtime/common/ldr/ldrELF.cpp
new file mode 100644
index 00000000..ad380088
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/ldrELF.cpp
@@ -0,0 +1,381 @@
+/* $Id: ldrELF.cpp $ */
+/** @file
+ * IPRT - Binary Image Loader, Executable and Linker Format (ELF).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/dbg.h>
+#include <iprt/string.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/err.h>
+#include <iprt/crypto/digest.h>
+#include <iprt/formats/elf32.h>
+#include <iprt/formats/elf64.h>
+#include <iprt/formats/elf-i386.h>
+#include <iprt/formats/elf-amd64.h>
+#include "internal/ldr.h"
+#include "internal/dbgmod.h"
+
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Finds an ELF symbol table string. */
+#define ELF_STR(pHdrs, iStr) ((pHdrs)->Rel.pStr + (iStr))
+/** Finds an ELF symbol table string. */
+#define ELF_DYN_STR(pHdrs, iStr) ((pHdrs)->Dyn.pStr + (iStr))
+/** Finds an ELF section header string. */
+#define ELF_SH_STR(pHdrs, iStr) ((pHdrs)->pShStr + (iStr))
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Magic string for RTLDRLNXMODSIG::achMagic */
+#define RTLDRLNXMODSIG_MAGIC "~Module signature appended~\n"
+AssertCompile(sizeof(RTLDRLNXMODSIG_MAGIC) == 29);
+
+/**
+ * Linux kernel module signature footer - found at the end of the file.
+ */
+typedef struct RTLDRLNXMODSIG
+{
+ /** Zero. */
+ uint8_t bAlgo;
+ /** Zero. */
+ uint8_t bHash;
+ /** Signature type (RTLDRLNXMODSIG_TYPE_PKCS7). */
+ uint8_t bType;
+ /** Zero. */
+ uint8_t cbSignerName;
+ /** Zero. */
+ uint8_t cbKeyId;
+ /** Zero padding. */
+ uint8_t abReserved[3];
+ /** The length of the signature preceeding this footer structure. */
+ uint32_t cbSignature;
+ /** Magic value identifying this structure. */
+ char achMagic[sizeof(RTLDRLNXMODSIG_MAGIC) - 1];
+} RTLDRLNXMODSIG;
+typedef RTLDRLNXMODSIG *PRTLDRLNXMODSIG;
+typedef RTLDRLNXMODSIG const *PCRTLDRLNXMODSIG;
+/** Signature type. */
+#define RTLDRLNXMODSIG_TYPE_PKCS7 2
+
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int rtLdrELFLnxKModQueryPropIsSigned(PRTLDRREADER pReader, bool *pfRet);
+static int rtLdrELFLnxKModQueryPropPkcs7SignedData(PRTLDRREADER pReader, void *pvBuf, size_t cbBuf, size_t *pcbRet);
+static DECLCALLBACK(int) rtldrELFLnxKModHashImage(PRTLDRMODINTERNAL pMod, RTDIGESTTYPE enmDigest, uint8_t *pabHash, size_t cbHash);
+#ifdef LOG_ENABLED
+static const char *rtldrElfGetShdrType(uint32_t iType);
+static const char *rtldrElfGetPhdrType(uint32_t iType);
+#endif
+
+
+/* Select ELF mode and include the template. */
+#define ELF_MODE 32
+#define Elf_Reloc Elf_Rel
+#include "ldrELFRelocatable.cpp.h"
+#undef ELF_MODE
+#undef Elf_Reloc
+
+
+#define ELF_MODE 64
+#define Elf_Reloc Elf_Rela
+#include "ldrELFRelocatable.cpp.h"
+#undef ELF_MODE
+#undef Elf_Reloc
+
+
+#ifdef LOG_ENABLED
+
+/**
+ * Gets the section type.
+ *
+ * @returns Pointer to read only string.
+ * @param iType The section type index.
+ */
+static const char *rtldrElfGetShdrType(uint32_t iType)
+{
+ switch (iType)
+ {
+ RT_CASE_RET_STR(SHT_NULL);
+ RT_CASE_RET_STR(SHT_PROGBITS);
+ RT_CASE_RET_STR(SHT_SYMTAB);
+ RT_CASE_RET_STR(SHT_STRTAB);
+ RT_CASE_RET_STR(SHT_RELA);
+ RT_CASE_RET_STR(SHT_HASH);
+ RT_CASE_RET_STR(SHT_DYNAMIC);
+ RT_CASE_RET_STR(SHT_NOTE);
+ RT_CASE_RET_STR(SHT_NOBITS);
+ RT_CASE_RET_STR(SHT_REL);
+ RT_CASE_RET_STR(SHT_SHLIB);
+ RT_CASE_RET_STR(SHT_DYNSYM);
+ default:
+ return "";
+ }
+}
+
+/**
+ * Gets the program header type.
+ *
+ * @returns Pointer to read only string.
+ * @param iType The section type index.
+ */
+static const char *rtldrElfGetPhdrType(uint32_t iType)
+{
+ switch (iType)
+ {
+ RT_CASE_RET_STR(PT_NULL);
+ RT_CASE_RET_STR(PT_LOAD);
+ RT_CASE_RET_STR(PT_DYNAMIC);
+ RT_CASE_RET_STR(PT_INTERP);
+ RT_CASE_RET_STR(PT_NOTE);
+ RT_CASE_RET_STR(PT_SHLIB);
+ RT_CASE_RET_STR(PT_PHDR);
+ RT_CASE_RET_STR(PT_TLS);
+ RT_CASE_RET_STR(PT_GNU_EH_FRAME);
+ RT_CASE_RET_STR(PT_GNU_STACK);
+ RT_CASE_RET_STR(PT_GNU_RELRO);
+ RT_CASE_RET_STR(PT_GNU_PROPERTY);
+ default:
+ return "";
+ }
+}
+
+#endif /* LOG_ENABLED*/
+
+/**
+ * Reads in what migt be a linux kernel module signature footer.
+ */
+static int rtLdrELFLnxKModReadFooter(PRTLDRREADER pReader, PRTLDRLNXMODSIG pSigFooter, uint64_t *pcbFile)
+{
+ /*
+ * Look for the linux module signature at the end of the file.
+ * This should be safe to read w/o any size checking as it is smaller than the elf header.
+ */
+ uint64_t cbFile = pReader->pfnSize(pReader);
+ *pcbFile = cbFile;
+
+ AssertCompile(sizeof(*pSigFooter) <= sizeof(Elf32_Ehdr));
+ return pReader->pfnRead(pReader, pSigFooter, sizeof(*pSigFooter), cbFile - sizeof(*pSigFooter));
+}
+
+
+/**
+ * Check that a linux kernel module signature footer is valid.
+ */
+static bool rtLdrELFLnxKModIsFooterValid(PCRTLDRLNXMODSIG pSigFooter, uint64_t cbFile)
+{
+ if (memcmp(pSigFooter->achMagic, RTLDRLNXMODSIG_MAGIC, sizeof(pSigFooter->achMagic)) == 0)
+ {
+ uint32_t const cbSignature = RT_N2H_U32(pSigFooter->cbSignature);
+ if (cbSignature > 32 && cbSignature + sizeof(*pSigFooter) < cbFile)
+ return pSigFooter->bAlgo == 0
+ && pSigFooter->bHash == 0
+ && pSigFooter->cbSignerName == 0
+ && pSigFooter->cbKeyId == 0;
+ }
+ return false;
+}
+
+
+/**
+ * Handles the linux kernel module signature part of RTLDRPROP_IS_SIGNED
+ * queries.
+ */
+static int rtLdrELFLnxKModQueryPropIsSigned(PRTLDRREADER pReader, bool *pfRet)
+{
+ *pfRet = false;
+ AssertReturn(pReader, VERR_INVALID_STATE);
+
+ uint64_t cbFile;
+ RTLDRLNXMODSIG SigFooter;
+ int rc = rtLdrELFLnxKModReadFooter(pReader, &SigFooter, &cbFile);
+ if (RT_SUCCESS(rc))
+ *pfRet = rtLdrELFLnxKModIsFooterValid(&SigFooter, cbFile);
+ return rc;
+}
+
+
+/**
+ * Handles the linux kernel module signature part of RTLDRPROP_IS_SIGNED
+ * queries.
+ */
+static int rtLdrELFLnxKModQueryPropPkcs7SignedData(PRTLDRREADER pReader, void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ AssertReturn(pReader, VERR_INVALID_STATE);
+
+ uint64_t cbFile;
+ RTLDRLNXMODSIG SigFooter;
+ int rc = rtLdrELFLnxKModReadFooter(pReader, &SigFooter, &cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ if ( rtLdrELFLnxKModIsFooterValid(&SigFooter, cbFile)
+ && SigFooter.bType == RTLDRLNXMODSIG_TYPE_PKCS7)
+ {
+ uint32_t const cbSignature = RT_N2H_U32(SigFooter.cbSignature);
+ *pcbRet = cbSignature;
+ if (cbSignature <= cbBuf)
+ rc = pReader->pfnRead(pReader, pvBuf, cbSignature, cbFile - sizeof(SigFooter) - cbSignature);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnHashImage,
+ * Handles the linux kernel module signatures.}
+ */
+static DECLCALLBACK(int) rtldrELFLnxKModHashImage(PRTLDRMODINTERNAL pMod, RTDIGESTTYPE enmDigest, uint8_t *pabHash, size_t cbHash)
+{
+ PRTLDRREADER pReader = pMod->pReader;
+ AssertReturn(pReader, VERR_INVALID_STATE);
+
+ /*
+ * Get the file size and subtract any linux kernel module signature from it
+ * since it's not part of the hash.
+ */
+ uint64_t cbFile;
+ RTLDRLNXMODSIG SigFooter;
+ int rc = rtLdrELFLnxKModReadFooter(pReader, &SigFooter, &cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ if (rtLdrELFLnxKModIsFooterValid(&SigFooter, cbFile))
+ cbFile -= sizeof(SigFooter) + RT_N2H_U32(SigFooter.cbSignature);
+
+ /*
+ * Now hash the file.
+ */
+ RTCRDIGEST hDigest;
+ rc = RTCrDigestCreateByType(&hDigest, enmDigest);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cbBuf = _64K;
+ void *pvBuf = RTMemTmpAlloc(_64K);
+ void *pvBufFree = pvBuf;
+ if (!pvBuf)
+ {
+ cbBuf = _4K;
+ pvBuf = alloca(_4K);
+ }
+
+ for (uint64_t offFile = 0; offFile < cbFile; )
+ {
+ uint64_t cbLeft = cbFile - offFile;
+ uint32_t cbToRead = cbLeft >= cbBuf ? cbBuf : (uint32_t)cbLeft;
+ rc = pReader->pfnRead(pReader, pvBuf, cbToRead, offFile);
+ AssertRCBreak(rc);
+
+ rc = RTCrDigestUpdate(hDigest, pvBuf, cbToRead);
+ offFile += cbToRead;
+ AssertRCBreak(rc);
+ }
+
+ RTMemTmpFree(pvBufFree);
+
+ if (RT_SUCCESS(rc))
+ rc = RTCrDigestFinal(hDigest, pabHash, cbHash);
+ RTCrDigestRelease(hDigest);
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Open an ELF image.
+ *
+ * @returns iprt status code.
+ * @param pReader The loader reader instance which will provide the raw image bits.
+ * @param fFlags Reserved, MBZ.
+ * @param enmArch Architecture specifier.
+ * @param phLdrMod Where to store the handle.
+ * @param pErrInfo Where to return extended error information. Optional.
+ */
+DECLHIDDEN(int) rtldrELFOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
+{
+ const char *pszLogName = pReader->pfnLogName(pReader); NOREF(pszLogName);
+
+ /*
+ * Read the ident to decide if this is 32-bit or 64-bit
+ * and worth dealing with.
+ */
+ uint8_t e_ident[EI_NIDENT];
+ int rc = pReader->pfnRead(pReader, &e_ident, sizeof(e_ident), 0);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if ( e_ident[EI_MAG0] != ELFMAG0
+ || e_ident[EI_MAG1] != ELFMAG1
+ || e_ident[EI_MAG2] != ELFMAG2
+ || e_ident[EI_MAG3] != ELFMAG3
+ || ( e_ident[EI_CLASS] != ELFCLASS32
+ && e_ident[EI_CLASS] != ELFCLASS64)
+ )
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Unsupported/invalid ident %.*Rhxs", pszLogName, sizeof(e_ident), e_ident);
+
+ if (e_ident[EI_DATA] != ELFDATA2LSB)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRELF_ODD_ENDIAN,
+ "%s: ELF endian %x is unsupported", pszLogName, e_ident[EI_DATA]);
+
+ if (e_ident[EI_CLASS] == ELFCLASS32)
+ rc = rtldrELF32Open(pReader, fFlags, enmArch, phLdrMod, pErrInfo);
+ else
+ rc = rtldrELF64Open(pReader, fFlags, enmArch, phLdrMod, pErrInfo);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/ldr/ldrELFRelocatable.cpp.h b/src/VBox/Runtime/common/ldr/ldrELFRelocatable.cpp.h
new file mode 100644
index 00000000..1eb33be8
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/ldrELFRelocatable.cpp.h
@@ -0,0 +1,3163 @@
+/* $Id: ldrELFRelocatable.cpp.h $ */
+/** @file
+ * IPRT - Binary Image Loader, Template for ELF Relocatable Images.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*******************************************************************************
+* Defined Constants And Macros *
+*******************************************************************************/
+#if ELF_MODE == 32
+# define RTLDRELF_NAME(name) rtldrELF32##name
+# define RTLDRELF_SUFF(name) name##32
+# define RTLDRELF_MID(pre,suff) pre##32##suff
+# define FMT_ELF_ADDR "%08RX32"
+# define FMT_ELF_ADDR7 "%07RX32"
+# define FMT_ELF_HALF "%04RX16"
+# define FMT_ELF_OFF "%08RX32"
+# define FMT_ELF_SIZE "%08RX32"
+# define FMT_ELF_SWORD "%RI32"
+# define FMT_ELF_WORD "%08RX32"
+# define FMT_ELF_XWORD "%08RX32"
+# define FMT_ELF_SXWORD "%RI32"
+# define Elf_Xword Elf32_Word
+# define Elf_Sxword Elf32_Sword
+
+#elif ELF_MODE == 64
+# define RTLDRELF_NAME(name) rtldrELF64##name
+# define RTLDRELF_SUFF(name) name##64
+# define RTLDRELF_MID(pre,suff) pre##64##suff
+# define FMT_ELF_ADDR "%016RX64"
+# define FMT_ELF_ADDR7 "%08RX64"
+# define FMT_ELF_HALF "%04RX16"
+# define FMT_ELF_SHALF "%RI16"
+# define FMT_ELF_OFF "%016RX64"
+# define FMT_ELF_SIZE "%016RX64"
+# define FMT_ELF_SWORD "%RI32"
+# define FMT_ELF_WORD "%08RX32"
+# define FMT_ELF_XWORD "%016RX64"
+# define FMT_ELF_SXWORD "%RI64"
+# define Elf_Xword Elf64_Xword
+# define Elf_Sxword Elf64_Sxword
+#endif
+
+#define Elf_Ehdr RTLDRELF_MID(Elf,_Ehdr)
+#define Elf_Phdr RTLDRELF_MID(Elf,_Phdr)
+#define Elf_Shdr RTLDRELF_MID(Elf,_Shdr)
+#define Elf_Sym RTLDRELF_MID(Elf,_Sym)
+#define Elf_Rel RTLDRELF_MID(Elf,_Rel)
+#define Elf_Rela RTLDRELF_MID(Elf,_Rela)
+#define Elf_Nhdr RTLDRELF_MID(Elf,_Nhdr)
+#define Elf_Dyn RTLDRELF_MID(Elf,_Dyn)
+#define Elf_Addr RTLDRELF_MID(Elf,_Addr)
+#define Elf_Half RTLDRELF_MID(Elf,_Half)
+#define Elf_Off RTLDRELF_MID(Elf,_Off)
+#define Elf_Size RTLDRELF_MID(Elf,_Size)
+#define Elf_Sword RTLDRELF_MID(Elf,_Sword)
+#define Elf_Word RTLDRELF_MID(Elf,_Word)
+
+#define RTLDRMODELF RTLDRELF_MID(RTLDRMODELF,RT_NOTHING)
+#define PRTLDRMODELF RTLDRELF_MID(PRTLDRMODELF,RT_NOTHING)
+
+#define RTLDRMODELFSHX RTLDRELF_MID(RTLDRMODELFSHX,RT_NOTHING)
+#define PRTLDRMODELFSHX RTLDRELF_MID(PRTLDRMODELFSHX,RT_NOTHING)
+
+#define ELF_R_SYM(info) RTLDRELF_MID(ELF,_R_SYM)(info)
+#define ELF_R_TYPE(info) RTLDRELF_MID(ELF,_R_TYPE)(info)
+#define ELF_R_INFO(sym, type) RTLDRELF_MID(ELF,_R_INFO)(sym, type)
+
+#define ELF_ST_BIND(info) RTLDRELF_MID(ELF,_ST_BIND)(info)
+
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Extra section info.
+ */
+typedef struct RTLDRMODELFSHX
+{
+ /** The corresponding program header. */
+ uint16_t idxPhdr;
+ /** The corresponding dynamic section entry (address). */
+ uint16_t idxDt;
+ /** The DT tag. */
+ uint32_t uDtTag;
+} RTLDRMODELFSHX;
+typedef RTLDRMODELFSHX *PRTLDRMODELFSHX;
+
+/**
+ * The ELF loader structure.
+ */
+typedef struct RTLDRMODELF
+{
+ /** Core module structure. */
+ RTLDRMODINTERNAL Core;
+ /** Pointer to readonly mapping of the image bits.
+ * This mapping is provided by the pReader. */
+ const void *pvBits;
+
+ /** The ELF header. */
+ Elf_Ehdr Ehdr;
+ /** Pointer to our copy of the section headers with sh_addr as RVAs.
+ * The virtual addresses in this array is the 0 based assignments we've given the image.
+ * Not valid if the image is DONE. */
+ Elf_Shdr *paShdrs;
+ /** Unmodified section headers (allocated after paShdrs, so no need to free).
+ * Not valid if the image is DONE. */
+ Elf_Shdr const *paOrgShdrs;
+ /** Runs parallel to paShdrs and is part of the same allocation. */
+ PRTLDRMODELFSHX paShdrExtras;
+ /** Base section number, either 1 or zero depending on whether we've
+ * re-used the NULL entry for .elf.headers in ET_EXEC/ET_DYN. */
+ unsigned iFirstSect;
+ /** Set if the SHF_ALLOC section headers are in order of sh_addr. */
+ bool fShdrInOrder;
+ /** The size of the loaded image. */
+ size_t cbImage;
+
+ /** The image base address if it's an EXEC or DYN image. */
+ Elf_Addr LinkAddress;
+
+ struct
+ {
+ /** The symbol section index. */
+ unsigned iSymSh;
+ /** Number of symbols in the table. */
+ unsigned cSyms;
+ /** Pointer to symbol table within RTLDRMODELF::pvBits. */
+ const Elf_Sym *paSyms;
+
+ /** The string section index. */
+ unsigned iStrSh;
+ /** Size of the string table. */
+ unsigned cbStr;
+ /** Pointer to string table within RTLDRMODELF::pvBits. */
+ const char *pStr;
+ } Rel /**< Regular symbols and strings. */
+ , Dyn /**< Dynamic symbols and strings. */;
+
+ /** Pointer to section header string table within RTLDRMODELF::pvBits. */
+ const char *pShStr;
+ /** Size of the section header string table. */
+ unsigned cbShStr;
+
+ /** The '.eh_frame' section index. Zero if not searched for, ~0U if not found. */
+ unsigned iShEhFrame;
+ /** The '.eh_frame_hdr' section index. Zero if not searched for, ~0U if not found. */
+ unsigned iShEhFrameHdr;
+
+ /** The '.dynamic' / SHT_DYNAMIC section index. ~0U if not present. */
+ unsigned iShDynamic;
+ /** Number of entries in paDynamic. */
+ unsigned cDynamic;
+ /** The dynamic section (NULL for ET_REL). */
+ Elf_Dyn *paDynamic;
+ /** Program headers (NULL for ET_REL). */
+ Elf_Phdr *paPhdrs;
+
+ /** Info extracted from PT_DYNAMIC and the program headers. */
+ struct
+ {
+ /** DT_RELA/DT_REL. */
+ Elf_Addr uPtrRelocs;
+ /** DT_RELASZ/DT_RELSZ. */
+ Elf_Xword cbRelocs;
+ /** Non-zero if we've seen DT_RELAENT/DT_RELENT. */
+ unsigned cbRelocEntry;
+ /** DT_RELA or DT_REL. */
+ unsigned uRelocType;
+ /** The index of the section header matching DT_RELA/DT_REL. */
+ unsigned idxShRelocs;
+
+ /** DT_JMPREL. */
+ Elf_Addr uPtrJmpRelocs;
+ /** DT_PLTRELSZ. */
+ Elf_Xword cbJmpRelocs;
+ /** DT_RELA or DT_REL (if we've seen DT_PLTREL). */
+ unsigned uJmpRelocType;
+ /** The index of the section header matching DT_JMPREL. */
+ unsigned idxShJmpRelocs;
+ } DynInfo;
+} RTLDRMODELF;
+/** Pointer to an ELF module instance. */
+typedef RTLDRMODELF *PRTLDRMODELF;
+
+
+/**
+ * Maps the image bits into memory and resolve pointers into it.
+ *
+ * @returns iprt status code.
+ * @param pModElf The ELF loader module instance data.
+ * @param fNeedsBits Set if we actually need the pvBits member.
+ * If we don't, we can simply read the string and symbol sections, thus saving memory.
+ */
+static int RTLDRELF_NAME(MapBits)(PRTLDRMODELF pModElf, bool fNeedsBits)
+{
+ NOREF(fNeedsBits);
+ if (pModElf->pvBits)
+ return VINF_SUCCESS;
+ int rc = pModElf->Core.pReader->pfnMap(pModElf->Core.pReader, &pModElf->pvBits);
+ if (RT_SUCCESS(rc))
+ {
+ const uint8_t *pu8 = (const uint8_t *)pModElf->pvBits;
+ if (pModElf->Rel.iSymSh != ~0U)
+ pModElf->Rel.paSyms = (const Elf_Sym *)(pu8 + pModElf->paShdrs[pModElf->Rel.iSymSh].sh_offset);
+ if (pModElf->Rel.iStrSh != ~0U)
+ pModElf->Rel.pStr = (const char *)(pu8 + pModElf->paShdrs[pModElf->Rel.iStrSh].sh_offset);
+ if (pModElf->Dyn.iSymSh != ~0U)
+ pModElf->Dyn.paSyms = (const Elf_Sym *)(pu8 + pModElf->paShdrs[pModElf->Dyn.iSymSh].sh_offset);
+ if (pModElf->Dyn.iStrSh != ~0U)
+ pModElf->Dyn.pStr = (const char *)(pu8 + pModElf->paShdrs[pModElf->Dyn.iStrSh].sh_offset);
+ pModElf->pShStr = (const char *)(pu8 + pModElf->paShdrs[pModElf->Ehdr.e_shstrndx].sh_offset);
+
+ /*
+ * Verify that the ends of the string tables have a zero terminator
+ * (this avoids duplicating the appropriate checks later in the code accessing the string tables).
+ *
+ * sh_offset and sh_size were verfied in RTLDRELF_NAME(ValidateSectionHeader)() already so they
+ * are safe to use.
+ */
+ AssertMsgStmt( pModElf->Rel.iStrSh == ~0U
+ || pModElf->Rel.pStr[pModElf->paShdrs[pModElf->Rel.iStrSh].sh_size - 1] == '\0',
+ ("The string table is not zero terminated!\n"),
+ rc = VERR_LDRELF_UNTERMINATED_STRING_TAB);
+ AssertMsgStmt( pModElf->Dyn.iStrSh == ~0U
+ || pModElf->Dyn.pStr[pModElf->paShdrs[pModElf->Dyn.iStrSh].sh_size - 1] == '\0',
+ ("The string table is not zero terminated!\n"),
+ rc = VERR_LDRELF_UNTERMINATED_STRING_TAB);
+ AssertMsgStmt(pModElf->pShStr[pModElf->paShdrs[pModElf->Ehdr.e_shstrndx].sh_size - 1] == '\0',
+ ("The section header string table is not zero terminated!\n"),
+ rc = VERR_LDRELF_UNTERMINATED_STRING_TAB);
+
+ if (RT_FAILURE(rc))
+ {
+ /* Unmap. */
+ int rc2 = pModElf->Core.pReader->pfnUnmap(pModElf->Core.pReader, pModElf->pvBits);
+ AssertRC(rc2);
+ pModElf->pvBits = NULL;
+ pModElf->Rel.paSyms = NULL;
+ pModElf->Rel.pStr = NULL;
+ pModElf->Dyn.paSyms = NULL;
+ pModElf->Dyn.pStr = NULL;
+ pModElf->pShStr = NULL;
+ }
+ }
+ return rc;
+}
+
+
+/*
+ *
+ * EXEC & DYN.
+ * EXEC & DYN.
+ * EXEC & DYN.
+ * EXEC & DYN.
+ * EXEC & DYN.
+ *
+ */
+
+/**
+ * Get the symbol and symbol value.
+ *
+ * @returns iprt status code.
+ * @param pModElf The ELF loader module instance data.
+ * @param BaseAddr The base address which the module is being fixedup to.
+ * @param pfnGetImport The callback function to use to resolve imports (aka unresolved externals).
+ * @param pvUser User argument to pass to the callback.
+ * @param iSym The symbol to get.
+ * @param ppSym Where to store the symbol pointer on success. (read only)
+ * @param pSymValue Where to store the symbol value on success.
+ */
+static int RTLDRELF_NAME(SymbolExecDyn)(PRTLDRMODELF pModElf, Elf_Addr BaseAddr, PFNRTLDRIMPORT pfnGetImport, void *pvUser,
+ Elf_Size iSym, const Elf_Sym **ppSym, Elf_Addr *pSymValue)
+{
+ /*
+ * Validate and find the symbol.
+ */
+ AssertMsgReturn(iSym < pModElf->Dyn.cSyms, ("iSym=%d is an invalid symbol index!\n", iSym), VERR_LDRELF_INVALID_SYMBOL_INDEX);
+ const Elf_Sym *pSym = &pModElf->Dyn.paSyms[iSym];
+ *ppSym = pSym;
+
+ AssertMsgReturn(pSym->st_name < pModElf->Dyn.cbStr,
+ ("iSym=%d st_name=%d str sh_size=%d\n", iSym, pSym->st_name, pModElf->Dyn.cbStr),
+ VERR_LDRELF_INVALID_SYMBOL_NAME_OFFSET);
+ const char * const pszName = pModElf->Dyn.pStr + pSym->st_name;
+
+ /*
+ * Determine the symbol value.
+ *
+ * Symbols needs different treatment depending on which section their are in.
+ * Undefined and absolute symbols goes into special non-existing sections.
+ */
+ switch (pSym->st_shndx)
+ {
+ /*
+ * Undefined symbol, needs resolving.
+ *
+ * Since ELF has no generic concept of importing from specific module (the OS/2 ELF format
+ * has but that's an OS extension and only applies to programs and dlls), we'll have to ask
+ * the resolver callback to do a global search.
+ */
+ case SHN_UNDEF:
+ {
+ /* Try to resolve the symbol. */
+ RTUINTPTR Value;
+ int rc = pfnGetImport(&pModElf->Core, "", pszName, ~0U, &Value, pvUser);
+ AssertMsgRCReturn(rc, ("Failed to resolve '%s' (iSym=" FMT_ELF_SIZE " rc=%Rrc\n", pszName, iSym, rc), rc);
+
+ *pSymValue = (Elf_Addr)Value;
+ AssertMsgReturn((RTUINTPTR)*pSymValue == Value,
+ ("Symbol value overflowed! '%s' (iSym=" FMT_ELF_SIZE "\n", pszName, iSym), VERR_SYMBOL_VALUE_TOO_BIG);
+
+ Log2(("rtldrELF: #%-3d - UNDEF " FMT_ELF_ADDR " '%s'\n", iSym, *pSymValue, pszName));
+ break;
+ }
+
+ /*
+ * Absolute symbols needs no fixing since they are, well, absolute.
+ */
+ case SHN_ABS:
+ *pSymValue = pSym->st_value;
+ Log2(("rtldrELF: #%-3d - ABS " FMT_ELF_ADDR " '%s'\n", iSym, *pSymValue, pszName));
+ break;
+
+ /*
+ * All other symbols are addressed relative the image base in DYN and EXEC binaries.
+ */
+ default:
+ AssertMsgReturn(pSym->st_shndx < pModElf->Ehdr.e_shnum,
+ ("iSym=%d st_shndx=%d e_shnum=%d pszName=%s\n", iSym, pSym->st_shndx, pModElf->Ehdr.e_shnum, pszName),
+ VERR_BAD_EXE_FORMAT);
+ *pSymValue = pSym->st_value + BaseAddr;
+ Log2(("rtldrELF: #%-3d - %5d " FMT_ELF_ADDR " '%s'\n", iSym, pSym->st_shndx, *pSymValue, pszName));
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+#if ELF_MODE == 32
+/** Helper for RelocateSectionExecDyn. */
+DECLINLINE(const Elf_Shdr *) RTLDRELF_NAME(RvaToSectionHeader)(PRTLDRMODELF pModElf, Elf_Addr uRva)
+{
+ const Elf_Shdr * const pShdrFirst = pModElf->paShdrs;
+ const Elf_Shdr *pShdr = pShdrFirst + pModElf->Ehdr.e_shnum;
+ while (--pShdr != pShdrFirst)
+ if (uRva - pShdr->sh_addr /*rva*/ < pShdr->sh_size)
+ return pShdr;
+ AssertFailed();
+ return pShdr;
+}
+#endif
+
+
+/**
+ * Applies the fixups for a section in an executable image.
+ *
+ * @returns iprt status code.
+ * @param pModElf The ELF loader module instance data.
+ * @param BaseAddr The base address which the module is being fixedup to.
+ * @param pfnGetImport The callback function to use to resolve imports (aka unresolved externals).
+ * @param pvUser User argument to pass to the callback.
+ * @param SecAddr The section address. This is the address the relocations are relative to.
+ * @param cbSec The section size. The relocations must be inside this.
+ * @param pu8SecBaseR Where we read section bits from.
+ * @param pu8SecBaseW Where we write section bits to.
+ * @param pvRelocs Pointer to where we read the relocations from.
+ * @param cbRelocs Size of the relocations.
+ */
+static int RTLDRELF_NAME(RelocateSectionExecDyn)(PRTLDRMODELF pModElf, Elf_Addr BaseAddr,
+ PFNRTLDRIMPORT pfnGetImport, void *pvUser,
+ const Elf_Addr SecAddr, Elf_Size cbSec,
+ const uint8_t *pu8SecBaseR, uint8_t *pu8SecBaseW,
+ const void *pvRelocs, Elf_Size cbRelocs)
+{
+#if ELF_MODE != 32
+ NOREF(pu8SecBaseR);
+#endif
+
+ /*
+ * Iterate the relocations.
+ * The relocations are stored in an array of Elf32_Rel records and covers the entire relocation section.
+ */
+#if ELF_MODE == 32
+ const Elf_Shdr *pShdr = pModElf->paShdrs;
+ const Elf_Addr offDelta = BaseAddr - pModElf->LinkAddress;
+#endif
+ const Elf_Reloc *paRels = (const Elf_Reloc *)pvRelocs;
+ const unsigned iRelMax = (unsigned)(cbRelocs / sizeof(paRels[0]));
+ AssertMsgReturn(iRelMax == cbRelocs / sizeof(paRels[0]), (FMT_ELF_SIZE "\n", cbRelocs / sizeof(paRels[0])),
+ VERR_IMAGE_TOO_BIG);
+ for (unsigned iRel = 0; iRel < iRelMax; iRel++)
+ {
+ /*
+ * Apply fixups not taking a symbol (will 'continue' rather than 'break').
+ */
+ AssertMsgReturn(paRels[iRel].r_offset < cbSec, (FMT_ELF_ADDR " " FMT_ELF_SIZE "\n", paRels[iRel].r_offset, cbSec),
+ VERR_LDRELF_INVALID_RELOCATION_OFFSET);
+#if ELF_MODE == 32
+ if (paRels[iRel].r_offset - pShdr->sh_addr /*rva*/ >= pShdr->sh_size)
+ pShdr = RTLDRELF_NAME(RvaToSectionHeader)(pModElf, paRels[iRel].r_offset);
+ static const Elf_Addr s_uZero = 0;
+ const Elf_Addr *pAddrR = RT_LIKELY(pShdr->sh_type != SHT_NOBITS) /* Where to read the addend. */
+ ? (const Elf_Addr *)(pu8SecBaseR + paRels[iRel].r_offset - pShdr->sh_addr /*rva*/
+ + pShdr->sh_offset)
+ : &s_uZero;
+#endif
+ Elf_Addr *pAddrW = (Elf_Addr *)(pu8SecBaseW + paRels[iRel].r_offset); /* Where to write the fixup. */
+ switch (ELF_R_TYPE(paRels[iRel].r_info))
+ {
+ /*
+ * Image relative (addend + base).
+ */
+#if ELF_MODE == 32
+ case R_386_RELATIVE:
+ {
+ const Elf_Addr Value = *pAddrR + BaseAddr;
+ *(uint32_t *)pAddrW = Value;
+ Log4((FMT_ELF_ADDR "/" FMT_ELF_ADDR7 ": R_386_RELATIVE Value=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, paRels[iRel].r_offset, Value));
+ AssertCompile(sizeof(Value) == sizeof(uint32_t));
+ continue;
+ }
+#elif ELF_MODE == 64
+ case R_X86_64_RELATIVE:
+ {
+ const Elf_Addr Value = paRels[iRel].r_addend + BaseAddr;
+ *(uint64_t *)pAddrW = (uint64_t)Value;
+ Log4((FMT_ELF_ADDR "/" FMT_ELF_ADDR7 ": R_X86_64_RELATIVE Value=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, paRels[iRel].r_offset, Value));
+ AssertCompile(sizeof(Value) == sizeof(uint64_t));
+ continue;
+ }
+#endif
+
+ /*
+ * R_XXX_NONE.
+ */
+#if ELF_MODE == 32
+ case R_386_NONE:
+#elif ELF_MODE == 64
+ case R_X86_64_NONE:
+#endif
+ continue;
+ }
+
+ /*
+ * Validate and find the symbol, resolve undefined ones.
+ */
+ const Elf_Sym *pSym = NULL; /* shut up gcc */
+ Elf_Addr SymValue = 0; /* shut up gcc-4 */
+ int rc = RTLDRELF_NAME(SymbolExecDyn)(pModElf, BaseAddr, pfnGetImport, pvUser, ELF_R_SYM(paRels[iRel].r_info), &pSym, &SymValue);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Apply the fixup.
+ */
+ switch (ELF_R_TYPE(paRels[iRel].r_info))
+ {
+#if ELF_MODE == 32
+ /*
+ * GOT/PLT.
+ */
+ case R_386_GLOB_DAT:
+ {
+ *(uint32_t *)pAddrW = (uint32_t)SymValue;
+ Log4((FMT_ELF_ADDR "/" FMT_ELF_ADDR7 ": R_386_GLOB_DAT Value=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, paRels[iRel].r_offset, SymValue));
+ AssertCompile(sizeof(SymValue) == sizeof(uint32_t));
+ break;
+ }
+
+ case R_386_JMP_SLOT:
+ {
+ *(uint32_t *)pAddrW = (uint32_t)SymValue;
+ Log4((FMT_ELF_ADDR "/" FMT_ELF_ADDR7 ": R_386_JMP_SLOT Value=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, paRels[iRel].r_offset, SymValue));
+ AssertCompile(sizeof(SymValue) == sizeof(uint32_t));
+ break;
+ }
+
+ /*
+ * Absolute addressing.
+ */
+ case R_386_32:
+ {
+ Elf_Addr Value;
+ if (pSym->st_shndx < pModElf->Ehdr.e_shnum)
+ Value = *pAddrR + offDelta; /* Simplified. */
+ else if (pSym->st_shndx == SHN_ABS)
+ continue; /* Internal fixup, no need to apply it. */
+ else if (pSym->st_shndx == SHN_UNDEF)
+ Value = SymValue + *pAddrR;
+ else
+ AssertFailedReturn(VERR_LDR_GENERAL_FAILURE); /** @todo SHN_COMMON */
+ *(uint32_t *)pAddrW = Value;
+ Log4((FMT_ELF_ADDR "/" FMT_ELF_ADDR7 ": R_386_32 Value=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, paRels[iRel].r_offset, Value));
+ break;
+ }
+
+ /*
+ * PC relative addressing.
+ */
+ case R_386_PC32:
+ {
+ Elf_Addr Value;
+ if (pSym->st_shndx < pModElf->Ehdr.e_shnum)
+ continue; /* Internal fixup, no need to apply it. */
+ else if (pSym->st_shndx == SHN_ABS)
+ Value = *pAddrR + offDelta; /* Simplified. */
+ else if (pSym->st_shndx == SHN_UNDEF)
+ {
+ const Elf_Addr SourceAddr = SecAddr + paRels[iRel].r_offset + BaseAddr; /* Where the source really is. */
+ Value = SymValue + *(uint32_t *)pAddrR - SourceAddr;
+ *(uint32_t *)pAddrW = Value;
+ }
+ else
+ AssertFailedReturn(VERR_LDR_GENERAL_FAILURE); /** @todo SHN_COMMON */
+ Log4((FMT_ELF_ADDR "/" FMT_ELF_ADDR7 ": R_386_PC32 Value=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, paRels[iRel].r_offset, Value));
+ break;
+ }
+
+#elif ELF_MODE == 64
+ /*
+ * GOT/PLT.
+ */
+ case R_X86_64_GLOB_DAT:
+ {
+ *(uint64_t *)pAddrW = (uint64_t)SymValue;
+ Log4((FMT_ELF_ADDR "/" FMT_ELF_ADDR7 ": R_X86_64_GLOB_DAT Value=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, paRels[iRel].r_offset, SymValue));
+ AssertCompile(sizeof(SymValue) == sizeof(uint64_t));
+ break;
+ }
+
+ case R_X86_64_JMP_SLOT:
+ {
+ *(uint64_t *)pAddrW = (uint64_t)SymValue;
+ Log4((FMT_ELF_ADDR "/" FMT_ELF_ADDR7 ": R_X86_64_JMP_SLOT Value=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, paRels[iRel].r_offset, SymValue));
+ AssertCompile(sizeof(SymValue) == sizeof(uint64_t));
+ break;
+ }
+
+ /*
+ * Absolute addressing.
+ */
+ case R_X86_64_64:
+ {
+ const Elf_Addr Value = SymValue + paRels[iRel].r_addend;
+ *(uint64_t *)pAddrW = Value;
+ Log4((FMT_ELF_ADDR "/" FMT_ELF_ADDR7 ": R_X86_64_64 Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, paRels[iRel].r_offset, Value, SymValue));
+ break;
+ }
+
+ /*
+ * Truncated 32-bit value (zero-extendedable to the 64-bit value).
+ */
+ case R_X86_64_32:
+ {
+ const Elf_Addr Value = SymValue + paRels[iRel].r_addend;
+ *(uint32_t *)pAddrW = (uint32_t)Value;
+ Log4((FMT_ELF_ADDR "/" FMT_ELF_ADDR7 ": R_X86_64_32 Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, paRels[iRel].r_offset, Value, SymValue));
+ AssertMsgReturn((Elf_Addr)*(uint32_t *)pAddrW == SymValue, ("Value=" FMT_ELF_ADDR "\n", SymValue),
+ VERR_SYMBOL_VALUE_TOO_BIG);
+ break;
+ }
+
+ /*
+ * Truncated 32-bit value (sign-extendedable to the 64-bit value).
+ */
+ case R_X86_64_32S:
+ {
+ const Elf_Addr Value = SymValue + paRels[iRel].r_addend;
+ *(int32_t *)pAddrW = (int32_t)Value;
+ Log4((FMT_ELF_ADDR "/" FMT_ELF_ADDR7 ": R_X86_64_32S Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, paRels[iRel].r_offset, Value, SymValue));
+ AssertMsgReturn((Elf_Addr)*(int32_t *)pAddrW == Value, ("Value=" FMT_ELF_ADDR "\n", Value), VERR_SYMBOL_VALUE_TOO_BIG); /** @todo check the sign-extending here. */
+ break;
+ }
+
+ /*
+ * PC relative addressing.
+ */
+ case R_X86_64_PC32:
+ {
+ const Elf_Addr SourceAddr = SecAddr + paRels[iRel].r_offset + BaseAddr; /* Where the source really is. */
+ const Elf_Addr Value = SymValue + paRels[iRel].r_addend - SourceAddr;
+ *(int32_t *)pAddrW = (int32_t)Value;
+ Log4((FMT_ELF_ADDR "/" FMT_ELF_ADDR7 ": R_X86_64_PC32 Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n",
+ SourceAddr, paRels[iRel].r_offset, Value, SymValue));
+ AssertMsgReturn((Elf_Addr)*(int32_t *)pAddrW == Value, ("Value=" FMT_ELF_ADDR "\n", Value), VERR_SYMBOL_VALUE_TOO_BIG); /** @todo check the sign-extending here. */
+ break;
+ }
+
+#endif
+ default:
+ AssertMsgFailed(("Unknown relocation type: %d (iRel=%d iRelMax=%d)\n",
+ ELF_R_TYPE(paRels[iRel].r_info), iRel, iRelMax));
+ return VERR_LDRELF_RELOCATION_NOT_SUPPORTED;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+
+/*
+ *
+ * REL
+ * REL
+ * REL
+ * REL
+ * REL
+ *
+ */
+
+/**
+ * Get the symbol and symbol value.
+ *
+ * @returns iprt status code.
+ * @param pModElf The ELF loader module instance data.
+ * @param BaseAddr The base address which the module is being fixedup to.
+ * @param pfnGetImport The callback function to use to resolve imports (aka unresolved externals).
+ * @param pvUser User argument to pass to the callback.
+ * @param iSym The symbol to get.
+ * @param ppSym Where to store the symbol pointer on success. (read only)
+ * @param pSymValue Where to store the symbol value on success.
+ */
+static int RTLDRELF_NAME(Symbol)(PRTLDRMODELF pModElf, Elf_Addr BaseAddr, PFNRTLDRIMPORT pfnGetImport, void *pvUser,
+ Elf_Size iSym, const Elf_Sym **ppSym, Elf_Addr *pSymValue)
+{
+ /*
+ * Validate and find the symbol.
+ */
+ AssertMsgReturn(iSym < pModElf->Rel.cSyms, ("iSym=%d is an invalid symbol index!\n", iSym), VERR_LDRELF_INVALID_SYMBOL_INDEX);
+ const Elf_Sym *pSym = &pModElf->Rel.paSyms[iSym];
+ *ppSym = pSym;
+
+ AssertMsgReturn(pSym->st_name < pModElf->Rel.cbStr,
+ ("iSym=%d st_name=%d str sh_size=%d\n", iSym, pSym->st_name, pModElf->Rel.cbStr),
+ VERR_LDRELF_INVALID_SYMBOL_NAME_OFFSET);
+ const char *pszName = ELF_STR(pModElf, pSym->st_name);
+
+ /*
+ * Determine the symbol value.
+ *
+ * Symbols needs different treatment depending on which section their are in.
+ * Undefined and absolute symbols goes into special non-existing sections.
+ */
+ switch (pSym->st_shndx)
+ {
+ /*
+ * Undefined symbol, needs resolving.
+ *
+ * Since ELF has no generic concept of importing from specific module (the OS/2 ELF format
+ * has but that's an OS extension and only applies to programs and dlls), we'll have to ask
+ * the resolver callback to do a global search.
+ */
+ case SHN_UNDEF:
+ {
+ /* Try to resolve the symbol. */
+ RTUINTPTR Value;
+ int rc = pfnGetImport(&pModElf->Core, "", pszName, ~0U, &Value, pvUser);
+ AssertMsgRCReturn(rc, ("Failed to resolve '%s' (iSym=" FMT_ELF_SIZE " rc=%Rrc\n", pszName, iSym, rc), rc);
+ *pSymValue = (Elf_Addr)Value;
+
+ AssertMsgReturn((RTUINTPTR)*pSymValue == Value,
+ ("Symbol value overflowed! '%s' (iSym=" FMT_ELF_SIZE ")\n", pszName, iSym),
+ VERR_SYMBOL_VALUE_TOO_BIG);
+
+ Log2(("rtldrELF: #%-3d - UNDEF " FMT_ELF_ADDR " '%s'\n", iSym, *pSymValue, pszName));
+ break;
+ }
+
+ /*
+ * Absolute symbols needs no fixing since they are, well, absolute.
+ */
+ case SHN_ABS:
+ *pSymValue = pSym->st_value;
+ Log2(("rtldrELF: #%-3d - ABS " FMT_ELF_ADDR " '%s'\n", iSym, *pSymValue, pszName));
+ break;
+
+ /*
+ * All other symbols are addressed relative to their section and need to be fixed up.
+ */
+ default:
+ if (pSym->st_shndx >= pModElf->Ehdr.e_shnum)
+ {
+ /* what about common symbols? */
+ AssertMsg(pSym->st_shndx < pModElf->Ehdr.e_shnum,
+ ("iSym=%d st_shndx=%d e_shnum=%d pszName=%s\n", iSym, pSym->st_shndx, pModElf->Ehdr.e_shnum, pszName));
+ return VERR_BAD_EXE_FORMAT;
+ }
+ *pSymValue = pSym->st_value + pModElf->paShdrs[pSym->st_shndx].sh_addr + BaseAddr;
+ Log2(("rtldrELF: #%-3d - %5d " FMT_ELF_ADDR " '%s'\n", iSym, pSym->st_shndx, *pSymValue, pszName));
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Applies the fixups for a sections.
+ *
+ * @returns iprt status code.
+ * @param pModElf The ELF loader module instance data.
+ * @param BaseAddr The base address which the module is being fixedup to.
+ * @param pfnGetImport The callback function to use to resolve imports (aka unresolved externals).
+ * @param pvUser User argument to pass to the callback.
+ * @param SecAddr The section address. This is the address the relocations are relative to.
+ * @param cbSec The section size. The relocations must be inside this.
+ * @param pu8SecBaseR Where we read section bits from.
+ * @param pu8SecBaseW Where we write section bits to.
+ * @param pvRelocs Pointer to where we read the relocations from.
+ * @param cbRelocs Size of the relocations.
+ */
+static int RTLDRELF_NAME(RelocateSectionRel)(PRTLDRMODELF pModElf, Elf_Addr BaseAddr, PFNRTLDRIMPORT pfnGetImport, void *pvUser,
+ const Elf_Addr SecAddr, Elf_Size cbSec, const uint8_t *pu8SecBaseR,
+ uint8_t *pu8SecBaseW, const void *pvRelocs, Elf_Size cbRelocs)
+{
+#if ELF_MODE != 32
+ NOREF(pu8SecBaseR);
+#endif
+
+ /*
+ * Iterate the relocations.
+ * The relocations are stored in an array of Elf32_Rel records and covers the entire relocation section.
+ */
+ const Elf_Reloc *paRels = (const Elf_Reloc *)pvRelocs;
+ const unsigned iRelMax = (unsigned)(cbRelocs / sizeof(paRels[0]));
+ AssertMsgReturn(iRelMax == cbRelocs / sizeof(paRels[0]), (FMT_ELF_SIZE "\n", cbRelocs / sizeof(paRels[0])), VERR_IMAGE_TOO_BIG);
+ for (unsigned iRel = 0; iRel < iRelMax; iRel++)
+ {
+ /*
+ * Skip R_XXX_NONE entries early to avoid confusion in the symbol
+ * getter code.
+ */
+#if ELF_MODE == 32
+ if (ELF_R_TYPE(paRels[iRel].r_info) == R_386_NONE)
+ continue;
+#elif ELF_MODE == 64
+ if (ELF_R_TYPE(paRels[iRel].r_info) == R_X86_64_NONE)
+ continue;
+#endif
+
+
+ /*
+ * Get the symbol.
+ */
+ const Elf_Sym *pSym = NULL; /* shut up gcc */
+ Elf_Addr SymValue = 0; /* shut up gcc-4 */
+ int rc = RTLDRELF_NAME(Symbol)(pModElf, BaseAddr, pfnGetImport, pvUser, ELF_R_SYM(paRels[iRel].r_info), &pSym, &SymValue);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ Log3(("rtldrELF: " FMT_ELF_ADDR " %02x %06x - " FMT_ELF_ADDR " %3d %02x %s\n",
+ paRels[iRel].r_offset, ELF_R_TYPE(paRels[iRel].r_info), (unsigned)ELF_R_SYM(paRels[iRel].r_info),
+ SymValue, (unsigned)pSym->st_shndx, pSym->st_info, ELF_STR(pModElf, pSym->st_name)));
+
+ /*
+ * Apply the fixup.
+ */
+ AssertMsgReturn(paRels[iRel].r_offset < cbSec, (FMT_ELF_ADDR " " FMT_ELF_SIZE "\n", paRels[iRel].r_offset, cbSec), VERR_LDRELF_INVALID_RELOCATION_OFFSET);
+#if ELF_MODE == 32
+ const Elf_Addr *pAddrR = (const Elf_Addr *)(pu8SecBaseR + paRels[iRel].r_offset); /* Where to read the addend. */
+#endif
+ Elf_Addr *pAddrW = (Elf_Addr *)(pu8SecBaseW + paRels[iRel].r_offset); /* Where to write the fixup. */
+ switch (ELF_R_TYPE(paRels[iRel].r_info))
+ {
+#if ELF_MODE == 32
+ /*
+ * Absolute addressing.
+ */
+ case R_386_32:
+ {
+ const Elf_Addr Value = SymValue + *pAddrR;
+ *(uint32_t *)pAddrW = Value;
+ Log4((FMT_ELF_ADDR": R_386_32 Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, Value, SymValue));
+ break;
+ }
+
+ /*
+ * PC relative addressing.
+ */
+ case R_386_PC32:
+ {
+ const Elf_Addr SourceAddr = SecAddr + paRels[iRel].r_offset + BaseAddr; /* Where the source really is. */
+ const Elf_Addr Value = SymValue + *(uint32_t *)pAddrR - SourceAddr;
+ *(uint32_t *)pAddrW = Value;
+ Log4((FMT_ELF_ADDR": R_386_PC32 Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n",
+ SourceAddr, Value, SymValue));
+ break;
+ }
+
+ /* ignore */
+ case R_386_NONE:
+ break;
+
+#elif ELF_MODE == 64
+
+ /*
+ * Absolute addressing
+ */
+ case R_X86_64_64:
+ {
+ const Elf_Addr Value = SymValue + paRels[iRel].r_addend;
+ *(uint64_t *)pAddrW = Value;
+ Log4((FMT_ELF_ADDR": R_X86_64_64 Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, Value, SymValue));
+ break;
+ }
+
+ /*
+ * Truncated 32-bit value (zero-extendedable to the 64-bit value).
+ */
+ case R_X86_64_32:
+ {
+ const Elf_Addr Value = SymValue + paRels[iRel].r_addend;
+ *(uint32_t *)pAddrW = (uint32_t)Value;
+ Log4((FMT_ELF_ADDR": R_X86_64_32 Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, Value, SymValue));
+ AssertMsgReturn((Elf_Addr)*(uint32_t *)pAddrW == Value, ("Value=" FMT_ELF_ADDR "\n", Value), VERR_SYMBOL_VALUE_TOO_BIG);
+ break;
+ }
+
+ /*
+ * Truncated 32-bit value (sign-extendedable to the 64-bit value).
+ */
+ case R_X86_64_32S:
+ {
+ const Elf_Addr Value = SymValue + paRels[iRel].r_addend;
+ *(int32_t *)pAddrW = (int32_t)Value;
+ Log4((FMT_ELF_ADDR": R_X86_64_32S Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n",
+ SecAddr + paRels[iRel].r_offset + BaseAddr, Value, SymValue));
+ AssertMsgReturn((Elf_Addr)*(int32_t *)pAddrW == Value, ("Value=" FMT_ELF_ADDR "\n", Value), VERR_SYMBOL_VALUE_TOO_BIG); /** @todo check the sign-extending here. */
+ break;
+ }
+
+ /*
+ * PC relative addressing.
+ */
+ case R_X86_64_PC32:
+ case R_X86_64_PLT32: /* binutils commit 451875b4f976a527395e9303224c7881b65e12ed feature/regression. */
+ {
+ const Elf_Addr SourceAddr = SecAddr + paRels[iRel].r_offset + BaseAddr; /* Where the source really is. */
+ const Elf_Addr Value = SymValue + paRels[iRel].r_addend - SourceAddr;
+ *(int32_t *)pAddrW = (int32_t)Value;
+ Log4((FMT_ELF_ADDR": R_X86_64_PC32 Value=" FMT_ELF_ADDR " SymValue=" FMT_ELF_ADDR "\n",
+ SourceAddr, Value, SymValue));
+ AssertMsgReturn((Elf_Addr)*(int32_t *)pAddrW == Value, ("Value=" FMT_ELF_ADDR "\n", Value), VERR_SYMBOL_VALUE_TOO_BIG); /** @todo check the sign-extending here. */
+ break;
+ }
+
+ /* ignore */
+ case R_X86_64_NONE:
+ break;
+#endif
+
+ default:
+ AssertMsgFailed(("Unknown relocation type: %d (iRel=%d iRelMax=%d)\n",
+ ELF_R_TYPE(paRels[iRel].r_info), iRel, iRelMax));
+ return VERR_LDRELF_RELOCATION_NOT_SUPPORTED;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+
+/** @copydoc RTLDROPS::pfnClose */
+static DECLCALLBACK(int) RTLDRELF_NAME(Close)(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;
+
+ if (pModElf->paShdrs)
+ {
+ RTMemFree(pModElf->paShdrs);
+ pModElf->paShdrs = NULL;
+ }
+
+ if (pModElf->paPhdrs)
+ {
+ RTMemFree(pModElf->paPhdrs);
+ pModElf->paPhdrs = NULL;
+ }
+
+ if (pModElf->paDynamic)
+ {
+ RTMemFree(pModElf->paDynamic);
+ pModElf->paDynamic = NULL;
+ }
+
+ if (pModElf->pvBits)
+ {
+ pModElf->Core.pReader->pfnUnmap(pModElf->Core.pReader, pModElf->pvBits);
+ pModElf->pvBits = NULL;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc RTLDROPS::Done */
+static DECLCALLBACK(int) RTLDRELF_NAME(Done)(PRTLDRMODINTERNAL pMod)
+{
+ NOREF(pMod); /*PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;*/
+ /** @todo Have to think more about this .... */
+ return -1;
+}
+
+
+/** @copydoc RTLDROPS::pfnEnumSymbols */
+static DECLCALLBACK(int) RTLDRELF_NAME(EnumSymbols)(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits,
+ RTUINTPTR BaseAddress, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;
+ NOREF(pvBits);
+
+ /*
+ * Validate the input.
+ */
+ Elf_Addr BaseAddr = (Elf_Addr)BaseAddress;
+ AssertMsgReturn((RTUINTPTR)BaseAddr == BaseAddress, ("%RTptr", BaseAddress), VERR_IMAGE_BASE_TOO_HIGH);
+
+ /*
+ * Make sure we've got the string and symbol tables. (We don't need the pvBits.)
+ */
+ int rc = RTLDRELF_NAME(MapBits)(pModElf, false);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Enumerate the symbol table.
+ */
+ const Elf_Sym *paSyms = pModElf->Rel.paSyms;
+ unsigned cSyms = pModElf->Rel.cSyms;
+ const char *pszzStr = pModElf->Rel.pStr;
+ unsigned cbStr = pModElf->Rel.cbStr;
+ if ( ( !(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_ALL)
+ && pModElf->Dyn.cSyms > 0)
+ || cSyms == 0)
+ {
+ paSyms = pModElf->Dyn.paSyms;
+ cSyms = pModElf->Dyn.cSyms;
+ pszzStr = pModElf->Dyn.pStr;
+ cbStr = pModElf->Dyn.cbStr;
+ }
+
+ for (unsigned iSym = 1; iSym < cSyms; iSym++)
+ {
+ /*
+ * Skip imports (undefined).
+ */
+ if (paSyms[iSym].st_shndx != SHN_UNDEF)
+ {
+ /*
+ * Calc value and get name.
+ */
+ Elf_Addr Value;
+ if (paSyms[iSym].st_shndx == SHN_ABS)
+ /* absolute symbols are not subject to any relocation. */
+ Value = paSyms[iSym].st_value;
+ else if (paSyms[iSym].st_shndx < pModElf->Ehdr.e_shnum)
+ {
+ if (pModElf->Ehdr.e_type == ET_REL)
+ /* relative to the section. */
+ Value = BaseAddr + paSyms[iSym].st_value + pModElf->paShdrs[paSyms[iSym].st_shndx].sh_addr;
+ else /* Fixed up for link address. */
+ Value = BaseAddr + paSyms[iSym].st_value - pModElf->LinkAddress;
+ }
+ else
+ {
+ AssertMsgFailed(("Arg! paSyms[%u].st_shndx=" FMT_ELF_HALF "\n", iSym, paSyms[iSym].st_shndx));
+ return VERR_BAD_EXE_FORMAT;
+ }
+
+ AssertMsgReturn(paSyms[iSym].st_name < cbStr,
+ ("String outside string table! iSym=%d paSyms[iSym].st_name=%#x\n", iSym, paSyms[iSym].st_name),
+ VERR_LDRELF_INVALID_SYMBOL_NAME_OFFSET);
+ const char * const pszName = pszzStr + paSyms[iSym].st_name;
+
+ /* String termination was already checked when the string table was mapped. */
+ if ( *pszName != '\0'
+ && ( (fFlags & RTLDR_ENUM_SYMBOL_FLAGS_ALL)
+ || ELF_ST_BIND(paSyms[iSym].st_info) == STB_GLOBAL) )
+ {
+ /*
+ * Call back.
+ */
+ AssertMsgReturn(Value == (RTUINTPTR)Value, (FMT_ELF_ADDR "\n", Value), VERR_SYMBOL_VALUE_TOO_BIG);
+ rc = pfnCallback(pMod, pszName, iSym, (RTUINTPTR)Value, pvUser);
+ if (rc)
+ return rc;
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc RTLDROPS::GetImageSize */
+static DECLCALLBACK(size_t) RTLDRELF_NAME(GetImageSize)(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;
+
+ return pModElf->cbImage;
+}
+
+
+/** @copydoc RTLDROPS::GetBits */
+static DECLCALLBACK(int) RTLDRELF_NAME(GetBits)(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;
+
+ /*
+ * This operation is currently only available on relocatable images.
+ */
+ switch (pModElf->Ehdr.e_type)
+ {
+ case ET_REL:
+ case ET_DYN:
+ break;
+ case ET_EXEC:
+ Log(("RTLdrELF: %s: Executable images are not supported yet!\n", pModElf->Core.pReader->pfnLogName(pModElf->Core.pReader)));
+ return VERR_LDRELF_EXEC;
+ default: AssertFailedReturn(VERR_BAD_EXE_FORMAT);
+ }
+
+ /*
+ * Load the bits into pvBits.
+ */
+ const Elf_Shdr *paShdrs = pModElf->paShdrs;
+ for (unsigned iShdr = 0; iShdr < pModElf->Ehdr.e_shnum; iShdr++)
+ {
+ if (paShdrs[iShdr].sh_flags & SHF_ALLOC)
+ {
+ AssertMsgReturn((size_t)paShdrs[iShdr].sh_size == (size_t)paShdrs[iShdr].sh_size, (FMT_ELF_SIZE "\n", paShdrs[iShdr].sh_size), VERR_IMAGE_TOO_BIG);
+ switch (paShdrs[iShdr].sh_type)
+ {
+ case SHT_NOBITS:
+ memset((uint8_t *)pvBits + paShdrs[iShdr].sh_addr, 0, (size_t)paShdrs[iShdr].sh_size);
+ break;
+
+ case SHT_PROGBITS:
+ default:
+ {
+ int rc = pModElf->Core.pReader->pfnRead(pModElf->Core.pReader, (uint8_t *)pvBits + paShdrs[iShdr].sh_addr,
+ (size_t)paShdrs[iShdr].sh_size, paShdrs[iShdr].sh_offset);
+ if (RT_FAILURE(rc))
+ {
+ Log(("RTLdrELF: %s: Read error when reading " FMT_ELF_SIZE " bytes at " FMT_ELF_OFF ", iShdr=%d\n",
+ pModElf->Core.pReader->pfnLogName(pModElf->Core.pReader),
+ paShdrs[iShdr].sh_size, paShdrs[iShdr].sh_offset, iShdr));
+ return rc;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Relocate the image.
+ */
+ return pModElf->Core.pOps->pfnRelocate(pMod, pvBits, BaseAddress, ~(RTUINTPTR)0, pfnGetImport, pvUser);
+}
+
+
+/** @copydoc RTLDROPS::Relocate */
+static DECLCALLBACK(int) RTLDRELF_NAME(Relocate)(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
+ RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;
+#ifdef LOG_ENABLED
+ const char *pszLogName = pModElf->Core.pReader->pfnLogName(pModElf->Core.pReader);
+#endif
+ NOREF(OldBaseAddress);
+
+ /*
+ * This operation is currently only available on relocatable images.
+ */
+ switch (pModElf->Ehdr.e_type)
+ {
+ case ET_REL:
+ case ET_DYN:
+ break;
+ case ET_EXEC:
+ Log(("RTLdrELF: %s: Executable images are not supported yet!\n", pszLogName));
+ return VERR_LDRELF_EXEC;
+ default: AssertFailedReturn(VERR_BAD_EXE_FORMAT);
+ }
+
+ /*
+ * Validate the input.
+ */
+ Elf_Addr BaseAddr = (Elf_Addr)NewBaseAddress;
+ AssertMsgReturn((RTUINTPTR)BaseAddr == NewBaseAddress, ("%RTptr", NewBaseAddress), VERR_IMAGE_BASE_TOO_HIGH);
+
+ /*
+ * Map the image bits if not already done and setup pointer into it.
+ */
+ int rc = RTLDRELF_NAME(MapBits)(pModElf, true);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Iterate the sections looking for interesting SHT_REL[A] sections.
+ *
+ * In ET_REL files the SHT_REL[A] sections have the section index of
+ * the section they contain fixups for in the sh_info member.
+ */
+ const Elf_Shdr *paShdrs = pModElf->paShdrs;
+ Log2(("rtLdrElf: %s: Fixing up image\n", pszLogName));
+ for (unsigned iShdr = 0; iShdr < pModElf->Ehdr.e_shnum; iShdr++)
+ {
+ const Elf_Shdr *pShdrRel = &paShdrs[iShdr];
+
+ /*
+ * Skip sections without interest to us.
+ */
+#if ELF_MODE == 32
+ if (pShdrRel->sh_type != SHT_REL)
+#else
+ if (pShdrRel->sh_type != SHT_RELA)
+#endif
+ continue;
+ if (pModElf->Ehdr.e_type == ET_REL)
+ {
+ if (pShdrRel->sh_info >= pModElf->Ehdr.e_shnum)
+ continue;
+ const Elf_Shdr *pShdr = &paShdrs[pShdrRel->sh_info]; /* the section to fixup. */
+ if (!(pShdr->sh_flags & SHF_ALLOC))
+ continue;
+
+ /*
+ * Relocate the section.
+ */
+ Log2(("rtldrELF: %s: Relocation records for #%d [%s] (sh_info=%d sh_link=%d) found in #%d [%s] (sh_info=%d sh_link=%d)\n",
+ pszLogName, (int)pShdrRel->sh_info, ELF_SH_STR(pModElf, pShdr->sh_name), (int)pShdr->sh_info, (int)pShdr->sh_link,
+ iShdr, ELF_SH_STR(pModElf, pShdrRel->sh_name), (int)pShdrRel->sh_info, (int)pShdrRel->sh_link));
+
+ rc = RTLDRELF_NAME(RelocateSectionRel)(pModElf, BaseAddr, pfnGetImport, pvUser,
+ pShdr->sh_addr,
+ pShdr->sh_size,
+ (const uint8_t *)pModElf->pvBits + pShdr->sh_offset,
+ (uint8_t *)pvBits + pShdr->sh_addr,
+ (const uint8_t *)pModElf->pvBits + pShdrRel->sh_offset,
+ pShdrRel->sh_size);
+ }
+ else
+ rc = RTLDRELF_NAME(RelocateSectionExecDyn)(pModElf, BaseAddr, pfnGetImport, pvUser,
+ 0, (Elf_Size)pModElf->cbImage,
+ (const uint8_t *)pModElf->pvBits /** @todo file offset ?? */,
+ (uint8_t *)pvBits,
+ (const uint8_t *)pModElf->pvBits + pShdrRel->sh_offset,
+ pShdrRel->sh_size);
+
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for pfnGetSymbolEx.
+ */
+static int RTLDRELF_NAME(ReturnSymbol)(PRTLDRMODELF pThis, const Elf_Sym *pSym, Elf_Addr uBaseAddr, PRTUINTPTR pValue)
+{
+ Elf_Addr Value;
+ if (pSym->st_shndx == SHN_ABS)
+ /* absolute symbols are not subject to any relocation. */
+ Value = pSym->st_value;
+ else if (pSym->st_shndx < pThis->Ehdr.e_shnum)
+ {
+ if (pThis->Ehdr.e_type == ET_REL)
+ /* relative to the section. */
+ Value = uBaseAddr + pSym->st_value + pThis->paShdrs[pSym->st_shndx].sh_addr;
+ else /* Fixed up for link address. */
+ Value = uBaseAddr + pSym->st_value - pThis->LinkAddress;
+ }
+ else
+ {
+ AssertMsgFailed(("Arg! pSym->st_shndx=%d\n", pSym->st_shndx));
+ return VERR_BAD_EXE_FORMAT;
+ }
+ AssertMsgReturn(Value == (RTUINTPTR)Value, (FMT_ELF_ADDR "\n", Value), VERR_SYMBOL_VALUE_TOO_BIG);
+ *pValue = (RTUINTPTR)Value;
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc RTLDROPS::pfnGetSymbolEx */
+static DECLCALLBACK(int) RTLDRELF_NAME(GetSymbolEx)(PRTLDRMODINTERNAL pMod, const void *pvBits, RTUINTPTR BaseAddress,
+ uint32_t iOrdinal, const char *pszSymbol, RTUINTPTR *pValue)
+{
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;
+ NOREF(pvBits);
+
+ /*
+ * Validate the input.
+ */
+ Elf_Addr uBaseAddr = (Elf_Addr)BaseAddress;
+ AssertMsgReturn((RTUINTPTR)uBaseAddr == BaseAddress, ("%RTptr", BaseAddress), VERR_IMAGE_BASE_TOO_HIGH);
+
+ /*
+ * Map the image bits if not already done and setup pointer into it.
+ */
+ int rc = RTLDRELF_NAME(MapBits)(pModElf, true);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Calc all kinds of pointers before we start iterating the symbol table.
+ */
+ const Elf_Sym *paSyms = pModElf->Rel.paSyms;
+ unsigned cSyms = pModElf->Rel.cSyms;
+ const char *pszzStr = pModElf->Rel.pStr;
+ unsigned cbStr = pModElf->Rel.cbStr;
+ if (pModElf->Dyn.cSyms > 0)
+ {
+ paSyms = pModElf->Dyn.paSyms;
+ cSyms = pModElf->Dyn.cSyms;
+ pszzStr = pModElf->Dyn.pStr;
+ cbStr = pModElf->Dyn.cbStr;
+ }
+
+ if (iOrdinal == UINT32_MAX)
+ {
+ for (unsigned iSym = 1; iSym < cSyms; iSym++)
+ {
+ /* Undefined symbols are not exports, they are imports. */
+ if ( paSyms[iSym].st_shndx != SHN_UNDEF
+ && ( ELF_ST_BIND(paSyms[iSym].st_info) == STB_GLOBAL
+ || ELF_ST_BIND(paSyms[iSym].st_info) == STB_WEAK))
+ {
+ /* Validate the name string and try match with it. */
+ AssertMsgReturn(paSyms[iSym].st_name < cbStr,
+ ("String outside string table! iSym=%d paSyms[iSym].st_name=%#x\n", iSym, paSyms[iSym].st_name),
+ VERR_LDRELF_INVALID_SYMBOL_NAME_OFFSET);
+ if (!strcmp(pszSymbol, pszzStr + paSyms[iSym].st_name))
+ {
+ /* matched! */
+ return RTLDRELF_NAME(ReturnSymbol)(pModElf, &paSyms[iSym], uBaseAddr, pValue);
+ }
+ }
+ }
+ }
+ else if (iOrdinal < cSyms)
+ {
+ if ( paSyms[iOrdinal].st_shndx != SHN_UNDEF
+ && ( ELF_ST_BIND(paSyms[iOrdinal].st_info) == STB_GLOBAL
+ || ELF_ST_BIND(paSyms[iOrdinal].st_info) == STB_WEAK))
+ return RTLDRELF_NAME(ReturnSymbol)(pModElf, &paSyms[iOrdinal], uBaseAddr, pValue);
+ }
+
+ return VERR_SYMBOL_NOT_FOUND;
+}
+
+
+/** @copydoc RTLDROPS::pfnEnumDbgInfo */
+static DECLCALLBACK(int) RTLDRELF_NAME(EnumDbgInfo)(PRTLDRMODINTERNAL pMod, const void *pvBits,
+ PFNRTLDRENUMDBG pfnCallback, void *pvUser)
+{
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;
+ RT_NOREF_PV(pvBits);
+
+ /*
+ * Map the image bits if not already done and setup pointer into it.
+ */
+ int rc = RTLDRELF_NAME(MapBits)(pModElf, true);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Do the enumeration.
+ */
+ const Elf_Shdr *paShdrs = pModElf->paOrgShdrs;
+ for (unsigned iShdr = 0; iShdr < pModElf->Ehdr.e_shnum; iShdr++)
+ {
+ /* Debug sections are expected to be PROGBITS and not allocated. */
+ if (paShdrs[iShdr].sh_type != SHT_PROGBITS)
+ continue;
+ if (paShdrs[iShdr].sh_flags & SHF_ALLOC)
+ continue;
+
+ RTLDRDBGINFO DbgInfo;
+ const char *pszSectName = ELF_SH_STR(pModElf, paShdrs[iShdr].sh_name);
+ if ( !strncmp(pszSectName, RT_STR_TUPLE(".debug_"))
+ || !strcmp(pszSectName, ".WATCOM_references") )
+ {
+ RT_ZERO(DbgInfo.u);
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF;
+ DbgInfo.pszExtFile = NULL;
+ DbgInfo.offFile = paShdrs[iShdr].sh_offset;
+ DbgInfo.cb = paShdrs[iShdr].sh_size;
+ DbgInfo.u.Dwarf.pszSection = pszSectName;
+ }
+ else if (!strcmp(pszSectName, ".gnu_debuglink"))
+ {
+ if ((paShdrs[iShdr].sh_size & 3) || paShdrs[iShdr].sh_size < 8)
+ return VERR_BAD_EXE_FORMAT;
+
+ RT_ZERO(DbgInfo.u);
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF_DWO;
+ DbgInfo.pszExtFile = (const char *)((uintptr_t)pModElf->pvBits + (uintptr_t)paShdrs[iShdr].sh_offset);
+ if (!RTStrEnd(DbgInfo.pszExtFile, paShdrs[iShdr].sh_size))
+ return VERR_BAD_EXE_FORMAT;
+ DbgInfo.u.Dwo.uCrc32 = *(uint32_t *)((uintptr_t)DbgInfo.pszExtFile + (uintptr_t)paShdrs[iShdr].sh_size
+ - sizeof(uint32_t));
+ DbgInfo.offFile = -1;
+ DbgInfo.cb = 0;
+ }
+ else
+ continue;
+
+ DbgInfo.LinkAddress = NIL_RTLDRADDR;
+ DbgInfo.iDbgInfo = iShdr - 1;
+
+ rc = pfnCallback(pMod, &DbgInfo, pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Locate the next allocated section by RVA (sh_addr).
+ *
+ * This is a helper for EnumSegments and SegOffsetToRva.
+ *
+ * @returns Pointer to the section header if found, NULL if none.
+ * @param pModElf The module instance.
+ * @param iShdrCur The current section header.
+ */
+static const Elf_Shdr *RTLDRELF_NAME(GetNextAllocatedSection)(PRTLDRMODELF pModElf, unsigned iShdrCur)
+{
+ unsigned const cShdrs = pModElf->Ehdr.e_shnum;
+ const Elf_Shdr * const paShdrs = pModElf->paShdrs;
+ if (pModElf->fShdrInOrder)
+ {
+ for (unsigned iShdr = iShdrCur + 1; iShdr < cShdrs; iShdr++)
+ if (paShdrs[iShdr].sh_flags & SHF_ALLOC)
+ return &paShdrs[iShdr];
+ }
+ else
+ {
+ Elf_Addr const uEndCur = paShdrs[iShdrCur].sh_addr + paShdrs[iShdrCur].sh_size;
+ Elf_Addr offBest = ~(Elf_Addr)0;
+ unsigned iBest = cShdrs;
+ for (unsigned iShdr = pModElf->iFirstSect; iShdr < cShdrs; iShdr++)
+ if ((paShdrs[iShdr].sh_flags & SHF_ALLOC) && iShdr != iShdrCur)
+ {
+ Elf_Addr const offDelta = paShdrs[iShdr].sh_addr - uEndCur;
+ if ( offDelta < offBest
+ && paShdrs[iShdr].sh_addr >= uEndCur)
+ {
+ offBest = offDelta;
+ iBest = iShdr;
+ }
+ }
+ if (iBest < cShdrs)
+ return &paShdrs[iBest];
+ }
+ return NULL;
+}
+
+
+/** @copydoc RTLDROPS::pfnEnumSegments. */
+static DECLCALLBACK(int) RTLDRELF_NAME(EnumSegments)(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
+{
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;
+
+ /*
+ * Map the image bits if not already done and setup pointer into it.
+ */
+ int rc = RTLDRELF_NAME(MapBits)(pModElf, true);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Do the enumeration.
+ */
+ char szName[32];
+ Elf_Addr uPrevMappedRva = 0;
+ const Elf_Shdr *paShdrs = pModElf->paShdrs;
+ const Elf_Shdr *paOrgShdrs = pModElf->paOrgShdrs;
+ for (unsigned iShdr = pModElf->iFirstSect; iShdr < pModElf->Ehdr.e_shnum; iShdr++)
+ {
+ RTLDRSEG Seg;
+ if (iShdr != 0)
+ {
+ Seg.pszName = ELF_SH_STR(pModElf, paShdrs[iShdr].sh_name);
+ Seg.cchName = (uint32_t)strlen(Seg.pszName);
+ if (Seg.cchName == 0)
+ {
+ Seg.pszName = szName;
+ Seg.cchName = (uint32_t)RTStrPrintf(szName, sizeof(szName), "UnamedSect%02u", iShdr);
+ }
+ }
+ else
+ {
+ Seg.pszName = ".elf.headers";
+ Seg.cchName = 12;
+ }
+ Seg.SelFlat = 0;
+ Seg.Sel16bit = 0;
+ Seg.fFlags = 0;
+ Seg.fProt = RTMEM_PROT_READ;
+ if (paShdrs[iShdr].sh_flags & SHF_WRITE)
+ Seg.fProt |= RTMEM_PROT_WRITE;
+ if (paShdrs[iShdr].sh_flags & SHF_EXECINSTR)
+ Seg.fProt |= RTMEM_PROT_EXEC;
+ Seg.cb = paShdrs[iShdr].sh_size;
+ Seg.Alignment = paShdrs[iShdr].sh_addralign;
+ if (paShdrs[iShdr].sh_flags & SHF_ALLOC)
+ {
+ Seg.LinkAddress = paOrgShdrs[iShdr].sh_addr;
+ Seg.RVA = paShdrs[iShdr].sh_addr;
+ const Elf_Shdr *pShdr2 = RTLDRELF_NAME(GetNextAllocatedSection)(pModElf, iShdr);
+ if (pShdr2)
+ Seg.cbMapped = pShdr2->sh_addr - paShdrs[iShdr].sh_addr;
+ else
+ Seg.cbMapped = pModElf->cbImage - paShdrs[iShdr].sh_addr;
+ uPrevMappedRva = Seg.RVA;
+ }
+ else
+ {
+ Seg.LinkAddress = NIL_RTLDRADDR;
+ Seg.RVA = NIL_RTLDRADDR;
+ Seg.cbMapped = NIL_RTLDRADDR;
+ }
+ if (paShdrs[iShdr].sh_type != SHT_NOBITS)
+ {
+ Seg.offFile = paShdrs[iShdr].sh_offset;
+ Seg.cbFile = paShdrs[iShdr].sh_size;
+ }
+ else
+ {
+ Seg.offFile = -1;
+ Seg.cbFile = 0;
+ }
+
+ rc = pfnCallback(pMod, &Seg, pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc RTLDROPS::pfnLinkAddressToSegOffset. */
+static DECLCALLBACK(int) RTLDRELF_NAME(LinkAddressToSegOffset)(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
+ uint32_t *piSeg, PRTLDRADDR poffSeg)
+{
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;
+
+ const Elf_Shdr *pShdrEnd = NULL;
+ unsigned cLeft = pModElf->Ehdr.e_shnum - pModElf->iFirstSect;
+ const Elf_Shdr *pShdr = &pModElf->paOrgShdrs[pModElf->Ehdr.e_shnum];
+ while (cLeft-- > 0)
+ {
+ pShdr--;
+ if (pShdr->sh_flags & SHF_ALLOC)
+ {
+ RTLDRADDR offSeg = LinkAddress - pShdr->sh_addr;
+ if (offSeg < pShdr->sh_size)
+ {
+ *poffSeg = offSeg;
+ *piSeg = cLeft;
+ return VINF_SUCCESS;
+ }
+ if (offSeg == pShdr->sh_size)
+ pShdrEnd = pShdr;
+ }
+ }
+
+ if (pShdrEnd)
+ {
+ *poffSeg = pShdrEnd->sh_size;
+ *piSeg = pShdrEnd - pModElf->paOrgShdrs - pModElf->iFirstSect;
+ return VINF_SUCCESS;
+ }
+
+ return VERR_LDR_INVALID_LINK_ADDRESS;
+}
+
+
+/** @copydoc RTLDROPS::pfnLinkAddressToRva. */
+static DECLCALLBACK(int) RTLDRELF_NAME(LinkAddressToRva)(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
+{
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;
+ uint32_t iSeg;
+ RTLDRADDR offSeg;
+ int rc = RTLDRELF_NAME(LinkAddressToSegOffset)(pMod, LinkAddress, &iSeg, &offSeg);
+ if (RT_SUCCESS(rc))
+ *pRva = pModElf->paShdrs[iSeg + pModElf->iFirstSect].sh_addr + offSeg;
+ return rc;
+}
+
+
+/** @copydoc RTLDROPS::pfnSegOffsetToRva. */
+static DECLCALLBACK(int) RTLDRELF_NAME(SegOffsetToRva)(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg,
+ PRTLDRADDR pRva)
+{
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;
+ if (iSeg >= pModElf->Ehdr.e_shnum - pModElf->iFirstSect)
+ return VERR_LDR_INVALID_SEG_OFFSET;
+
+ iSeg += pModElf->iFirstSect; /* skip section 0 if not used */
+ if (offSeg > pModElf->paShdrs[iSeg].sh_size)
+ {
+ const Elf_Shdr *pShdr2 = RTLDRELF_NAME(GetNextAllocatedSection)(pModElf, iSeg);
+ if ( !pShdr2
+ || offSeg > (pShdr2->sh_addr - pModElf->paShdrs[iSeg].sh_addr))
+ return VERR_LDR_INVALID_SEG_OFFSET;
+ }
+
+ if (!(pModElf->paShdrs[iSeg].sh_flags & SHF_ALLOC))
+ return VERR_LDR_INVALID_SEG_OFFSET;
+
+ *pRva = pModElf->paShdrs[iSeg].sh_addr;
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc RTLDROPS::pfnRvaToSegOffset. */
+static DECLCALLBACK(int) RTLDRELF_NAME(RvaToSegOffset)(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva,
+ uint32_t *piSeg, PRTLDRADDR poffSeg)
+{
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)pMod;
+ Elf_Addr PrevAddr = 0;
+ unsigned cLeft = pModElf->Ehdr.e_shnum - pModElf->iFirstSect;
+ const Elf_Shdr *pShdr = &pModElf->paShdrs[pModElf->Ehdr.e_shnum];
+ while (cLeft-- > 0)
+ {
+ pShdr--;
+ if (pShdr->sh_flags & SHF_ALLOC)
+ {
+ Elf_Addr cbSeg = PrevAddr ? PrevAddr - pShdr->sh_addr : pShdr->sh_size;
+ RTLDRADDR offSeg = Rva - pShdr->sh_addr;
+ if (offSeg <= cbSeg)
+ {
+ *poffSeg = offSeg;
+ *piSeg = cLeft;
+ return VINF_SUCCESS;
+ }
+ PrevAddr = pShdr->sh_addr;
+ }
+ }
+
+ return VERR_LDR_INVALID_RVA;
+}
+
+
+/** @callback_method_impl{FNRTLDRIMPORT, Stub used by ReadDbgInfo.} */
+static DECLCALLBACK(int) RTLDRELF_NAME(GetImportStubCallback)(RTLDRMOD hLdrMod, const char *pszModule, const char *pszSymbol,
+ unsigned uSymbol, PRTLDRADDR pValue, void *pvUser)
+{
+ RT_NOREF_PV(hLdrMod); RT_NOREF_PV(pszModule); RT_NOREF_PV(pszSymbol);
+ RT_NOREF_PV(uSymbol); RT_NOREF_PV(pValue); RT_NOREF_PV(pvUser);
+ return VERR_SYMBOL_NOT_FOUND;
+}
+
+
+/** @copydoc RTLDROPS::pfnReadDbgInfo. */
+static DECLCALLBACK(int) RTLDRELF_NAME(ReadDbgInfo)(PRTLDRMODINTERNAL pMod, uint32_t iDbgInfo, RTFOFF off,
+ size_t cb, void *pvBuf)
+{
+ PRTLDRMODELF pThis = (PRTLDRMODELF)pMod;
+ LogFlow(("%s: iDbgInfo=%#x off=%RTfoff cb=%#zu\n", __FUNCTION__, iDbgInfo, off, cb));
+
+ /*
+ * Input validation.
+ */
+ AssertReturn(iDbgInfo < pThis->Ehdr.e_shnum && iDbgInfo + 1 < pThis->Ehdr.e_shnum, VERR_INVALID_PARAMETER);
+ iDbgInfo++;
+ AssertReturn(!(pThis->paShdrs[iDbgInfo].sh_flags & SHF_ALLOC), VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->paShdrs[iDbgInfo].sh_type == SHT_PROGBITS, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->paShdrs[iDbgInfo].sh_offset == (uint64_t)off, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->paShdrs[iDbgInfo].sh_size == cb, VERR_INVALID_PARAMETER);
+ uint64_t cbRawImage = pThis->Core.pReader->pfnSize(pThis->Core.pReader);
+ AssertReturn(off >= 0 && cb <= cbRawImage && (uint64_t)off + cb <= cbRawImage, VERR_INVALID_PARAMETER);
+
+ /*
+ * Read it from the file and look for fixup sections.
+ */
+ int rc;
+ if (pThis->pvBits)
+ memcpy(pvBuf, (const uint8_t *)pThis->pvBits + (size_t)off, cb);
+ else
+ {
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvBuf, cb, off);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ uint32_t iRelocs = iDbgInfo + 1;
+ if ( iRelocs >= pThis->Ehdr.e_shnum
+ || pThis->paShdrs[iRelocs].sh_info != iDbgInfo
+ || ( pThis->paShdrs[iRelocs].sh_type != SHT_REL
+ && pThis->paShdrs[iRelocs].sh_type != SHT_RELA) )
+ {
+ iRelocs = 0;
+ while ( iRelocs < pThis->Ehdr.e_shnum
+ && ( pThis->paShdrs[iRelocs].sh_info != iDbgInfo
+ || ( pThis->paShdrs[iRelocs].sh_type != SHT_REL
+ && pThis->paShdrs[iRelocs].sh_type != SHT_RELA)) )
+ iRelocs++;
+ }
+ if ( iRelocs < pThis->Ehdr.e_shnum
+ && pThis->paShdrs[iRelocs].sh_size > 0)
+ {
+ /*
+ * Load the relocations.
+ */
+ uint8_t *pbRelocsBuf = NULL;
+ const uint8_t *pbRelocs;
+ if (pThis->pvBits)
+ pbRelocs = (const uint8_t *)pThis->pvBits + pThis->paShdrs[iRelocs].sh_offset;
+ else
+ {
+ pbRelocs = pbRelocsBuf = (uint8_t *)RTMemTmpAlloc(pThis->paShdrs[iRelocs].sh_size);
+ if (!pbRelocsBuf)
+ return VERR_NO_TMP_MEMORY;
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pbRelocsBuf,
+ pThis->paShdrs[iRelocs].sh_size,
+ pThis->paShdrs[iRelocs].sh_offset);
+ if (RT_FAILURE(rc))
+ {
+ RTMemTmpFree(pbRelocsBuf);
+ return rc;
+ }
+ }
+
+ /*
+ * Apply the relocations.
+ */
+ if (pThis->Ehdr.e_type == ET_REL)
+ rc = RTLDRELF_NAME(RelocateSectionRel)(pThis, pThis->LinkAddress,
+ RTLDRELF_NAME(GetImportStubCallback), NULL /*pvUser*/,
+ pThis->paShdrs[iDbgInfo].sh_addr,
+ pThis->paShdrs[iDbgInfo].sh_size,
+ (const uint8_t *)pvBuf,
+ (uint8_t *)pvBuf,
+ pbRelocs,
+ pThis->paShdrs[iRelocs].sh_size);
+ else
+ rc = RTLDRELF_NAME(RelocateSectionExecDyn)(pThis, pThis->LinkAddress,
+ RTLDRELF_NAME(GetImportStubCallback), NULL /*pvUser*/,
+ pThis->paShdrs[iDbgInfo].sh_addr,
+ pThis->paShdrs[iDbgInfo].sh_size,
+ (const uint8_t *)pvBuf,
+ (uint8_t *)pvBuf,
+ pbRelocs,
+ pThis->paShdrs[iRelocs].sh_size);
+
+ RTMemTmpFree(pbRelocsBuf);
+ }
+ else
+ rc = VINF_SUCCESS;
+ return rc;
+}
+
+
+/**
+ * Handles RTLDRPROP_BUILDID queries.
+ */
+static int RTLDRELF_NAME(QueryPropBuildId)(PRTLDRMODELF pThis, void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ /*
+ * Map the image bits if not already done and setup pointer into it.
+ */
+ int rc = RTLDRELF_NAME(MapBits)(pThis, true);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Search for the build ID.
+ */
+ const Elf_Shdr *paShdrs = pThis->paOrgShdrs;
+ for (unsigned iShdr = 0; iShdr < pThis->Ehdr.e_shnum; iShdr++)
+ {
+ const char *pszSectName = ELF_SH_STR(pThis, paShdrs[iShdr].sh_name);
+
+ if (!strcmp(pszSectName, ".note.gnu.build-id"))
+ {
+ if ((paShdrs[iShdr].sh_size & 3) || paShdrs[iShdr].sh_size < sizeof(Elf_Nhdr))
+ return VERR_BAD_EXE_FORMAT;
+
+ Elf_Nhdr *pNHdr = (Elf_Nhdr *)((uintptr_t)pThis->pvBits + (uintptr_t)paShdrs[iShdr].sh_offset);
+ if ( pNHdr->n_namesz > paShdrs[iShdr].sh_size
+ || pNHdr->n_descsz > paShdrs[iShdr].sh_size
+ || (paShdrs[iShdr].sh_size - pNHdr->n_descsz) < pNHdr->n_namesz
+ || pNHdr->n_type != NT_GNU_BUILD_ID)
+ return VERR_BAD_EXE_FORMAT;
+
+ const char *pszOwner = (const char *)(pNHdr + 1);
+ if ( !RTStrEnd(pszOwner, pNHdr->n_namesz)
+ || strcmp(pszOwner, "GNU"))
+ return VERR_BAD_EXE_FORMAT;
+
+ if (cbBuf < pNHdr->n_descsz)
+ return VERR_BUFFER_OVERFLOW;
+
+ memcpy(pvBuf, pszOwner + pNHdr->n_namesz, pNHdr->n_descsz);
+ *pcbRet = pNHdr->n_descsz;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_NOT_FOUND;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnQueryProp} */
+static DECLCALLBACK(int) RTLDRELF_NAME(QueryProp)(PRTLDRMODINTERNAL pMod, RTLDRPROP enmProp, void const *pvBits,
+ void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ PRTLDRMODELF pThis = (PRTLDRMODELF)pMod;
+ RT_NOREF(pvBits);
+ switch (enmProp)
+ {
+ case RTLDRPROP_BUILDID:
+ return RTLDRELF_NAME(QueryPropBuildId)(pThis, pvBuf, cbBuf, pcbRet);
+
+ case RTLDRPROP_IS_SIGNED:
+ *pcbRet = sizeof(bool);
+ return rtLdrELFLnxKModQueryPropIsSigned(pThis->Core.pReader, (bool *)pvBuf);
+
+ case RTLDRPROP_PKCS7_SIGNED_DATA:
+ *pcbRet = sizeof(bool);
+ return rtLdrELFLnxKModQueryPropPkcs7SignedData(pThis->Core.pReader, pvBuf, cbBuf, pcbRet);
+
+ default:
+ return VERR_NOT_FOUND;
+ }
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnUnwindFrame}
+ */
+static DECLCALLBACK(int)
+RTLDRELF_NAME(UnwindFrame)(PRTLDRMODINTERNAL pMod, void const *pvBits, uint32_t iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ PRTLDRMODELF pThis = (PRTLDRMODELF)pMod;
+ LogFlow(("%s: iSeg=%#x off=%RTptr\n", __FUNCTION__, iSeg, off));
+
+ /*
+ * Process the input address, making us both RVA and proper seg:offset out of it.
+ */
+ int rc;
+ RTLDRADDR uRva = off;
+ if (iSeg == UINT32_MAX)
+ rc = RTLDRELF_NAME(RvaToSegOffset)(pMod, uRva, &iSeg, &off);
+ else
+ rc = RTLDRELF_NAME(SegOffsetToRva)(pMod, iSeg, off, &uRva);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Map the image bits if not already done and setup pointer into it.
+ */
+ RT_NOREF(pvBits); /** @todo Try use passed in pvBits? */
+ rc = RTLDRELF_NAME(MapBits)(pThis, true);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Do we need to search for .eh_frame and .eh_frame_hdr?
+ */
+ if (pThis->iShEhFrame == 0)
+ {
+ pThis->iShEhFrame = ~0U;
+ pThis->iShEhFrameHdr = ~0U;
+ unsigned cLeft = 2;
+ for (unsigned iShdr = 1; iShdr < pThis->Ehdr.e_shnum; iShdr++)
+ {
+ const char *pszName = ELF_SH_STR(pThis, pThis->paShdrs[iShdr].sh_name);
+ if ( pszName[0] == '.'
+ && pszName[1] == 'e'
+ && pszName[2] == 'h'
+ && pszName[3] == '_'
+ && pszName[4] == 'f'
+ && pszName[5] == 'r'
+ && pszName[6] == 'a'
+ && pszName[7] == 'm'
+ && pszName[8] == 'e')
+ {
+ if (pszName[9] == '\0')
+ pThis->iShEhFrame = iShdr;
+ else if ( pszName[9] == '_'
+ && pszName[10] == 'h'
+ && pszName[11] == 'd'
+ && pszName[12] == 'r'
+ && pszName[13] == '\0')
+ pThis->iShEhFrameHdr = iShdr;
+ else
+ continue;
+ if (--cLeft == 0)
+ break;
+ }
+ }
+ }
+
+ /*
+ * Any info present?
+ */
+ unsigned iShdr = pThis->iShEhFrame;
+ if ( iShdr != ~0U
+ && pThis->paShdrs[iShdr].sh_size > 0)
+ {
+ if (pThis->paShdrs[iShdr].sh_flags & SHF_ALLOC)
+ return rtDwarfUnwind_EhData((uint8_t const *)pThis->pvBits + pThis->paShdrs[iShdr].sh_addr,
+ pThis->paShdrs[iShdr].sh_size, pThis->paShdrs[iShdr].sh_addr,
+ iSeg, off, uRva, pState, pThis->Core.enmArch);
+ }
+ return VERR_DBG_NO_UNWIND_INFO;
+}
+
+
+/**
+ * The ELF module operations.
+ */
+static RTLDROPS RTLDRELF_MID(s_rtldrElf,Ops) =
+{
+#if ELF_MODE == 32
+ "elf32",
+#elif ELF_MODE == 64
+ "elf64",
+#endif
+ RTLDRELF_NAME(Close),
+ NULL, /* Get Symbol */
+ RTLDRELF_NAME(Done),
+ RTLDRELF_NAME(EnumSymbols),
+ /* ext: */
+ RTLDRELF_NAME(GetImageSize),
+ RTLDRELF_NAME(GetBits),
+ RTLDRELF_NAME(Relocate),
+ RTLDRELF_NAME(GetSymbolEx),
+ NULL /*pfnQueryForwarderInfo*/,
+ RTLDRELF_NAME(EnumDbgInfo),
+ RTLDRELF_NAME(EnumSegments),
+ RTLDRELF_NAME(LinkAddressToSegOffset),
+ RTLDRELF_NAME(LinkAddressToRva),
+ RTLDRELF_NAME(SegOffsetToRva),
+ RTLDRELF_NAME(RvaToSegOffset),
+ RTLDRELF_NAME(ReadDbgInfo),
+ RTLDRELF_NAME(QueryProp),
+ NULL /*pfnVerifySignature*/,
+ rtldrELFLnxKModHashImage,
+ RTLDRELF_NAME(UnwindFrame),
+ 42
+};
+
+
+
+/**
+ * Validates the ELF header.
+ *
+ * @returns iprt status code.
+ * @param pEhdr Pointer to the ELF header.
+ * @param cbRawImage The size of the raw image.
+ * @param pszLogName The log name.
+ * @param penmArch Where to return the architecture.
+ * @param pErrInfo Where to return extended error info. Optional.
+ */
+static int RTLDRELF_NAME(ValidateElfHeader)(const Elf_Ehdr *pEhdr, uint64_t cbRawImage, const char *pszLogName,
+ PRTLDRARCH penmArch, PRTERRINFO pErrInfo)
+{
+ Log3(("RTLdrELF: e_ident: %.*Rhxs\n"
+ "RTLdrELF: e_type: " FMT_ELF_HALF "\n"
+ "RTLdrELF: e_version: " FMT_ELF_HALF "\n"
+ "RTLdrELF: e_entry: " FMT_ELF_ADDR "\n"
+ "RTLdrELF: e_phoff: " FMT_ELF_OFF "\n"
+ "RTLdrELF: e_shoff: " FMT_ELF_OFF "\n"
+ "RTLdrELF: e_flags: " FMT_ELF_WORD "\n"
+ "RTLdrELF: e_ehsize: " FMT_ELF_HALF "\n"
+ "RTLdrELF: e_phentsize: " FMT_ELF_HALF "\n"
+ "RTLdrELF: e_phnum: " FMT_ELF_HALF "\n"
+ "RTLdrELF: e_shentsize: " FMT_ELF_HALF "\n"
+ "RTLdrELF: e_shnum: " FMT_ELF_HALF "\n"
+ "RTLdrELF: e_shstrndx: " FMT_ELF_HALF "\n",
+ RT_ELEMENTS(pEhdr->e_ident), &pEhdr->e_ident[0], pEhdr->e_type, pEhdr->e_version,
+ pEhdr->e_entry, pEhdr->e_phoff, pEhdr->e_shoff,pEhdr->e_flags, pEhdr->e_ehsize, pEhdr->e_phentsize,
+ pEhdr->e_phnum, pEhdr->e_shentsize, pEhdr->e_shnum, pEhdr->e_shstrndx));
+
+ if ( pEhdr->e_ident[EI_MAG0] != ELFMAG0
+ || pEhdr->e_ident[EI_MAG1] != ELFMAG1
+ || pEhdr->e_ident[EI_MAG2] != ELFMAG2
+ || pEhdr->e_ident[EI_MAG3] != ELFMAG3)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Invalid ELF magic (%.*Rhxs)", pszLogName, sizeof(pEhdr->e_ident), pEhdr->e_ident);
+ if (pEhdr->e_ident[EI_CLASS] != RTLDRELF_SUFF(ELFCLASS))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Invalid ELF class (%.*Rhxs)", pszLogName, sizeof(pEhdr->e_ident), pEhdr->e_ident);
+ if (pEhdr->e_ident[EI_DATA] != ELFDATA2LSB)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRELF_ODD_ENDIAN,
+ "%s: ELF endian %x is unsupported", pszLogName, pEhdr->e_ident[EI_DATA]);
+ if (pEhdr->e_version != EV_CURRENT)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRELF_VERSION,
+ "%s: ELF version %x is unsupported", pszLogName, pEhdr->e_version);
+
+ if (sizeof(Elf_Ehdr) != pEhdr->e_ehsize)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Elf header e_ehsize is %d expected %d!", pszLogName, pEhdr->e_ehsize, sizeof(Elf_Ehdr));
+ if ( sizeof(Elf_Phdr) != pEhdr->e_phentsize
+ && ( pEhdr->e_phnum != 0
+ || pEhdr->e_type == ET_DYN
+ || pEhdr->e_type == ET_EXEC))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: Elf header e_phentsize is %d expected %d!",
+ pszLogName, pEhdr->e_phentsize, sizeof(Elf_Phdr));
+ if (sizeof(Elf_Shdr) != pEhdr->e_shentsize)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: Elf header e_shentsize is %d expected %d!",
+ pszLogName, pEhdr->e_shentsize, sizeof(Elf_Shdr));
+
+ switch (pEhdr->e_type)
+ {
+ case ET_REL:
+ case ET_EXEC:
+ case ET_DYN:
+ break;
+ default:
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: image type %#x is not supported!",
+ pszLogName, pEhdr->e_type);
+ }
+
+ switch (pEhdr->e_machine)
+ {
+#if ELF_MODE == 32
+ case EM_386:
+ case EM_486:
+ *penmArch = RTLDRARCH_X86_32;
+ break;
+#elif ELF_MODE == 64
+ case EM_X86_64:
+ *penmArch = RTLDRARCH_AMD64;
+ break;
+#endif
+ default:
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRELF_MACHINE,
+ "%s: machine type %u is not supported!", pszLogName, pEhdr->e_machine);
+ }
+
+ if ( pEhdr->e_phoff < pEhdr->e_ehsize
+ && !(pEhdr->e_phoff && pEhdr->e_phnum)
+ && pEhdr->e_phnum)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: The program headers overlap with the ELF header! e_phoff=" FMT_ELF_OFF,
+ pszLogName, pEhdr->e_phoff);
+ if ( pEhdr->e_phoff + pEhdr->e_phnum * pEhdr->e_phentsize > cbRawImage
+ || pEhdr->e_phoff + pEhdr->e_phnum * pEhdr->e_phentsize < pEhdr->e_phoff)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: The program headers extends beyond the file! e_phoff=" FMT_ELF_OFF " e_phnum=" FMT_ELF_HALF,
+ pszLogName, pEhdr->e_phoff, pEhdr->e_phnum);
+
+
+ if ( pEhdr->e_shoff < pEhdr->e_ehsize
+ && !(pEhdr->e_shoff && pEhdr->e_shnum))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: The section headers overlap with the ELF header! e_shoff=" FMT_ELF_OFF,
+ pszLogName, pEhdr->e_shoff);
+ if ( pEhdr->e_shoff + pEhdr->e_shnum * pEhdr->e_shentsize > cbRawImage
+ || pEhdr->e_shoff + pEhdr->e_shnum * pEhdr->e_shentsize < pEhdr->e_shoff)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: The section headers extends beyond the file! e_shoff=" FMT_ELF_OFF " e_shnum=" FMT_ELF_HALF,
+ pszLogName, pEhdr->e_shoff, pEhdr->e_shnum);
+
+ if (pEhdr->e_shstrndx == 0 || pEhdr->e_shstrndx > pEhdr->e_shnum)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: The section headers string table is out of bounds! e_shstrndx=" FMT_ELF_HALF " e_shnum=" FMT_ELF_HALF,
+ pszLogName, pEhdr->e_shstrndx, pEhdr->e_shnum);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Gets the section header name.
+ *
+ * @returns pszName.
+ * @param pEhdr The elf header.
+ * @param offName The offset of the section header name.
+ * @param pszName Where to store the name.
+ * @param cbName The size of the buffer pointed to by pszName.
+ */
+const char *RTLDRELF_NAME(GetSHdrName)(PRTLDRMODELF pModElf, Elf_Word offName, char *pszName, size_t cbName)
+{
+ RTFOFF off = pModElf->paShdrs[pModElf->Ehdr.e_shstrndx].sh_offset + offName;
+ int rc = pModElf->Core.pReader->pfnRead(pModElf->Core.pReader, pszName, cbName - 1, off);
+ if (RT_FAILURE(rc))
+ {
+ /* read by for byte. */
+ for (unsigned i = 0; i < cbName; i++, off++)
+ {
+ rc = pModElf->Core.pReader->pfnRead(pModElf->Core.pReader, pszName + i, 1, off);
+ if (RT_FAILURE(rc))
+ {
+ pszName[i] = '\0';
+ break;
+ }
+ }
+ }
+
+ pszName[cbName - 1] = '\0';
+ return pszName;
+}
+
+
+/**
+ * Validates a section header.
+ *
+ * @returns iprt status code.
+ * @param pModElf Pointer to the module structure.
+ * @param iShdr The index of section header which should be validated.
+ * The section headers are found in the pModElf->paShdrs array.
+ * @param cbRawImage The size of the raw image.
+ * @param pszLogName The log name.
+ * @param pErrInfo Where to return extended error info. Optional.
+ */
+static int RTLDRELF_NAME(ValidateSectionHeader)(PRTLDRMODELF pModElf, unsigned iShdr, uint64_t cbRawImage,
+ const char *pszLogName, PRTERRINFO pErrInfo)
+{
+ const Elf_Shdr *pShdr = &pModElf->paShdrs[iShdr];
+ char szSectionName[80]; NOREF(szSectionName);
+ Log3(("RTLdrELF: Section Header #%d:\n"
+ "RTLdrELF: sh_name: " FMT_ELF_WORD " - %s\n"
+ "RTLdrELF: sh_type: " FMT_ELF_WORD " (%s)\n"
+ "RTLdrELF: sh_flags: " FMT_ELF_XWORD "\n"
+ "RTLdrELF: sh_addr: " FMT_ELF_ADDR "\n"
+ "RTLdrELF: sh_offset: " FMT_ELF_OFF "\n"
+ "RTLdrELF: sh_size: " FMT_ELF_XWORD "\n"
+ "RTLdrELF: sh_link: " FMT_ELF_WORD "\n"
+ "RTLdrELF: sh_info: " FMT_ELF_WORD "\n"
+ "RTLdrELF: sh_addralign: " FMT_ELF_XWORD "\n"
+ "RTLdrELF: sh_entsize: " FMT_ELF_XWORD "\n",
+ iShdr,
+ pShdr->sh_name, RTLDRELF_NAME(GetSHdrName)(pModElf, pShdr->sh_name, szSectionName, sizeof(szSectionName)),
+ pShdr->sh_type, rtldrElfGetShdrType(pShdr->sh_type), pShdr->sh_flags, pShdr->sh_addr,
+ pShdr->sh_offset, pShdr->sh_size, pShdr->sh_link, pShdr->sh_info, pShdr->sh_addralign,
+ pShdr->sh_entsize));
+
+ if (iShdr == 0)
+ {
+ if ( pShdr->sh_name != 0
+ || pShdr->sh_type != SHT_NULL
+ || pShdr->sh_flags != 0
+ || pShdr->sh_addr != 0
+ || pShdr->sh_size != 0
+ || pShdr->sh_offset != 0
+ || pShdr->sh_link != SHN_UNDEF
+ || pShdr->sh_addralign != 0
+ || pShdr->sh_entsize != 0 )
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Bad #0 section: %.*Rhxs", pszLogName, sizeof(*pShdr), pShdr);
+ return VINF_SUCCESS;
+ }
+
+ if (pShdr->sh_name >= pModElf->cbShStr)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Shdr #%d: sh_name (%d) is beyond the end of the section header string table (%d)!",
+ pszLogName, iShdr, pShdr->sh_name, pModElf->cbShStr);
+
+ if (pShdr->sh_link >= pModElf->Ehdr.e_shnum)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Shdr #%d: sh_link (%d) is beyond the end of the section table (%d)!",
+ pszLogName, iShdr, pShdr->sh_link, pModElf->Ehdr.e_shnum);
+
+ switch (pShdr->sh_type)
+ {
+ /** @todo find specs and check up which sh_info fields indicates section table entries */
+ case 12301230:
+ if (pShdr->sh_info >= pModElf->Ehdr.e_shnum)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Shdr #%d: sh_info (%d) is beyond the end of the section table (%d)!",
+ pszLogName, iShdr, pShdr->sh_link, pModElf->Ehdr.e_shnum);
+ break;
+
+ case SHT_NULL:
+ break;
+ case SHT_PROGBITS:
+ case SHT_SYMTAB:
+ case SHT_STRTAB:
+ case SHT_RELA:
+ case SHT_HASH:
+ case SHT_DYNAMIC:
+ case SHT_NOTE:
+ case SHT_NOBITS:
+ case SHT_REL:
+ case SHT_SHLIB:
+ case SHT_DYNSYM:
+ /*
+ * For these types sh_info doesn't have any special meaning, or anything which
+ * we need/can validate now.
+ */
+ break;
+
+
+ default:
+ Log(("RTLdrELF: %s: Warning, unknown type %d!\n", pszLogName, pShdr->sh_type));
+ break;
+ }
+
+ if ( pShdr->sh_type != SHT_NOBITS
+ && pShdr->sh_size)
+ {
+ uint64_t offEnd = pShdr->sh_offset + pShdr->sh_size;
+ if ( offEnd > cbRawImage
+ || offEnd < (uint64_t)pShdr->sh_offset)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Shdr #%d: sh_offset (" FMT_ELF_OFF ") + sh_size (" FMT_ELF_XWORD " = %RX64) is beyond the end of the file (%RX64)!",
+ pszLogName, iShdr, pShdr->sh_offset, pShdr->sh_size, offEnd, cbRawImage);
+ if (pShdr->sh_offset < sizeof(Elf_Ehdr))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Shdr #%d: sh_offset (" FMT_ELF_OFF ") + sh_size (" FMT_ELF_XWORD ") is starting in the ELF header!",
+ pszLogName, iShdr, pShdr->sh_offset, pShdr->sh_size);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Process the section headers.
+ *
+ * @returns iprt status code.
+ * @param pModElf Pointer to the module structure.
+ * @param paShdrs The section headers.
+ * @param cbRawImage The size of the raw image.
+ * @param pszLogName The log name.
+ * @param pErrInfo Where to return extended error info. Optional.
+ */
+static int RTLDRELF_NAME(ValidateAndProcessSectionHeaders)(PRTLDRMODELF pModElf, Elf_Shdr *paShdrs, uint64_t cbRawImage,
+ const char *pszLogName, PRTERRINFO pErrInfo)
+{
+ Elf_Addr uNextAddr = 0;
+ for (unsigned i = 0; i < pModElf->Ehdr.e_shnum; i++)
+ {
+ int rc = RTLDRELF_NAME(ValidateSectionHeader)(pModElf, i, cbRawImage, pszLogName, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * We're looking for symbol tables.
+ */
+ if (paShdrs[i].sh_type == SHT_SYMTAB)
+ {
+ if (pModElf->Rel.iSymSh != ~0U)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRELF_MULTIPLE_SYMTABS,
+ "%s: Multiple symbol tabs! iSymSh=%d i=%d", pszLogName, pModElf->Rel.iSymSh, i);
+ pModElf->Rel.iSymSh = i;
+ pModElf->Rel.cSyms = (unsigned)(paShdrs[i].sh_size / sizeof(Elf_Sym));
+ AssertBreakStmt(pModElf->Rel.cSyms == paShdrs[i].sh_size / sizeof(Elf_Sym), rc = VERR_IMAGE_TOO_BIG);
+ pModElf->Rel.iStrSh = paShdrs[i].sh_link;
+ pModElf->Rel.cbStr = (unsigned)paShdrs[pModElf->Rel.iStrSh].sh_size;
+ AssertBreakStmt(pModElf->Rel.cbStr == paShdrs[pModElf->Rel.iStrSh].sh_size, rc = VERR_IMAGE_TOO_BIG);
+ }
+ else if (paShdrs[i].sh_type == SHT_DYNSYM)
+ {
+ if (pModElf->Dyn.iSymSh != ~0U)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRELF_MULTIPLE_SYMTABS,
+ "%s: Multiple dynamic symbol tabs! iSymSh=%d i=%d", pszLogName, pModElf->Dyn.iSymSh, i);
+ if (pModElf->Ehdr.e_type != ET_DYN && pModElf->Ehdr.e_type != ET_EXEC)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Unexpected SHT_DYNSYM (i=%d) for e_type=%d", pszLogName, i, pModElf->Ehdr.e_type);
+ pModElf->Dyn.iSymSh = i;
+ pModElf->Dyn.cSyms = (unsigned)(paShdrs[i].sh_size / sizeof(Elf_Sym));
+ AssertBreakStmt(pModElf->Dyn.cSyms == paShdrs[i].sh_size / sizeof(Elf_Sym), rc = VERR_IMAGE_TOO_BIG);
+ pModElf->Dyn.iStrSh = paShdrs[i].sh_link;
+ pModElf->Dyn.cbStr = (unsigned)paShdrs[pModElf->Dyn.iStrSh].sh_size;
+ AssertBreakStmt(pModElf->Dyn.cbStr == paShdrs[pModElf->Dyn.iStrSh].sh_size, rc = VERR_IMAGE_TOO_BIG);
+ }
+ /*
+ * We're also look for the dynamic section.
+ */
+ else if (paShdrs[i].sh_type == SHT_DYNAMIC)
+ {
+ if (pModElf->iShDynamic != ~0U)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Multiple dynamic sections! iShDynamic=%d i=%d",
+ pszLogName, pModElf->iShDynamic, i);
+ if (pModElf->Ehdr.e_type != ET_DYN && pModElf->Ehdr.e_type != ET_EXEC)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Unexpected SHT_DYNAMIC (i=%d) for e_type=%d", pszLogName, i, pModElf->Ehdr.e_type);
+ if (paShdrs[i].sh_entsize != sizeof(Elf_Dyn))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: SHT_DYNAMIC (i=%d) sh_entsize=" FMT_ELF_XWORD ", expected %#zx",
+ pszLogName, i, paShdrs[i].sh_entsize, sizeof(Elf_Dyn));
+ pModElf->iShDynamic = i;
+ Elf_Xword const cDynamic = paShdrs[i].sh_size / sizeof(Elf_Dyn);
+ if (cDynamic > _64K || cDynamic < 2)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: SHT_DYNAMIC (i=%d) sh_size=" FMT_ELF_XWORD " is out of range (2..64K)",
+ pszLogName, i, paShdrs[i].sh_size);
+ pModElf->cDynamic = (unsigned)cDynamic;
+ }
+
+ /*
+ * Special checks for the section string table.
+ */
+ if (i == pModElf->Ehdr.e_shstrndx)
+ {
+ if (paShdrs[i].sh_type != SHT_STRTAB)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Section header string table is not a SHT_STRTAB: %#x",
+ pszLogName, paShdrs[i].sh_type);
+ if (paShdrs[i].sh_size == 0)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: Section header string table is empty", pszLogName);
+ }
+
+ /*
+ * Kluge for the .data..percpu segment in 64-bit linux kernels.
+ */
+ if (paShdrs[i].sh_flags & SHF_ALLOC)
+ {
+ if ( paShdrs[i].sh_addr == 0
+ && paShdrs[i].sh_addr < uNextAddr)
+ {
+ Elf_Addr uAddr = RT_ALIGN_T(uNextAddr, paShdrs[i].sh_addralign, Elf_Addr);
+ Log(("RTLdrElf: Out of order section #%d; adjusting sh_addr from " FMT_ELF_ADDR " to " FMT_ELF_ADDR "\n",
+ i, paShdrs[i].sh_addr, uAddr));
+ paShdrs[i].sh_addr = uAddr;
+ }
+ uNextAddr = paShdrs[i].sh_addr + paShdrs[i].sh_size;
+ }
+ } /* for each section header */
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Process the section headers.
+ *
+ * @returns iprt status code.
+ * @param pModElf Pointer to the module structure.
+ * @param paShdrs The section headers.
+ * @param cbRawImage The size of the raw image.
+ * @param pszLogName The log name.
+ * @param pErrInfo Where to return extended error info. Optional.
+ */
+static int RTLDRELF_NAME(ValidateAndProcessDynamicInfo)(PRTLDRMODELF pModElf, uint64_t cbRawImage, uint32_t fFlags,
+ const char *pszLogName, PRTERRINFO pErrInfo)
+{
+ /*
+ * Check preconditions.
+ */
+ AssertReturn(pModElf->Ehdr.e_type == ET_DYN || pModElf->Ehdr.e_type == ET_EXEC, VERR_INTERNAL_ERROR_2);
+ if (pModElf->Ehdr.e_phnum <= 1 || pModElf->Ehdr.e_phnum >= _32K)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: e_phnum=%u is out of bounds (2..32K)", pszLogName, pModElf->Ehdr.e_phnum);
+ if (pModElf->iShDynamic == ~0U)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: no .dynamic section", pszLogName);
+ AssertReturn(pModElf->cDynamic > 1 && pModElf->cDynamic <= _64K, VERR_INTERNAL_ERROR_3);
+
+ /* ASSUME that the sections are ordered by address. That simplifies
+ validation code further down. */
+ AssertReturn(pModElf->Ehdr.e_shnum >= 2, VERR_INTERNAL_ERROR_4);
+ Elf_Shdr const *paShdrs = pModElf->paShdrs;
+ Elf_Addr uPrevEnd = paShdrs[1].sh_addr + paShdrs[1].sh_size;
+ for (unsigned i = 2; i < pModElf->Ehdr.e_shnum; i++)
+ if (paShdrs[i].sh_flags & SHF_ALLOC)
+ {
+ if (uPrevEnd > paShdrs[i].sh_addr)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: section %u is out of order: uPrevEnd=" FMT_ELF_ADDR " sh_addr=" FMT_ELF_ADDR,
+ pszLogName, i, uPrevEnd, paShdrs[i].sh_addr);
+ uPrevEnd = paShdrs[i].sh_addr + paShdrs[i].sh_size;
+ }
+
+ /* Must have string and symbol tables. */
+ if (pModElf->Dyn.iStrSh == ~0U)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: No dynamic string table section", pszLogName);
+ if (pModElf->Dyn.iSymSh == ~0U)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: No dynamic symbol table section", pszLogName);
+
+ /*
+ * Load the program headers.
+ */
+ size_t const cbPhdrs = sizeof(pModElf->paPhdrs[0]) * pModElf->Ehdr.e_phnum;
+ Elf_Phdr *paPhdrs = (Elf_Phdr *)RTMemAllocZ(cbPhdrs);
+ pModElf->paPhdrs = paPhdrs;
+ AssertReturn(paPhdrs, VERR_NO_MEMORY);
+
+ int rc = pModElf->Core.pReader->pfnRead(pModElf->Core.pReader, paPhdrs, cbPhdrs, pModElf->Ehdr.e_phoff);
+ if (RT_FAILURE(rc))
+ return RTERRINFO_LOG_SET_F(pErrInfo, rc, "%s: pfnRead(,,%#zx, " FMT_ELF_OFF ") -> %Rrc",
+ pszLogName, cbPhdrs, pModElf->Ehdr.e_phoff, rc);
+
+ /*
+ * Validate them.
+ */
+ unsigned cbPage = _4K; /** @todo generalize architecture specific stuff using its own code template header. */
+ switch (pModElf->Core.enmArch)
+ {
+ case RTLDRARCH_AMD64:
+ case RTLDRARCH_X86_32:
+ break;
+ default:
+ AssertFailedBreak(/** @todo page size for got.plt hacks */);
+ }
+ unsigned iLoad = 0;
+ unsigned iLoadShdr = 1; /* ASSUMES ordered (checked above). */
+ unsigned cDynamic = 0;
+ Elf_Addr cbImage = 0;
+ Elf_Addr uLinkAddress = ~(Elf_Addr)0;
+ for (unsigned i = 0; i < pModElf->Ehdr.e_phnum; i++)
+ {
+ const Elf_Phdr * const pPhdr = &paPhdrs[i];
+ Log3(("RTLdrELF: Program Header #%d:\n"
+ "RTLdrELF: p_type: " FMT_ELF_WORD " (%s)\n"
+ "RTLdrELF: p_flags: " FMT_ELF_WORD "\n"
+ "RTLdrELF: p_offset: " FMT_ELF_OFF "\n"
+ "RTLdrELF: p_vaddr: " FMT_ELF_ADDR "\n"
+ "RTLdrELF: p_paddr: " FMT_ELF_ADDR "\n"
+ "RTLdrELF: p_filesz: " FMT_ELF_XWORD "\n"
+ "RTLdrELF: p_memsz: " FMT_ELF_XWORD "\n"
+ "RTLdrELF: p_align: " FMT_ELF_XWORD "\n",
+ i,
+ pPhdr->p_type, rtldrElfGetPhdrType(pPhdr->p_type), pPhdr->p_flags, pPhdr->p_offset,
+ pPhdr->p_vaddr, pPhdr->p_paddr, pPhdr->p_filesz, pPhdr->p_memsz, pPhdr->p_align));
+
+ if (pPhdr->p_type == DT_NULL)
+ continue;
+
+ if ( pPhdr->p_filesz != 0
+ && ( pPhdr->p_offset >= cbRawImage
+ || pPhdr->p_filesz > cbRawImage
+ || pPhdr->p_offset + pPhdr->p_filesz > cbRawImage))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Prog Hdr #%u: bogus p_offset=" FMT_ELF_OFF " & p_filesz=" FMT_ELF_XWORD " (file size %#RX64)",
+ pszLogName, i, pPhdr->p_offset, pPhdr->p_filesz, cbRawImage);
+
+ if (pPhdr->p_flags & ~(Elf64_Word)(PF_X | PF_R | PF_W))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: Prog Hdr #%u: bogus p_flags=" FMT_ELF_WORD,
+ pszLogName, i, pPhdr->p_flags);
+
+ if (!RT_IS_POWER_OF_TWO(pPhdr->p_align))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: Prog Hdr #%u: bogus p_align=" FMT_ELF_XWORD,
+ pszLogName, i, pPhdr->p_align);
+
+ if ( pPhdr->p_align > 1
+ && pPhdr->p_memsz > 0
+ && pPhdr->p_filesz > 0
+ && (pPhdr->p_offset & (pPhdr->p_align - 1)) != (pPhdr->p_vaddr & (pPhdr->p_align - 1)))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Prog Hdr #%u: misaligned p_offset=" FMT_ELF_OFF " p_vaddr=" FMT_ELF_ADDR " p_align=" FMT_ELF_XWORD,
+ pszLogName, i, pPhdr->p_offset, pPhdr->p_vaddr, pPhdr->p_align);
+
+ /* Do some type specfic checks: */
+ switch (pPhdr->p_type)
+ {
+ case PT_LOAD:
+ {
+ if (pPhdr->p_memsz < pPhdr->p_filesz)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Prog Hdr #%u/LOAD#%u: bogus p_memsz=" FMT_ELF_XWORD " or p_filesz=" FMT_ELF_XWORD,
+ pszLogName, i, iLoad, pPhdr->p_memsz, pPhdr->p_filesz);
+ cbImage = pPhdr->p_vaddr + pPhdr->p_memsz;
+ if (iLoad == 0)
+ uLinkAddress = pPhdr->p_vaddr;
+
+ /* Find the corresponding sections, checking their addresses and
+ file offsets since the rest of the code is still section based
+ rather than using program headers as it should... */
+ Elf_Off off = pPhdr->p_offset;
+ Elf_Addr uAddr = pPhdr->p_vaddr;
+ Elf_Xword cbMem = pPhdr->p_memsz;
+ Elf_Xword cbFile = pPhdr->p_filesz;
+
+ /* HACK to allow loading isolinux-debug.elf where program headers aren't
+ sorted by virtual address. */
+ if ( (fFlags & RTLDR_O_FOR_DEBUG)
+ && uAddr != paShdrs[iLoadShdr].sh_addr)
+ {
+ for (unsigned iShdr = 1; iShdr < pModElf->Ehdr.e_shnum; iShdr++)
+ if (uAddr == paShdrs[iShdr].sh_addr)
+ {
+ iLoadShdr = iShdr;
+ break;
+ }
+ }
+
+ while (cbMem > 0)
+ {
+ if (iLoadShdr < pModElf->Ehdr.e_shnum)
+ { /* likely */ }
+ else if (iLoadShdr == pModElf->Ehdr.e_shnum)
+ {
+ /** @todo anything else to check here? */
+ iLoadShdr++;
+ break;
+ }
+ else
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Prog Hdr #%u/LOAD#%u: Out of sections at " FMT_ELF_ADDR " LB " FMT_ELF_XWORD,
+ pszLogName, i, iLoad, uAddr, cbMem);
+ if (!(paShdrs[iLoadShdr].sh_flags & SHF_ALLOC))
+ {
+ if ( paShdrs[iLoadShdr].sh_type != SHT_NOBITS
+ && paShdrs[iLoadShdr].sh_size > 0
+ && off < paShdrs[iLoadShdr].sh_offset + paShdrs[iLoadShdr].sh_size
+ && paShdrs[iLoadShdr].sh_offset < off + cbMem)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Prog Hdr #%u/LOAD#%u: Overlaps with !SHF_ALLOC section at " FMT_ELF_OFF " LB " FMT_ELF_XWORD,
+ pszLogName, i, iLoad, paShdrs[iLoadShdr].sh_offset, paShdrs[iLoadShdr].sh_size);
+ pModElf->paShdrExtras[iLoadShdr].idxPhdr = UINT16_MAX;
+ iLoadShdr++;
+ continue;
+ }
+
+ if (uAddr != paShdrs[iLoadShdr].sh_addr)
+ {
+ /* Before the first section we expect headers to be loaded, so
+ that the file is simply mapped from file offset zero. */
+ if ( iLoadShdr == 1
+ && iLoad == 0
+ && paShdrs[1].sh_addr == paShdrs[1].sh_offset
+ && cbFile >= paShdrs[1].sh_offset
+ && cbMem >= paShdrs[1].sh_offset)
+ {
+ /* Modify paShdrs[0] to describe the gap. ".elf.headers" */
+ pModElf->iFirstSect = 0;
+ pModElf->paShdrs[0].sh_name = 0;
+ pModElf->paShdrs[0].sh_type = SHT_PROGBITS;
+ pModElf->paShdrs[0].sh_flags = SHF_ALLOC
+ | (pPhdr->p_flags & PF_W ? SHF_WRITE : 0)
+ | (pPhdr->p_flags & PF_X ? SHF_EXECINSTR : 0);
+ pModElf->paShdrs[0].sh_addr = uAddr;
+ pModElf->paShdrs[0].sh_offset = off;
+ pModElf->paShdrs[0].sh_size = paShdrs[1].sh_offset;
+ pModElf->paShdrs[0].sh_link = 0;
+ pModElf->paShdrs[0].sh_info = 0;
+ pModElf->paShdrs[0].sh_addralign = pPhdr->p_align;
+ pModElf->paShdrs[0].sh_entsize = 0;
+ *(Elf_Shdr *)pModElf->paOrgShdrs = pModElf->paShdrs[0]; /* (necessary for segment enumeration) */
+
+ uAddr += paShdrs[1].sh_offset;
+ cbMem -= paShdrs[1].sh_offset;
+ cbFile -= paShdrs[1].sh_offset;
+ off = paShdrs[1].sh_offset;
+ }
+ /* Alignment padding? Allow up to a page size. */
+ else if ( paShdrs[iLoadShdr].sh_addr > uAddr
+ && paShdrs[iLoadShdr].sh_addr - uAddr
+ < RT_MAX(paShdrs[iLoadShdr].sh_addralign, cbPage /*got.plt hack*/))
+ {
+ Elf_Xword cbAlignPadding = paShdrs[iLoadShdr].sh_addr - uAddr;
+ if (cbAlignPadding >= cbMem)
+ break;
+ cbMem -= cbAlignPadding;
+ uAddr += cbAlignPadding;
+ if (cbFile > cbAlignPadding)
+ {
+ off += cbAlignPadding;
+ cbFile -= cbAlignPadding;
+ }
+ else
+ {
+ off += cbFile;
+ cbFile = 0;
+ }
+ }
+ }
+
+ if ( uAddr == paShdrs[iLoadShdr].sh_addr
+ && cbMem >= paShdrs[iLoadShdr].sh_size
+ && ( paShdrs[iLoadShdr].sh_type != SHT_NOBITS
+ ? off == paShdrs[iLoadShdr].sh_offset
+ && cbFile >= paShdrs[iLoadShdr].sh_size /* this might be too strict... */
+ : cbFile == 0
+ || cbMem > paShdrs[iLoadShdr].sh_size /* isolinux.elf: linker merge no-bits and progbits sections */) )
+ {
+ if ( paShdrs[iLoadShdr].sh_type != SHT_NOBITS
+ || cbFile != 0)
+ {
+ off += paShdrs[iLoadShdr].sh_size;
+ cbFile -= paShdrs[iLoadShdr].sh_size;
+ }
+ uAddr += paShdrs[iLoadShdr].sh_size;
+ cbMem -= paShdrs[iLoadShdr].sh_size;
+ }
+ else
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Prog Hdr #%u/LOAD#%u: Mismatch at " FMT_ELF_ADDR " LB " FMT_ELF_XWORD " (file " FMT_ELF_OFF " LB " FMT_ELF_XWORD ") with section #%u " FMT_ELF_ADDR " LB " FMT_ELF_XWORD " (file " FMT_ELF_OFF " sh_type=" FMT_ELF_WORD ")",
+ pszLogName, i, iLoad, uAddr, cbMem, off, cbFile,
+ iLoadShdr, paShdrs[iLoadShdr].sh_addr, paShdrs[iLoadShdr].sh_size,
+ paShdrs[iLoadShdr].sh_offset, paShdrs[iLoadShdr].sh_type);
+
+ pModElf->paShdrExtras[iLoadShdr].idxPhdr = iLoad;
+ iLoadShdr++;
+ } /* section loop */
+
+ iLoad++;
+ break;
+ }
+
+ case PT_DYNAMIC:
+ {
+ const Elf_Shdr *pShdr = &pModElf->paShdrs[pModElf->iShDynamic];
+ if (pPhdr->p_offset != pShdr->sh_offset)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Prog Hdr #%u/DYNAMIC: p_offset=" FMT_ELF_OFF " expected " FMT_ELF_OFF,
+ pszLogName, i, pPhdr->p_offset, pShdr->sh_offset);
+ if (RT_MAX(pPhdr->p_memsz, pPhdr->p_filesz) != pShdr->sh_size)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: Prog Hdr #%u/DYNAMIC: expected " FMT_ELF_XWORD " for RT_MAX(p_memsz=" FMT_ELF_XWORD ", p_filesz=" FMT_ELF_XWORD ")",
+ pszLogName, i, pShdr->sh_size, pPhdr->p_memsz, pPhdr->p_filesz);
+ cDynamic++;
+ break;
+ }
+ }
+ }
+
+ if (iLoad == 0)
+ return RTERRINFO_LOG_SET_F(pErrInfo, rc, "%s: No PT_LOAD program headers", pszLogName);
+ if (cDynamic != 1)
+ return RTERRINFO_LOG_SET_F(pErrInfo, rc, "%s: No program header for the DYNAMIC section", pszLogName);
+
+ cbImage -= uLinkAddress;
+ pModElf->cbImage = (uint64_t)cbImage;
+ pModElf->LinkAddress = uLinkAddress;
+ AssertReturn(pModElf->cbImage == cbImage, VERR_INTERNAL_ERROR_5);
+ Log3(("RTLdrELF: LinkAddress=" FMT_ELF_ADDR " cbImage=" FMT_ELF_ADDR " (from PT_LOAD)\n", uLinkAddress, cbImage));
+
+ for (; iLoadShdr < pModElf->Ehdr.e_shnum; iLoadShdr++)
+ if ( !(paShdrs[iLoadShdr].sh_flags & SHF_ALLOC)
+ || paShdrs[iLoadShdr].sh_size == 0)
+ pModElf->paShdrExtras[iLoadShdr].idxPhdr = UINT16_MAX;
+ else
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: No PT_LOAD for section #%u " FMT_ELF_ADDR " LB " FMT_ELF_XWORD " (file " FMT_ELF_OFF " sh_type=" FMT_ELF_WORD ")",
+ pszLogName, iLoadShdr, paShdrs[iLoadShdr].sh_addr, paShdrs[iLoadShdr].sh_size,
+ paShdrs[iLoadShdr].sh_offset, paShdrs[iLoadShdr].sh_type);
+
+ /*
+ * Load and validate the dynamic table. We have got / will get most of the
+ * info we need from the section table, so we must make sure this matches up.
+ */
+ Log3(("RTLdrELF: Dynamic section - %u entries\n", pModElf->cDynamic));
+ size_t const cbDynamic = pModElf->cDynamic * sizeof(pModElf->paDynamic[0]);
+ Elf_Dyn * const paDynamic = (Elf_Dyn *)RTMemAlloc(cbDynamic);
+ AssertReturn(paDynamic, VERR_NO_MEMORY);
+ pModElf->paDynamic = paDynamic;
+
+ rc = pModElf->Core.pReader->pfnRead(pModElf->Core.pReader, paDynamic, cbDynamic, paShdrs[pModElf->iShDynamic].sh_offset);
+ if (RT_FAILURE(rc))
+ return RTERRINFO_LOG_SET_F(pErrInfo, rc, "%s: pfnRead(,,%#zx, " FMT_ELF_OFF ") -> %Rrc",
+ pszLogName, cbDynamic, paShdrs[pModElf->iShDynamic].sh_offset, rc);
+
+ for (uint32_t i = 0; i < pModElf->cDynamic; i++)
+ {
+#define LOG_VALIDATE_PTR_RET(szName) do { \
+ Log3(("RTLdrELF: DT[%u]: %16s " FMT_ELF_ADDR "\n", i, szName, paDynamic[i].d_un.d_ptr)); \
+ if ((uint64_t)paDynamic[i].d_un.d_ptr - uLinkAddress < cbImage) { /* likely */ } \
+ else return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT[%u]/" szName ": Invalid address " FMT_ELF_ADDR " (valid range: " FMT_ELF_ADDR " LB " FMT_ELF_ADDR ")", \
+ pszLogName, i, paDynamic[i].d_un.d_ptr, uLinkAddress, cbImage); \
+ } while (0)
+#define LOG_VALIDATE_PTR_VAL_RET(szName, uExpected) do { \
+ Log3(("RTLdrELF: DT[%u]: %16s " FMT_ELF_ADDR "\n", i, szName, (uint64_t)paDynamic[i].d_un.d_ptr)); \
+ if (paDynamic[i].d_un.d_ptr == (Elf_Addr)(uExpected)) { /* likely */ } \
+ else return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT[%u]/" szName ": " FMT_ELF_ADDR ", expected " FMT_ELF_ADDR, \
+ pszLogName, i, paDynamic[i].d_un.d_ptr, (Elf_Addr)(uExpected)); \
+ } while (0)
+#define LOG_VALIDATE_STR_RET(szName) do { \
+ Log3(("RTLdrELF: DT[%u]: %16s %#RX64\n", i, szName, (uint64_t)paDynamic[i].d_un.d_val)); \
+ if ((uint64_t)paDynamic[i].d_un.d_val < pModElf->Dyn.cbStr) { /* likely */ } \
+ else return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT[%u]/" szName ": Invalid string table offset %#RX64 (max %#x)", \
+ pszLogName, i, (uint64_t)paDynamic[i].d_un.d_val, pModElf->Dyn.cbStr); \
+ } while (0)
+#define LOG_VALIDATE_VAL_RET(szName, uExpected) do { \
+ Log3(("RTLdrELF: DT[%u]: %16s %#RX64\n", i, szName, (uint64_t)paDynamic[i].d_un.d_val)); \
+ if ((uint64_t)paDynamic[i].d_un.d_val == (uint64_t)(uExpected)) { /* likely */ } \
+ else return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT[%u]/" szName ": %#RX64, expected %#RX64", \
+ pszLogName, i, (uint64_t)paDynamic[i].d_un.d_val, (uint64_t)(uExpected)); \
+ } while (0)
+#define SET_RELOC_TYPE_RET(a_szName, a_uType) do { \
+ if (pModElf->DynInfo.uRelocType == 0 || pModElf->DynInfo.uRelocType == (a_uType)) \
+ pModElf->DynInfo.uRelocType = (a_uType); \
+ else return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT[%u]/" a_szName ": Mixing DT_RELA and DT_REL", pszLogName, i); \
+ } while (0)
+#define SET_INFO_FIELD_RET(a_szName, a_Field, a_Value, a_UnsetValue, a_szFmt) do { \
+ if ((a_Field) == (a_UnsetValue) && (a_Value) != (a_UnsetValue)) \
+ (a_Field) = (a_Value); /* likely */ \
+ else if ((a_Field) != (a_UnsetValue)) \
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT[%u]/" a_szName ": Multiple entries (first value " a_szFmt ", second " a_szFmt ")", pszLogName, i, (a_Field), (a_Value)); \
+ else if ((a_Value) != (a_UnsetValue)) \
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT[%u]/" a_szName ": Unexpected value " a_szFmt, pszLogName, i, (a_Value)); \
+ } while (0)
+#define FIND_MATCHING_SECTION_RET(a_szName, a_ExtraMatchExpr, a_idxShFieldToSet) do { \
+ unsigned iSh; \
+ for (iSh = 1; iSh < pModElf->Ehdr.e_shnum; iSh++) \
+ if ( paShdrs[iSh].sh_addr == paDynamic[i].d_un.d_ptr \
+ && (a_ExtraMatchExpr)) \
+ { \
+ (a_idxShFieldToSet) = iSh; \
+ if (pModElf->paShdrExtras[iSh].idxDt != UINT16_MAX) \
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, \
+ "%s: DT[%u]/" a_szName ": section #%u (" FMT_ELF_ADDR ") already referenced by DT[%u]", \
+ pszLogName, i, iSh, paShdrs[iSh].sh_addr, pModElf->paShdrExtras[iSh].idxDt); \
+ pModElf->paShdrExtras[iSh].idxDt = i; \
+ pModElf->paShdrExtras[iSh].uDtTag = (uint32_t)paDynamic[i].d_tag; \
+ break; \
+ } \
+ if (iSh < pModElf->Ehdr.e_shnum) { /* likely */ } \
+ else return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT[%u]/" a_szName ": No matching section for " FMT_ELF_ADDR, pszLogName, i, paDynamic[i].d_un.d_ptr); \
+ } while (0)
+#define ONLY_FOR_DEBUG_OR_VALIDATION_RET(a_szName) do { \
+ if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)) { /* likely */ } \
+ else return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT[%u]/" a_szName ": Not supported (" FMT_ELF_ADDR ")", pszLogName, i, paDynamic[i].d_un.d_ptr); \
+ } while (0)
+#define LOG_NON_VALUE_ENTRY(a_szName) Log3(("RTLdrELF: DT[%u]: %16s (%#RX64)\n", i, a_szName, (uint64_t)paDynamic[i].d_un.d_val))
+
+ switch (paDynamic[i].d_tag)
+ {
+ case DT_NULL:
+ LOG_NON_VALUE_ENTRY("DT_NULL");
+ for (unsigned iNull = i + 1; iNull < pModElf->cDynamic; iNull++)
+ if (paDynamic[i].d_tag == DT_NULL) /* Not technically a bug, but let's try being extremely strict for now */
+ LOG_NON_VALUE_ENTRY("DT_NULL");
+ else if (!(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: DT[%u]/DT_NULL: Dynamic section isn't zero padded (extra #%u of #%u)",
+ pszLogName, i, iNull - i, pModElf->cDynamic - i);
+ i = pModElf->cDynamic;
+ break;
+ case DT_NEEDED:
+ LOG_VALIDATE_STR_RET("DT_NEEDED");
+ break;
+ case DT_PLTRELSZ:
+ Log3(("RTLdrELF: DT[%u]: %16s %#RX64 bytes\n", i, "DT_PLTRELSZ", (uint64_t)paDynamic[i].d_un.d_val));
+ SET_INFO_FIELD_RET("DT_PLTRELSZ", pModElf->DynInfo.cbJmpRelocs, (Elf_Xword)paDynamic[i].d_un.d_val, 0, FMT_ELF_XWORD);
+ break;
+ case DT_PLTGOT:
+ LOG_VALIDATE_PTR_RET("DT_PLTGOT");
+ break;
+ case DT_HASH:
+ LOG_VALIDATE_PTR_RET("DT_HASH");
+ break;
+ case DT_STRTAB:
+ LOG_VALIDATE_PTR_VAL_RET("DT_STRTAB", paShdrs[pModElf->Dyn.iStrSh].sh_addr);
+ pModElf->paShdrExtras[pModElf->Dyn.iStrSh].idxDt = i;
+ pModElf->paShdrExtras[pModElf->Dyn.iSymSh].uDtTag = DT_STRTAB;
+ break;
+ case DT_SYMTAB:
+ LOG_VALIDATE_PTR_VAL_RET("DT_SYMTAB", paShdrs[pModElf->Dyn.iSymSh].sh_addr);
+ pModElf->paShdrExtras[pModElf->Dyn.iSymSh].idxDt = i;
+ pModElf->paShdrExtras[pModElf->Dyn.iSymSh].uDtTag = DT_SYMTAB;
+ break;
+ case DT_RELA:
+ LOG_VALIDATE_PTR_RET("DT_RELA");
+ SET_RELOC_TYPE_RET("DT_RELA", DT_RELA);
+ SET_INFO_FIELD_RET("DT_RELA", pModElf->DynInfo.uPtrRelocs, paDynamic[i].d_un.d_ptr, ~(Elf_Addr)0, FMT_ELF_ADDR);
+ FIND_MATCHING_SECTION_RET("DT_RELA", paShdrs[iSh].sh_type == SHT_RELA, pModElf->DynInfo.idxShRelocs);
+ break;
+ case DT_RELASZ:
+ Log3(("RTLdrELF: DT[%u]: %16s %#RX64 bytes\n", i, "DT_RELASZ", (uint64_t)paDynamic[i].d_un.d_val));
+ SET_RELOC_TYPE_RET("DT_RELASZ", DT_RELA);
+ SET_INFO_FIELD_RET("DT_RELASZ", pModElf->DynInfo.cbRelocs, (Elf_Xword)paDynamic[i].d_un.d_val, 0, FMT_ELF_XWORD);
+ break;
+ case DT_RELAENT:
+ LOG_VALIDATE_VAL_RET("DT_RELAENT", sizeof(Elf_Rela));
+ SET_RELOC_TYPE_RET("DT_RELAENT", DT_RELA);
+ SET_INFO_FIELD_RET("DT_RELAENT", pModElf->DynInfo.cbRelocEntry, (unsigned)sizeof(Elf_Rela), 0, "%u");
+ break;
+ case DT_STRSZ:
+ LOG_VALIDATE_VAL_RET("DT_STRSZ", pModElf->Dyn.cbStr);
+ break;
+ case DT_SYMENT:
+ LOG_VALIDATE_VAL_RET("DT_SYMENT", sizeof(Elf_Sym));
+ break;
+ case DT_INIT:
+ LOG_VALIDATE_PTR_RET("DT_INIT");
+ ONLY_FOR_DEBUG_OR_VALIDATION_RET("DT_INIT");
+ break;
+ case DT_FINI:
+ LOG_VALIDATE_PTR_RET("DT_FINI");
+ ONLY_FOR_DEBUG_OR_VALIDATION_RET("DT_FINI");
+ break;
+ case DT_SONAME:
+ LOG_VALIDATE_STR_RET("DT_SONAME");
+ break;
+ case DT_RPATH:
+ LOG_VALIDATE_STR_RET("DT_RPATH");
+ break;
+ case DT_SYMBOLIC:
+ LOG_NON_VALUE_ENTRY("DT_SYMBOLIC");
+ break;
+ case DT_REL:
+ LOG_VALIDATE_PTR_RET("DT_REL");
+ SET_RELOC_TYPE_RET("DT_REL", DT_REL);
+ SET_INFO_FIELD_RET("DT_REL", pModElf->DynInfo.uPtrRelocs, paDynamic[i].d_un.d_ptr, ~(Elf_Addr)0, FMT_ELF_ADDR);
+ FIND_MATCHING_SECTION_RET("DT_REL", paShdrs[iSh].sh_type == SHT_REL, pModElf->DynInfo.idxShRelocs);
+ break;
+ case DT_RELSZ:
+ Log3(("RTLdrELF: DT[%u]: %16s %#RX64 bytes\n", i, "DT_RELSZ", (uint64_t)paDynamic[i].d_un.d_val));
+ SET_RELOC_TYPE_RET("DT_RELSZ", DT_REL);
+ SET_INFO_FIELD_RET("DT_RELSZ", pModElf->DynInfo.cbRelocs, (Elf_Xword)paDynamic[i].d_un.d_val, 0, FMT_ELF_XWORD);
+ break;
+ case DT_RELENT:
+ LOG_VALIDATE_VAL_RET("DT_RELENT", sizeof(Elf_Rel));
+ SET_RELOC_TYPE_RET("DT_RELENT", DT_REL);
+ SET_INFO_FIELD_RET("DT_RELENT", pModElf->DynInfo.cbRelocEntry, (unsigned)sizeof(Elf_Rel), 0, "%u");
+ break;
+ case DT_PLTREL:
+ if (paDynamic[i].d_un.d_val != DT_RELA && paDynamic[i].d_un.d_val != DT_REL)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT[%u]/DT_PLTREL: Invalid value %#RX64",
+ pszLogName, i, (uint64_t)paDynamic[i].d_un.d_val);
+ Log3(("RTLdrELF: DT[%u]: %16s DT_REL%s\n", i, "DT_PLTREL", paDynamic[i].d_un.d_val == DT_RELA ? "A" : ""));
+ SET_INFO_FIELD_RET("DT_PLTREL", pModElf->DynInfo.uJmpRelocType, (unsigned)paDynamic[i].d_un.d_val, 0, "%u");
+ break;
+ case DT_DEBUG:
+ /*
+ * DT_DEBUG is filled in by the dynamic linker to point a debugger to the head of the link map,
+ * it can point anywhere in userspace. For binaries not being executed it will be 0,
+ * so there is nothing we can validate here (and it is not required as we don't use
+ * this dynamic section). See https://ypl.coffee/dl-resolve-full-relro/ for more information.
+ */
+ break;
+ case DT_TEXTREL:
+ LOG_NON_VALUE_ENTRY("DT_TEXTREL");
+ break;
+ case DT_JMPREL:
+ LOG_VALIDATE_PTR_RET("DT_JMPREL");
+ SET_INFO_FIELD_RET("DT_JMPREL", pModElf->DynInfo.uPtrJmpRelocs, paDynamic[i].d_un.d_ptr, ~(Elf_Addr)0, FMT_ELF_ADDR);
+ FIND_MATCHING_SECTION_RET("DT_JMPREL", 1, pModElf->DynInfo.idxShJmpRelocs);
+ break;
+ case DT_BIND_NOW:
+ LOG_NON_VALUE_ENTRY("DT_BIND_NOW");
+ break;
+ case DT_INIT_ARRAY:
+ LOG_VALIDATE_PTR_RET("DT_INIT_ARRAY");
+ ONLY_FOR_DEBUG_OR_VALIDATION_RET("DT_INIT_ARRAY");
+ break;
+ case DT_FINI_ARRAY:
+ LOG_VALIDATE_PTR_RET("DT_FINI_ARRAY");
+ ONLY_FOR_DEBUG_OR_VALIDATION_RET("DT_FINI_ARRAY");
+ break;
+ case DT_INIT_ARRAYSZ:
+ Log3(("RTLdrELF: DT[%u]: %16s %#RX64 bytes\n", i, "DT_INIT_ARRAYSZ", (uint64_t)paDynamic[i].d_un.d_val));
+ ONLY_FOR_DEBUG_OR_VALIDATION_RET("DT_INIT_ARRAYSZ");
+ break;
+ case DT_FINI_ARRAYSZ:
+ Log3(("RTLdrELF: DT[%u]: %16s %#RX64 bytes\n", i, "DT_FINI_ARRAYSZ", (uint64_t)paDynamic[i].d_un.d_val));
+ ONLY_FOR_DEBUG_OR_VALIDATION_RET("DT_FINI_ARRAYSZ");
+ break;
+ case DT_RUNPATH:
+ LOG_VALIDATE_STR_RET("DT_RUNPATH");
+ break;
+ case DT_FLAGS:
+ Log3(("RTLdrELF: DT[%u]: %16s %#RX64\n", i, "DT_FLAGS", (uint64_t)paDynamic[i].d_un.d_val));
+ break;
+ case DT_PREINIT_ARRAY:
+ LOG_VALIDATE_PTR_RET("DT_PREINIT_ARRAY");
+ ONLY_FOR_DEBUG_OR_VALIDATION_RET("DT_PREINIT_ARRAY");
+ break;
+ case DT_PREINIT_ARRAYSZ:
+ Log3(("RTLdrELF: DT[%u]: %16s %#RX64 bytes\n", i, "DT_PREINIT_ARRAYSZ", (uint64_t)paDynamic[i].d_un.d_val));
+ ONLY_FOR_DEBUG_OR_VALIDATION_RET("DT_PREINIT_ARRAYSZ");
+ break;
+ default:
+ if ( paDynamic[i].d_tag < DT_ENCODING
+ || paDynamic[i].d_tag >= DT_LOOS
+ || (paDynamic[i].d_tag & 1))
+ Log3(("RTLdrELF: DT[%u]: %#010RX64 %#RX64%s\n", i, (uint64_t)paDynamic[i].d_tag,
+ (uint64_t)paDynamic[i].d_un.d_val, paDynamic[i].d_un.d_val >= DT_ENCODING ? " (val)" : ""));
+ else
+ {
+ Log3(("RTLdrELF: DT[%u]: %#010RX64 " FMT_ELF_ADDR " (addr)\n",
+ i, (uint64_t)paDynamic[i].d_tag, paDynamic[i].d_un.d_ptr));
+ if ((uint64_t)paDynamic[i].d_un.d_ptr - uLinkAddress >= cbImage)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: DT[%u]/%#RX64: Invalid address " FMT_ELF_ADDR " (valid range: " FMT_ELF_ADDR " LB " FMT_ELF_ADDR ")",
+ pszLogName, i, (uint64_t)paDynamic[i].d_tag,
+ paDynamic[i].d_un.d_ptr, uLinkAddress, cbImage);
+ }
+ break;
+ }
+#undef LOG_VALIDATE_VAL_RET
+#undef LOG_VALIDATE_STR_RET
+#undef LOG_VALIDATE_PTR_VAL_RET
+#undef LOG_VALIDATE_PTR_RET
+#undef SET_RELOC_TYPE_RET
+#undef SET_INFO_FIELD_RET
+#undef FIND_MATCHING_SECTION_RET
+#undef ONLY_FOR_DEBUG_OR_VALIDATION_RET
+ }
+
+ /*
+ * Validate the relocation information we've gathered.
+ */
+ Elf_Word uShTypeArch = SHT_RELA; /** @todo generalize architecture specific stuff using its own code template header. */
+ switch (pModElf->Core.enmArch)
+ {
+ case RTLDRARCH_AMD64:
+ break;
+ case RTLDRARCH_X86_32:
+ uShTypeArch = SHT_REL;
+ break;
+ default:
+ AssertFailedBreak(/** @todo page size for got.plt hacks */);
+
+ }
+
+ if (pModElf->DynInfo.uRelocType != 0)
+ {
+ const char * const pszModifier = pModElf->DynInfo.uRelocType == DT_RELA ? "A" : "";
+ if (pModElf->DynInfo.uPtrRelocs == ~(Elf_Addr)0)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: Missing DT_REL%s", pszLogName, pszModifier);
+ if (pModElf->DynInfo.cbRelocs == 0)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: Missing DT_REL%sSZ", pszLogName, pszModifier);
+ if (pModElf->DynInfo.cbRelocEntry == 0)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: Missing DT_REL%sENT", pszLogName, pszModifier);
+ Elf_Shdr const *pShdrRelocs = &paShdrs[pModElf->DynInfo.idxShRelocs];
+ Elf_Word const uShType = pModElf->DynInfo.uJmpRelocType == DT_RELA ? SHT_RELA : SHT_REL;
+ if (pShdrRelocs->sh_type != uShType)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT_REL%s* does not match section type: %u vs %u",
+ pszLogName, pszModifier, pShdrRelocs->sh_type, uShType);
+ if (pShdrRelocs->sh_size != pModElf->DynInfo.cbRelocs)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT_REL%sSZ does not match section size: %u vs %u",
+ pszLogName, pszModifier, pShdrRelocs->sh_size, pModElf->DynInfo.cbRelocs);
+ if (uShType != uShTypeArch)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT_REL%s* does not match architecture: %u, arch wants %u",
+ pszLogName, pszModifier, uShType, uShTypeArch);
+ }
+
+ if ( pModElf->DynInfo.uPtrJmpRelocs != ~(Elf_Addr)0
+ || pModElf->DynInfo.cbJmpRelocs != 0
+ || pModElf->DynInfo.uJmpRelocType != 0)
+ {
+ if (pModElf->DynInfo.uPtrJmpRelocs == ~(Elf_Addr)0)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: Missing DT_JMPREL", pszLogName);
+ if (pModElf->DynInfo.cbJmpRelocs == 0)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: Missing DT_PLTRELSZ", pszLogName);
+ if (pModElf->DynInfo.uJmpRelocType == 0)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: Missing DT_PLTREL", pszLogName);
+ Elf_Shdr const *pShdrRelocs = &paShdrs[pModElf->DynInfo.idxShJmpRelocs];
+ Elf_Word const uShType = pModElf->DynInfo.uJmpRelocType == DT_RELA ? SHT_RELA : SHT_REL;
+ if (pShdrRelocs->sh_type != uShType)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT_PLTREL does not match section type: %u vs %u",
+ pszLogName, pShdrRelocs->sh_type, uShType);
+ if (pShdrRelocs->sh_size != pModElf->DynInfo.cbJmpRelocs)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT_PLTRELSZ does not match section size: %u vs %u",
+ pszLogName, pShdrRelocs->sh_size, pModElf->DynInfo.cbJmpRelocs);
+ if (uShType != uShTypeArch)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "%s: DT_PLTREL does not match architecture: %u, arch wants %u",
+ pszLogName, uShType, uShTypeArch);
+ }
+
+ /*
+ * Check that there aren't any other relocations hiding in the section table.
+ */
+ for (uint32_t i = 1; i < pModElf->Ehdr.e_shnum; i++)
+ if ( (paShdrs[i].sh_type == SHT_REL || paShdrs[i].sh_type == SHT_RELA)
+ && pModElf->paShdrExtras[i].uDtTag != DT_REL
+ && pModElf->paShdrExtras[i].uDtTag != DT_RELA
+ && pModElf->paShdrExtras[i].uDtTag != DT_JMPREL)
+ {
+ char szSecHdrNm[80];
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "%s: section header #%u (%s type=" FMT_ELF_WORD " size=" FMT_ELF_XWORD ") contains relocations not referenced by the dynamic section",
+ pszLogName, i,
+ RTLDRELF_NAME(GetSHdrName)(pModElf, paShdrs[i].sh_name, szSecHdrNm, sizeof(szSecHdrNm)),
+ paShdrs[i].sh_type, paShdrs[i].sh_size);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * Opens an ELF image, fixed bitness.
+ *
+ * @returns iprt status code.
+ * @param pReader The loader reader instance which will provide the raw image bits.
+ * @param fFlags Reserved, MBZ.
+ * @param enmArch Architecture specifier.
+ * @param phLdrMod Where to store the handle.
+ * @param pErrInfo Where to return extended error info. Optional.
+ */
+static int RTLDRELF_NAME(Open)(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
+{
+ const char *pszLogName = pReader->pfnLogName(pReader);
+ uint64_t cbRawImage = pReader->pfnSize(pReader);
+ RT_NOREF_PV(fFlags);
+
+ /*
+ * Create the loader module instance.
+ */
+ PRTLDRMODELF pModElf = (PRTLDRMODELF)RTMemAllocZ(sizeof(*pModElf));
+ if (!pModElf)
+ return VERR_NO_MEMORY;
+
+ pModElf->Core.u32Magic = RTLDRMOD_MAGIC;
+ pModElf->Core.eState = LDR_STATE_INVALID;
+ pModElf->Core.pReader = pReader;
+ pModElf->Core.enmFormat = RTLDRFMT_ELF;
+ pModElf->Core.enmType = RTLDRTYPE_OBJECT;
+ pModElf->Core.enmEndian = RTLDRENDIAN_LITTLE;
+#if ELF_MODE == 32
+ pModElf->Core.enmArch = RTLDRARCH_X86_32;
+#else
+ pModElf->Core.enmArch = RTLDRARCH_AMD64;
+#endif
+ //pModElf->pvBits = NULL;
+ //pModElf->Ehdr = {0};
+ //pModElf->paShdrs = NULL;
+ //pModElf->Rel.paSyms = NULL;
+ pModElf->Rel.iSymSh = ~0U;
+ //pModElf->Rel.cSyms = 0;
+ pModElf->Rel.iStrSh = ~0U;
+ //pModElf->Rel.cbStr = 0;
+ //pModElf->Rel.pStr = NULL;
+ //pModElf->Dyn.paSyms = NULL;
+ pModElf->Dyn.iSymSh = ~0U;
+ //pModElf->Dyn.cSyms = 0;
+ pModElf->Dyn.iStrSh = ~0U;
+ //pModElf->Dyn.cbStr = 0;
+ //pModElf->Dyn.pStr = NULL;
+ pModElf->iFirstSect = 1;
+ //pModElf->fShdrInOrder = false;
+ //pModElf->cbImage = 0;
+ pModElf->LinkAddress = ~(Elf_Addr)0;
+ //pModElf->cbShStr = 0;
+ //pModElf->pShStr = NULL;
+ //pModElf->iShEhFrame = 0;
+ //pModElf->iShEhFrameHdr= 0;
+ pModElf->iShDynamic = ~0U;
+ //pModElf->cDynamic = 0;
+ //pModElf->paDynamic = NULL;
+ //pModElf->paPhdrs = NULL;
+ pModElf->DynInfo.uPtrRelocs = ~(Elf_Addr)0;
+ //pModElf->DynInfo.cbRelocs = 0;
+ //pModElf->DynInfo.cbRelocEntry = 0;
+ //pModElf->DynInfo.uRelocType = 0;
+ //pModElf->DynInfo.idxShRelocs = 0;
+ pModElf->DynInfo.uPtrJmpRelocs = ~(Elf_Addr)0;
+ //pModElf->DynInfo.cbJmpRelocs = 0;
+ //pModElf->DynInfo.uJmpRelocType = 0;
+ //pModElf->DynInfo.idxShJmpRelocs = 0;
+
+ /*
+ * Read and validate the ELF header and match up the CPU architecture.
+ */
+ int rc = pReader->pfnRead(pReader, &pModElf->Ehdr, sizeof(pModElf->Ehdr), 0);
+ if (RT_SUCCESS(rc))
+ {
+ RTLDRARCH enmArchImage = RTLDRARCH_INVALID; /* shut up gcc */
+ rc = RTLDRELF_NAME(ValidateElfHeader)(&pModElf->Ehdr, cbRawImage, pszLogName, &enmArchImage, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if ( enmArch != RTLDRARCH_WHATEVER
+ && enmArch != enmArchImage)
+ rc = VERR_LDR_ARCH_MISMATCH;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Read the section headers, keeping a prestine copy for the module
+ * introspection methods.
+ */
+ size_t const cbShdrs = pModElf->Ehdr.e_shnum * sizeof(Elf_Shdr);
+ Elf_Shdr *paShdrs = (Elf_Shdr *)RTMemAlloc(cbShdrs * 2 + sizeof(RTLDRMODELFSHX) * pModElf->Ehdr.e_shnum);
+ if (paShdrs)
+ {
+ pModElf->paShdrs = paShdrs;
+ rc = pReader->pfnRead(pReader, paShdrs, cbShdrs, pModElf->Ehdr.e_shoff);
+ if (RT_SUCCESS(rc))
+ {
+ memcpy(&paShdrs[pModElf->Ehdr.e_shnum], paShdrs, cbShdrs);
+ pModElf->paOrgShdrs = &paShdrs[pModElf->Ehdr.e_shnum];
+
+ pModElf->paShdrExtras = (PRTLDRMODELFSHX)&pModElf->paOrgShdrs[pModElf->Ehdr.e_shnum];
+ memset(pModElf->paShdrExtras, 0xff, sizeof(RTLDRMODELFSHX) * pModElf->Ehdr.e_shnum);
+
+ pModElf->cbShStr = paShdrs[pModElf->Ehdr.e_shstrndx].sh_size;
+
+ /*
+ * Validate the section headers and find relevant sections.
+ */
+ rc = RTLDRELF_NAME(ValidateAndProcessSectionHeaders)(pModElf, paShdrs, cbRawImage, pszLogName, pErrInfo);
+
+ /*
+ * Read validate and process program headers if ET_DYN or ET_EXEC.
+ */
+ if (RT_SUCCESS(rc) && (pModElf->Ehdr.e_type == ET_DYN || pModElf->Ehdr.e_type == ET_EXEC))
+ rc = RTLDRELF_NAME(ValidateAndProcessDynamicInfo)(pModElf, cbRawImage, fFlags, pszLogName, pErrInfo);
+
+ /*
+ * Massage the section headers.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ if (pModElf->Ehdr.e_type == ET_REL)
+ {
+ /* Do allocations and figure the image size: */
+ pModElf->LinkAddress = 0;
+ for (unsigned i = 1; i < pModElf->Ehdr.e_shnum; i++)
+ if (paShdrs[i].sh_flags & SHF_ALLOC)
+ {
+ paShdrs[i].sh_addr = paShdrs[i].sh_addralign
+ ? RT_ALIGN_T(pModElf->cbImage, paShdrs[i].sh_addralign, Elf_Addr)
+ : (Elf_Addr)pModElf->cbImage;
+ Elf_Addr EndAddr = paShdrs[i].sh_addr + paShdrs[i].sh_size;
+ if (pModElf->cbImage < EndAddr)
+ {
+ pModElf->cbImage = (size_t)EndAddr;
+ AssertMsgBreakStmt(pModElf->cbImage == EndAddr, (FMT_ELF_ADDR "\n", EndAddr), rc = VERR_IMAGE_TOO_BIG);
+ }
+ Log2(("RTLdrElf: %s: Assigned " FMT_ELF_ADDR " to section #%d\n", pszLogName, paShdrs[i].sh_addr, i));
+ }
+ }
+ else
+ {
+ /* Convert sh_addr to RVA: */
+ Assert(pModElf->LinkAddress != ~(Elf_Addr)0);
+ for (unsigned i = 0 /*!*/; i < pModElf->Ehdr.e_shnum; i++)
+ if (paShdrs[i].sh_flags & SHF_ALLOC)
+ paShdrs[i].sh_addr -= pModElf->LinkAddress;
+ }
+ }
+
+ /*
+ * Check if the sections are in order by address, as that will simplify
+ * enumeration and address translation.
+ */
+ pModElf->fShdrInOrder = true;
+ Elf_Addr uEndAddr = 0;
+ for (unsigned i = pModElf->iFirstSect; i < pModElf->Ehdr.e_shnum; i++)
+ if (paShdrs[i].sh_flags & SHF_ALLOC)
+ {
+ if (uEndAddr <= paShdrs[i].sh_addr)
+ uEndAddr = paShdrs[i].sh_addr + paShdrs[i].sh_size;
+ else
+ {
+ pModElf->fShdrInOrder = false;
+ break;
+ }
+ }
+
+ Log2(("RTLdrElf: iSymSh=%u cSyms=%u iStrSh=%u cbStr=%u rc=%Rrc cbImage=%#zx LinkAddress=" FMT_ELF_ADDR " fShdrInOrder=%RTbool\n",
+ pModElf->Rel.iSymSh, pModElf->Rel.cSyms, pModElf->Rel.iStrSh, pModElf->Rel.cbStr, rc,
+ pModElf->cbImage, pModElf->LinkAddress, pModElf->fShdrInOrder));
+ if (RT_SUCCESS(rc))
+ {
+ pModElf->Core.pOps = &RTLDRELF_MID(s_rtldrElf,Ops);
+ pModElf->Core.eState = LDR_STATE_OPENED;
+ *phLdrMod = &pModElf->Core;
+
+ LogFlow(("%s: %s: returns VINF_SUCCESS *phLdrMod=%p\n", __FUNCTION__, pszLogName, *phLdrMod));
+ return VINF_SUCCESS;
+ }
+ }
+
+ RTMemFree(paShdrs);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ RTMemFree(pModElf);
+ LogFlow(("%s: returns %Rrc\n", __FUNCTION__, rc));
+ return rc;
+}
+
+
+
+
+/*******************************************************************************
+* Cleanup Constants And Macros *
+*******************************************************************************/
+#undef RTLDRELF_NAME
+#undef RTLDRELF_SUFF
+#undef RTLDRELF_MID
+
+#undef FMT_ELF_ADDR
+#undef FMT_ELF_ADDR7
+#undef FMT_ELF_HALF
+#undef FMT_ELF_SHALF
+#undef FMT_ELF_OFF
+#undef FMT_ELF_SIZE
+#undef FMT_ELF_SWORD
+#undef FMT_ELF_WORD
+#undef FMT_ELF_XWORD
+#undef FMT_ELF_SXWORD
+
+#undef Elf_Ehdr
+#undef Elf_Phdr
+#undef Elf_Shdr
+#undef Elf_Sym
+#undef Elf_Rel
+#undef Elf_Rela
+#undef Elf_Reloc
+#undef Elf_Nhdr
+#undef Elf_Dyn
+
+#undef Elf_Addr
+#undef Elf_Half
+#undef Elf_Off
+#undef Elf_Size
+#undef Elf_Sword
+#undef Elf_Word
+#undef Elf_Xword
+#undef Elf_Sxword
+
+#undef RTLDRMODELF
+#undef PRTLDRMODELF
+
+#undef ELF_R_SYM
+#undef ELF_R_TYPE
+#undef ELF_R_INFO
+
+#undef ELF_ST_BIND
+
diff --git a/src/VBox/Runtime/common/ldr/ldrEx.cpp b/src/VBox/Runtime/common/ldr/ldrEx.cpp
new file mode 100644
index 00000000..d4a644ab
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/ldrEx.cpp
@@ -0,0 +1,773 @@
+/* $Id: ldrEx.cpp $ */
+/** @file
+ * IPRT - Binary Image Loader, Extended Features.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/dbg.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/md5.h>
+#include <iprt/mem.h>
+#include <iprt/sha.h>
+#include <iprt/string.h>
+#include <iprt/formats/mz.h>
+#include <iprt/formats/mach-o.h>
+#include "internal/ldr.h"
+
+#if defined(LDR_ONLY_PE) || defined(LDR_ONLY_MACHO)
+# undef LDR_WITH_PE
+# undef LDR_WITH_ELF
+# undef LDR_WITH_LX
+# undef LDR_WITH_LE
+# undef LDR_WITH_MACHO
+# undef LDR_WITH_NE
+# undef LDR_WITH_MZ
+# undef LDR_WITH_AOUT
+# ifdef LDR_ONLY_PE
+# define LDR_WITH_PE
+# endif
+# ifdef LDR_ONLY_MACHO
+# define LDR_WITH_MACHO
+# endif
+#endif
+
+
+RTDECL(int) RTLdrOpenWithReader(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTLDRMOD phMod, PRTERRINFO pErrInfo)
+{
+ /*
+ * Resolve RTLDRARCH_HOST.
+ */
+ if (enmArch == RTLDRARCH_HOST)
+ enmArch = RTLdrGetHostArch();
+
+ /*
+ * Read and verify the file signature.
+ */
+ union
+ {
+ char ach[4];
+ uint16_t au16[2];
+ uint32_t u32;
+ } uSign;
+ int rc = pReader->pfnRead(pReader, &uSign, sizeof(uSign), 0);
+ if (RT_FAILURE(rc))
+ return rc;
+ if ( uSign.au16[0] != IMAGE_DOS_SIGNATURE
+ && uSign.u32 != IMAGE_NT_SIGNATURE
+ && uSign.u32 != IMAGE_ELF_SIGNATURE
+ && uSign.au16[0] != IMAGE_LX_SIGNATURE
+ && uSign.u32 != IMAGE_MACHO64_SIGNATURE
+ && uSign.u32 != IMAGE_MACHO64_SIGNATURE_OE
+ && uSign.u32 != IMAGE_MACHO32_SIGNATURE
+ && uSign.u32 != IMAGE_MACHO32_SIGNATURE_OE
+ && uSign.u32 != IMAGE_FAT_SIGNATURE
+ && uSign.u32 != IMAGE_FAT_SIGNATURE_OE )
+ {
+ Log(("rtldrOpenWithReader: %s: unknown magic %#x / '%.4s\n", pReader->pfnLogName(pReader), uSign.u32, &uSign.ach[0]));
+ return VERR_INVALID_EXE_SIGNATURE;
+ }
+ uint32_t offHdr = 0;
+ if (uSign.au16[0] == IMAGE_DOS_SIGNATURE)
+ {
+ rc = pReader->pfnRead(pReader, &offHdr, sizeof(offHdr), RT_UOFFSETOF(IMAGE_DOS_HEADER, e_lfanew));
+ if (RT_FAILURE(rc))
+ return rc;
+
+ if (offHdr <= sizeof(IMAGE_DOS_HEADER))
+ {
+ Log(("rtldrOpenWithReader: %s: no new header / invalid offset %#RX32\n", pReader->pfnLogName(pReader), offHdr));
+ return VERR_INVALID_EXE_SIGNATURE;
+ }
+ rc = pReader->pfnRead(pReader, &uSign, sizeof(uSign), offHdr);
+ if (RT_FAILURE(rc))
+ return rc;
+ if ( uSign.u32 != IMAGE_NT_SIGNATURE
+ && uSign.au16[0] != IMAGE_LX_SIGNATURE
+ && uSign.au16[0] != IMAGE_LE_SIGNATURE
+ && uSign.au16[0] != IMAGE_NE_SIGNATURE)
+ {
+ Log(("rtldrOpenWithReader: %s: unknown new magic %#x / '%.4s\n", pReader->pfnLogName(pReader), uSign.u32, &uSign.ach[0]));
+ return VERR_INVALID_EXE_SIGNATURE;
+ }
+ }
+
+ /*
+ * Create image interpreter instance depending on the signature.
+ */
+ if (uSign.u32 == IMAGE_NT_SIGNATURE)
+#ifdef LDR_WITH_PE
+ rc = rtldrPEOpen(pReader, fFlags, enmArch, offHdr, phMod, pErrInfo);
+#else
+ rc = VERR_PE_EXE_NOT_SUPPORTED;
+#endif
+ else if (uSign.u32 == IMAGE_ELF_SIGNATURE)
+#if defined(LDR_WITH_ELF)
+ rc = rtldrELFOpen(pReader, fFlags, enmArch, phMod, pErrInfo);
+#else
+ rc = VERR_ELF_EXE_NOT_SUPPORTED;
+#endif
+ else if ( uSign.u32 == IMAGE_MACHO64_SIGNATURE
+ || uSign.u32 == IMAGE_MACHO64_SIGNATURE_OE
+ || uSign.u32 == IMAGE_MACHO32_SIGNATURE
+ || uSign.u32 == IMAGE_MACHO32_SIGNATURE_OE)
+#if defined(LDR_WITH_MACHO)
+ rc = rtldrMachOOpen(pReader, fFlags, enmArch, offHdr, phMod, pErrInfo);
+#else
+ rc = VERR_INVALID_EXE_SIGNATURE;
+#endif
+ else if ( uSign.u32 == IMAGE_FAT_SIGNATURE
+ || uSign.u32 == IMAGE_FAT_SIGNATURE_OE)
+#if defined(LDR_WITH_MACHO)
+ rc = rtldrFatOpen(pReader, fFlags, enmArch, phMod, pErrInfo);
+#else
+ rc = VERR_INVALID_EXE_SIGNATURE;
+#endif
+ else if (uSign.au16[0] == IMAGE_LX_SIGNATURE)
+#ifdef LDR_WITH_LX
+ rc = rtldrLXOpen(pReader, fFlags, enmArch, offHdr, phMod, pErrInfo);
+#else
+ rc = VERR_LX_EXE_NOT_SUPPORTED;
+#endif
+ else if (uSign.au16[0] == IMAGE_LE_SIGNATURE)
+#ifdef LDR_WITH_LE
+ rc = rtldrLEOpen(pReader, fFlags, enmArch, phMod, pErrInfo);
+#else
+ rc = VERR_LE_EXE_NOT_SUPPORTED;
+#endif
+ else if (uSign.au16[0] == IMAGE_NE_SIGNATURE)
+#ifdef LDR_WITH_NE
+ rc = rtldrNEOpen(pReader, fFlags, enmArch, phMod, pErrInfo);
+#else
+ rc = VERR_NE_EXE_NOT_SUPPORTED;
+#endif
+ else if (uSign.au16[0] == IMAGE_DOS_SIGNATURE)
+#ifdef LDR_WITH_MZ
+ rc = rtldrMZOpen(pReader, fFlags, enmArch, phMod, pErrInfo);
+#else
+ rc = VERR_MZ_EXE_NOT_SUPPORTED;
+#endif
+ else if (/* uSign.u32 == IMAGE_AOUT_A_SIGNATURE
+ || uSign.u32 == IMAGE_AOUT_Z_SIGNATURE*/ /** @todo find the aout magics in emx or binutils. */
+ 0)
+#ifdef LDR_WITH_AOUT
+ rc = rtldrAOUTOpen(pReader, fFlags, enmArch, phMod, pErrInfo);
+#else
+ rc = VERR_AOUT_EXE_NOT_SUPPORTED;
+#endif
+ else
+ {
+ Log(("rtldrOpenWithReader: %s: the format isn't implemented %#x / '%.4s\n", pReader->pfnLogName(pReader), uSign.u32, &uSign.ach[0]));
+ rc = VERR_INVALID_EXE_SIGNATURE;
+ }
+
+ LogFlow(("rtldrOpenWithReader: %s: returns %Rrc *phMod=%p\n", pReader->pfnLogName(pReader), rc, *phMod));
+ return rc;
+}
+
+
+RTDECL(size_t) RTLdrSize(RTLDRMOD hLdrMod)
+{
+ LogFlow(("RTLdrSize: hLdrMod=%RTldrm\n", hLdrMod));
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), ~(size_t)0);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ AssertMsgReturn(pMod->eState == LDR_STATE_OPENED, ("eState=%d\n", pMod->eState), ~(size_t)0);
+
+ /*
+ * Do it.
+ */
+ size_t cb = pMod->pOps->pfnGetImageSize(pMod);
+ LogFlow(("RTLdrSize: returns %zu\n", cb));
+ return cb;
+}
+RT_EXPORT_SYMBOL(RTLdrSize);
+
+
+RTDECL(int) RTLdrGetBits(RTLDRMOD hLdrMod, void *pvBits, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ LogFlow(("RTLdrGetBits: hLdrMod=%RTldrm pvBits=%p BaseAddress=%RTptr pfnGetImport=%p pvUser=%p\n",
+ hLdrMod, pvBits, BaseAddress, pfnGetImport, pvUser));
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBits, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pfnGetImport, VERR_INVALID_POINTER);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ AssertMsgReturn(pMod->eState == LDR_STATE_OPENED, ("eState=%d\n", pMod->eState), VERR_WRONG_ORDER);
+
+ /*
+ * Do it.
+ */
+ int rc = pMod->pOps->pfnGetBits(pMod, pvBits, BaseAddress, pfnGetImport, pvUser);
+ LogFlow(("RTLdrGetBits: returns %Rrc\n",rc));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrGetBits);
+
+
+RTDECL(int) RTLdrRelocate(RTLDRMOD hLdrMod, void *pvBits, RTLDRADDR NewBaseAddress, RTLDRADDR OldBaseAddress,
+ PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ LogFlow(("RTLdrRelocate: hLdrMod=%RTldrm pvBits=%p NewBaseAddress=%RTptr OldBaseAddress=%RTptr pfnGetImport=%p pvUser=%p\n",
+ hLdrMod, pvBits, NewBaseAddress, OldBaseAddress, pfnGetImport, pvUser));
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBits, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfnGetImport, VERR_INVALID_POINTER);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ AssertMsgReturn(pMod->eState == LDR_STATE_OPENED, ("eState=%d\n", pMod->eState), VERR_WRONG_ORDER);
+
+ /*
+ * Do it.
+ */
+ int rc = pMod->pOps->pfnRelocate(pMod, pvBits, NewBaseAddress, OldBaseAddress, pfnGetImport, pvUser);
+ LogFlow(("RTLdrRelocate: returns %Rrc\n", rc));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrRelocate);
+
+
+RTDECL(int) RTLdrGetSymbolEx(RTLDRMOD hLdrMod, const void *pvBits, RTLDRADDR BaseAddress,
+ uint32_t iOrdinal, const char *pszSymbol, PRTLDRADDR pValue)
+{
+ LogFlow(("RTLdrGetSymbolEx: hLdrMod=%RTldrm pvBits=%p BaseAddress=%RTptr iOrdinal=%#x pszSymbol=%p:{%s} pValue=%p\n",
+ hLdrMod, pvBits, BaseAddress, iOrdinal, pszSymbol, pszSymbol, pValue));
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ AssertPtrNullReturn(pvBits, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pszSymbol, VERR_INVALID_POINTER);
+ AssertReturn(pszSymbol || iOrdinal != UINT32_MAX, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pValue, VERR_INVALID_POINTER);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+
+ /*
+ * Do it.
+ */
+ int rc;
+ if (pMod->pOps->pfnGetSymbolEx)
+ rc = pMod->pOps->pfnGetSymbolEx(pMod, pvBits, BaseAddress, iOrdinal, pszSymbol, pValue);
+ else if (!BaseAddress && !pvBits && iOrdinal == UINT32_MAX)
+ {
+ void *pvValue;
+ rc = pMod->pOps->pfnGetSymbol(pMod, pszSymbol, &pvValue);
+ if (RT_SUCCESS(rc))
+ *pValue = (uintptr_t)pvValue;
+ }
+ else
+ AssertMsgFailedReturn(("BaseAddress=%RTptr pvBits=%p\n", BaseAddress, pvBits), VERR_INVALID_FUNCTION);
+ LogFlow(("RTLdrGetSymbolEx: returns %Rrc *pValue=%p\n", rc, *pValue));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrGetSymbolEx);
+
+
+RTDECL(int) RTLdrQueryForwarderInfo(RTLDRMOD hLdrMod, const void *pvBits, uint32_t iOrdinal, const char *pszSymbol,
+ PRTLDRIMPORTINFO pInfo, size_t cbInfo)
+{
+ LogFlow(("RTLdrQueryForwarderInfo: hLdrMod=%RTldrm pvBits=%p iOrdinal=%#x pszSymbol=%p:{%s} pInfo=%p cbInfo=%zu\n",
+ hLdrMod, pvBits, iOrdinal, pszSymbol, pszSymbol, pInfo, cbInfo));
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ AssertPtrNullReturn(pvBits, VERR_INVALID_POINTER);
+ AssertMsgReturn(pszSymbol, ("pszSymbol=%p\n", pszSymbol), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pInfo, VERR_INVALID_PARAMETER);
+ AssertReturn(cbInfo >= sizeof(*pInfo), VERR_INVALID_PARAMETER);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+
+ /*
+ * Do it.
+ */
+ int rc;
+ if (pMod->pOps->pfnQueryForwarderInfo)
+ {
+ rc = pMod->pOps->pfnQueryForwarderInfo(pMod, pvBits, iOrdinal, pszSymbol, pInfo, cbInfo);
+ if (RT_SUCCESS(rc))
+ LogFlow(("RTLdrQueryForwarderInfo: returns %Rrc pInfo={%#x,%#x,%s,%s}\n", rc,
+ pInfo->iSelfOrdinal, pInfo->iOrdinal, pInfo->pszSymbol, pInfo->szModule));
+ else
+ LogFlow(("RTLdrQueryForwarderInfo: returns %Rrc\n", rc));
+ }
+ else
+ {
+ LogFlow(("RTLdrQueryForwarderInfo: returns VERR_NOT_SUPPORTED\n"));
+ rc = VERR_NOT_SUPPORTED;
+ }
+ return rc;
+
+}
+RT_EXPORT_SYMBOL(RTLdrQueryForwarderInfo);
+
+
+RTDECL(int) RTLdrEnumSymbols(RTLDRMOD hLdrMod, unsigned fFlags, const void *pvBits, RTLDRADDR BaseAddress,
+ PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ LogFlow(("RTLdrEnumSymbols: hLdrMod=%RTldrm fFlags=%#x pvBits=%p BaseAddress=%RTptr pfnCallback=%p pvUser=%p\n",
+ hLdrMod, fFlags, pvBits, BaseAddress, pfnCallback, pvUser));
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ AssertPtrNullReturn(pvBits, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ //AssertMsgReturn(pMod->eState == LDR_STATE_OPENED, ("eState=%d\n", pMod->eState), VERR_WRONG_ORDER);
+
+ /*
+ * Do it.
+ */
+ int rc = pMod->pOps->pfnEnumSymbols(pMod, fFlags, pvBits, BaseAddress, pfnCallback, pvUser);
+ LogFlow(("RTLdrEnumSymbols: returns %Rrc\n", rc));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrEnumSymbols);
+
+
+RTDECL(int) RTLdrEnumDbgInfo(RTLDRMOD hLdrMod, const void *pvBits, PFNRTLDRENUMDBG pfnCallback, void *pvUser)
+{
+ LogFlow(("RTLdrEnumDbgInfo: hLdrMod=%RTldrm pvBits=%p pfnCallback=%p pvUser=%p\n",
+ hLdrMod, pvBits, pfnCallback, pvUser));
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ AssertPtrNullReturn(pvBits, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ //AssertMsgReturn(pMod->eState == LDR_STATE_OPENED, ("eState=%d\n", pMod->eState), VERR_WRONG_ORDER);
+
+ /*
+ * Do it.
+ */
+ int rc;
+ if (pMod->pOps->pfnEnumDbgInfo)
+ rc = pMod->pOps->pfnEnumDbgInfo(pMod, pvBits, pfnCallback, pvUser);
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ LogFlow(("RTLdrEnumDbgInfo: returns %Rrc\n", rc));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrEnumDbgInfo);
+
+
+RTDECL(int) RTLdrEnumSegments(RTLDRMOD hLdrMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
+{
+ LogFlow(("RTLdrEnumSegments: hLdrMod=%RTldrm pfnCallback=%p pvUser=%p\n",
+ hLdrMod, pfnCallback, pvUser));
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ //AssertMsgReturn(pMod->eState == LDR_STATE_OPENED, ("eState=%d\n", pMod->eState), VERR_WRONG_ORDER);
+
+ /*
+ * Do it.
+ */
+ int rc;
+ if (pMod->pOps->pfnEnumSegments)
+ rc = pMod->pOps->pfnEnumSegments(pMod, pfnCallback, pvUser);
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ LogFlow(("RTLdrEnumSegments: returns %Rrc\n", rc));
+ return rc;
+
+}
+RT_EXPORT_SYMBOL(RTLdrEnumSegments);
+
+
+RTDECL(int) RTLdrLinkAddressToSegOffset(RTLDRMOD hLdrMod, RTLDRADDR LinkAddress, uint32_t *piSeg, PRTLDRADDR poffSeg)
+{
+ LogFlow(("RTLdrLinkAddressToSegOffset: hLdrMod=%RTldrm LinkAddress=%RTptr piSeg=%p poffSeg=%p\n",
+ hLdrMod, LinkAddress, piSeg, poffSeg));
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ AssertPtrReturn(piSeg, VERR_INVALID_POINTER);
+ AssertPtrReturn(poffSeg, VERR_INVALID_POINTER);
+
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ //AssertMsgReturn(pMod->eState == LDR_STATE_OPENED, ("eState=%d\n", pMod->eState), VERR_WRONG_ORDER);
+
+ *piSeg = UINT32_MAX;
+ *poffSeg = ~(RTLDRADDR)0;
+
+ /*
+ * Do it.
+ */
+ int rc;
+ if (pMod->pOps->pfnLinkAddressToSegOffset)
+ rc = pMod->pOps->pfnLinkAddressToSegOffset(pMod, LinkAddress, piSeg, poffSeg);
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ LogFlow(("RTLdrLinkAddressToSegOffset: returns %Rrc %#x:%RTptr\n", rc, *piSeg, *poffSeg));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrLinkAddressToSegOffset);
+
+
+RTDECL(int) RTLdrLinkAddressToRva(RTLDRMOD hLdrMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
+{
+ LogFlow(("RTLdrLinkAddressToRva: hLdrMod=%RTldrm LinkAddress=%RTptr pRva=%p\n",
+ hLdrMod, LinkAddress, pRva));
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ AssertPtrReturn(pRva, VERR_INVALID_POINTER);
+
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ //AssertMsgReturn(pMod->eState == LDR_STATE_OPENED, ("eState=%d\n", pMod->eState), VERR_WRONG_ORDER);
+
+ *pRva = ~(RTLDRADDR)0;
+
+ /*
+ * Do it.
+ */
+ int rc;
+ if (pMod->pOps->pfnLinkAddressToRva)
+ rc = pMod->pOps->pfnLinkAddressToRva(pMod, LinkAddress, pRva);
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ LogFlow(("RTLdrLinkAddressToRva: returns %Rrc %RTptr\n", rc, *pRva));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrLinkAddressToRva);
+
+
+RTDECL(int) RTLdrSegOffsetToRva(RTLDRMOD hLdrMod, uint32_t iSeg, RTLDRADDR offSeg, PRTLDRADDR pRva)
+{
+ LogFlow(("RTLdrSegOffsetToRva: hLdrMod=%RTldrm iSeg=%#x offSeg=%RTptr pRva=%p\n", hLdrMod, iSeg, offSeg, pRva));
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ AssertPtrReturn(pRva, VERR_INVALID_POINTER);
+
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ //AssertMsgReturn(pMod->eState == LDR_STATE_OPENED, ("eState=%d\n", pMod->eState), VERR_WRONG_ORDER);
+
+ *pRva = ~(RTLDRADDR)0;
+
+ /*
+ * Do it.
+ */
+ int rc;
+ if (pMod->pOps->pfnSegOffsetToRva)
+ rc = pMod->pOps->pfnSegOffsetToRva(pMod, iSeg, offSeg, pRva);
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ LogFlow(("RTLdrSegOffsetToRva: returns %Rrc %RTptr\n", rc, *pRva));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrSegOffsetToRva);
+
+RTDECL(int) RTLdrRvaToSegOffset(RTLDRMOD hLdrMod, RTLDRADDR Rva, uint32_t *piSeg, PRTLDRADDR poffSeg)
+{
+ LogFlow(("RTLdrRvaToSegOffset: hLdrMod=%RTldrm Rva=%RTptr piSeg=%p poffSeg=%p\n",
+ hLdrMod, Rva, piSeg, poffSeg));
+
+ /*
+ * Validate input.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ AssertPtrReturn(piSeg, VERR_INVALID_POINTER);
+ AssertPtrReturn(poffSeg, VERR_INVALID_POINTER);
+
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ //AssertMsgReturn(pMod->eState == LDR_STATE_OPENED, ("eState=%d\n", pMod->eState), VERR_WRONG_ORDER);
+
+ *piSeg = UINT32_MAX;
+ *poffSeg = ~(RTLDRADDR)0;
+
+ /*
+ * Do it.
+ */
+ int rc;
+ if (pMod->pOps->pfnRvaToSegOffset)
+ rc = pMod->pOps->pfnRvaToSegOffset(pMod, Rva, piSeg, poffSeg);
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ LogFlow(("RTLdrRvaToSegOffset: returns %Rrc %#x:%RTptr\n", rc, *piSeg, *poffSeg));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrRvaToSegOffset);
+
+
+RTDECL(int) RTLdrQueryProp(RTLDRMOD hLdrMod, RTLDRPROP enmProp, void *pvBuf, size_t cbBuf)
+{
+ return RTLdrQueryPropEx(hLdrMod, enmProp, NULL /*pvBits*/, pvBuf, cbBuf, NULL);
+}
+RT_EXPORT_SYMBOL(RTLdrQueryProp);
+
+
+RTDECL(int) RTLdrQueryPropEx(RTLDRMOD hLdrMod, RTLDRPROP enmProp, void *pvBits, void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), RTLDRENDIAN_INVALID);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+
+ AssertPtrNullReturn(pcbRet, VERR_INVALID_POINTER);
+ size_t cbRet;
+ if (!pcbRet)
+ pcbRet = &cbRet;
+
+ /*
+ * Do some pre screening of the input
+ */
+ switch (enmProp)
+ {
+ case RTLDRPROP_UUID:
+ *pcbRet = sizeof(RTUUID);
+ AssertReturn(cbBuf == sizeof(RTUUID), VERR_INVALID_PARAMETER);
+ break;
+ case RTLDRPROP_TIMESTAMP_SECONDS:
+ *pcbRet = sizeof(int64_t);
+ AssertReturn(cbBuf == sizeof(int32_t) || cbBuf == sizeof(int64_t), VERR_INVALID_PARAMETER);
+ *pcbRet = cbBuf;
+ break;
+ case RTLDRPROP_IS_SIGNED:
+ *pcbRet = sizeof(bool);
+ AssertReturn(cbBuf == sizeof(bool), VERR_INVALID_PARAMETER);
+ break;
+ case RTLDRPROP_PKCS7_SIGNED_DATA:
+ case RTLDRPROP_SHA1_PAGE_HASHES:
+ case RTLDRPROP_SHA256_PAGE_HASHES:
+ *pcbRet = 0;
+ break;
+ case RTLDRPROP_HASHABLE_PAGES:
+ *pcbRet = sizeof(uint32_t);
+ AssertReturn(cbBuf >= sizeof(uint32_t), VERR_INVALID_PARAMETER);
+ break;
+ case RTLDRPROP_SIGNATURE_CHECKS_ENFORCED:
+ *pcbRet = sizeof(bool);
+ AssertReturn(cbBuf == sizeof(bool), VERR_INVALID_PARAMETER);
+ break;
+ case RTLDRPROP_IMPORT_COUNT:
+ *pcbRet = sizeof(uint32_t);
+ AssertReturn(cbBuf == sizeof(uint32_t), VERR_INVALID_PARAMETER);
+ break;
+ case RTLDRPROP_IMPORT_MODULE:
+ *pcbRet = sizeof(uint32_t);
+ AssertReturn(cbBuf >= sizeof(uint32_t), VERR_INVALID_PARAMETER);
+ break;
+ case RTLDRPROP_FILE_OFF_HEADER:
+ *pcbRet = sizeof(uint64_t);
+ AssertReturn(cbBuf == sizeof(uint32_t) || cbBuf == sizeof(uint64_t), VERR_INVALID_PARAMETER);
+ break;
+ case RTLDRPROP_INTERNAL_NAME:
+ case RTLDRPROP_UNWIND_TABLE:
+ *pcbRet = 0;
+ break;
+
+ case RTLDRPROP_UNWIND_INFO:
+ AssertReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbBuf >= sizeof(uint32_t), VERR_INVALID_PARAMETER);
+ *pcbRet = 0;
+ break;
+
+ case RTLDRPROP_BUILDID:
+ *pcbRet = 0;
+ break;
+
+ default:
+ AssertFailedReturn(VERR_INVALID_FUNCTION);
+ }
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+
+ /*
+ * Call the image specific worker, if there is one.
+ */
+ if (!pMod->pOps->pfnQueryProp)
+ return VERR_NOT_SUPPORTED;
+ return pMod->pOps->pfnQueryProp(pMod, enmProp, pvBits, pvBuf, cbBuf, pcbRet);
+}
+RT_EXPORT_SYMBOL(RTLdrQueryPropEx);
+
+
+RTDECL(int) RTLdrVerifySignature(RTLDRMOD hLdrMod, PFNRTLDRVALIDATESIGNEDDATA pfnCallback, void *pvUser, PRTERRINFO pErrInfo)
+{
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+
+ /*
+ * Call the image specific worker, if there is one.
+ */
+ if (!pMod->pOps->pfnVerifySignature)
+ return VERR_NOT_SUPPORTED;
+ return pMod->pOps->pfnVerifySignature(pMod, pfnCallback, pvUser, pErrInfo);
+}
+RT_EXPORT_SYMBOL(RTLdrVerifySignature);
+
+
+RTDECL(int) RTLdrHashImage(RTLDRMOD hLdrMod, RTDIGESTTYPE enmDigest, uint8_t *pabHash, size_t cbHash)
+{
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+
+ /*
+ * Make sure there is sufficient space for the wanted digest and that
+ * it's supported.
+ */
+ switch (enmDigest)
+ {
+ case RTDIGESTTYPE_MD5: AssertReturn(cbHash >= RTMD5_HASH_SIZE, VERR_BUFFER_OVERFLOW); break;
+ case RTDIGESTTYPE_SHA1: AssertReturn(cbHash >= RTSHA1_HASH_SIZE, VERR_BUFFER_OVERFLOW); break;
+ case RTDIGESTTYPE_SHA256: AssertReturn(cbHash >= RTSHA256_HASH_SIZE, VERR_BUFFER_OVERFLOW); break;
+ case RTDIGESTTYPE_SHA512: AssertReturn(cbHash >= RTSHA512_HASH_SIZE, VERR_BUFFER_OVERFLOW); break;
+ default:
+ if (enmDigest > RTDIGESTTYPE_INVALID && enmDigest < RTDIGESTTYPE_END)
+ return VERR_NOT_SUPPORTED;
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+ AssertPtrReturn(pabHash, VERR_INVALID_POINTER);
+
+ /*
+ * Call the image specific worker, if there is one.
+ */
+ if (!pMod->pOps->pfnHashImage)
+ return VERR_NOT_SUPPORTED;
+ return pMod->pOps->pfnHashImage(pMod, enmDigest, pabHash, cbHash);
+}
+RT_EXPORT_SYMBOL(RTLdrHashImage);
+
+
+RTDECL(int) RTLdrUnwindFrame(RTLDRMOD hLdrMod, void const *pvBits, uint32_t iSeg, RTLDRADDR off, PRTDBGUNWINDSTATE pState)
+{
+ /*
+ * Validate.
+ */
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+ AssertPtr(pState);
+ AssertReturn(pState->u32Magic == RTDBGUNWINDSTATE_MAGIC, VERR_INVALID_MAGIC);
+
+ /*
+ * Pass on the work.
+ */
+ if (pMod->pOps->pfnUnwindFrame)
+ return pMod->pOps->pfnUnwindFrame(pMod, pvBits, iSeg, off, pState);
+ return VERR_DBG_NO_UNWIND_INFO;
+}
+RT_EXPORT_SYMBOL(RTLdrUnwindFrame);
+
+
+/**
+ * Internal method used by the IPRT debug bits.
+ *
+ * @returns IPRT status code.
+ * @param hLdrMod The loader handle which executable we wish to
+ * read from.
+ * @param pvBuf The output buffer.
+ * @param iDbgInfo The debug info ordinal number if the request
+ * corresponds exactly to a debug info part from
+ * pfnEnumDbgInfo. Otherwise, pass UINT32_MAX.
+ * @param off Where in the executable file to start reading.
+ * @param cb The number of bytes to read.
+ *
+ * @remarks Fixups will only be applied if @a iDbgInfo is specified.
+ */
+DECLHIDDEN(int) rtLdrReadAt(RTLDRMOD hLdrMod, void *pvBuf, uint32_t iDbgInfo, RTFOFF off, size_t cb)
+{
+ AssertMsgReturn(rtldrIsValid(hLdrMod), ("hLdrMod=%p\n", hLdrMod), VERR_INVALID_HANDLE);
+ PRTLDRMODINTERNAL pMod = (PRTLDRMODINTERNAL)hLdrMod;
+
+ if (iDbgInfo != UINT32_MAX)
+ {
+ AssertReturn(pMod->pOps->pfnReadDbgInfo, VERR_NOT_SUPPORTED);
+ return pMod->pOps->pfnReadDbgInfo(pMod, iDbgInfo, off, cb, pvBuf);
+ }
+
+ AssertReturn(pMod->pReader, VERR_NOT_SUPPORTED);
+ return pMod->pReader->pfnRead(pMod->pReader, pvBuf, cb, off);
+}
+
+
+RTDECL(const char *) RTLdrArchName(RTLDRARCH enmArch)
+{
+ switch (enmArch)
+ {
+ case RTLDRARCH_INVALID: return "INVALID";
+ case RTLDRARCH_WHATEVER: return "WHATEVER";
+ case RTLDRARCH_HOST: return "HOST";
+ case RTLDRARCH_AMD64: return "AMD64";
+ case RTLDRARCH_X86_16: return "X86_16";
+ case RTLDRARCH_X86_32: return "X86_32";
+ case RTLDRARCH_ARM32: return "ARM32";
+ case RTLDRARCH_ARM64: return "ARM64";
+
+ case RTLDRARCH_END:
+ case RTLDRARCH_32BIT_HACK:
+ break;
+ }
+ return "UNKNOWN";
+}
+RT_EXPORT_SYMBOL(RTLdrArchName);
+
diff --git a/src/VBox/Runtime/common/ldr/ldrFile.cpp b/src/VBox/Runtime/common/ldr/ldrFile.cpp
new file mode 100644
index 00000000..ea570524
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/ldrFile.cpp
@@ -0,0 +1,317 @@
+/* $Id: ldrFile.cpp $ */
+/** @file
+ * IPRT - Binary Image Loader, The File Oriented Parts.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloc.h>
+#include <iprt/file.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/formats/mz.h>
+#include "internal/ldr.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * File Reader instance.
+ * This provides raw image bits from a file.
+ */
+typedef struct RTLDRREADERFILE
+{
+ /** The core. */
+ RTLDRREADER Core;
+ /** The file. */
+ RTFILE hFile;
+ /** The file size. */
+ uint64_t cbFile;
+ /** The current offset. */
+ RTFOFF off;
+ /** Number of users or the mapping. */
+ RTUINT cMappings;
+ /** Pointer to the in memory mapping. */
+ void *pvMapping;
+ /** The filename (variable size). */
+ char szFilename[1];
+} RTLDRREADERFILE, *PRTLDRREADERFILE;
+
+
+/** @copydoc RTLDRREADER::pfnRead */
+static DECLCALLBACK(int) rtldrFileRead(PRTLDRREADER pReader, void *pvBuf, size_t cb, RTFOFF off)
+{
+ PRTLDRREADERFILE pFileReader = (PRTLDRREADERFILE)pReader;
+
+ /*
+ * Seek.
+ */
+ if (pFileReader->off != off)
+ {
+ int rc = RTFileSeek(pFileReader->hFile, off, RTFILE_SEEK_BEGIN, NULL);
+ if (RT_FAILURE(rc))
+ {
+ pFileReader->off = -1;
+ return rc;
+ }
+ pFileReader->off = off;
+ }
+
+ /*
+ * Read.
+ */
+ int rc = RTFileRead(pFileReader->hFile, pvBuf, cb, NULL);
+ if (RT_SUCCESS(rc))
+ pFileReader->off += cb;
+ else
+ pFileReader->off = -1;
+ return rc;
+}
+
+
+/** @copydoc RTLDRREADER::pfnTell */
+static DECLCALLBACK(RTFOFF) rtldrFileTell(PRTLDRREADER pReader)
+{
+ PRTLDRREADERFILE pFileReader = (PRTLDRREADERFILE)pReader;
+ return pFileReader->off;
+}
+
+
+/** @copydoc RTLDRREADER::pfnSize */
+static DECLCALLBACK(uint64_t) rtldrFileSize(PRTLDRREADER pReader)
+{
+ PRTLDRREADERFILE pFileReader = (PRTLDRREADERFILE)pReader;
+ return pFileReader->cbFile;
+}
+
+
+/** @copydoc RTLDRREADER::pfnLogName */
+static DECLCALLBACK(const char *) rtldrFileLogName(PRTLDRREADER pReader)
+{
+ PRTLDRREADERFILE pFileReader = (PRTLDRREADERFILE)pReader;
+ return pFileReader->szFilename;
+}
+
+
+/** @copydoc RTLDRREADER::pfnMap */
+static DECLCALLBACK(int) rtldrFileMap(PRTLDRREADER pReader, const void **ppvBits)
+{
+ PRTLDRREADERFILE pFileReader = (PRTLDRREADERFILE)pReader;
+
+ /*
+ * Already mapped?
+ */
+ if (pFileReader->pvMapping)
+ {
+ pFileReader->cMappings++;
+ *ppvBits = pFileReader->pvMapping;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Allocate memory.
+ */
+ size_t cb = (size_t)pFileReader->cbFile;
+ if ((uint64_t)cb != pFileReader->cbFile)
+ return VERR_IMAGE_TOO_BIG;
+ pFileReader->pvMapping = RTMemAlloc(cb);
+ if (!pFileReader->pvMapping)
+ return VERR_NO_MEMORY;
+ int rc = rtldrFileRead(pReader, pFileReader->pvMapping, cb, 0);
+ if (RT_SUCCESS(rc))
+ {
+ pFileReader->cMappings = 1;
+ *ppvBits = pFileReader->pvMapping;
+ }
+ else
+ {
+ RTMemFree(pFileReader->pvMapping);
+ pFileReader->pvMapping = NULL;
+ }
+
+ return rc;
+}
+
+
+/** @copydoc RTLDRREADER::pfnUnmap */
+static DECLCALLBACK(int) rtldrFileUnmap(PRTLDRREADER pReader, const void *pvBits)
+{
+ PRTLDRREADERFILE pFileReader = (PRTLDRREADERFILE)pReader;
+ AssertReturn(pFileReader->cMappings > 0, VERR_INVALID_PARAMETER);
+
+ if (!--pFileReader->cMappings)
+ {
+ RTMemFree(pFileReader->pvMapping);
+ pFileReader->pvMapping = NULL;
+ }
+
+ NOREF(pvBits);
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc RTLDRREADER::pfnDestroy */
+static DECLCALLBACK(int) rtldrFileDestroy(PRTLDRREADER pReader)
+{
+ int rc = VINF_SUCCESS;
+ PRTLDRREADERFILE pFileReader = (PRTLDRREADERFILE)pReader;
+
+ Assert(!pFileReader->cMappings);
+
+ if (pFileReader->hFile != NIL_RTFILE)
+ {
+ rc = RTFileClose(pFileReader->hFile);
+ AssertRC(rc);
+ pFileReader->hFile = NIL_RTFILE;
+ }
+
+ if (pFileReader->pvMapping)
+ {
+ RTMemFree(pFileReader->pvMapping);
+ pFileReader->pvMapping = NULL;
+ }
+
+ RTMemFree(pFileReader);
+ return rc;
+}
+
+
+/**
+ * Opens a loader file reader.
+ *
+ * @returns iprt status code.
+ * @param ppReader Where to store the reader instance on success.
+ * @param pszFilename The file to open.
+ */
+static int rtldrFileCreate(PRTLDRREADER *ppReader, const char *pszFilename)
+{
+ size_t cchFilename = strlen(pszFilename);
+ int rc = VERR_NO_MEMORY;
+ PRTLDRREADERFILE pFileReader = (PRTLDRREADERFILE)RTMemAlloc(sizeof(*pFileReader) + cchFilename);
+ if (pFileReader)
+ {
+ memcpy(pFileReader->szFilename, pszFilename, cchFilename + 1);
+ rc = RTFileOpen(&pFileReader->hFile, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileQuerySize(pFileReader->hFile, &pFileReader->cbFile);
+ if (RT_SUCCESS(rc))
+ {
+ pFileReader->Core.uMagic = RTLDRREADER_MAGIC;
+ pFileReader->Core.pfnRead = rtldrFileRead;
+ pFileReader->Core.pfnTell = rtldrFileTell;
+ pFileReader->Core.pfnSize = rtldrFileSize;
+ pFileReader->Core.pfnLogName = rtldrFileLogName;
+ pFileReader->Core.pfnMap = rtldrFileMap;
+ pFileReader->Core.pfnUnmap = rtldrFileUnmap;
+ pFileReader->Core.pfnDestroy = rtldrFileDestroy;
+ pFileReader->off = 0;
+ pFileReader->cMappings = 0;
+ pFileReader->pvMapping = NULL;
+ *ppReader = &pFileReader->Core;
+ return VINF_SUCCESS;
+ }
+
+ RTFileClose(pFileReader->hFile);
+ }
+ RTMemFree(pFileReader);
+ }
+ *ppReader = NULL;
+ return rc;
+}
+
+
+/**
+ * Open a binary image file.
+ *
+ * @returns iprt status code.
+ * @param pszFilename Image filename.
+ * @param fFlags Valid RTLDR_O_XXX combination.
+ * @param enmArch CPU architecture specifier for the image to be loaded.
+ * @param phLdrMod Where to store the handle to the loader module.
+ */
+RTDECL(int) RTLdrOpen(const char *pszFilename, uint32_t fFlags, RTLDRARCH enmArch, PRTLDRMOD phLdrMod)
+{
+ return RTLdrOpenEx(pszFilename, fFlags, enmArch, phLdrMod, NULL /*pErrInfo*/);
+}
+RT_EXPORT_SYMBOL(RTLdrOpen);
+
+
+/**
+ * Open a binary image file, extended version.
+ *
+ * @returns iprt status code.
+ * @param pszFilename Image filename.
+ * @param fFlags Valid RTLDR_O_XXX combination.
+ * @param enmArch CPU architecture specifier for the image to be loaded.
+ * @param phLdrMod Where to store the handle to the loader module.
+ * @param pErrInfo Where to return extended error information. Optional.
+ */
+RTDECL(int) RTLdrOpenEx(const char *pszFilename, uint32_t fFlags, RTLDRARCH enmArch, PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
+{
+ LogFlow(("RTLdrOpenEx: pszFilename=%p:{%s} fFlags=%#x enmArch=%d phLdrMod=%p\n",
+ pszFilename, pszFilename, fFlags, enmArch, phLdrMod));
+ AssertMsgReturn(!(fFlags & ~RTLDR_O_VALID_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(enmArch > RTLDRARCH_INVALID && enmArch < RTLDRARCH_END, ("%d\n", enmArch), VERR_INVALID_PARAMETER);
+
+ /*
+ * Create file reader & invoke worker which identifies and calls the image interpreter.
+ */
+ PRTLDRREADER pReader;
+ int rc = rtldrFileCreate(&pReader, pszFilename);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrOpenWithReader(pReader, fFlags, enmArch, phLdrMod, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlow(("RTLdrOpenEx: return %Rrc *phLdrMod=%p\n", rc, *phLdrMod));
+ return rc;
+ }
+ pReader->pfnDestroy(pReader);
+ }
+ *phLdrMod = NIL_RTLDRMOD;
+ LogFlow(("RTLdrOpenEx: return %Rrc\n", rc));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrOpenEx);
+
diff --git a/src/VBox/Runtime/common/ldr/ldrLX.cpp b/src/VBox/Runtime/common/ldr/ldrLX.cpp
new file mode 100644
index 00000000..a61e8d50
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/ldrLX.cpp
@@ -0,0 +1,3121 @@
+/* $Id: ldrLX.cpp $ */
+/** @file
+ * kLdr - The Module Interpreter for the Linear eXecutable (LX) Format.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ * --------------------------------------------------------------------
+ *
+ * This code is based on: kLdr/kLdrModLX.c from kStuff r113.
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+
+#include <iprt/formats/lx.h>
+#include <iprt/formats/pecoff.h>
+#include <iprt/formats/codeview.h>
+#include <iprt/formats/elf32.h>
+#include "internal/ldr.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) Assert(expr)
+#else
+# define KLDRMODLX_ASSERT(expr) do {} while (0)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Instance data for the LX module interpreter.
+ */
+typedef struct KLDRMODLX
+{
+ /** Core module structure. */
+ RTLDRMODINTERNAL Core;
+
+ /** Pointer to the user mapping. */
+ const void *pvMapping;
+ /** The size of the mapped LX image. */
+ size_t cbMapped;
+ /** Reserved flags. */
+ uint32_t f32Reserved;
+
+ /** The offset of the LX header. */
+ RTFOFF offHdr;
+ /** Copy of the LX header. */
+ struct e32_exe Hdr;
+
+ /** Pointer to the loader section.
+ * Allocated together with this strcture. */
+ const uint8_t *pbLoaderSection;
+ /** Pointer to the last byte in the loader section. */
+ const uint8_t *pbLoaderSectionLast;
+ /** Pointer to the object table in the loader section. */
+ const struct o32_obj *paObjs;
+ /** Pointer to the object page map table in the loader section. */
+ const struct o32_map *paPageMappings;
+ /** Pointer to the resource table in the loader section. */
+ const struct rsrc32 *paRsrcs;
+ /** Pointer to the resident name table in the loader section. */
+ const uint8_t *pbResNameTab;
+ /** Pointer to the entry table in the loader section. */
+ const uint8_t *pbEntryTab;
+
+ /** Pointer to the non-resident name table. */
+ uint8_t *pbNonResNameTab;
+ /** Pointer to the last byte in the non-resident name table. */
+ const uint8_t *pbNonResNameTabLast;
+
+ /** Pointer to the fixup section. */
+ uint8_t *pbFixupSection;
+ /** Pointer to the last byte in the fixup section. */
+ const uint8_t *pbFixupSectionLast;
+ /** Pointer to the fixup page table within pvFixupSection. */
+ const uint32_t *paoffPageFixups;
+ /** Pointer to the fixup record table within pvFixupSection. */
+ const uint8_t *pbFixupRecs;
+ /** Pointer to the import module name table within pvFixupSection. */
+ const uint8_t *pbImportMods;
+ /** Pointer to the import module name table within pvFixupSection. */
+ const uint8_t *pbImportProcs;
+
+ /** Pointer to the module name (in the resident name table). */
+ const char *pszName;
+ /** The name length. */
+ size_t cchName;
+
+ /** The target CPU. */
+ RTLDRCPU enmCpu;
+ /** Number of segments in aSegments. */
+ uint32_t cSegments;
+ /** Segment info. */
+ RT_FLEXIBLE_ARRAY_EXTENSION
+ RTLDRSEG aSegments[RT_FLEXIBLE_ARRAY];
+} KLDRMODLX, *PKLDRMODLX;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int kldrModLXHasDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits);
+static DECLCALLBACK(int) rtldrLX_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
+ RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser);
+static const uint8_t *kldrModLXDoNameTableLookupByOrdinal(const uint8_t *pbNameTable, ssize_t cbNameTable, uint32_t iOrdinal);
+static int kldrModLXDoNameLookup(PKLDRMODLX pModLX, const char *pchSymbol, size_t cchSymbol, uint32_t *piSymbol);
+static const uint8_t *kldrModLXDoNameTableLookupByName(const uint8_t *pbNameTable, ssize_t cbNameTable,
+ const char *pchSymbol, size_t cchSymbol);
+static int kldrModLXGetImport(PKLDRMODLX pThis, const void *pvBits, uint32_t iImport,
+ char *pszName, size_t cchName, size_t *pcbNeeded);
+static int kldrModLXDoLoadBits(PKLDRMODLX pModLX, void *pvBits);
+static int kldrModLXDoIterDataUnpacking(uint8_t *pbDst, const uint8_t *pbSrc, int cbSrc);
+static int kldrModLXDoIterData2Unpacking(uint8_t *pbDst, const uint8_t *pbSrc, int cbSrc);
+static void kLdrModLXMemCopyW(uint8_t *pbDst, const uint8_t *pbSrc, int cb);
+static int kldrModLXDoForwarderQuery(PKLDRMODLX pModLX, const struct e32_entry *pEntry,
+ PFNRTLDRIMPORT pfnGetForwarder, void *pvUser, PRTLDRADDR puValue, uint32_t *pfKind);
+#if 0
+static int kldrModLXDoProtect(PKLDRMODLX pModLX, void *pvBits, unsigned fUnprotectOrProtect);
+static int kldrModLXDoCallDLL(PKLDRMODLX pModLX, void *pvMapping, unsigned uOp, uintptr_t uHandle);
+static int32_t kldrModLXDoCall(uintptr_t uEntrypoint, uintptr_t uHandle, uint32_t uOp, void *pvReserved);
+#endif
+static int kldrModLXDoLoadFixupSection(PKLDRMODLX pModLX);
+static int kldrModLXDoReloc(uint8_t *pbPage, int off, RTLDRADDR PageAddress, const struct r32_rlc *prlc,
+ int iSelector, RTLDRADDR uValue, uint32_t fKind);
+
+
+/**
+ * Separate function for reading creating the LX module instance to
+ * simplify cleanup on failure.
+ */
+static int kldrModLXDoCreate(PRTLDRREADER pRdr, RTFOFF offNewHdr, uint32_t fFlags, PKLDRMODLX *ppModLX, PRTERRINFO pErrInfo)
+{
+ struct e32_exe Hdr;
+ PKLDRMODLX pModLX;
+ uint32_t off, offEnd;
+ uint32_t i;
+ int fCanOptimizeMapping;
+ uint32_t NextRVA;
+
+ RT_NOREF(fFlags);
+ *ppModLX = NULL;
+
+ /*
+ * Read the signature and file header.
+ */
+ int rc = pRdr->pfnRead(pRdr, &Hdr, sizeof(Hdr), offNewHdr > 0 ? offNewHdr : 0);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc, "Error reading LX header at %RTfoff: %Rrc", offNewHdr, rc);
+ if ( Hdr.e32_magic[0] != E32MAGIC1
+ || Hdr.e32_magic[1] != E32MAGIC2)
+ return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "Not LX magic: %02x %02x", Hdr.e32_magic[0], Hdr.e32_magic[1]);
+
+ /* 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 VERR_LDRLX_BAD_HEADER;
+
+ /* Some rough sanity checks. */
+ offEnd = pRdr->pfnSize(pRdr) >= (uint64_t)~(uint32_t)16 ? ~(uint32_t)16 : (uint32_t)pRdr->pfnSize(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 VERR_LDRLX_BAD_HEADER;
+
+ /* Verify the loader section. */
+ offEnd = Hdr.e32_objtab + Hdr.e32_ldrsize;
+ if (Hdr.e32_objtab < sizeof(Hdr) && Hdr.e32_objcnt)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRLX_BAD_LOADER_SECTION,
+ "Object table is inside the header: %#x", Hdr.e32_objtab);
+ off = Hdr.e32_objtab + sizeof(struct o32_obj) * Hdr.e32_objcnt;
+ if (off > offEnd)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRLX_BAD_LOADER_SECTION,
+ "Object table spans beyond the executable: e32_objcnt=%u", Hdr.e32_objcnt);
+ if (Hdr.e32_objcnt >= _32K)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRLX_BAD_LOADER_SECTION, "Too many segments: %#x\n", Hdr.e32_objcnt);
+ if ( Hdr.e32_objmap
+ && (Hdr.e32_objmap < off || Hdr.e32_objmap > offEnd))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRLX_BAD_LOADER_SECTION,
+ "Bad object page map table offset: %#x", Hdr.e32_objmap);
+ if ( Hdr.e32_rsrccnt
+ && ( Hdr.e32_rsrctab < off
+ || Hdr.e32_rsrctab > offEnd
+ || Hdr.e32_rsrctab + sizeof(struct rsrc32) * Hdr.e32_rsrccnt > offEnd))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRLX_BAD_LOADER_SECTION,
+ "Resource table is out of bounds: %#x entries at %#x", Hdr.e32_rsrccnt, Hdr.e32_rsrctab);
+ if ( Hdr.e32_restab
+ && (Hdr.e32_restab < off || Hdr.e32_restab > offEnd - 2))
+ return VERR_LDRLX_BAD_LOADER_SECTION;
+ if ( Hdr.e32_enttab
+ && (Hdr.e32_enttab < off || Hdr.e32_enttab >= offEnd))
+ return VERR_LDRLX_BAD_LOADER_SECTION;
+ if ( Hdr.e32_dircnt
+ && (Hdr.e32_dirtab < off || Hdr.e32_dirtab > offEnd - 2))
+ return VERR_LDRLX_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 VERR_LDRLX_BAD_FIXUP_SECTION;
+ if ( Hdr.e32_impmod
+ && (Hdr.e32_impmod < off || Hdr.e32_impmod > offEnd || Hdr.e32_impmod + Hdr.e32_impmodcnt > offEnd))
+ return VERR_LDRLX_BAD_FIXUP_SECTION;
+ if ( Hdr.e32_impproc
+ && (Hdr.e32_impproc < off || Hdr.e32_impproc > offEnd))
+ return VERR_LDRLX_BAD_FIXUP_SECTION;
+
+ /*
+ * Calc the instance size, allocate and initialize it.
+ */
+ size_t cbModLXAndSegments = RT_ALIGN_Z(RT_UOFFSETOF_DYN(KLDRMODLX, aSegments[Hdr.e32_objcnt + 1]), 8);
+ cbModLXAndSegments += sizeof("segXXXXX") * (Hdr.e32_objcnt + 1);
+
+ pModLX = (PKLDRMODLX)RTMemAlloc(cbModLXAndSegments + Hdr.e32_ldrsize + 2 /*for two extra zeros*/);
+ if (!pModLX)
+ return VERR_NO_MEMORY;
+ *ppModLX = pModLX;
+
+ /* Core & CPU. */
+ pModLX->Core.u32Magic = 0; /* set by caller. */
+ pModLX->Core.eState = LDR_STATE_OPENED;
+ pModLX->Core.pOps = NULL; /* set by caller. */
+ pModLX->Core.pReader = pRdr;
+ switch (Hdr.e32_cpu)
+ {
+ case E32CPU286:
+ pModLX->enmCpu = RTLDRCPU_I80286;
+ pModLX->Core.enmArch = RTLDRARCH_X86_16;
+ break;
+ case E32CPU386:
+ pModLX->enmCpu = RTLDRCPU_I386;
+ pModLX->Core.enmArch = RTLDRARCH_X86_32;
+ break;
+ case E32CPU486:
+ pModLX->enmCpu = RTLDRCPU_I486;
+ pModLX->Core.enmArch = RTLDRARCH_X86_32;
+ break;
+ }
+ pModLX->Core.enmEndian = RTLDRENDIAN_LITTLE;
+ pModLX->Core.enmFormat = RTLDRFMT_LX;
+ switch (Hdr.e32_mflags & E32MODMASK)
+ {
+ case E32MODEXE:
+ pModLX->Core.enmType = !(Hdr.e32_mflags & E32NOINTFIX)
+ ? RTLDRTYPE_EXECUTABLE_RELOCATABLE
+ : RTLDRTYPE_EXECUTABLE_FIXED;
+ break;
+
+ case E32MODDLL:
+ case E32PROTDLL:
+ case E32MODPROTDLL:
+ pModLX->Core.enmType = !(Hdr.e32_mflags & E32SYSDLL)
+ ? RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE
+ : RTLDRTYPE_SHARED_LIBRARY_FIXED;
+ break;
+
+ case E32MODPDEV:
+ case E32MODVDEV:
+ pModLX->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE;
+ break;
+ }
+
+ /* KLDRMODLX */
+ pModLX->cSegments = Hdr.e32_objcnt;
+ pModLX->pszName = NULL; /* finalized further down */
+ pModLX->cchName = 0;
+ pModLX->pvMapping = 0;
+ pModLX->cbMapped = 0;
+ pModLX->f32Reserved = 0;
+
+ pModLX->offHdr = offNewHdr >= 0 ? offNewHdr : 0;
+ memcpy(&pModLX->Hdr, &Hdr, sizeof(Hdr));
+
+ pModLX->pbLoaderSection = (uint8_t *)pModLX + cbModLXAndSegments;
+ 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 = pRdr->pfnRead(pRdr, (void *)pModLX->pbLoaderSection, pModLX->Hdr.e32_ldrsize, pModLX->Hdr.e32_objtab + pModLX->offHdr);
+ if (RT_FAILURE(rc))
+ return rc;
+ ((uint8_t *)pModLX->pbLoaderSectionLast)[1] = 0;
+ ((uint8_t *)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)
+ pModLX->pszName = (const char *)kldrModLXDoNameTableLookupByOrdinal(pModLX->pbResNameTab,
+ pModLX->pbLoaderSectionLast - pModLX->pbResNameTab + 1,
+ 0);
+ if (!pModLX->pszName)
+ return VERR_LDRLX_NO_SONAME;
+ pModLX->cchName = *(const uint8_t *)pModLX->pszName++;
+ if ( pModLX->pszName[pModLX->cchName] != '\0'
+ || pModLX->cchName != strlen(pModLX->pszName))
+ return VERR_LDRLX_BAD_SONAME;
+
+ /*
+ * Quick validation of the object table.
+ */
+ for (i = 0; i < pModLX->cSegments; i++)
+ {
+ if (pModLX->paObjs[i].o32_base & (OBJPAGELEN - 1))
+ return VERR_LDRLX_BAD_OBJECT_TABLE;
+ if (pModLX->paObjs[i].o32_base + pModLX->paObjs[i].o32_size <= pModLX->paObjs[i].o32_base)
+ return VERR_LDRLX_BAD_OBJECT_TABLE;
+ if (pModLX->paObjs[i].o32_mapsize > (pModLX->paObjs[i].o32_size + (OBJPAGELEN - 1)))
+ return VERR_LDRLX_BAD_OBJECT_TABLE;
+ if ( pModLX->paObjs[i].o32_mapsize
+ && ( (uint8_t *)&pModLX->paPageMappings[pModLX->paObjs[i].o32_pagemap] > pModLX->pbLoaderSectionLast
+ || (uint8_t *)&pModLX->paPageMappings[pModLX->paObjs[i].o32_pagemap + pModLX->paObjs[i].o32_mapsize]
+ > pModLX->pbLoaderSectionLast))
+ return VERR_LDRLX_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 VERR_LDRLX_BAD_OBJECT_TABLE;
+ if (pModLX->paObjs[i].o32_base < pModLX->paObjs[i - 1].o32_base + pModLX->paObjs[i - 1].o32_mapsize)
+ return VERR_LDRLX_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.
+ *
+ * However, this screws up DwARF debug info, let's not do this
+ * when the purpose is reading debug info.
+ */
+ /** @todo Add flag for enabling this optimization. */
+ fCanOptimizeMapping = !(Hdr.e32_mflags & (E32NOINTFIX | E32SYSDLL))
+ && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION));
+ NextRVA = 0;
+
+ /*
+ * Setup the KLDRMOD segment array.
+ */
+ char *pszSegNm = (char *)&pModLX->aSegments[pModLX->cSegments];
+ for (i = 0; i < pModLX->cSegments; i++)
+ {
+ /* dummy segment name */
+ pModLX->aSegments[i].pszName = pszSegNm;
+ size_t cchName = RTStrPrintf(pszSegNm, sizeof("segXXXXX"), "seg%u", i);
+ pszSegNm += cchName + 1;
+ pModLX->aSegments[i].cchName = (uint32_t)cchName;
+
+ /* unused */
+ pModLX->aSegments[i].offFile = -1;
+ pModLX->aSegments[i].cbFile = -1;
+ pModLX->aSegments[i].SelFlat = 0;
+ pModLX->aSegments[i].Sel16bit = 0;
+
+ /* flags */
+ pModLX->aSegments[i].fFlags = 0;
+ if (pModLX->paObjs[i].o32_flags & OBJBIGDEF)
+ pModLX->aSegments[i].fFlags = RTLDRSEG_FLAG_16BIT;
+ if (pModLX->paObjs[i].o32_flags & OBJALIAS16)
+ pModLX->aSegments[i].fFlags = RTLDRSEG_FLAG_OS2_ALIAS16;
+ if (pModLX->paObjs[i].o32_flags & OBJCONFORM)
+ pModLX->aSegments[i].fFlags = RTLDRSEG_FLAG_OS2_CONFORM;
+ if (pModLX->paObjs[i].o32_flags & OBJIOPL)
+ pModLX->aSegments[i].fFlags = RTLDRSEG_FLAG_OS2_IOPL;
+
+ /* size and addresses */
+ pModLX->aSegments[i].Alignment = OBJPAGELEN;
+ pModLX->aSegments[i].cb = pModLX->paObjs[i].o32_size;
+ pModLX->aSegments[i].LinkAddress = pModLX->paObjs[i].o32_base;
+ pModLX->aSegments[i].RVA = NextRVA;
+ if ( fCanOptimizeMapping
+ || i + 1 >= pModLX->cSegments
+ || (pModLX->paObjs[i].o32_flags & OBJRSRC)
+ || (pModLX->paObjs[i + 1].o32_flags & OBJRSRC))
+ pModLX->aSegments[i].cbMapped = RT_ALIGN_Z(pModLX->paObjs[i].o32_size, OBJPAGELEN);
+ else
+ pModLX->aSegments[i].cbMapped = pModLX->paObjs[i + 1].o32_base - pModLX->paObjs[i].o32_base;
+ /** @todo Above probably doesn't work for os2krnl and other images
+ * non-sequential virtual address assignments. */
+ NextRVA += (uint32_t)pModLX->aSegments[i].cbMapped;
+
+ /* protection */
+ switch ( pModLX->paObjs[i].o32_flags
+ & (OBJSHARED | OBJREAD | OBJWRITE | OBJEXEC))
+ {
+ case 0:
+ case OBJSHARED:
+ pModLX->aSegments[i].fProt = 0;
+ break;
+ case OBJREAD:
+ case OBJREAD | OBJSHARED:
+ pModLX->aSegments[i].fProt = RTMEM_PROT_READ;
+ break;
+ case OBJWRITE:
+ case OBJWRITE | OBJREAD:
+ pModLX->aSegments[i].fProt = RTMEM_PROT_READ | RTMEM_PROT_WRITECOPY;
+ break;
+ case OBJWRITE | OBJSHARED:
+ case OBJWRITE | OBJSHARED | OBJREAD:
+ pModLX->aSegments[i].fProt = RTMEM_PROT_READ | RTMEM_PROT_WRITE;
+ break;
+ case OBJEXEC:
+ case OBJEXEC | OBJSHARED:
+ pModLX->aSegments[i].fProt = RTMEM_PROT_EXEC;
+ break;
+ case OBJEXEC | OBJREAD:
+ case OBJEXEC | OBJREAD | OBJSHARED:
+ pModLX->aSegments[i].fProt = RTMEM_PROT_EXEC | RTMEM_PROT_READ;
+ break;
+ case OBJEXEC | OBJWRITE:
+ case OBJEXEC | OBJWRITE | OBJREAD:
+ pModLX->aSegments[i].fProt = RTMEM_PROT_EXEC | RTMEM_PROT_READ | RTMEM_PROT_WRITECOPY;
+ break;
+ case OBJEXEC | OBJWRITE | OBJSHARED:
+ case OBJEXEC | OBJWRITE | OBJSHARED | OBJREAD:
+ pModLX->aSegments[i].fProt = RTMEM_PROT_EXEC | RTMEM_PROT_READ | RTMEM_PROT_WRITE;
+ break;
+ }
+ if ((pModLX->paObjs[i].o32_flags & (OBJREAD | OBJWRITE | OBJEXEC | OBJRSRC)) == OBJRSRC)
+ pModLX->aSegments[i].fProt = RTMEM_PROT_READ;
+ /*pModLX->aSegments[i].f16bit = !(pModLX->paObjs[i].o32_flags & OBJBIGDEF)
+ pModLX->aSegments[i].fIOPL = !(pModLX->paObjs[i].o32_flags & OBJIOPL)
+ pModLX->aSegments[i].fConforming = !(pModLX->paObjs[i].o32_flags & OBJCONFORM) */
+ }
+
+ /* set the mapping size */
+ pModLX->cbMapped = NextRVA;
+
+ /*
+ * We're done.
+ */
+ *ppModLX = pModLX;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtldrLX_Close(PRTLDRMODINTERNAL pMod)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ KLDRMODLX_ASSERT(!pModLX->pvMapping);
+
+ if (pModLX->pbNonResNameTab)
+ {
+ RTMemFree(pModLX->pbNonResNameTab);
+ pModLX->pbNonResNameTab = NULL;
+ }
+ if (pModLX->pbFixupSection)
+ {
+ RTMemFree(pModLX->pbFixupSection);
+ pModLX->pbFixupSection = NULL;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Resolved base address aliases.
+ *
+ * @param pModLX The interpreter module instance
+ * @param pBaseAddress The base address, IN & OUT.
+ */
+static void kldrModLXResolveBaseAddress(PKLDRMODLX pModLX, PRTLDRADDR pBaseAddress)
+{
+ if (*pBaseAddress == RTLDR_BASEADDRESS_LINK)
+ *pBaseAddress = pModLX->aSegments[0].LinkAddress;
+}
+
+
+static int kldrModLXQuerySymbol(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, uint32_t iSymbol,
+ const char *pchSymbol, size_t cchSymbol, const char *pszVersion,
+ PFNRTLDRIMPORT pfnGetForwarder, void *pvUser, PRTLDRADDR puValue, uint32_t *pfKind)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ uint32_t iOrdinal;
+ int rc;
+ const struct b32_bundle *pBundle;
+ RT_NOREF(pvBits);
+ RT_NOREF(pszVersion);
+
+ /*
+ * Give up at once if there is no entry table.
+ */
+ if (!pModLX->Hdr.e32_enttab)
+ return VERR_SYMBOL_NOT_FOUND;
+
+ /*
+ * Translate the symbol name into an ordinal.
+ */
+ if (pchSymbol)
+ {
+ rc = kldrModLXDoNameLookup(pModLX, pchSymbol, cchSymbol, &iSymbol);
+ if (RT_FAILURE(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 size_t s_cbEntry[] = { 0, 3, 5, 5, 7 };
+
+ /*
+ * Check for a hit first.
+ */
+ iOrdinal += pBundle->b32_cnt;
+ if (iSymbol < iOrdinal)
+ {
+ uint32_t offObject;
+ const struct e32_entry *pEntry = (const struct e32_entry *)((uintptr_t)(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 VERR_SYMBOL_NOT_FOUND;
+
+ /* e32_flags + a 16-bit offset. */
+ case ENTRY16:
+ offObject = pEntry->e32_variant.e32_offset.offset16;
+ if (pfKind)
+ *pfKind = RTLDRSYMKIND_16BIT | RTLDRSYMKIND_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 = RTLDRSYMKIND_16BIT | RTLDRSYMKIND_CODE;
+ break;
+
+ /* e32_flags + a 32-bit offset. */
+ case ENTRY32:
+ offObject = pEntry->e32_variant.e32_offset.offset32;
+ if (pfKind)
+ *pfKind = RTLDRSYMKIND_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 VERR_LDRLX_BAD_BUNDLE;
+ }
+
+ /*
+ * Validate the object number and calc the return address.
+ */
+ if ( pBundle->b32_obj <= 0
+ || pBundle->b32_obj > pModLX->cSegments)
+ return VERR_LDRLX_BAD_BUNDLE;
+ if (puValue)
+ *puValue = BaseAddress
+ + offObject
+ + pModLX->aSegments[pBundle->b32_obj - 1].RVA;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Skip the bundle.
+ */
+ if (pBundle->b32_type > ENTRYFWD)
+ {
+ KLDRMODLX_ASSERT(!"Bad type"); /** @todo figure out TYPEINFO. */
+ return VERR_LDRLX_BAD_BUNDLE;
+ }
+ if (pBundle->b32_type == 0)
+ pBundle = (const struct b32_bundle *)((const uint8_t *)pBundle + 2);
+ else
+ pBundle = (const struct b32_bundle *)((const uint8_t *)(pBundle + 1) + s_cbEntry[pBundle->b32_type] * pBundle->b32_cnt);
+ }
+
+ return VERR_SYMBOL_NOT_FOUND;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnGetSymbolEx}
+ */
+static DECLCALLBACK(int) rtldrLX_GetSymbolEx(PRTLDRMODINTERNAL pMod, const void *pvBits, RTUINTPTR BaseAddress,
+ uint32_t iOrdinal, const char *pszSymbol, RTUINTPTR *pValue)
+{
+ uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
+ return kldrModLXQuerySymbol(pMod, pvBits, BaseAddress, iOrdinal, pszSymbol, pszSymbol ? strlen(pszSymbol) : 0,
+ NULL, NULL, NULL, pValue, &fKind);
+}
+
+
+/**
+ * Do name lookup.
+ *
+ * @returns IPRT status code.
+ * @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, size_t cchSymbol, uint32_t *piSymbol)
+{
+
+ /*
+ * First do a hash table lookup.
+ */
+ /** @todo hash name table for speed. */
+
+ /*
+ * Search the name tables.
+ */
+ const uint8_t *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 VERR_SYMBOL_NOT_FOUND;
+
+ *piSymbol = *(const uint16_t *)(pbName + 1 + *pbName);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * 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 uint8_t *kldrModLXDoNameTableLookupByName(const uint8_t *pbNameTable, ssize_t cbNameTable,
+ const char *pchSymbol, size_t cchSymbol)
+{
+ /*
+ * Determin the namelength up front so we can skip anything which doesn't matches the length.
+ */
+ uint8_t cbSymbol8Bit = (uint8_t)cchSymbol;
+ if (cbSymbol8Bit != cchSymbol)
+ return NULL; /* too long. */
+
+ /*
+ * Walk the name table.
+ */
+ while (*pbNameTable != 0 && cbNameTable > 0)
+ {
+ const uint8_t cbName = *pbNameTable;
+
+ cbNameTable -= cbName + 1 + 2;
+ if (cbNameTable < 0)
+ break;
+
+ if ( cbName == cbSymbol8Bit
+ && !memcmp(pbNameTable + 1, pchSymbol, cbName))
+ return pbNameTable;
+
+ /* next entry */
+ pbNameTable += cbName + 1 + 2;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Deal with a forwarder entry.
+ *
+ * @returns IPRT status code.
+ * @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,
+ PFNRTLDRIMPORT pfnGetForwarder, void *pvUser, PRTLDRADDR puValue, uint32_t *pfKind)
+{
+ if (!pfnGetForwarder)
+ return VERR_LDR_FORWARDER;
+
+ /*
+ * Validate the entry import module ordinal.
+ */
+ if ( !pEntry->e32_variant.e32_fwd.modord
+ || pEntry->e32_variant.e32_fwd.modord > pModLX->Hdr.e32_impmodcnt)
+ return VERR_LDRLX_BAD_FORWARDER;
+
+ char szImpModule[256];
+ int rc = kldrModLXGetImport(pModLX, NULL, pEntry->e32_variant.e32_fwd.modord - 1, szImpModule, sizeof(szImpModule), NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Figure out the parameters.
+ */
+ uint32_t iSymbol;
+ const char *pszSymbol;
+ char szSymbol[256];
+ if (pEntry->e32_flags & FWD_ORDINAL)
+ {
+ iSymbol = pEntry->e32_variant.e32_fwd.value;
+ pszSymbol = NULL; /* no symbol name. */
+ }
+ else
+ {
+ const uint8_t *pbName;
+
+ /* load the fixup section if necessary. */
+ if (!pModLX->pbImportProcs)
+ {
+ rc = kldrModLXDoLoadFixupSection(pModLX);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /* Make name pointer. */
+ pbName = pModLX->pbImportProcs + pEntry->e32_variant.e32_fwd.value;
+ if ( pbName >= pModLX->pbFixupSectionLast
+ || pbName < pModLX->pbFixupSection
+ || !*pbName)
+ return VERR_LDRLX_BAD_FORWARDER;
+
+
+ /* check for '#' name. */
+ if (pbName[1] == '#')
+ {
+ uint8_t cbLeft = *pbName;
+ const uint8_t *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 VERR_LDRLX_BAD_FORWARDER;
+ if (uDigit >= uBase)
+ return VERR_LDRLX_BAD_FORWARDER;
+
+ /* insert the digit */
+ iSymbol *= uBase;
+ iSymbol += uDigit;
+ }
+ if (!iSymbol)
+ return VERR_LDRLX_BAD_FORWARDER;
+
+ pszSymbol = NULL; /* no symbol name. */
+ }
+ else
+ {
+ memcpy(szSymbol, pbName + 1, *pbName);
+ szSymbol[*pbName] = '\0';
+ pszSymbol = szSymbol;
+ iSymbol = UINT32_MAX;
+ }
+ }
+
+ /*
+ * Resolve the forwarder.
+ */
+ rc = pfnGetForwarder(&pModLX->Core, szImpModule, pszSymbol, iSymbol, puValue, /*pfKind, */pvUser);
+ if (RT_SUCCESS(rc) && pfKind)
+ *pfKind |= RTLDRSYMKIND_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 IPRT status code.
+ * @param pModLX The PE module interpreter instance.
+ */
+static int kldrModLXDoLoadFixupSection(PKLDRMODLX pModLX)
+{
+ void *pv = RTMemAlloc(pModLX->Hdr.e32_fixupsize);
+ if (!pv)
+ return VERR_NO_MEMORY;
+
+ uint32_t off = pModLX->Hdr.e32_objtab + pModLX->Hdr.e32_ldrsize;
+ int rc = pModLX->Core.pReader->pfnRead(pModLX->Core.pReader, pv, pModLX->Hdr.e32_fixupsize,
+ off + pModLX->offHdr);
+ if (RT_SUCCESS(rc))
+ {
+ pModLX->pbFixupSection = (uint8_t *)pv;
+ pModLX->pbFixupSectionLast = pModLX->pbFixupSection + pModLX->Hdr.e32_fixupsize;
+ KLDRMODLX_ASSERT(!pModLX->paoffPageFixups);
+ if (pModLX->Hdr.e32_fpagetab)
+ pModLX->paoffPageFixups = (const uint32_t *)(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
+ RTMemFree(pv);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnEnumSymbols}
+ */
+static DECLCALLBACK(int) rtldrLX_EnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits,
+ RTUINTPTR BaseAddress, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ RT_NOREF(pvBits);
+ RT_NOREF(fFlags);
+
+ kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
+
+ /*
+ * Enumerate the entry table.
+ * (The entry table is made up of bundles of similar exports.)
+ */
+ int rc = VINF_SUCCESS;
+ uint32_t iOrdinal = 1;
+ const struct b32_bundle *pBundle = (const struct b32_bundle *)pModLX->pbEntryTab;
+ while (pBundle->b32_cnt && iOrdinal)
+ {
+ static const size_t s_cbEntry[] = { 0, 3, 5, 5, 7 };
+
+ /*
+ * Enum the entries in the bundle.
+ */
+ if (pBundle->b32_type != EMPTY)
+ {
+ const struct e32_entry *pEntry;
+ size_t cbEntry;
+ RTLDRADDR BundleRVA;
+ unsigned cLeft;
+
+
+ /* Validate the bundle. */
+ switch (pBundle->b32_type)
+ {
+ case ENTRY16:
+ case GATE16:
+ case ENTRY32:
+ if ( pBundle->b32_obj <= 0
+ || pBundle->b32_obj > pModLX->cSegments)
+ return VERR_LDRLX_BAD_BUNDLE;
+ BundleRVA = pModLX->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 VERR_LDRLX_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)
+ {
+ RTLDRADDR uValue;
+ uint32_t fKind;
+ int fFoundName;
+ const uint8_t *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 = RTLDRSYMKIND_16BIT | RTLDRSYMKIND_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 = RTLDRSYMKIND_16BIT | RTLDRSYMKIND_CODE;
+ break;
+
+ /* e32_flags + a 32-bit offset. */
+ case ENTRY32:
+ uValue = BaseAddress + BundleRVA + pEntry->e32_variant.e32_offset.offset32;
+ fKind = RTLDRSYMKIND_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 = RTLDRSYMKIND_FORWARDER;
+ break;
+
+ default: /* shut up gcc. */
+ uValue = 0;
+ fKind = RTLDRSYMKIND_NO_BIT | RTLDRSYMKIND_NO_TYPE;
+ break;
+ }
+
+ /*
+ * Any symbol names?
+ */
+ fFoundName = 0;
+ char szName[256];
+
+ /* resident name table. */
+ pbName = pModLX->pbResNameTab;
+ if (pbName)
+ {
+ do
+ {
+ pbName = kldrModLXDoNameTableLookupByOrdinal(pbName, pModLX->pbLoaderSectionLast - pbName + 1, iOrdinal);
+ if (!pbName)
+ break;
+ fFoundName = 1;
+ memcpy(szName, (const char *)pbName + 1, *pbName);
+ szName[*pbName] = '\0';
+ rc = pfnCallback(pMod, szName, iOrdinal, uValue, /*fKind,*/ pvUser);
+ if (rc != VINF_SUCCESS)
+ 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;
+ memcpy(szName, (const char *)pbName + 1, *pbName);
+ szName[*pbName] = '\0';
+ rc = pfnCallback(pMod, szName, iOrdinal, uValue, /*fKind,*/ pvUser);
+ if (rc != VINF_SUCCESS)
+ 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)
+ {
+ RT_NOREF(fKind);
+ rc = pfnCallback(pMod, NULL /*pszName*/, iOrdinal, uValue, /*fKind,*/ pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ }
+
+ /* next */
+ iOrdinal++;
+ pEntry = (const struct e32_entry *)((uintptr_t)pEntry + cbEntry);
+ }
+ }
+
+ /*
+ * The next bundle.
+ */
+ if (pBundle->b32_type > ENTRYFWD)
+ {
+ KLDRMODLX_ASSERT(!"Bad type"); /** @todo figure out TYPEINFO. */
+ return VERR_LDRLX_BAD_BUNDLE;
+ }
+ if (pBundle->b32_type == 0)
+ pBundle = (const struct b32_bundle *)((const uint8_t *)pBundle + 2);
+ else
+ pBundle = (const struct b32_bundle *)((const uint8_t *)(pBundle + 1) + s_cbEntry[pBundle->b32_type] * pBundle->b32_cnt);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * 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 uint8_t *kldrModLXDoNameTableLookupByOrdinal(const uint8_t *pbNameTable, ssize_t cbNameTable, uint32_t iOrdinal)
+{
+ while (*pbNameTable != 0 && cbNameTable > 0)
+ {
+ const uint8_t cbName = *pbNameTable;
+ uint32_t 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;
+}
+
+
+static int kldrModLXGetImport(PKLDRMODLX pModLX, const void *pvBits, uint32_t iImport, char *pszName, size_t cchName,
+ size_t *pcbNeeded)
+{
+ const uint8_t *pb;
+ int rc;
+ RT_NOREF(pvBits);
+
+ /*
+ * Validate
+ */
+ if (iImport >= pModLX->Hdr.e32_impmodcnt)
+ return VERR_LDRLX_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+
+ /*
+ * Lazy loading the fixup section.
+ */
+ if (!pModLX->pbImportMods)
+ {
+ rc = kldrModLXDoLoadFixupSection(pModLX);
+ if (RT_FAILURE(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 (pcbNeeded)
+ *pcbNeeded = *pb + 1;
+ if (*pb < cchName)
+ {
+ memcpy(pszName, pb + 1, *pb);
+ pszName[*pb] = '\0';
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ memcpy(pszName, pb + 1, cchName);
+ if (cchName)
+ pszName[cchName - 1] = '\0';
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+
+ return rc;
+}
+
+#if 0
+
+/** @copydoc kLdrModNumberOfImports */
+static int32_t kldrModLXNumberOfImports(PRTLDRMODINTERNAL pMod, const void *pvBits)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ RT_NOREF(pvBits);
+ return pModLX->Hdr.e32_impmodcnt;
+}
+
+
+/** @copydoc kLdrModGetStackInfo */
+static int kldrModLXGetStackInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ const uint32_t i = pModLX->Hdr.e32_stackobj;
+ RT_NOREF(pvBits);
+
+ if ( i
+ && i <= pModLX->cSegments
+ && pModLX->Hdr.e32_esp <= pModLX->aSegments[i - 1].LinkAddress + pModLX->aSegments[i - 1].cb
+ && pModLX->Hdr.e32_stacksize
+ && pModLX->Hdr.e32_esp - pModLX->Hdr.e32_stacksize >= pModLX->aSegments[i - 1].LinkAddress)
+ {
+
+ kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
+ pStackInfo->LinkAddress = pModLX->Hdr.e32_esp - pModLX->Hdr.e32_stacksize;
+ pStackInfo->Address = BaseAddress
+ + pModLX->aSegments[i - 1].RVA
+ + pModLX->Hdr.e32_esp - pModLX->Hdr.e32_stacksize - pModLX->aSegments[i - 1].LinkAddress;
+ }
+ else
+ {
+ pSt0ackInfo->Address = NIL_RTLDRADDR;
+ pStackInfo->LinkAddress = NIL_RTLDRADDR;
+ }
+ pStackInfo->cbStack = pModLX->Hdr.e32_stacksize;
+ pStackInfo->cbStackThread = 0;
+
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModQueryMainEntrypoint */
+static int kldrModLXQueryMainEntrypoint(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PRTLDRADDR pMainEPAddress)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ RT_NOREF(pvBits);
+
+ /*
+ * Convert the address from the header.
+ */
+ kldrModLXResolveBaseAddress(pModLX, &BaseAddress);
+ *pMainEPAddress = pModLX->Hdr.e32_startobj
+ && pModLX->Hdr.e32_startobj <= pModLX->cSegments
+ && pModLX->Hdr.e32_eip < pModLX->aSegments[pModLX->Hdr.e32_startobj - 1].cb
+ ? BaseAddress + pModLX->aSegments[pModLX->Hdr.e32_startobj - 1].RVA + pModLX->Hdr.e32_eip
+ : NIL_RTLDRADDR;
+ return VINF_SUCCESS;
+}
+
+#endif
+
+/** Helper for rtldrLX_EnumDbgInfo. */
+static int rtldrLx_EnumDbgInfoHelper(PKLDRMODLX pModLX, PFNRTLDRENUMDBG pfnCallback, void *pvUser,
+ uint8_t *pbBuf, uint32_t cbRead, uint32_t offDbgInfo, bool *pfReturn)
+{
+ RTLDRDBGINFO DbgInfo;
+ uint32_t iDbgInfo = 0;
+ uint32_t cbDbgInfo = pModLX->Hdr.e32_debuglen;
+
+ /*
+ * Recent watcom linkers emit PE style IMAGE_DEBUG_MISC for specifying
+ * external file with CV info.
+ */
+ if (cbRead >= sizeof(IMAGE_DEBUG_MISC))
+ {
+ PCIMAGE_DEBUG_MISC pMisc = (PCIMAGE_DEBUG_MISC)pbBuf;
+ if ( pMisc->DataType == IMAGE_DEBUG_MISC_EXENAME
+ && pMisc->Length <= cbRead
+ && pMisc->Length >= RT_UOFFSETOF(IMAGE_DEBUG_MISC, Data[4])
+ && pMisc->Unicode == 0
+ && pMisc->Reserved[0] == 0
+ && pMisc->Reserved[1] == 0
+ && pMisc->Reserved[2] == 0
+ && pMisc->Data[0] >= 0x20
+ && pMisc->Data[0] < 0x7f
+ && pMisc->Data[1] >= 0x20
+ && pMisc->Data[1] < 0x7f
+ && pMisc->Data[2] >= 0x20
+ && pMisc->Data[2] < 0x7f )
+ {
+ uint32_t cchMaxName = pMisc->Length - RT_UOFFSETOF(IMAGE_DEBUG_MISC, Data[0]);
+ for (uint32_t cchName = 3; cchName < cchMaxName; cchName++)
+ {
+ char const ch = pMisc->Data[cchName];
+ if (ch == 0)
+ {
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW;
+ DbgInfo.iDbgInfo = iDbgInfo;
+ DbgInfo.offFile = offDbgInfo;
+ DbgInfo.LinkAddress = NIL_RTLDRADDR;
+ DbgInfo.cb = pMisc->Length;
+ DbgInfo.pszExtFile = (char *)&pMisc->Data[0];
+ DbgInfo.u.Cv.cbImage = pModLX->Hdr.e32_mpages * pModLX->Hdr.e32_pagesize;
+ DbgInfo.u.Cv.uTimestamp = 0;
+ DbgInfo.u.Cv.uMajorVer = 0;
+ DbgInfo.u.Cv.uMinorVer = 0;
+
+ *pfReturn = true;
+ int rc = pfnCallback(&pModLX->Core, &DbgInfo, pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ }
+ else if (ch >= 0x30 && ch < 0x7f)
+ continue;
+ break;
+ }
+
+ /* Skip it. */
+ pbBuf += pMisc->Length;
+ cbRead -= pMisc->Length;
+ offDbgInfo += pMisc->Length;
+ cbDbgInfo -= pMisc->Length;
+ iDbgInfo++;
+ }
+ }
+
+ /*
+ * Look for codeview signature.
+ */
+ RTCVHDR const *pCvHdr = (RTCVHDR const *)pbBuf;
+ if ( cbRead > sizeof(*pCvHdr)
+ && pCvHdr->off >= sizeof(*pCvHdr)
+ && pCvHdr->off < cbDbgInfo)
+ {
+ switch (pCvHdr->u32Magic)
+ {
+ case RTCVHDR_MAGIC_NB11:
+ case RTCVHDR_MAGIC_NB09:
+ case RTCVHDR_MAGIC_NB08:
+ case RTCVHDR_MAGIC_NB07:
+ case RTCVHDR_MAGIC_NB06:
+ case RTCVHDR_MAGIC_NB05:
+ case RTCVHDR_MAGIC_NB04:
+ case RTCVHDR_MAGIC_NB02:
+ case RTCVHDR_MAGIC_NB01:
+ case RTCVHDR_MAGIC_NB00:
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW;
+ DbgInfo.iDbgInfo = iDbgInfo;
+ DbgInfo.offFile = offDbgInfo;
+ DbgInfo.LinkAddress = NIL_RTLDRADDR;
+ DbgInfo.cb = cbDbgInfo;
+ DbgInfo.pszExtFile = NULL;
+ DbgInfo.u.Cv.cbImage = pModLX->Hdr.e32_mpages * pModLX->Hdr.e32_pagesize;
+ DbgInfo.u.Cv.uTimestamp = 0;
+ DbgInfo.u.Cv.uMajorVer = 0;
+ DbgInfo.u.Cv.uMinorVer = 0;
+
+ *pfReturn = true;
+ return pfnCallback(&pModLX->Core, &DbgInfo, pvUser);
+ }
+ }
+
+ /*
+ * Watcom wraps its DWARF output in an ELF image, so look for and ELF magic.
+ */
+ Elf32_Ehdr const *pElfHdr = (Elf32_Ehdr const *)pbBuf;
+ if ( cbRead >= sizeof(*pElfHdr)
+ && pElfHdr->e_ident[EI_MAG0] == ELFMAG0
+ && pElfHdr->e_ident[EI_MAG1] == ELFMAG1
+ && pElfHdr->e_ident[EI_MAG2] == ELFMAG2
+ && pElfHdr->e_ident[EI_MAG3] == ELFMAG3
+ && pElfHdr->e_ident[EI_CLASS] == ELFCLASS32
+ && pElfHdr->e_ident[EI_DATA] == ELFDATA2LSB
+ && pElfHdr->e_ident[EI_VERSION] == EV_CURRENT
+ && pElfHdr->e_shentsize == sizeof(Elf32_Shdr)
+ && pElfHdr->e_shnum >= 2
+ && pElfHdr->e_shnum < _32K + 10
+ && pElfHdr->e_shstrndx <= pElfHdr->e_shnum
+ && pElfHdr->e_shstrndx > 0
+ )
+ {
+ /** @todo try use pBuf for reading into and try to read more at once. */
+ uint32_t const offShdrs = pElfHdr->e_shoff + offDbgInfo;
+ uint32_t const cShdrs = pElfHdr->e_shnum;
+ uint32_t const cbShdr = pElfHdr->e_shentsize;
+ int rc = VINF_SUCCESS;
+
+ /* Read the section string table. */
+ Elf32_Shdr Shdr;
+ int rc2 = pModLX->Core.pReader->pfnRead(pModLX->Core.pReader, &Shdr, sizeof(Shdr),
+ offShdrs + pElfHdr->e_shstrndx * cbShdr);
+ if ( RT_SUCCESS(rc2)
+ && Shdr.sh_offset > 0
+ && Shdr.sh_size > 0
+ && Shdr.sh_size < _256K
+ && Shdr.sh_type == SHT_STRTAB)
+ {
+ uint32_t const cbStrTab = Shdr.sh_size;
+ char * const pszStrTab = (char *)RTMemTmpAlloc(cbStrTab + 2);
+ if (pszStrTab)
+ {
+ rc2 = pModLX->Core.pReader->pfnRead(pModLX->Core.pReader, pszStrTab, Shdr.sh_size, offDbgInfo + Shdr.sh_offset);
+ if (RT_SUCCESS(rc2))
+ {
+ pszStrTab[cbStrTab] = '\0';
+
+ /* Iterate the sections, one by one. */
+ for (uint32_t i = 1; i < cShdrs; i++)
+ {
+ rc = pModLX->Core.pReader->pfnRead(pModLX->Core.pReader, &Shdr, sizeof(Shdr), offShdrs + i * cbShdr);
+ if ( RT_SUCCESS(rc)
+ && Shdr.sh_name < cbStrTab
+ && strncmp(&pszStrTab[Shdr.sh_name], RT_STR_TUPLE(".debug_")) == 0)
+ {
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF;
+ DbgInfo.iDbgInfo = iDbgInfo;
+ DbgInfo.offFile = offDbgInfo + Shdr.sh_offset;
+ DbgInfo.LinkAddress = NIL_RTLDRADDR;
+ DbgInfo.cb = Shdr.sh_size;
+ DbgInfo.pszExtFile = NULL;
+ DbgInfo.u.Dwarf.pszSection = &pszStrTab[Shdr.sh_name];
+
+ *pfReturn = true;
+ rc = pfnCallback(&pModLX->Core, &DbgInfo, pvUser);
+ if (rc != VINF_SUCCESS)
+ break;
+ iDbgInfo++;
+ }
+ }
+ }
+ RTMemTmpFree(pszStrTab);
+ }
+ }
+ return rc;
+ }
+
+ /*
+ * Watcom debug info? Don't know how to detect it...
+ */
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnEnumDbgInfo}
+ */
+static DECLCALLBACK(int) rtldrLX_EnumDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits,
+ PFNRTLDRENUMDBG pfnCallback, void *pvUser)
+{
+ /*PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);*/
+ RT_NOREF(pfnCallback);
+ RT_NOREF(pvUser);
+
+ /*
+ * Quit immediately if no debug info.
+ */
+ if (kldrModLXHasDbgInfo(pMod, pvBits))
+ return VINF_SUCCESS;
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+
+ /*
+ * Read the debug info and look for familiar magics and structures.
+ */
+ union
+ {
+ uint8_t ab[1024];
+ IMAGE_DEBUG_MISC Misc;
+ RTCVHDR CvHdr;
+ } uBuf;
+
+ bool fReturn = false;
+
+ /* Try the offset without header displacement first. */
+ uint32_t cbToRead = RT_MIN(pModLX->Hdr.e32_debuglen, sizeof(uBuf));
+ int rc = pModLX->Core.pReader->pfnRead(pModLX->Core.pReader, &uBuf, cbToRead, pModLX->Hdr.e32_debuginfo);
+ if (RT_SUCCESS(rc))
+ rc = rtldrLx_EnumDbgInfoHelper(pModLX, pfnCallback, pvUser, &uBuf.ab[0], cbToRead, pModLX->Hdr.e32_debuginfo, &fReturn);
+
+ /* If that didn't yield anything, try displaying it by the header offset. */
+ if (!fReturn && pModLX->offHdr > 0)
+ {
+ rc = pModLX->Core.pReader->pfnRead(pModLX->Core.pReader, &uBuf, cbToRead, pModLX->Hdr.e32_debuginfo + pModLX->offHdr);
+ if (RT_SUCCESS(rc))
+ rc = rtldrLx_EnumDbgInfoHelper(pModLX, pfnCallback, pvUser, &uBuf.ab[0], cbToRead,
+ pModLX->Hdr.e32_debuginfo + pModLX->offHdr, &fReturn);
+ }
+ return rc;
+}
+
+
+static int kldrModLXHasDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ RT_NOREF(pvBits);
+
+ /*
+ * Don't currently bother with linkers which doesn't advertise it in the header.
+ */
+ if ( !pModLX->Hdr.e32_debuginfo
+ || !pModLX->Hdr.e32_debuglen)
+ return VERR_NOT_FOUND;
+ return VINF_SUCCESS;
+}
+
+#if 0
+
+/** @copydoc kLdrModMap */
+static int kldrModLXMap(PRTLDRMODINTERNAL pMod)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ unsigned fFixed;
+ void *pvBase;
+ int rc;
+
+ /*
+ * Already mapped?
+ */
+ if (pModLX->pvMapping)
+ return KLDR_ERR_ALREADY_MAPPED;
+
+ /*
+ * Allocate memory for it.
+ */
+ /* fixed image? */
+ fFixed = pModLX->Core.enmType == RTLDRTYPE_EXECUTABLE_FIXED
+ || pModLX->Core.enmType == RTLDRTYPE_SHARED_LIBRARY_FIXED;
+ if (!fFixed)
+ pvBase = NULL;
+ else
+ {
+ pvBase = (void *)(uintptr_t)pModLX->aSegments[0].LinkAddress;
+ if ((uintptr_t)pvBase != pModLX->aSegments[0].LinkAddress)
+ return KLDR_ERR_ADDRESS_OVERFLOW;
+ }
+ rc = kHlpPageAlloc(&pvBase, pModLX->cbMapped, KPROT_EXECUTE_READWRITE, fFixed);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Load the bits, apply page protection, and update the segment table.
+ */
+ rc = kldrModLXDoLoadBits(pModLX, pvBase);
+ if (RT_SUCCESS(rc))
+ rc = kldrModLXDoProtect(pModLX, pvBase, 0 /* protect */);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t i;
+ for (i = 0; i < pModLX->cSegments; i++)
+ {
+ if (pModLX->aSegments[i].RVA != NIL_RTLDRADDR)
+ pModLX->aSegments[i].MapAddress = (uintptr_t)pvBase + (uintptr_t)pModLX->aSegments[i].RVA;
+ }
+ pModLX->pvMapping = pvBase;
+ }
+ else
+ kHlpPageFree(pvBase, pModLX->cbMapped);
+ return rc;
+}
+
+#endif
+
+/**
+ * Loads the LX pages into the specified memory mapping.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pModLX The LX module interpreter instance.
+ * @param pvBits Where to load the bits.
+ */
+static int kldrModLXDoLoadBits(PKLDRMODLX pModLX, void *pvBits)
+{
+ const PRTLDRREADER pRdr = pModLX->Core.pReader;
+ uint8_t *pbTmpPage = NULL;
+ int rc = VINF_SUCCESS;
+ uint32_t i;
+
+ /*
+ * Iterate the segments.
+ */
+ for (i = 0; i < pModLX->Hdr.e32_objcnt; i++)
+ {
+ const struct o32_obj * const pObj = &pModLX->paObjs[i];
+ const uint32_t cPages = (uint32_t)(pModLX->aSegments[i].cbMapped / OBJPAGELEN);
+ uint32_t iPage;
+ uint8_t *pbPage = (uint8_t *)pvBits + (uintptr_t)pModLX->aSegments[i].RVA;
+
+ /*
+ * Iterate the page map pages.
+ */
+ for (iPage = 0; RT_SUCCESS(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 = pRdr->pfnRead(pRdr, pbPage, OBJPAGELEN,
+ pModLX->Hdr.e32_datapage + (pMap->o32_pagedataoffset << pModLX->Hdr.e32_pageshift));
+ else if (pMap->o32_pagesize < OBJPAGELEN)
+ {
+ rc = pRdr->pfnRead(pRdr, pbPage, pMap->o32_pagesize,
+ pModLX->Hdr.e32_datapage + (pMap->o32_pagedataoffset << pModLX->Hdr.e32_pageshift));
+ memset(pbPage + pMap->o32_pagesize, 0, OBJPAGELEN - pMap->o32_pagesize);
+ }
+ else
+ rc = VERR_LDRLX_BAD_PAGE_MAP;
+ break;
+
+ case ITERDATA:
+ case ITERDATA2:
+ /* make sure we've got a temp page .*/
+ if (!pbTmpPage)
+ {
+ pbTmpPage = (uint8_t *)RTMemAlloc(OBJPAGELEN + 256);
+ if (!pbTmpPage)
+ break;
+ }
+ /* validate the size. */
+ if (pMap->o32_pagesize > OBJPAGELEN + 252)
+ {
+ rc = VERR_LDRLX_BAD_PAGE_MAP;
+ break;
+ }
+
+ /* read it and ensure 4 extra zero bytes. */
+ rc = pRdr->pfnRead(pRdr, pbTmpPage, pMap->o32_pagesize,
+ pModLX->Hdr.e32_datapage + (pMap->o32_pagedataoffset << pModLX->Hdr.e32_pageshift));
+ if (RT_FAILURE(rc))
+ break;
+ memset(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:
+ memset(pbPage, 0, OBJPAGELEN);
+ break;
+
+ case RANGE:
+ KLDRMODLX_ASSERT(!"RANGE");
+ RT_FALL_THRU();
+ default:
+ rc = VERR_LDRLX_BAD_PAGE_MAP;
+ break;
+ }
+ }
+ if (RT_FAILURE(rc))
+ break;
+
+ /*
+ * Zero the remaining pages.
+ */
+ if (iPage < cPages)
+ memset(pbPage, 0, (cPages - iPage) * OBJPAGELEN);
+ }
+
+ if (pbTmpPage)
+ RTMemFree(pbTmpPage);
+ return rc;
+}
+
+
+/**
+ * Unpacks iterdata (aka EXEPACK).
+ *
+ * @returns IPRT status code.
+ * @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(uint8_t *pbDst, const uint8_t *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 VERR_LDRLX_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 VERR_LDRLX_BAD_ITERDATA;
+
+ cbSrc -= 4 + 1;
+ if (cbSrc < -4)
+ return VERR_LDRLX_BAD_ITERDATA;
+
+ memset(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 VERR_LDRLX_BAD_ITERDATA;
+
+ cbSrc -= 4 + pIter->LX_nBytes;
+ if (cbSrc < -4)
+ return VERR_LDRLX_BAD_ITERDATA;
+
+ for (i = pIter->LX_nIter; i > 0; i--, pbDst += pIter->LX_nBytes)
+ memcpy(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)
+ memset(pbDst, 0, cbDst);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Unpacks iterdata (aka EXEPACK).
+ *
+ * @returns IPRT status code.
+ * @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(uint8_t *pbDst, const uint8_t *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 VERR_LDRLX_BAD_ITERDATA2;
+ cbSrc -= cb + 1;
+ if (cbSrc < 0)
+ return VERR_LDRLX_BAD_ITERDATA2;
+ memcpy(pbDst, ++pbSrc, cb);
+ pbDst += cb;
+ pbSrc += cb;
+ }
+ else if (cbSrc < 2)
+ return VERR_LDRLX_BAD_ITERDATA2;
+ else
+ {
+ const int cb = pbSrc[1];
+ if (!cb)
+ goto l_endloop;
+ cbDst -= cb;
+ if (cbDst < 0)
+ return VERR_LDRLX_BAD_ITERDATA2;
+ cbSrc -= 3;
+ if (cbSrc < 0)
+ return VERR_LDRLX_BAD_ITERDATA2;
+ memset(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 VERR_LDRLX_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 VERR_LDRLX_BAD_ITERDATA2;
+ cbDst -= cb1;
+ if (cbDst < 0)
+ return VERR_LDRLX_BAD_ITERDATA2;
+ memcpy(pbDst, pbSrc, cb1);
+ pbDst += cb1;
+ pbSrc += cb1;
+
+ if (off > OBJPAGELEN - (unsigned)cbDst)
+ return VERR_LDRLX_BAD_ITERDATA2;
+ cbDst -= cb2;
+ if (cbDst < 0)
+ return VERR_LDRLX_BAD_ITERDATA2;
+ memmove(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 VERR_LDRLX_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 VERR_LDRLX_BAD_ITERDATA2;
+ cbDst -= cb;
+ if (cbDst < 0)
+ return VERR_LDRLX_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 VERR_LDRLX_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 VERR_LDRLX_BAD_ITERDATA2;
+ cbDst -= cb1;
+ if (cbDst < 0)
+ return VERR_LDRLX_BAD_ITERDATA2;
+ memcpy(pbDst, pbSrc, cb1);
+ pbDst += cb1;
+ pbSrc += cb1;
+
+ if (off > OBJPAGELEN - (unsigned)cbDst)
+ return VERR_LDRLX_BAD_ITERDATA2;
+ cbDst -= cb2;
+ if (cbDst < 0)
+ return VERR_LDRLX_BAD_ITERDATA2;
+ kLdrModLXMemCopyW(pbDst, pbDst - off, cb2);
+ pbDst += cb2;
+ }
+ break;
+ }
+ } /* type switch. */
+ } /* unpack loop */
+
+l_endloop:
+
+
+ /*
+ * Zero remainder of the page.
+ */
+ if (cbDst > 0)
+ memset(pbDst, 0, cbDst);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * 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(uint8_t *pbDst, const uint8_t *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)
+ *(uint16_t *)pbDst = *(const uint16_t *)pbSrc;
+ break;
+
+ default:
+ /* 32-bit copy (unaligned) */
+ if (cb & 1)
+ *pbDst++ = *pbSrc++;
+ if (cb & 2)
+ {
+ *(uint16_t *)pbDst = *(const uint16_t *)pbSrc;
+ pbDst += 2;
+ pbSrc += 2;
+ }
+ for (cb >>= 2; cb > 0; cb--, pbDst += 4, pbSrc += 4)
+ *(uint32_t *)pbDst = *(const uint32_t *)pbSrc;
+ break;
+ }
+}
+
+#if 0
+
+/**
+ * Unprotects or protects the specified image mapping.
+ *
+ * @returns IPRT status code.
+ *
+ * @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)
+{
+ uint32_t i;
+
+ /*
+ * Change object protection.
+ */
+ for (i = 0; i < pModLX->cSegments; i++)
+ {
+ int rc;
+ void *pv;
+ KPROT enmProt;
+
+ /* calc new protection. */
+ enmProt = pModLX->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 = (uint8_t *)pvBits + pModLX->aSegments[i].RVA;
+
+ rc = kHlpPageProtect(pv, pModLX->aSegments[i].cbMapped, enmProt);
+ if (RT_FAILURE(rc))
+ break;
+
+ /** @todo the gap page should be marked NOACCESS! */
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModUnmap */
+static int kldrModLXUnmap(PRTLDRMODINTERNAL pMod)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ uint32_t 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 < pModLX->cSegments; i++)
+ pModLX->aSegments[i].MapAddress = 0;
+
+ return rc;
+}
+
+
+/** @copydoc kLdrModAllocTLS */
+static int kldrModLXAllocTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+
+ /* no tls, just do the error checking. */
+ if ( pvMapping == KLDRMOD_INT_MAP
+ && pModLX->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModFreeTLS */
+static void kldrModLXFreeTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
+{
+ /* no tls. */
+ RT_NOREF(pMod);
+ RT_NOREF(pvMapping);
+
+}
+
+
+/** @copydoc kLdrModReload */
+static int kldrModLXReload(PRTLDRMODINTERNAL pMod)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ 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 (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Load the bits again.
+ */
+ rc = kldrModLXDoLoadBits(pModLX, (void *)pModLX->pvMapping);
+
+ /*
+ * Restore protection.
+ */
+ rc2 = kldrModLXDoProtect(pModLX, (void *)pModLX->pvMapping, 0 /* protect */);
+ if (RT_SUCCESS(rc) && RT_FAILURE(rc2))
+ rc = rc2;
+ return rc;
+}
+
+
+/** @copydoc kLdrModFixupMapping */
+static int kldrModLXFixupMapping(PRTLDRMODINTERNAL pMod, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ 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 (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Apply fixups and resolve imports.
+ */
+ rc = rtldrLX_RelocateBits(pMod, (void *)pModLX->pvMapping, (uintptr_t)pModLX->pvMapping,
+ pModLX->aSegments[0].LinkAddress, pfnGetImport, pvUser);
+
+ /*
+ * Restore protection.
+ */
+ rc2 = kldrModLXDoProtect(pModLX, (void *)pModLX->pvMapping, 0 /* protect */);
+ if (RT_SUCCESS(rc) && RT_FAILURE(rc2))
+ rc = rc2;
+ return rc;
+}
+
+
+/** @copydoc kLdrModCallInit */
+static int kldrModLXCallInit(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ 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 = VINF_SUCCESS;
+ 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, uintptr_t uHandle)
+{
+ int rc;
+
+ /*
+ * If no entrypoint there isn't anything to be done.
+ */
+ if ( !pModLX->Hdr.e32_startobj
+ || pModLX->Hdr.e32_startobj > pModLX->Hdr.e32_objcnt)
+ return VINF_SUCCESS;
+
+ /*
+ * Invoke the entrypoint and convert the boolean result to a kLdr status code.
+ */
+ rc = kldrModLXDoCall((uintptr_t)pvMapping
+ + (uintptr_t)pModLX->aSegments[pModLX->Hdr.e32_startobj - 1].RVA
+ + pModLX->Hdr.e32_eip,
+ uHandle, uOp, NULL);
+ if (rc)
+ rc = VINF_SUCCESS;
+ else if (uOp == 0 /* attach */)
+ rc = KLDR_ERR_MODULE_INIT_FAILED;
+ else /* detach: ignore failures */
+ rc = VINF_SUCCESS;
+ return rc;
+}
+
+
+/**
+ * Do a 3 parameter callback.
+ *
+ * @returns 32-bit callback return.
+ * @param uEntrypoint The address of the function to be called.
+ * @param uHandle The first argument, the module handle.
+ * @param uOp The second argumnet, the reason we're calling.
+ * @param pvReserved The third argument, reserved argument. (figure this one out)
+ */
+static int32_t kldrModLXDoCall(uintptr_t uEntrypoint, uintptr_t uHandle, uint32_t uOp, void *pvReserved)
+{
+#if defined(__X86__) || defined(__i386__) || defined(_M_IX86)
+ int32_t rc;
+/** @todo try/except */
+
+ /*
+ * Paranoia.
+ */
+# ifdef __GNUC__
+ __asm__ __volatile__(
+ "pushl %2\n\t"
+ "pushl %1\n\t"
+ "pushl %0\n\t"
+ "lea 12(%%esp), %2\n\t"
+ "call *%3\n\t"
+ "movl %2, %%esp\n\t"
+ : "=a" (rc)
+ : "d" (uOp),
+ "S" (0),
+ "c" (uEntrypoint),
+ "0" (uHandle));
+# elif defined(_MSC_VER)
+ __asm {
+ mov eax, [uHandle]
+ mov edx, [uOp]
+ mov ecx, 0
+ mov ebx, [uEntrypoint]
+ push edi
+ mov edi, esp
+ push ecx
+ push edx
+ push eax
+ call ebx
+ mov esp, edi
+ pop edi
+ mov [rc], eax
+ }
+# else
+# error "port me!"
+# endif
+ RT_NOREF(pvReserved);
+ return rc;
+
+#else
+ RT_NOREF(uEntrypoint);
+ RT_NOREF(uHandle);
+ RT_NOREF(uOp);
+ RT_NOREF(pvReserved);
+ return KCPU_ERR_ARCH_CPU_NOT_COMPATIBLE;
+#endif
+}
+
+
+/** @copydoc kLdrModCallTerm */
+static int kldrModLXCallTerm(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+
+ /*
+ * 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 VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModCallThread */
+static int kldrModLXCallThread(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle, unsigned fAttachingOrDetaching)
+{
+ /* no thread attach/detach callout. */
+ RT_NOREF(pMod);
+ RT_NOREF(pvMapping);
+ RT_NOREF(uHandle);
+ RT_NOREF(fAttachingOrDetaching);
+ return VINF_SUCCESS;
+}
+
+#endif
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnGetImageSize}
+ */
+static DECLCALLBACK(size_t) rtldrLX_GetImageSize(PRTLDRMODINTERNAL pMod)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ return pModLX->cbMapped;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnGetBits}
+ */
+static DECLCALLBACK(int) rtldrLX_GetBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR BaseAddress,
+ PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+
+ /*
+ * Load the image bits.
+ */
+ int rc = kldrModLXDoLoadBits(pModLX, pvBits);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Perform relocations.
+ */
+ rc = rtldrLX_RelocateBits(pMod, pvBits, BaseAddress, pModLX->aSegments[0].LinkAddress, pfnGetImport, pvUser);
+ }
+ return rc;
+}
+
+
+/* GCC goes boinkers if we put this inside the function. */
+union RELOC_VISIBILITY_STUPIDITY
+{
+ const uint8_t *pb;
+ const struct r32_rlc *prlc;
+};
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnRelocate}
+ */
+static DECLCALLBACK(int) rtldrLX_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
+ RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PKLDRMODLX pModLX = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ uint32_t iSeg;
+ int rc;
+
+ /*
+ * Do we need to to *anything*?
+ */
+ if ( NewBaseAddress == OldBaseAddress
+ && NewBaseAddress == pModLX->paObjs[0].o32_base
+ && !pModLX->Hdr.e32_impmodcnt)
+ return VINF_SUCCESS;
+
+ /*
+ * Load the fixup section.
+ */
+ if (!pModLX->pbFixupSection)
+ {
+ rc = kldrModLXDoLoadFixupSection(pModLX);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Iterate the segments.
+ */
+ for (iSeg = 0; iSeg < pModLX->Hdr.e32_objcnt; iSeg++)
+ {
+ const struct o32_obj * const pObj = &pModLX->paObjs[iSeg];
+ RTLDRADDR PageAddress = NewBaseAddress + pModLX->aSegments[iSeg].RVA;
+ uint32_t iPage;
+ uint8_t *pbPage = (uint8_t *)pvBits + (uintptr_t)pModLX->aSegments[iSeg].RVA;
+
+ /*
+ * Iterate the page map pages.
+ */
+ for (iPage = 0, rc = VINF_SUCCESS;
+ RT_SUCCESS(rc) && iPage < pObj->o32_mapsize;
+ iPage++, pbPage += OBJPAGELEN, PageAddress += OBJPAGELEN)
+ {
+ const uint8_t * const pbFixupRecEnd = pModLX->pbFixupRecs + pModLX->paoffPageFixups[iPage + pObj->o32_pagemap];
+ const uint8_t *pb = pModLX->pbFixupRecs + pModLX->paoffPageFixups[iPage + pObj->o32_pagemap - 1];
+ RTLDRADDR uValue = NIL_RTLDRADDR;
+ uint32_t fKind = 0;
+ int iSelector;
+
+ /* sanity */
+ if (pbFixupRecEnd < pb)
+ return VERR_LDR_BAD_FIXUP;
+ if (pbFixupRecEnd - 1 > pModLX->pbFixupSectionLast)
+ return VERR_LDR_BAD_FIXUP;
+ if (pb < pModLX->pbFixupSection)
+ return VERR_LDR_BAD_FIXUP;
+
+ /*
+ * Iterate the fixup record.
+ */
+ while (pb < pbFixupRecEnd)
+ {
+ union RELOC_VISIBILITY_STUPIDITY u;
+ char szImpModule[256];
+ 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:
+ {
+ uint16_t iTrgObject;
+ uint32_t offTrgObject;
+
+ /* the object */
+ if (u.prlc->nr_flags & NR16OBJMOD)
+ {
+ iTrgObject = *(const uint16_t *)pb;
+ pb += 2;
+ }
+ else
+ iTrgObject = *pb++;
+ iTrgObject--;
+ if (iTrgObject >= pModLX->Hdr.e32_objcnt)
+ return VERR_LDR_BAD_FIXUP;
+
+ /* the target */
+ if ((u.prlc->nr_stype & NRSRCMASK) != NRSSEG)
+ {
+ if (u.prlc->nr_flags & NR32BITOFF)
+ {
+ offTrgObject = *(const uint32_t *)pb;
+ pb += 4;
+ }
+ else
+ {
+ offTrgObject = *(const uint16_t *)pb;
+ pb += 2;
+ }
+
+ /* calculate the symbol info. */
+ uValue = offTrgObject + NewBaseAddress + pModLX->aSegments[iTrgObject].RVA;
+ }
+ else
+ uValue = NewBaseAddress + pModLX->aSegments[iTrgObject].RVA;
+ if ( (u.prlc->nr_stype & NRALIAS)
+ || (pModLX->aSegments[iTrgObject].fFlags & RTLDRSEG_FLAG_16BIT))
+ iSelector = pModLX->aSegments[iTrgObject].Sel16bit;
+ else
+ iSelector = pModLX->aSegments[iTrgObject].SelFlat;
+ fKind = 0;
+ break;
+ }
+
+ /*
+ * Import by symbol ordinal.
+ */
+ case NRRORD:
+ {
+ uint16_t iModule;
+ uint32_t iSymbol;
+
+ /* the module ordinal */
+ if (u.prlc->nr_flags & NR16OBJMOD)
+ {
+ iModule = *(const uint16_t *)pb;
+ pb += 2;
+ }
+ else
+ iModule = *pb++;
+ iModule--;
+ if (iModule >= pModLX->Hdr.e32_impmodcnt)
+ return VERR_LDR_BAD_FIXUP;
+ rc = kldrModLXGetImport(pModLX, NULL, iModule, szImpModule, sizeof(szImpModule), NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+#if 1
+ if (u.prlc->nr_flags & NRICHAIN)
+ return VERR_LDR_BAD_FIXUP;
+#endif
+
+ /* . */
+ if (u.prlc->nr_flags & NR32BITOFF)
+ {
+ iSymbol = *(const uint32_t *)pb;
+ pb += 4;
+ }
+ else if (!(u.prlc->nr_flags & NR8BITORD))
+ {
+ iSymbol = *(const uint16_t *)pb;
+ pb += 2;
+ }
+ else
+ iSymbol = *pb++;
+
+ /* resolve it. */
+ rc = pfnGetImport(pMod, szImpModule, NULL, iSymbol, &uValue, /*&fKind,*/ pvUser);
+ if (RT_FAILURE(rc))
+ return rc;
+ iSelector = -1;
+ break;
+ }
+
+ /*
+ * Import by symbol name.
+ */
+ case NRRNAM:
+ {
+ uint32_t iModule;
+ uint16_t offSymbol;
+ const uint8_t *pbSymbol;
+
+ /* the module ordinal */
+ if (u.prlc->nr_flags & NR16OBJMOD)
+ {
+ iModule = *(const uint16_t *)pb;
+ pb += 2;
+ }
+ else
+ iModule = *pb++;
+ iModule--;
+ if (iModule >= pModLX->Hdr.e32_impmodcnt)
+ return VERR_LDR_BAD_FIXUP;
+ rc = kldrModLXGetImport(pModLX, NULL, iModule, szImpModule, sizeof(szImpModule), NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+#if 1
+ if (u.prlc->nr_flags & NRICHAIN)
+ return VERR_LDR_BAD_FIXUP;
+#endif
+
+ /* . */
+ if (u.prlc->nr_flags & NR32BITOFF)
+ {
+ offSymbol = *(const uint32_t *)pb;
+ pb += 4;
+ }
+ else if (!(u.prlc->nr_flags & NR8BITORD))
+ {
+ offSymbol = *(const uint16_t *)pb;
+ pb += 2;
+ }
+ else
+ offSymbol = *pb++;
+ pbSymbol = pModLX->pbImportProcs + offSymbol;
+ if ( pbSymbol < pModLX->pbImportProcs
+ || pbSymbol > pModLX->pbFixupSectionLast)
+ return VERR_LDR_BAD_FIXUP;
+ char szSymbol[256];
+ memcpy(szSymbol, pbSymbol + 1, *pbSymbol);
+ szSymbol[*pbSymbol] = '\0';
+
+ /* resolve it. */
+ rc = pfnGetImport(pMod, szImpModule, szSymbol, UINT32_MAX, &uValue, /*&fKind,*/ pvUser);
+ if (RT_FAILURE(rc))
+ return rc;
+ iSelector = -1;
+ break;
+ }
+
+ case NRRENT:
+ KLDRMODLX_ASSERT(!"NRRENT");
+ RT_FALL_THRU();
+ default:
+ iSelector = -1;
+ break;
+ }
+
+ /* addend */
+ if (u.prlc->nr_flags & NRADD)
+ {
+ if (u.prlc->nr_flags & NR32BITADD)
+ {
+ uValue += *(const uint32_t *)pb;
+ pb += 4;
+ }
+ else
+ {
+ uValue += *(const uint16_t *)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)
+ *(uint32_t *)&pbPage[off] = (uint32_t)uValue;
+ else if ( (u.prlc->nr_stype & NRSRCMASK) == NRSOFF32
+ && off >= 0
+ && off <= (int)OBJPAGELEN - 4)
+ *(uint32_t *)&pbPage[off] = (uint32_t)(uValue - (PageAddress + off + 4));
+ else
+ {
+ /* generic */
+ rc = kldrModLXDoReloc(pbPage, off, PageAddress, u.prlc, iSelector, uValue, fKind);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ }
+ else if (!(u.prlc->nr_flags & NRICHAIN))
+ {
+ const int16_t *poffSrc = (const int16_t *)pb;
+ uint8_t 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)
+ *(uint32_t *)&pbPage[off] = (uint32_t)uValue;
+ else
+ {
+ rc = kldrModLXDoReloc(pbPage, off, PageAddress, u.prlc, iSelector, uValue, fKind);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ }
+ }
+ else if ((u.prlc->nr_stype & NRSRCMASK) == NRSOFF32)
+ {
+ while (c-- > 0)
+ {
+ int off = *poffSrc++;
+ if (off >= 0 && off <= (int)OBJPAGELEN - 4)
+ *(uint32_t *)&pbPage[off] = (uint32_t)(uValue - (PageAddress + off + 4));
+ else
+ {
+ rc = kldrModLXDoReloc(pbPage, off, PageAddress, u.prlc, iSelector, uValue, fKind);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ }
+ }
+ else
+ {
+ while (c-- > 0)
+ {
+ rc = kldrModLXDoReloc(pbPage, *poffSrc++, PageAddress, u.prlc, iSelector, uValue, fKind);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ }
+ pb = (const uint8_t *)poffSrc;
+ }
+ else
+ {
+ /* This is a pain because it will require virgin pages on a relocation. */
+ KLDRMODLX_ASSERT(!"NRICHAIN");
+ return VERR_LDRLX_NRICHAIN_NOT_SUPPORTED;
+ }
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * 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 IPRT status code.
+ * @param pbPage The page in which to apply the fixup.
+ * @param off Page relative offset of where to apply the offset.
+ * @param PageAddress The page address.
+ * @param prlc The relocation record.
+ * @param iSelector Selector value, -1 if flat.
+ * @param uValue The target value.
+ * @param fKind The target kind.
+ */
+static int kldrModLXDoReloc(uint8_t *pbPage, int off, RTLDRADDR PageAddress, const struct r32_rlc *prlc,
+ int iSelector, RTLDRADDR uValue, uint32_t fKind)
+{
+#pragma pack(1) /* just to be sure */
+ union
+ {
+ uint8_t ab[6];
+ uint32_t off32;
+ uint16_t off16;
+ uint8_t off8;
+ struct
+ {
+ uint16_t off;
+ uint16_t Sel;
+ } Far16;
+ struct
+ {
+ uint32_t off;
+ uint16_t Sel;
+ } Far32;
+ } uData;
+#pragma pack()
+ const uint8_t *pbSrc;
+ uint8_t *pbDst;
+ uint8_t cb;
+
+ RT_NOREF(fKind);
+
+ /*
+ * Compose the fixup data.
+ */
+ switch (prlc->nr_stype & NRSRCMASK)
+ {
+ case NRSBYT:
+ uData.off8 = (uint8_t)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 = (uint16_t)uValue;
+ uData.Far16.Sel = iSelector;
+ cb = 4;
+ break;
+ case NRSOFF:
+ uData.off16 = (uint16_t)uValue;
+ cb = 2;
+ break;
+ case NRPTR48:
+ if (iSelector == -1)
+ {
+ /* fixme */
+ }
+ uData.Far32.off = (uint32_t)uValue;
+ uData.Far32.Sel = iSelector;
+ cb = 6;
+ break;
+ case NROFF32:
+ uData.off32 = (uint32_t)uValue;
+ cb = 4;
+ break;
+ case NRSOFF32:
+ uData.off32 = (uint32_t)(uValue - (PageAddress + off + 4));
+ cb = 4;
+ break;
+ default:
+ return VERR_LDRLX_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 VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnEnumSegments}
+ */
+static DECLCALLBACK(int) rtldrLX_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
+{
+ PKLDRMODLX pThis = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ uint32_t const cSegments = pThis->cSegments;
+ for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
+ {
+ int rc = pfnCallback(pMod, &pThis->aSegments[iSeg], pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnLinkAddressToSegOffset}
+ */
+static DECLCALLBACK(int) rtldrLX_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
+ uint32_t *piSeg, PRTLDRADDR poffSeg)
+{
+ PKLDRMODLX pThis = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ uint32_t const cSegments = pThis->cSegments;
+ for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
+ {
+ RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].LinkAddress;
+ if ( offSeg < pThis->aSegments[iSeg].cbMapped
+ || offSeg < pThis->aSegments[iSeg].cb)
+ {
+ *piSeg = iSeg;
+ *poffSeg = offSeg;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_LDR_INVALID_LINK_ADDRESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnLinkAddressToRva}
+ */
+static DECLCALLBACK(int) rtldrLX_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
+{
+ PKLDRMODLX pThis = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ uint32_t const cSegments = pThis->cSegments;
+ for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
+ {
+ RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].LinkAddress;
+ if ( offSeg < pThis->aSegments[iSeg].cbMapped
+ || offSeg < pThis->aSegments[iSeg].cb)
+ {
+ *pRva = pThis->aSegments[iSeg].RVA + offSeg;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_LDR_INVALID_RVA;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnSegOffsetToRva}
+ */
+static DECLCALLBACK(int) rtldrLX_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg, PRTLDRADDR pRva)
+{
+ PKLDRMODLX pThis = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+
+ if (iSeg >= pThis->cSegments)
+ return VERR_LDR_INVALID_SEG_OFFSET;
+ PCRTLDRSEG pSegment = &pThis->aSegments[iSeg];
+
+ if ( offSeg > pSegment->cbMapped
+ && offSeg > pSegment->cb
+ && ( pSegment->cbFile < 0
+ || offSeg > (uint64_t)pSegment->cbFile))
+ return VERR_LDR_INVALID_SEG_OFFSET;
+
+ *pRva = pSegment->RVA + offSeg;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnRvaToSegOffset}
+ */
+static DECLCALLBACK(int) rtldrLX_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva, uint32_t *piSeg, PRTLDRADDR poffSeg)
+{
+ PKLDRMODLX pThis = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ uint32_t const cSegments = pThis->cSegments;
+ for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
+ {
+ RTLDRADDR offSeg = Rva - pThis->aSegments[iSeg].RVA;
+ if ( offSeg < pThis->aSegments[iSeg].cbMapped
+ || offSeg < pThis->aSegments[iSeg].cb)
+ {
+ *piSeg = iSeg;
+ *poffSeg = offSeg;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_LDR_INVALID_RVA;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnReadDbgInfo}
+ */
+static DECLCALLBACK(int) rtldrLX_ReadDbgInfo(PRTLDRMODINTERNAL pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void *pvBuf)
+{
+ PKLDRMODLX pThis = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ RT_NOREF(iDbgInfo);
+ return pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvBuf, cb, off);
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnQueryProp}
+ */
+static DECLCALLBACK(int) rtldrLX_QueryProp(PRTLDRMODINTERNAL pMod, RTLDRPROP enmProp, void const *pvBits,
+ void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ PKLDRMODLX pThis = RT_FROM_MEMBER(pMod, KLDRMODLX, Core);
+ int rc;
+ switch (enmProp)
+ {
+ case RTLDRPROP_IMPORT_COUNT:
+ Assert(cbBuf == sizeof(uint32_t));
+ Assert(*pcbRet == cbBuf);
+ *(uint32_t *)pvBuf = pThis->Hdr.e32_impmodcnt;
+ rc = VINF_SUCCESS;
+ break;
+
+ case RTLDRPROP_IMPORT_MODULE:
+ rc = kldrModLXGetImport(pThis, pvBits, *(uint32_t const *)pvBuf, (char *)pvBuf, cbBuf, pcbRet);
+ break;
+
+ case RTLDRPROP_INTERNAL_NAME:
+ *pcbRet = pThis->cchName + 1;
+ if (cbBuf >= pThis->cchName + 1)
+ {
+ memcpy(pvBuf, pThis->pszName, pThis->cchName + 1);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ break;
+
+
+ default:
+ rc = VERR_NOT_FOUND;
+ break;
+ }
+ RT_NOREF_PV(pvBits);
+ return rc;
+}
+
+
+/**
+ * Operations for a Mach-O module interpreter.
+ */
+static const RTLDROPS s_rtldrLXOps=
+{
+ "LX",
+ rtldrLX_Close,
+ NULL,
+ NULL /*pfnDone*/,
+ rtldrLX_EnumSymbols,
+ /* ext */
+ rtldrLX_GetImageSize,
+ rtldrLX_GetBits,
+ rtldrLX_RelocateBits,
+ rtldrLX_GetSymbolEx,
+ NULL /*pfnQueryForwarderInfo*/,
+ rtldrLX_EnumDbgInfo,
+ rtldrLX_EnumSegments,
+ rtldrLX_LinkAddressToSegOffset,
+ rtldrLX_LinkAddressToRva,
+ rtldrLX_SegOffsetToRva,
+ rtldrLX_RvaToSegOffset,
+ rtldrLX_ReadDbgInfo,
+ rtldrLX_QueryProp,
+ NULL /*pfnVerifySignature*/,
+ NULL /*pfnHashImage*/,
+ NULL /*pfnUnwindFrame*/,
+ 42
+};
+
+
+/**
+ * Handles opening LX images.
+ */
+DECLHIDDEN(int) rtldrLXOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offLxHdr,
+ PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
+{
+
+ /*
+ * Create the instance data and do a minimal header validation.
+ */
+ PKLDRMODLX pThis = NULL;
+ int rc = kldrModLXDoCreate(pReader, offLxHdr, fFlags, &pThis, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Match up against the requested CPU architecture.
+ */
+ if ( enmArch == RTLDRARCH_WHATEVER
+ || pThis->Core.enmArch == enmArch)
+ {
+ pThis->Core.pOps = &s_rtldrLXOps;
+ pThis->Core.u32Magic = RTLDRMOD_MAGIC;
+ *phLdrMod = &pThis->Core;
+ return VINF_SUCCESS;
+ }
+ rc = VERR_LDR_ARCH_MISMATCH;
+ }
+ if (pThis)
+ RTMemFree(pThis);
+ return rc;
+
+}
+
diff --git a/src/VBox/Runtime/common/ldr/ldrMachO.cpp b/src/VBox/Runtime/common/ldr/ldrMachO.cpp
new file mode 100644
index 00000000..7d5c655a
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/ldrMachO.cpp
@@ -0,0 +1,5734 @@
+/* $Id: ldrMachO.cpp $ */
+/** @file
+ * kLdr - The Module Interpreter for the MACH-O format.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ * --------------------------------------------------------------------
+ *
+ * This code is based on: kLdr/kLdrModMachO.c from kStuff r113.
+ *
+ * Copyright (c) 2006-2013 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/base64.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/sha.h>
+#include <iprt/crypto/digest.h>
+
+#include <iprt/formats/mach-o.h>
+#include <iprt/crypto/applecodesign.h>
+#include "internal/ldr.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def RTLDRMODMACHO_STRICT
+ * Define RTLDRMODMACHO_STRICT to enabled strict checks in RTLDRMODMACHO. */
+#define RTLDRMODMACHO_STRICT 1
+#define RTLDRMODMACHO_STRICT2
+
+/** @def RTLDRMODMACHO_ASSERT
+ * Assert that an expression is true when KLDR_STRICT is defined.
+ */
+#ifdef RTLDRMODMACHO_STRICT
+# define RTLDRMODMACHO_ASSERT(expr) Assert(expr)
+#else
+# define RTLDRMODMACHO_ASSERT(expr) do {} while (0)
+#endif
+
+/** @def RTLDRMODMACHO_CHECK_RETURN
+ * Checks that an expression is true and return if it isn't.
+ * This is a debug aid.
+ */
+#ifdef RTLDRMODMACHO_STRICT2
+# define RTLDRMODMACHO_CHECK_RETURN(expr, rc) AssertReturn(expr, rc)
+#else
+# define RTLDRMODMACHO_CHECK_RETURN(expr, rc) do { if (RT_LIKELY(expr)) {/* likely */ } else return (rc); } while (0)
+#endif
+
+/** @def RTLDRMODMACHO_CHECK_MSG_RETURN
+ * Checks that an expression is true and return if it isn't.
+ * This is a debug aid.
+ */
+#ifdef RTLDRMODMACHO_STRICT2
+# define RTLDRMODMACHO_CHECK_MSG_RETURN(expr, msgargs, rc) AssertMsgReturn(expr, msgargs, rc)
+#else
+# define RTLDRMODMACHO_CHECK_MSG_RETURN(expr, msgargs, rc) do { if (RT_LIKELY(expr)) {/* likely */ } else return (rc); } while (0)
+#endif
+
+/** @def RTLDRMODMACHO_CHECK_RETURN
+ * Checks that an expression is true and return if it isn't.
+ * This is a debug aid.
+ */
+#ifdef RTLDRMODMACHO_STRICT2
+# define RTLDRMODMACHO_FAILED_RETURN(rc) AssertFailedReturn(rc)
+#else
+# define RTLDRMODMACHO_FAILED_RETURN(rc) return (rc)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Mach-O section details.
+ */
+typedef struct RTLDRMODMACHOSECT
+{
+ /** The size of the section (in bytes). */
+ RTLDRADDR cb;
+ /** The link address of this section. */
+ RTLDRADDR LinkAddress;
+ /** The RVA of this section. */
+ RTLDRADDR RVA;
+ /** The file offset of this section.
+ * This is -1 if the section doesn't have a file backing. */
+ RTFOFF offFile;
+ /** The number of fixups. */
+ uint32_t cFixups;
+ /** The array of fixups. (lazy loaded) */
+ macho_relocation_union_t *paFixups;
+ /** Array of virgin data running parallel to paFixups */
+ PRTUINT64U pauFixupVirginData;
+ /** The file offset of the fixups for this section.
+ * This is -1 if the section doesn't have any fixups. */
+ RTFOFF offFixups;
+ /** Mach-O section flags. */
+ uint32_t fFlags;
+ /** kLdr segment index. */
+ uint32_t iSegment;
+ /** Pointer to the Mach-O section structure. */
+ void *pvMachoSection;
+} RTLDRMODMACHOSECT, *PRTLDRMODMACHOSECT;
+
+/**
+ * Extra per-segment info.
+ *
+ * This is corresponds to a kLdr segment, not a Mach-O segment!
+ */
+typedef struct RTLDRMODMACHOSEG
+{
+ /** Common segment info. */
+ RTLDRSEG SegInfo;
+
+ /** The orignal segment number (in case we had to resort it). */
+ uint32_t iOrgSegNo;
+ /** The number of sections in the segment. */
+ uint32_t cSections;
+ /** Pointer to the sections belonging to this segment.
+ * The array resides in the big memory chunk allocated for
+ * the module handle, so it doesn't need freeing. */
+ PRTLDRMODMACHOSECT paSections;
+
+} RTLDRMODMACHOSEG, *PRTLDRMODMACHOSEG;
+
+/**
+ * Instance data for the Mach-O MH_OBJECT module interpreter.
+ * @todo interpret the other MH_* formats.
+ */
+typedef struct RTLDRMODMACHO
+{
+ /** Core module structure. */
+ RTLDRMODINTERNAL Core;
+
+ /** The minium cpu this module was built for.
+ * This might not be accurate, so use kLdrModCanExecuteOn() to check. */
+ RTLDRCPU enmCpu;
+ /** The number of segments in the module. */
+ uint32_t cSegments;
+
+ /** Pointer to the RDR file mapping of the raw file bits. NULL if not mapped. */
+ const void *pvBits;
+ /** Pointer to the user mapping. */
+ void *pvMapping;
+ /** The module open flags. */
+ uint32_t fOpenFlags;
+
+ /** The offset of the image. (FAT fun.) */
+ RTFOFF offImage;
+ /** The link address. */
+ RTLDRADDR LinkAddress;
+ /** The size of the mapped image. */
+ RTLDRADDR cbImage;
+ /** Whether we're capable of loading the image. */
+ bool fCanLoad;
+ /** Whether we're creating a global offset table segment.
+ * This dependes on the cputype and image type. */
+ bool fMakeGot;
+ /** The size of a indirect GOT jump stub entry.
+ * This is 0 if not needed. */
+ uint32_t cbJmpStub;
+ /** Effective file type. If the original was a MH_OBJECT file, the
+ * corresponding MH_DSYM needs the segment translation of a MH_OBJECT too.
+ * The MH_DSYM normally has a separate __DWARF segment, but this is
+ * automatically skipped during the transation. */
+ uint32_t uEffFileType;
+ /** Pointer to the load commands. (endian converted) */
+ uint8_t *pbLoadCommands;
+ /** The Mach-O header. (endian converted)
+ * @remark The reserved field is only valid for real 64-bit headers. */
+ mach_header_64_t Hdr;
+
+ /** The offset of the symbol table. */
+ RTFOFF offSymbols;
+ /** The number of symbols. */
+ uint32_t cSymbols;
+ /** The pointer to the loaded symbol table. */
+ void *pvaSymbols;
+ /** The offset of the string table. */
+ RTFOFF offStrings;
+ /** The size of the of the string table. */
+ uint32_t cchStrings;
+ /** Pointer to the loaded string table. */
+ char *pchStrings;
+ /** Pointer to the dynamic symbol table command if present. */
+ dysymtab_command_t *pDySymTab;
+ /** The indirect symbol table (size given by pDySymTab->nindirectsymb).
+ * @remarks Host endian. */
+ uint32_t *paidxIndirectSymbols;
+ /** Dynamic relocations, first pDySymTab->nextrel external relocs followed by
+ * pDySymTab->nlocrel local ones. */
+ macho_relocation_union_t *paRelocations;
+ /** Array of virgin data running parallel to paRelocations */
+ PRTUINT64U pauRelocationsVirginData;
+
+ /** The image UUID, all zeros if not found. */
+ uint8_t abImageUuid[16];
+
+ /** The code signature offset. */
+ uint32_t offCodeSignature;
+ /** The code signature size (0 if not signed). */
+ uint32_t cbCodeSignature;
+ /** Pointer to the code signature blob if loaded. */
+ union
+ {
+ uint8_t *pb;
+ PCRTCRAPLCSSUPERBLOB pSuper;
+ } PtrCodeSignature;
+ /** File offset of segment 0 (relative to Mach-O header). */
+ uint64_t offSeg0ForCodeSign;
+ /** File size of segment 0. */
+ uint64_t cbSeg0ForCodeSign;
+ /** Segment 0 flags. */
+ uint64_t fSeg0ForCodeSign;
+
+ /** The RVA of the Global Offset Table. */
+ RTLDRADDR GotRVA;
+ /** The RVA of the indirect GOT jump stubs. */
+ RTLDRADDR JmpStubsRVA;
+
+ /** The number of sections. */
+ uint32_t cSections;
+ /** Pointer to the section array running in parallel to the Mach-O one. */
+ PRTLDRMODMACHOSECT paSections;
+
+ /** Array of segments parallel to the one in KLDRMOD. */
+ RTLDRMODMACHOSEG aSegments[1];
+} RTLDRMODMACHO;
+/** Pointer instance data for an Mach-O module. */
+typedef RTLDRMODMACHO *PRTLDRMODMACHO;
+
+/**
+ * Code directory data.
+ */
+typedef struct RTLDRMACHCODEDIR
+{
+ PCRTCRAPLCSCODEDIRECTORY pCodeDir;
+ /** The slot type. */
+ uint32_t uSlot;
+ /** The naturalized size. */
+ uint32_t cb;
+ /** The digest type. */
+ RTDIGESTTYPE enmDigest;
+} RTLDRMACHCODEDIR;
+/** Pointer to code directory data. */
+typedef RTLDRMACHCODEDIR *PRTLDRMACHCODEDIR;
+
+/**
+ * Decoded apple Mach-O signature data.
+ * @note The raw signature data lives in RTLDRMODMACHO::PtrCodeSignature.
+ */
+typedef struct RTLDRMACHOSIGNATURE
+{
+ /** Number of code directory slots. */
+ uint32_t cCodeDirs;
+ /** Code directories. */
+ RTLDRMACHCODEDIR aCodeDirs[6];
+
+ /** The index of the PKCS#7 slot. */
+ uint32_t idxPkcs7;
+ /** The size of the PKCS#7 data. */
+ uint32_t cbPkcs7;
+ /** Pointer to the PKCS#7 data. */
+ uint8_t const *pbPkcs7;
+ /** Parsed PKCS#7 data. */
+ RTCRPKCS7CONTENTINFO ContentInfo;
+ /** Pointer to the decoded SignedData inside the ContentInfo member. */
+ PRTCRPKCS7SIGNEDDATA pSignedData;
+} RTLDRMACHOSIGNATURE;
+/** Pointer to decoded apple code signing data. */
+typedef RTLDRMACHOSIGNATURE *PRTLDRMACHOSIGNATURE;
+
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#if 0
+static int32_t kldrModMachONumberOfImports(PRTLDRMODINTERNAL pMod, const void *pvBits);
+#endif
+static DECLCALLBACK(int) rtldrMachO_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
+ RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser);
+
+
+static int kldrModMachOPreParseLoadCommands(uint8_t *pbLoadCommands, const mach_header_32_t *pHdr, PRTLDRREADER pRdr, RTFOFF offImage,
+ uint32_t fOpenFlags, uint32_t *pcSegments, uint32_t *pcSections, uint32_t *pcbStringPool,
+ bool *pfCanLoad, PRTLDRADDR pLinkAddress, uint8_t *puEffFileType, PRTERRINFO pErrInfo);
+static int kldrModMachOParseLoadCommands(PRTLDRMODMACHO pThis, char *pbStringPool, uint32_t cbStringPool);
+
+static int kldrModMachOLoadObjSymTab(PRTLDRMODMACHO pThis);
+static int kldrModMachOLoadFixups(PRTLDRMODMACHO pThis, RTFOFF offFixups, uint32_t cFixups, macho_relocation_union_t **ppaFixups);
+
+static int kldrModMachODoQuerySymbol32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms, const char *pchStrings,
+ uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol, const char *pchSymbol,
+ uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind);
+static int kldrModMachODoQuerySymbol64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms, const char *pchStrings,
+ uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol, const char *pchSymbol,
+ uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind);
+static int kldrModMachODoEnumSymbols32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
+ const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
+ uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser);
+static int kldrModMachODoEnumSymbols64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
+ const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
+ uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser);
+static int kldrModMachOObjDoImports(PRTLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser);
+static int kldrModMachOObjDoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress);
+static int kldrModMachOApplyFixupsGeneric32Bit(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, size_t cbSectBits, RTLDRADDR uBitsRva,
+ RTLDRADDR uBitsLinkAddr, const macho_relocation_union_t *paFixups,
+ const uint32_t cFixups, PCRTUINT64U const pauVirginData,
+ macho_nlist_32_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress);
+static int kldrModMachOApplyFixupsAMD64(PRTLDRMODMACHO pThis, uint8_t *pbSectBits, size_t cbSectBits, RTLDRADDR uBitsRva,
+ const macho_relocation_union_t *paFixups,
+ const uint32_t cFixups, PCRTUINT64U const pauVirginData,
+ macho_nlist_64_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress);
+
+static int kldrModMachOMakeGOT(PRTLDRMODMACHO pThis, void *pvBits, RTLDRADDR NewBaseAddress);
+
+/*static int kldrModMachODoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress, RTLDRADDR OldBaseAddress);
+static int kldrModMachODoImports(PRTLDRMODMACHO pThis, void *pvMapping, PFNRTLDRIMPORT pfnGetImport, void *pvUser);*/
+
+
+
+/**
+ * Separate function for reading creating the Mach-O module instance to
+ * simplify cleanup on failure.
+ */
+static int kldrModMachODoCreate(PRTLDRREADER pRdr, RTFOFF offImage, uint32_t fOpenFlags,
+ PRTLDRMODMACHO *ppModMachO, PRTERRINFO pErrInfo)
+{
+ *ppModMachO = NULL;
+
+ /*
+ * Read the Mach-O header.
+ */
+ union
+ {
+ mach_header_32_t Hdr32;
+ mach_header_64_t Hdr64;
+ } s;
+ Assert(&s.Hdr32.magic == &s.Hdr64.magic);
+ Assert(&s.Hdr32.flags == &s.Hdr64.flags);
+ int rc = pRdr->pfnRead(pRdr, &s, sizeof(s), offImage);
+ if (rc)
+ return RTErrInfoSetF(pErrInfo, rc, "Error reading Mach-O header at %RTfoff: %Rrc", offImage, rc);
+ if ( s.Hdr32.magic != IMAGE_MACHO32_SIGNATURE
+ && s.Hdr32.magic != IMAGE_MACHO64_SIGNATURE)
+ {
+ if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE
+ || s.Hdr32.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ return VERR_LDRMACHO_OTHER_ENDIAN_NOT_SUPPORTED;
+ return VERR_INVALID_EXE_SIGNATURE;
+ }
+
+ /* sanity checks. */
+ if ( s.Hdr32.sizeofcmds > pRdr->pfnSize(pRdr) - sizeof(mach_header_32_t)
+ || s.Hdr32.sizeofcmds < sizeof(load_command_t) * s.Hdr32.ncmds
+ || (s.Hdr32.flags & ~MH_VALID_FLAGS))
+ return VERR_LDRMACHO_BAD_HEADER;
+
+ bool fMakeGot;
+ uint8_t cbJmpStub;
+ switch (s.Hdr32.cputype)
+ {
+ case CPU_TYPE_X86:
+ fMakeGot = false;
+ cbJmpStub = 0;
+ break;
+ case CPU_TYPE_X86_64:
+ fMakeGot = s.Hdr32.filetype == MH_OBJECT || s.Hdr32.filetype == MH_KEXT_BUNDLE;
+ cbJmpStub = fMakeGot ? 8 : 0;
+ break;
+ case CPU_TYPE_ARM64:
+ fMakeGot = s.Hdr32.filetype == MH_OBJECT || s.Hdr32.filetype == MH_KEXT_BUNDLE;
+ cbJmpStub = fMakeGot ? 8 : 0; /** @todo Not sure if this is right. Need to expore ARM64/MachO a bit more... */
+ break;
+ default:
+ return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
+ }
+
+ if ( s.Hdr32.filetype != MH_OBJECT
+ && s.Hdr32.filetype != MH_EXECUTE
+ && s.Hdr32.filetype != MH_DYLIB
+ && s.Hdr32.filetype != MH_BUNDLE
+ && s.Hdr32.filetype != MH_DSYM
+ && s.Hdr32.filetype != MH_KEXT_BUNDLE)
+ return VERR_LDRMACHO_UNSUPPORTED_FILE_TYPE;
+
+ /*
+ * Read and pre-parse the load commands to figure out how many segments we'll be needing.
+ */
+ uint8_t *pbLoadCommands = (uint8_t *)RTMemAlloc(s.Hdr32.sizeofcmds);
+ if (!pbLoadCommands)
+ return VERR_NO_MEMORY;
+ rc = pRdr->pfnRead(pRdr, pbLoadCommands, s.Hdr32.sizeofcmds,
+ s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE
+ || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE
+ ? sizeof(mach_header_32_t) + offImage
+ : sizeof(mach_header_64_t) + offImage);
+
+ uint32_t cSegments = 0;
+ uint32_t cSections = 0;
+ uint32_t cbStringPool = 0;
+ bool fCanLoad = true;
+ RTLDRADDR LinkAddress = NIL_RTLDRADDR;
+ uint8_t uEffFileType = 0;
+ if (RT_SUCCESS(rc))
+ rc = kldrModMachOPreParseLoadCommands(pbLoadCommands, &s.Hdr32, pRdr, offImage, fOpenFlags,
+ &cSegments, &cSections, &cbStringPool, &fCanLoad, &LinkAddress, &uEffFileType,
+ pErrInfo);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pbLoadCommands);
+ return rc;
+ }
+ cSegments += fMakeGot;
+
+
+ /*
+ * Calc the instance size, allocate and initialize it.
+ */
+ size_t const cbModAndSegs = RT_ALIGN_Z(RT_UOFFSETOF_DYN(RTLDRMODMACHO, aSegments[cSegments])
+ + sizeof(RTLDRMODMACHOSECT) * cSections, 16);
+ PRTLDRMODMACHO pThis = (PRTLDRMODMACHO)RTMemAlloc(cbModAndSegs + cbStringPool);
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ *ppModMachO = pThis;
+ pThis->pbLoadCommands = pbLoadCommands;
+ pThis->offImage = offImage;
+
+ /* Core & CPU.*/
+ pThis->Core.u32Magic = 0; /* set by caller */
+ pThis->Core.eState = LDR_STATE_OPENED;
+ pThis->Core.pOps = NULL; /* set by caller. */
+ pThis->Core.pReader = pRdr;
+ switch (s.Hdr32.cputype)
+ {
+ case CPU_TYPE_X86:
+ pThis->Core.enmArch = RTLDRARCH_X86_32;
+ pThis->Core.enmEndian = RTLDRENDIAN_LITTLE;
+ switch (s.Hdr32.cpusubtype)
+ {
+ case CPU_SUBTYPE_I386_ALL: /* == CPU_SUBTYPE_386 */
+ pThis->enmCpu = RTLDRCPU_X86_32_BLEND;
+ break;
+ case CPU_SUBTYPE_486:
+ pThis->enmCpu = RTLDRCPU_I486;
+ break;
+ case CPU_SUBTYPE_486SX:
+ pThis->enmCpu = RTLDRCPU_I486SX;
+ break;
+ case CPU_SUBTYPE_PENT: /* == CPU_SUBTYPE_586 */
+ pThis->enmCpu = RTLDRCPU_I586;
+ break;
+ case CPU_SUBTYPE_PENTPRO:
+ case CPU_SUBTYPE_PENTII_M3:
+ case CPU_SUBTYPE_PENTII_M5:
+ case CPU_SUBTYPE_CELERON:
+ case CPU_SUBTYPE_CELERON_MOBILE:
+ case CPU_SUBTYPE_PENTIUM_3:
+ case CPU_SUBTYPE_PENTIUM_3_M:
+ case CPU_SUBTYPE_PENTIUM_3_XEON:
+ pThis->enmCpu = RTLDRCPU_I686;
+ break;
+ case CPU_SUBTYPE_PENTIUM_M:
+ case CPU_SUBTYPE_PENTIUM_4:
+ case CPU_SUBTYPE_PENTIUM_4_M:
+ case CPU_SUBTYPE_XEON:
+ case CPU_SUBTYPE_XEON_MP:
+ pThis->enmCpu = RTLDRCPU_P4;
+ break;
+
+ default:
+ /* Hack for kextutil output. */
+ if ( s.Hdr32.cpusubtype == 0
+ && s.Hdr32.filetype == MH_OBJECT)
+ break;
+ return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
+ }
+ break;
+
+ case CPU_TYPE_X86_64:
+ pThis->Core.enmArch = RTLDRARCH_AMD64;
+ pThis->Core.enmEndian = RTLDRENDIAN_LITTLE;
+ switch (s.Hdr32.cpusubtype & ~CPU_SUBTYPE_MASK)
+ {
+ case CPU_SUBTYPE_X86_64_ALL: pThis->enmCpu = RTLDRCPU_AMD64_BLEND; break;
+ default:
+ return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
+ }
+ break;
+
+ case CPU_TYPE_ARM64:
+ pThis->Core.enmArch = RTLDRARCH_ARM64;
+ pThis->Core.enmEndian = RTLDRENDIAN_LITTLE;
+ switch (s.Hdr32.cpusubtype & ~CPU_SUBTYPE_MASK)
+ {
+ case CPU_SUBTYPE_ARM64_ALL: pThis->enmCpu = RTLDRCPU_ARM64_BLEND; break;
+ case CPU_SUBTYPE_ARM64_V8: pThis->enmCpu = RTLDRCPU_ARM64_V8; break;
+ case CPU_SUBTYPE_ARM64E: pThis->enmCpu = RTLDRCPU_ARM64E; break;
+ default:
+ return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
+ }
+ break;
+
+ default:
+ return VERR_LDRMACHO_UNSUPPORTED_MACHINE;
+ }
+
+ pThis->Core.enmFormat = RTLDRFMT_MACHO;
+ switch (s.Hdr32.filetype)
+ {
+ case MH_OBJECT: pThis->Core.enmType = RTLDRTYPE_OBJECT; break;
+ case MH_EXECUTE: pThis->Core.enmType = RTLDRTYPE_EXECUTABLE_FIXED; break;
+ case MH_DYLIB: pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
+ case MH_BUNDLE: pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
+ case MH_KEXT_BUNDLE:pThis->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; break;
+ case MH_DSYM: pThis->Core.enmType = RTLDRTYPE_DEBUG_INFO; break;
+ default:
+ return VERR_LDRMACHO_UNSUPPORTED_FILE_TYPE;
+ }
+
+ /* RTLDRMODMACHO */
+ pThis->cSegments = cSegments;
+ pThis->pvBits = NULL;
+ pThis->pvMapping = NULL;
+ pThis->fOpenFlags = fOpenFlags;
+ pThis->Hdr = s.Hdr64;
+ if ( s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE
+ || s.Hdr32.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ pThis->Hdr.reserved = 0;
+ pThis->LinkAddress = LinkAddress;
+ pThis->cbImage = 0;
+ pThis->fCanLoad = fCanLoad;
+ pThis->fMakeGot = fMakeGot;
+ pThis->cbJmpStub = cbJmpStub;
+ pThis->uEffFileType = uEffFileType;
+ pThis->offSymbols = 0;
+ pThis->cSymbols = 0;
+ pThis->pvaSymbols = NULL;
+ pThis->pDySymTab = NULL;
+ pThis->paRelocations = NULL;
+ pThis->pauRelocationsVirginData = NULL;
+ pThis->paidxIndirectSymbols = NULL;
+ pThis->offStrings = 0;
+ pThis->cchStrings = 0;
+ pThis->pchStrings = NULL;
+ memset(pThis->abImageUuid, 0, sizeof(pThis->abImageUuid));
+ pThis->offCodeSignature = 0;
+ pThis->cbCodeSignature = 0;
+ pThis->PtrCodeSignature.pb = NULL;
+ pThis->GotRVA = NIL_RTLDRADDR;
+ pThis->JmpStubsRVA = NIL_RTLDRADDR;
+ pThis->cSections = cSections;
+ pThis->paSections = (PRTLDRMODMACHOSECT)&pThis->aSegments[pThis->cSegments];
+
+ /*
+ * Setup the KLDRMOD segment array.
+ */
+ rc = kldrModMachOParseLoadCommands(pThis, (char *)pThis + cbModAndSegs, cbStringPool);
+
+ /*
+ * We're done.
+ */
+ return rc;
+}
+
+
+/**
+ * Converts, validates and preparses the load commands before we carve
+ * out the module instance.
+ *
+ * The conversion that's preformed is format endian to host endian. The
+ * preparsing has to do with segment counting, section counting and string pool
+ * sizing.
+ *
+ * Segment are created in two different ways, depending on the file type.
+ *
+ * For object files there is only one segment command without a given segment
+ * name. The sections inside that segment have different segment names and are
+ * not sorted by their segname attribute. We create one segment for each
+ * section, with the segment name being 'segname.sectname' in order to hopefully
+ * keep the names unique. Debug sections does not get segments.
+ *
+ * For non-object files, one kLdr segment is created for each Mach-O segment.
+ * Debug segments is not exposed by kLdr via the kLdr segment table, but via the
+ * debug enumeration callback API.
+ *
+ * @returns IPRT status code.
+ * @param pbLoadCommands The load commands to parse.
+ * @param pHdr The header.
+ * @param pRdr The file reader.
+ * @param offImage The image header (FAT fun).
+ * @param fOpenFlags RTLDR_O_XXX.
+ * @param pcSegments Where to store the segment count.
+ * @param pcSections Where to store the section count.
+ * @param pcbStringPool Where to store the string pool size.
+ * @param pfCanLoad Where to store the can-load-image indicator.
+ * @param pLinkAddress Where to store the image link address (i.e. the
+ * lowest segment address).
+ * @param puEffFileType Where to store the effective file type.
+ * @param pErrInfo Where to return additional error info. Optional.
+ */
+static int kldrModMachOPreParseLoadCommands(uint8_t *pbLoadCommands, const mach_header_32_t *pHdr, PRTLDRREADER pRdr,
+ RTFOFF offImage, uint32_t fOpenFlags, uint32_t *pcSegments, uint32_t *pcSections,
+ uint32_t *pcbStringPool, bool *pfCanLoad, PRTLDRADDR pLinkAddress,
+ uint8_t *puEffFileType, PRTERRINFO pErrInfo)
+{
+ union
+ {
+ uint8_t *pb;
+ load_command_t *pLoadCmd;
+ segment_command_32_t *pSeg32;
+ segment_command_64_t *pSeg64;
+ thread_command_t *pThread;
+ symtab_command_t *pSymTab;
+ dysymtab_command_t *pDySymTab;
+ uuid_command_t *pUuid;
+ } u;
+ const uint64_t cbFile = pRdr->pfnSize(pRdr) - offImage;
+ int const fConvertEndian = pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE;
+ uint32_t cSegments = 0;
+ uint32_t cSections = 0;
+ size_t cbStringPool = 0;
+ uint32_t cLeft = pHdr->ncmds;
+ uint32_t cbLeft = pHdr->sizeofcmds;
+ uint8_t *pb = pbLoadCommands;
+ int cSegmentCommands = 0;
+ int cSymbolTabs = 0;
+ uint32_t cSymbols = 0; /* Copy of u.pSymTab->nsyms. */
+ uint32_t cDySymbolTabs = 0;
+ bool fDySymbolTabWithRelocs = false;
+ uint32_t cSectionsWithRelocs = 0;
+ uint8_t uEffFileType = *puEffFileType = pHdr->filetype;
+
+ *pcSegments = 0;
+ *pcSections = 0;
+ *pcbStringPool = 0;
+ *pfCanLoad = true;
+ *pLinkAddress = ~(RTLDRADDR)0;
+
+ while (cLeft-- > 0)
+ {
+ u.pb = pb;
+
+ /*
+ * Convert and validate command header.
+ */
+ RTLDRMODMACHO_CHECK_RETURN(cbLeft >= sizeof(load_command_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ if (fConvertEndian)
+ {
+ u.pLoadCmd->cmd = RT_BSWAP_U32(u.pLoadCmd->cmd);
+ u.pLoadCmd->cmdsize = RT_BSWAP_U32(u.pLoadCmd->cmdsize);
+ }
+ RTLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize <= cbLeft, VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ cbLeft -= u.pLoadCmd->cmdsize;
+ pb += u.pLoadCmd->cmdsize;
+
+ /*
+ * Segment macros for avoiding code duplication.
+ */
+ /* Validation code shared with the 64-bit variant. */
+ #define VALIDATE_AND_ADD_SEGMENT(a_cBits) \
+ do { \
+ bool fSkipSeg = !strcmp(pSrcSeg->segname, "__DWARF") /* Note: Not for non-object files. */ \
+ || ( !strcmp(pSrcSeg->segname, "__CTF") /* Their CTF tool did/does weird things, */ \
+ && pSrcSeg->vmsize == 0) /* overlapping vmaddr and zero vmsize. */ \
+ || (cSectionsLeft > 0 && (pFirstSect->flags & S_ATTR_DEBUG)); \
+ \
+ /* MH_DSYM files for MH_OBJECT files must have MH_OBJECT segment translation. */ \
+ if ( uEffFileType == MH_DSYM \
+ && cSegmentCommands == 0 \
+ && pSrcSeg->segname[0] == '\0') \
+ *puEffFileType = uEffFileType = MH_OBJECT; \
+ \
+ RTLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize == 0 \
+ || ( pSrcSeg->fileoff <= cbFile \
+ && (uint64_t)pSrcSeg->fileoff + pSrcSeg->filesize <= cbFile), \
+ VERR_LDRMACHO_BAD_LOAD_COMMAND); \
+ RTLDRMODMACHO_CHECK_RETURN( pSrcSeg->filesize <= pSrcSeg->vmsize \
+ || (fSkipSeg && !strcmp(pSrcSeg->segname, "__CTF") /* see above */), \
+ VERR_LDRMACHO_BAD_LOAD_COMMAND); \
+ RTLDRMODMACHO_CHECK_RETURN(!(~pSrcSeg->maxprot & pSrcSeg->initprot), \
+ VERR_LDRMACHO_BAD_LOAD_COMMAND); \
+ RTLDRMODMACHO_CHECK_MSG_RETURN(!(pSrcSeg->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1 | SG_READ_ONLY)), \
+ ("flags=%#x %s\n", pSrcSeg->flags, pSrcSeg->segname), \
+ VERR_LDRMACHO_BAD_LOAD_COMMAND); \
+ RTLDRMODMACHO_CHECK_RETURN( pSrcSeg->nsects * sizeof(section_##a_cBits##_t) \
+ <= u.pLoadCmd->cmdsize - sizeof(segment_command_##a_cBits##_t), \
+ VERR_LDRMACHO_BAD_LOAD_COMMAND); \
+ RTLDRMODMACHO_CHECK_RETURN( uEffFileType != MH_OBJECT \
+ || cSegmentCommands == 0 \
+ || ( cSegmentCommands == 1 \
+ && uEffFileType == MH_OBJECT \
+ && pHdr->filetype == MH_DSYM \
+ && fSkipSeg), \
+ VERR_LDRMACHO_BAD_OBJECT_FILE); \
+ cSegmentCommands++; \
+ \
+ /* Add the segment, if not object file. */ \
+ if (!fSkipSeg && uEffFileType != MH_OBJECT) \
+ { \
+ cbStringPool += RTStrNLen(&pSrcSeg->segname[0], sizeof(pSrcSeg->segname)) + 1; \
+ cSegments++; \
+ if (cSegments == 1) /* The link address is set by the first segment. */ \
+ *pLinkAddress = pSrcSeg->vmaddr; \
+ } \
+ } while (0)
+
+
+ /* Validation code shared with the 64-bit variant. */
+ #define VALIDATE_AND_ADD_SECTION(a_cBits) \
+ do { \
+ int fFileBits; \
+ \
+ /* validate */ \
+ if (uEffFileType != MH_OBJECT) \
+ RTLDRMODMACHO_CHECK_RETURN(!strcmp(pSect->segname, pSrcSeg->segname),\
+ VERR_LDRMACHO_BAD_SECTION); \
+ \
+ switch (pSect->flags & SECTION_TYPE) \
+ { \
+ case S_ZEROFILL: \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
+ fFileBits = 0; \
+ break; \
+ case S_REGULAR: \
+ case S_CSTRING_LITERALS: \
+ case S_COALESCED: \
+ case S_4BYTE_LITERALS: \
+ case S_8BYTE_LITERALS: \
+ case S_16BYTE_LITERALS: \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
+ fFileBits = 1; \
+ break; \
+ \
+ case S_SYMBOL_STUBS: \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
+ /* reserved2 == stub size. 0 has been seen (corecrypto.kext) */ \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->reserved2 < 64, VERR_LDRMACHO_BAD_SECTION); \
+ fFileBits = 1; \
+ break; \
+ \
+ case S_NON_LAZY_SYMBOL_POINTERS: \
+ case S_LAZY_SYMBOL_POINTERS: \
+ case S_LAZY_DYLIB_SYMBOL_POINTERS: \
+ /* (reserved 1 = is indirect symbol table index) */ \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
+ Log(("ldrMachO: Can't load because of section flags: %#x\n", pSect->flags & SECTION_TYPE)); \
+ *pfCanLoad = false; \
+ fFileBits = -1; /* __DATA.__got in the 64-bit mach_kernel has bits, any things without bits? */ \
+ break; \
+ \
+ case S_MOD_INIT_FUNC_POINTERS: \
+ /** @todo this requires a query API or flag... (e.g. C++ constructors) */ \
+ RTLDRMODMACHO_CHECK_RETURN(fOpenFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION), \
+ VERR_LDRMACHO_UNSUPPORTED_INIT_SECTION); \
+ RT_FALL_THRU(); \
+ case S_MOD_TERM_FUNC_POINTERS: \
+ /** @todo this requires a query API or flag... (e.g. C++ destructors) */ \
+ RTLDRMODMACHO_CHECK_RETURN(fOpenFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION), \
+ VERR_LDRMACHO_UNSUPPORTED_TERM_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
+ fFileBits = 1; \
+ break; /* ignored */ \
+ \
+ case S_LITERAL_POINTERS: \
+ case S_DTRACE_DOF: \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved1, VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reserved2, VERR_LDRMACHO_BAD_SECTION); \
+ fFileBits = 1; \
+ break; \
+ \
+ case S_INTERPOSING: \
+ case S_GB_ZEROFILL: \
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNSUPPORTED_SECTION); \
+ \
+ default: \
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_UNKNOWN_SECTION); \
+ } \
+ RTLDRMODMACHO_CHECK_RETURN(!(pSect->flags & ~( S_ATTR_PURE_INSTRUCTIONS | S_ATTR_NO_TOC | S_ATTR_STRIP_STATIC_SYMS \
+ | S_ATTR_NO_DEAD_STRIP | S_ATTR_LIVE_SUPPORT | S_ATTR_SELF_MODIFYING_CODE \
+ | S_ATTR_DEBUG | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_EXT_RELOC \
+ | S_ATTR_LOC_RELOC | SECTION_TYPE)), \
+ VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN((pSect->flags & S_ATTR_DEBUG) == (pFirstSect->flags & S_ATTR_DEBUG), \
+ VERR_LDRMACHO_MIXED_DEBUG_SECTION_FLAGS); \
+ \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->addr - pSrcSeg->vmaddr <= pSrcSeg->vmsize, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN( pSect->addr - pSrcSeg->vmaddr + pSect->size <= pSrcSeg->vmsize \
+ || !strcmp(pSrcSeg->segname, "__CTF") /* see above */, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->align < 31, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ /* Workaround for buggy ld64 (or as, llvm, ++) that produces a misaligned __TEXT.__unwind_info. */ \
+ /* Seen: pSect->align = 4, pSect->addr = 0x5ebe14. Just adjust the alignment down. */ \
+ if ( ((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSect->addr) \
+ && pSect->align == 4 \
+ && strcmp(pSect->sectname, "__unwind_info") == 0) \
+ pSect->align = 2; \
+ RTLDRMODMACHO_CHECK_RETURN(!((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSect->addr), \
+ VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN(!((RT_BIT_32(pSect->align) - UINT32_C(1)) & pSrcSeg->vmaddr), \
+ VERR_LDRMACHO_BAD_SECTION); \
+ \
+ /* Adjust the section offset before we check file offset. */ \
+ offSect = (offSect + RT_BIT_64(pSect->align) - UINT64_C(1)) & ~(RT_BIT_64(pSect->align) - UINT64_C(1)); \
+ if (pSect->addr) \
+ { \
+ RTLDRMODMACHO_CHECK_RETURN(offSect <= pSect->addr - pSrcSeg->vmaddr, VERR_LDRMACHO_BAD_SECTION); \
+ if (offSect < pSect->addr - pSrcSeg->vmaddr) \
+ offSect = pSect->addr - pSrcSeg->vmaddr; \
+ } \
+ \
+ if (fFileBits && pSect->offset == 0 && pSrcSeg->fileoff == 0 && pHdr->filetype == MH_DSYM) \
+ fFileBits = 0; \
+ if (fFileBits) \
+ { \
+ if (uEffFileType != MH_OBJECT) \
+ { \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->offset == pSrcSeg->fileoff + offSect, \
+ VERR_LDRMACHO_NON_CONT_SEG_BITS); \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->offset - pSrcSeg->fileoff <= pSrcSeg->filesize, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ } \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->offset <= cbFile, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)pSect->offset + pSect->size <= cbFile, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ } \
+ else \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->offset == 0, VERR_LDRMACHO_BAD_SECTION); \
+ \
+ if (!pSect->nreloc) \
+ RTLDRMODMACHO_CHECK_RETURN(!pSect->reloff, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ else \
+ { \
+ RTLDRMODMACHO_CHECK_RETURN(pSect->reloff <= cbFile, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ RTLDRMODMACHO_CHECK_RETURN( (uint64_t)pSect->reloff \
+ + (RTFOFF)pSect->nreloc * sizeof(macho_relocation_info_t) \
+ <= cbFile, \
+ VERR_LDRMACHO_BAD_SECTION); \
+ cSectionsWithRelocs++; \
+ } \
+ \
+ /* Validate against file type (pointless?) and count the section, for object files add segment. */ \
+ switch (uEffFileType) \
+ { \
+ case MH_OBJECT: \
+ if ( !(pSect->flags & S_ATTR_DEBUG) \
+ && strcmp(pSect->segname, "__DWARF")) \
+ { \
+ cbStringPool += RTStrNLen(&pSect->segname[0], sizeof(pSect->segname)) + 1; \
+ cbStringPool += RTStrNLen(&pSect->sectname[0], sizeof(pSect->sectname)) + 1; \
+ cSegments++; \
+ if (cSegments == 1) /* The link address is set by the first segment. */ \
+ *pLinkAddress = pSect->addr; \
+ } \
+ RT_FALL_THRU(); \
+ case MH_EXECUTE: \
+ case MH_DYLIB: \
+ case MH_BUNDLE: \
+ case MH_DSYM: \
+ case MH_KEXT_BUNDLE: \
+ cSections++; \
+ break; \
+ default: \
+ RTLDRMODMACHO_FAILED_RETURN(VERR_INVALID_PARAMETER); \
+ } \
+ \
+ /* Advance the section offset, since we're also aligning it. */ \
+ offSect += pSect->size; \
+ } while (0) /* VALIDATE_AND_ADD_SECTION */
+
+ /*
+ * Convert endian if needed, parse and validate the command.
+ */
+ switch (u.pLoadCmd->cmd)
+ {
+ case LC_SEGMENT_32:
+ {
+ segment_command_32_t *pSrcSeg = (segment_command_32_t *)u.pLoadCmd;
+ section_32_t *pFirstSect = (section_32_t *)(pSrcSeg + 1);
+ section_32_t *pSect = pFirstSect;
+ uint32_t cSectionsLeft = pSrcSeg->nsects;
+ uint64_t offSect = 0;
+
+ /* Convert and verify the segment. */
+ RTLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_32_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ RTLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pHdr->magic == IMAGE_MACHO32_SIGNATURE, VERR_LDRMACHO_BIT_MIX);
+ if (fConvertEndian)
+ {
+ pSrcSeg->vmaddr = RT_BSWAP_U32(pSrcSeg->vmaddr);
+ pSrcSeg->vmsize = RT_BSWAP_U32(pSrcSeg->vmsize);
+ pSrcSeg->fileoff = RT_BSWAP_U32(pSrcSeg->fileoff);
+ pSrcSeg->filesize = RT_BSWAP_U32(pSrcSeg->filesize);
+ pSrcSeg->maxprot = RT_BSWAP_U32(pSrcSeg->maxprot);
+ pSrcSeg->initprot = RT_BSWAP_U32(pSrcSeg->initprot);
+ pSrcSeg->nsects = RT_BSWAP_U32(pSrcSeg->nsects);
+ pSrcSeg->flags = RT_BSWAP_U32(pSrcSeg->flags);
+ }
+
+ VALIDATE_AND_ADD_SEGMENT(32);
+
+
+ /*
+ * Convert, validate and parse the sections.
+ */
+ cSectionsLeft = pSrcSeg->nsects;
+ pFirstSect = pSect = (section_32_t *)(pSrcSeg + 1);
+ while (cSectionsLeft-- > 0)
+ {
+ if (fConvertEndian)
+ {
+ pSect->addr = RT_BSWAP_U32(pSect->addr);
+ pSect->size = RT_BSWAP_U32(pSect->size);
+ pSect->offset = RT_BSWAP_U32(pSect->offset);
+ pSect->align = RT_BSWAP_U32(pSect->align);
+ pSect->reloff = RT_BSWAP_U32(pSect->reloff);
+ pSect->nreloc = RT_BSWAP_U32(pSect->nreloc);
+ pSect->flags = RT_BSWAP_U32(pSect->flags);
+ pSect->reserved1 = RT_BSWAP_U32(pSect->reserved1);
+ pSect->reserved2 = RT_BSWAP_U32(pSect->reserved2);
+ }
+
+ VALIDATE_AND_ADD_SECTION(32);
+
+ /* next */
+ pSect++;
+ }
+ break;
+ }
+
+ case LC_SEGMENT_64:
+ {
+ segment_command_64_t *pSrcSeg = (segment_command_64_t *)u.pLoadCmd;
+ section_64_t *pFirstSect = (section_64_t *)(pSrcSeg + 1);
+ section_64_t *pSect = pFirstSect;
+ uint32_t cSectionsLeft = pSrcSeg->nsects;
+ uint64_t offSect = 0;
+
+ /* Convert and verify the segment. */
+ RTLDRMODMACHO_CHECK_RETURN(u.pLoadCmd->cmdsize >= sizeof(segment_command_64_t), VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ RTLDRMODMACHO_CHECK_RETURN( pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE
+ || pHdr->magic == IMAGE_MACHO64_SIGNATURE, VERR_LDRMACHO_BIT_MIX);
+ if (fConvertEndian)
+ {
+ pSrcSeg->vmaddr = RT_BSWAP_U64(pSrcSeg->vmaddr);
+ pSrcSeg->vmsize = RT_BSWAP_U64(pSrcSeg->vmsize);
+ pSrcSeg->fileoff = RT_BSWAP_U64(pSrcSeg->fileoff);
+ pSrcSeg->filesize = RT_BSWAP_U64(pSrcSeg->filesize);
+ pSrcSeg->maxprot = RT_BSWAP_U32(pSrcSeg->maxprot);
+ pSrcSeg->initprot = RT_BSWAP_U32(pSrcSeg->initprot);
+ pSrcSeg->nsects = RT_BSWAP_U32(pSrcSeg->nsects);
+ pSrcSeg->flags = RT_BSWAP_U32(pSrcSeg->flags);
+ }
+
+ VALIDATE_AND_ADD_SEGMENT(64);
+
+ /*
+ * Convert, validate and parse the sections.
+ */
+ while (cSectionsLeft-- > 0)
+ {
+ if (fConvertEndian)
+ {
+ pSect->addr = RT_BSWAP_U64(pSect->addr);
+ pSect->size = RT_BSWAP_U64(pSect->size);
+ pSect->offset = RT_BSWAP_U32(pSect->offset);
+ pSect->align = RT_BSWAP_U32(pSect->align);
+ pSect->reloff = RT_BSWAP_U32(pSect->reloff);
+ pSect->nreloc = RT_BSWAP_U32(pSect->nreloc);
+ pSect->flags = RT_BSWAP_U32(pSect->flags);
+ pSect->reserved1 = RT_BSWAP_U32(pSect->reserved1);
+ pSect->reserved2 = RT_BSWAP_U32(pSect->reserved2);
+ }
+
+ VALIDATE_AND_ADD_SECTION(64);
+
+ /* next */
+ pSect++;
+ }
+ break;
+ } /* LC_SEGMENT_64 */
+
+
+ case LC_SYMTAB:
+ {
+ size_t cbSym;
+ if (fConvertEndian)
+ {
+ u.pSymTab->symoff = RT_BSWAP_U32(u.pSymTab->symoff);
+ u.pSymTab->nsyms = RT_BSWAP_U32(u.pSymTab->nsyms);
+ u.pSymTab->stroff = RT_BSWAP_U32(u.pSymTab->stroff);
+ u.pSymTab->strsize = RT_BSWAP_U32(u.pSymTab->strsize);
+ }
+
+ /* verify */
+ cbSym = pHdr->magic == IMAGE_MACHO32_SIGNATURE
+ || pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+ ? sizeof(macho_nlist_32_t)
+ : sizeof(macho_nlist_64_t);
+ if ( u.pSymTab->symoff >= cbFile
+ || (uint64_t)u.pSymTab->symoff + u.pSymTab->nsyms * cbSym > cbFile)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ if ( u.pSymTab->stroff >= cbFile
+ || (uint64_t)u.pSymTab->stroff + u.pSymTab->strsize > cbFile)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+
+ /* Only one object table, please. */
+ cSymbolTabs++;
+ if (cSymbolTabs != 1)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_OBJECT_FILE);
+
+ cSymbols = u.pSymTab->nsyms;
+ break;
+ }
+
+ case LC_DYSYMTAB:
+ {
+ if (pHdr->filetype == MH_OBJECT)
+ RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSet(pErrInfo, VERR_LDRMACHO_BAD_OBJECT_FILE,
+ "Not expecting LC_DYSYMTAB in MH_OBJECT"));
+ if (fConvertEndian)
+ {
+ u.pDySymTab->ilocalsym = RT_BSWAP_U32(u.pDySymTab->ilocalsym);
+ u.pDySymTab->nlocalsym = RT_BSWAP_U32(u.pDySymTab->nlocalsym);
+ u.pDySymTab->iextdefsym = RT_BSWAP_U32(u.pDySymTab->iextdefsym);
+ u.pDySymTab->nextdefsym = RT_BSWAP_U32(u.pDySymTab->nextdefsym);
+ u.pDySymTab->iundefsym = RT_BSWAP_U32(u.pDySymTab->iundefsym);
+ u.pDySymTab->nundefsym = RT_BSWAP_U32(u.pDySymTab->nundefsym);
+ u.pDySymTab->tocoff = RT_BSWAP_U32(u.pDySymTab->tocoff);
+ u.pDySymTab->ntoc = RT_BSWAP_U32(u.pDySymTab->ntoc);
+ u.pDySymTab->modtaboff = RT_BSWAP_U32(u.pDySymTab->modtaboff);
+ u.pDySymTab->nmodtab = RT_BSWAP_U32(u.pDySymTab->nmodtab);
+ u.pDySymTab->extrefsymoff = RT_BSWAP_U32(u.pDySymTab->extrefsymoff);
+ u.pDySymTab->nextrefsym = RT_BSWAP_U32(u.pDySymTab->nextrefsym);
+ u.pDySymTab->indirectsymboff = RT_BSWAP_U32(u.pDySymTab->indirectsymboff);
+ u.pDySymTab->nindirectsymb = RT_BSWAP_U32(u.pDySymTab->nindirectsymb);
+ u.pDySymTab->extreloff = RT_BSWAP_U32(u.pDySymTab->extreloff);
+ u.pDySymTab->nextrel = RT_BSWAP_U32(u.pDySymTab->nextrel);
+ u.pDySymTab->locreloff = RT_BSWAP_U32(u.pDySymTab->locreloff);
+ u.pDySymTab->nlocrel = RT_BSWAP_U32(u.pDySymTab->nlocrel);
+ }
+
+ /* verify */
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)u.pDySymTab->ilocalsym + u.pDySymTab->nlocalsym <= cSymbols,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "ilocalsym=%#x + nlocalsym=%#x vs cSymbols=%#x",
+ u.pDySymTab->ilocalsym, u.pDySymTab->nlocalsym, cSymbols));
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)u.pDySymTab->iextdefsym + u.pDySymTab->nextdefsym <= cSymbols,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "iextdefsym=%#x + nextdefsym=%#x vs cSymbols=%#x",
+ u.pDySymTab->iextdefsym, u.pDySymTab->nextdefsym, cSymbols));
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)u.pDySymTab->iundefsym + u.pDySymTab->nundefsym <= cSymbols,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "iundefsym=%#x + nundefsym=%#x vs cSymbols=%#x",
+ u.pDySymTab->iundefsym, u.pDySymTab->nundefsym, cSymbols));
+ RTLDRMODMACHO_CHECK_RETURN( (uint64_t)u.pDySymTab->tocoff + u.pDySymTab->ntoc * sizeof(dylib_table_of_contents_t)
+ <= cbFile,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "tocoff=%#x + ntoc=%#x vs cbFile=%#RX64",
+ u.pDySymTab->tocoff, u.pDySymTab->ntoc, cbFile));
+ const uint32_t cbModTabEntry = pHdr->magic == IMAGE_MACHO32_SIGNATURE
+ || pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+ ? sizeof(dylib_module_32_t) : sizeof(dylib_module_64_t);
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)u.pDySymTab->modtaboff + u.pDySymTab->nmodtab * cbModTabEntry <= cbFile,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "modtaboff=%#x + nmodtab=%#x cbModTabEntry=%#x vs cbFile=%#RX64",
+ u.pDySymTab->modtaboff, u.pDySymTab->nmodtab, cbModTabEntry, cbFile));
+ RTLDRMODMACHO_CHECK_RETURN( (uint64_t)u.pDySymTab->extrefsymoff + u.pDySymTab->nextrefsym * sizeof(dylib_reference_t)
+ <= cbFile,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "extrefsymoff=%#x + nextrefsym=%#x vs cbFile=%#RX64",
+ u.pDySymTab->extrefsymoff, u.pDySymTab->nextrefsym, cbFile));
+ RTLDRMODMACHO_CHECK_RETURN( (uint64_t)u.pDySymTab->indirectsymboff + u.pDySymTab->nindirectsymb * sizeof(uint32_t)
+ <= cbFile,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "indirectsymboff=%#x + nindirectsymb=%#x vs cbFile=%#RX64",
+ u.pDySymTab->indirectsymboff, u.pDySymTab->nindirectsymb, cbFile));
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)u.pDySymTab->extreloff + u.pDySymTab->nextrel * sizeof(macho_relocation_info_t) <= cbFile,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "extreloff=%#x + nextrel=%#x vs cbFile=%#RX64",
+ u.pDySymTab->extreloff, u.pDySymTab->nextrel, cbFile));
+ RTLDRMODMACHO_CHECK_RETURN((uint64_t)u.pDySymTab->locreloff + u.pDySymTab->nlocrel * sizeof(macho_relocation_info_t) <= cbFile,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "locreloff=%#x + nlocrel=%#x vs cbFile=%#RX64",
+ u.pDySymTab->locreloff, u.pDySymTab->nlocrel, cbFile));
+ cDySymbolTabs++;
+ fDySymbolTabWithRelocs |= (u.pDySymTab->nlocrel + u.pDySymTab->nextrel) != 0;
+ break;
+ }
+
+ case LC_THREAD:
+ case LC_UNIXTHREAD:
+ {
+ uint32_t *pu32 = (uint32_t *)(u.pb + sizeof(load_command_t));
+ uint32_t cItemsLeft = (u.pThread->cmdsize - sizeof(load_command_t)) / sizeof(uint32_t);
+ while (cItemsLeft)
+ {
+ /* convert & verify header items ([0] == flavor, [1] == uint32_t count). */
+ if (cItemsLeft < 2)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ if (fConvertEndian)
+ {
+ pu32[0] = RT_BSWAP_U32(pu32[0]);
+ pu32[1] = RT_BSWAP_U32(pu32[1]);
+ }
+ if (pu32[1] + 2 > cItemsLeft)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+
+ /* convert & verify according to flavor. */
+ switch (pu32[0])
+ {
+ /** @todo */
+ default:
+ break;
+ }
+
+ /* next */
+ cItemsLeft -= pu32[1] + 2;
+ pu32 += pu32[1] + 2;
+ }
+ break;
+ }
+
+ case LC_UUID:
+ if (u.pUuid->cmdsize != sizeof(uuid_command_t))
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ /** @todo Check anything here need converting? */
+ break;
+
+ case LC_CODE_SIGNATURE:
+ if (u.pUuid->cmdsize != sizeof(linkedit_data_command_t))
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ break;
+
+ case LC_VERSION_MIN_MACOSX:
+ case LC_VERSION_MIN_IPHONEOS:
+ if (u.pUuid->cmdsize != sizeof(version_min_command_t))
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+ break;
+
+ case LC_SOURCE_VERSION: /* Harmless. It just gives a clue regarding the source code revision/version. */
+ case LC_BUILD_VERSION: /* Harmless. It just gives a clue regarding the tool/sdk versions. */
+ case LC_DATA_IN_CODE: /* Ignore */
+ case LC_DYLIB_CODE_SIGN_DRS:/* Ignore */
+ /** @todo valid command size. */
+ break;
+
+ case LC_FUNCTION_STARTS: /** @todo dylib++ */
+ /* Ignore for now. */
+ break;
+ case LC_ID_DYLIB: /** @todo dylib */
+ case LC_LOAD_DYLIB: /** @todo dylib */
+ case LC_LOAD_DYLINKER: /** @todo dylib */
+ case LC_TWOLEVEL_HINTS: /** @todo dylib */
+ case LC_LOAD_WEAK_DYLIB: /** @todo dylib */
+ case LC_ID_DYLINKER: /** @todo dylib */
+ case LC_RPATH: /** @todo dylib */
+ case LC_SEGMENT_SPLIT_INFO: /** @todo dylib++ */
+ case LC_REEXPORT_DYLIB: /** @todo dylib */
+ case LC_DYLD_INFO: /** @todo dylib */
+ case LC_DYLD_INFO_ONLY: /** @todo dylib */
+ case LC_LOAD_UPWARD_DYLIB: /** @todo dylib */
+ case LC_DYLD_ENVIRONMENT: /** @todo dylib */
+ case LC_MAIN: /** @todo parse this and find and entry point or smth. */
+ /** @todo valid command size. */
+ if (!(fOpenFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
+ RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNSUPPORTED_LOAD_COMMAND,
+ "cmd=%#x", u.pLoadCmd->cmd));
+ Log(("ldrMachO: Can't load because of load command: %#x\n", u.pLoadCmd->cmd));
+ *pfCanLoad = false;
+ break;
+
+ case LC_LOADFVMLIB:
+ case LC_IDFVMLIB:
+ case LC_IDENT:
+ case LC_FVMFILE:
+ case LC_PREPAGE:
+ case LC_PREBOUND_DYLIB:
+ case LC_ROUTINES:
+ case LC_ROUTINES_64:
+ case LC_SUB_FRAMEWORK:
+ case LC_SUB_UMBRELLA:
+ case LC_SUB_CLIENT:
+ case LC_SUB_LIBRARY:
+ case LC_PREBIND_CKSUM:
+ case LC_SYMSEG:
+ RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNSUPPORTED_LOAD_COMMAND,
+ "cmd=%#x", u.pLoadCmd->cmd));
+
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_UNKNOWN_LOAD_COMMAND,
+ "cmd=%#x", u.pLoadCmd->cmd));
+ }
+ }
+
+ /* be strict. */
+ if (cbLeft)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_LOAD_COMMAND);
+
+ RTLDRMODMACHO_CHECK_RETURN(cDySymbolTabs <= 1,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "More than one LC_DYSYMTAB command: %u", cDySymbolTabs));
+ RTLDRMODMACHO_CHECK_RETURN(!fDySymbolTabWithRelocs || cSectionsWithRelocs == 0,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "Have relocations both in sections and LC_DYSYMTAB"));
+ if (!cSegments)
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_OBJECT_FILE);
+
+ switch (uEffFileType)
+ {
+ case MH_OBJECT:
+ case MH_EXECUTE:
+ RTLDRMODMACHO_CHECK_RETURN(!fDySymbolTabWithRelocs || (fOpenFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)),
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "Did not expect relocations in LC_DYSYMTAB (file type %u)", uEffFileType));
+ break;
+
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_KEXT_BUNDLE:
+ RTLDRMODMACHO_CHECK_RETURN(cDySymbolTabs > 0,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "No LC_DYSYMTAB command (file type %u)", uEffFileType));
+ RTLDRMODMACHO_CHECK_RETURN(fDySymbolTabWithRelocs || cSectionsWithRelocs == 0,
+ RTErrInfoSetF(pErrInfo, VERR_LDRMACHO_BAD_LOAD_COMMAND,
+ "Expected relocations in LC_DYSYMTAB (file type %u)", uEffFileType));
+ break;
+
+ case MH_DSYM:
+ break;
+ }
+
+ /*
+ * Set return values and return.
+ */
+ *pcSegments = cSegments;
+ *pcSections = cSections;
+ *pcbStringPool = (uint32_t)cbStringPool;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Parses the load commands after we've carved out the module instance.
+ *
+ * This fills in the segment table and perhaps some other properties.
+ *
+ * @returns IPRT status code.
+ * @param pThis The module.
+ * @param pbStringPool The string pool
+ * @param cbStringPool The size of the string pool.
+ */
+static int kldrModMachOParseLoadCommands(PRTLDRMODMACHO pThis, char *pbStringPool, uint32_t cbStringPool)
+{
+ union
+ {
+ const uint8_t *pb;
+ const load_command_t *pLoadCmd;
+ const segment_command_32_t *pSeg32;
+ const segment_command_64_t *pSeg64;
+ const symtab_command_t *pSymTab;
+ const uuid_command_t *pUuid;
+ const linkedit_data_command_t *pData;
+ } u;
+ uint32_t cLeft = pThis->Hdr.ncmds;
+ uint32_t cbLeft = pThis->Hdr.sizeofcmds;
+ const uint8_t *pb = pThis->pbLoadCommands;
+ PRTLDRMODMACHOSEG pDstSeg = &pThis->aSegments[0];
+ PRTLDRMODMACHOSECT pSectExtra = pThis->paSections;
+ const uint32_t cSegments = pThis->cSegments;
+ PRTLDRMODMACHOSEG pSegItr;
+ bool fFirstSeg = true;
+ RT_NOREF(cbStringPool);
+
+ while (cLeft-- > 0)
+ {
+ u.pb = pb;
+ cbLeft -= u.pLoadCmd->cmdsize;
+ pb += u.pLoadCmd->cmdsize;
+
+ /*
+ * Convert endian if needed, parse and validate the command.
+ */
+ switch (u.pLoadCmd->cmd)
+ {
+ case LC_SEGMENT_32:
+ {
+ const segment_command_32_t *pSrcSeg = (const segment_command_32_t *)u.pLoadCmd;
+ section_32_t *pFirstSect = (section_32_t *)(pSrcSeg + 1);
+ section_32_t *pSect = pFirstSect;
+ uint32_t cSectionsLeft = pSrcSeg->nsects;
+
+ /* Adds a segment, used by the macro below and thus shared with the 64-bit segment variant. */
+#define NEW_SEGMENT(a_cBits, a_achName1, a_fObjFile, a_achName2, a_SegAddr, a_cbSeg, a_fFileBits, a_offFile, a_cbFile) \
+ do { \
+ pDstSeg->SegInfo.pszName = pbStringPool; \
+ pDstSeg->SegInfo.cchName = (uint32_t)RTStrNLen(a_achName1, sizeof(a_achName1)); \
+ memcpy(pbStringPool, a_achName1, pDstSeg->SegInfo.cchName); \
+ pbStringPool += pDstSeg->SegInfo.cchName; \
+ if (a_fObjFile) \
+ { /* MH_OBJECT: Add '.sectname' - sections aren't sorted by segments. */ \
+ size_t cchName2 = RTStrNLen(a_achName2, sizeof(a_achName2)); \
+ *pbStringPool++ = '.'; \
+ memcpy(pbStringPool, a_achName2, cchName2); \
+ pbStringPool += cchName2; \
+ pDstSeg->SegInfo.cchName += (uint32_t)cchName2; \
+ } \
+ *pbStringPool++ = '\0'; \
+ pDstSeg->SegInfo.SelFlat = 0; \
+ pDstSeg->SegInfo.Sel16bit = 0; \
+ pDstSeg->SegInfo.fFlags = 0; \
+ pDstSeg->SegInfo.fProt = RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC; /** @todo fixme! */ \
+ pDstSeg->SegInfo.cb = (a_cbSeg); \
+ pDstSeg->SegInfo.Alignment = 1; /* updated while parsing sections. */ \
+ pDstSeg->SegInfo.LinkAddress = (a_SegAddr); \
+ if (a_fFileBits) \
+ { \
+ pDstSeg->SegInfo.offFile = (RTFOFF)((a_offFile) + pThis->offImage); \
+ pDstSeg->SegInfo.cbFile = (RTFOFF)(a_cbFile); \
+ } \
+ else \
+ { \
+ pDstSeg->SegInfo.offFile = -1; \
+ pDstSeg->SegInfo.cbFile = -1; \
+ } \
+ pDstSeg->SegInfo.RVA = (a_SegAddr) - pThis->LinkAddress; \
+ pDstSeg->SegInfo.cbMapped = 0; \
+ \
+ pDstSeg->iOrgSegNo = (uint32_t)(pDstSeg - &pThis->aSegments[0]); \
+ pDstSeg->cSections = 0; \
+ pDstSeg->paSections = pSectExtra; \
+ } while (0)
+
+ /* Closes the new segment - part of NEW_SEGMENT. */
+#define CLOSE_SEGMENT() \
+ do { \
+ pDstSeg->cSections = (uint32_t)(pSectExtra - pDstSeg->paSections); \
+ pDstSeg++; \
+ } while (0)
+
+
+ /* Shared with the 64-bit variant. */
+#define ADD_SEGMENT_AND_ITS_SECTIONS(a_cBits) \
+ do { \
+ bool fAddSegOuter = false; \
+ \
+ /* \
+ * Check that the segment name is unique. We couldn't do that \
+ * in the preparsing stage. \
+ */ \
+ if (pThis->uEffFileType != MH_OBJECT) \
+ for (pSegItr = &pThis->aSegments[0]; pSegItr != pDstSeg; pSegItr++) \
+ if (!strncmp(pSegItr->SegInfo.pszName, pSrcSeg->segname, sizeof(pSrcSeg->segname))) \
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_DUPLICATE_SEGMENT_NAME); \
+ \
+ /* \
+ * Create a new segment, unless we're supposed to skip this one. \
+ */ \
+ if ( pThis->uEffFileType != MH_OBJECT \
+ && (cSectionsLeft == 0 || !(pFirstSect->flags & S_ATTR_DEBUG)) \
+ && strcmp(pSrcSeg->segname, "__DWARF") \
+ && strcmp(pSrcSeg->segname, "__CTF") ) \
+ { \
+ NEW_SEGMENT(a_cBits, pSrcSeg->segname, false /*a_fObjFile*/, "" /*a_achName2*/, \
+ pSrcSeg->vmaddr, pSrcSeg->vmsize, \
+ pSrcSeg->filesize != 0, pSrcSeg->fileoff, pSrcSeg->filesize); \
+ fAddSegOuter = true; \
+ } \
+ \
+ /* \
+ * Convert and parse the sections. \
+ */ \
+ while (cSectionsLeft-- > 0) \
+ { \
+ /* New segment if object file. */ \
+ bool fAddSegInner = false; \
+ if ( pThis->uEffFileType == MH_OBJECT \
+ && !(pSect->flags & S_ATTR_DEBUG) \
+ && strcmp(pSrcSeg->segname, "__DWARF") \
+ && strcmp(pSrcSeg->segname, "__CTF") ) \
+ { \
+ Assert(!fAddSegOuter); \
+ NEW_SEGMENT(a_cBits, pSect->segname, true /*a_fObjFile*/, pSect->sectname, \
+ pSect->addr, pSect->size, \
+ pSect->offset != 0, pSect->offset, pSect->size); \
+ fAddSegInner = true; \
+ } \
+ \
+ /* Section data extract. */ \
+ pSectExtra->cb = pSect->size; \
+ pSectExtra->RVA = pSect->addr - pDstSeg->SegInfo.LinkAddress; \
+ pSectExtra->LinkAddress = pSect->addr; \
+ if (pSect->offset) \
+ pSectExtra->offFile = pSect->offset + pThis->offImage; \
+ else \
+ pSectExtra->offFile = -1; \
+ pSectExtra->cFixups = pSect->nreloc; \
+ pSectExtra->paFixups = NULL; \
+ pSectExtra->pauFixupVirginData = NULL; \
+ if (pSect->nreloc) \
+ pSectExtra->offFixups = pSect->reloff + pThis->offImage; \
+ else \
+ pSectExtra->offFixups = -1; \
+ pSectExtra->fFlags = pSect->flags; \
+ pSectExtra->iSegment = (uint32_t)(pDstSeg - &pThis->aSegments[0]); \
+ pSectExtra->pvMachoSection = pSect; \
+ \
+ /* Update the segment alignment, if we're not skipping it. */ \
+ if ( (fAddSegOuter || fAddSegInner) \
+ && pDstSeg->SegInfo.Alignment < ((RTLDRADDR)1 << pSect->align)) \
+ pDstSeg->SegInfo.Alignment = (RTLDRADDR)1 << pSect->align; \
+ \
+ /* Next section, and if object file next segment. */ \
+ pSectExtra++; \
+ pSect++; \
+ if (fAddSegInner) \
+ CLOSE_SEGMENT(); \
+ } \
+ \
+ /* Close the segment and advance. */ \
+ if (fAddSegOuter) \
+ CLOSE_SEGMENT(); \
+ \
+ /* Take down 'execSeg' info for signing */ \
+ if (fFirstSeg) \
+ { \
+ fFirstSeg = false; \
+ pThis->offSeg0ForCodeSign = pSrcSeg->fileoff; \
+ pThis->cbSeg0ForCodeSign = pSrcSeg->filesize; /** @todo file or vm size? */ \
+ pThis->fSeg0ForCodeSign = pSrcSeg->flags; \
+ } \
+ } while (0) /* ADD_SEGMENT_AND_ITS_SECTIONS */
+
+ ADD_SEGMENT_AND_ITS_SECTIONS(32);
+ break;
+ }
+
+ case LC_SEGMENT_64:
+ {
+ const segment_command_64_t *pSrcSeg = (const segment_command_64_t *)u.pLoadCmd;
+ section_64_t *pFirstSect = (section_64_t *)(pSrcSeg + 1);
+ section_64_t *pSect = pFirstSect;
+ uint32_t cSectionsLeft = pSrcSeg->nsects;
+
+ ADD_SEGMENT_AND_ITS_SECTIONS(64);
+ break;
+ }
+
+ case LC_SYMTAB:
+ switch (pThis->uEffFileType)
+ {
+ case MH_OBJECT:
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ case MH_DSYM:
+ case MH_KEXT_BUNDLE:
+ pThis->offSymbols = u.pSymTab->symoff + pThis->offImage;
+ pThis->cSymbols = u.pSymTab->nsyms;
+ pThis->offStrings = u.pSymTab->stroff + pThis->offImage;
+ pThis->cchStrings = u.pSymTab->strsize;
+ break;
+ }
+ break;
+
+ case LC_DYSYMTAB:
+ pThis->pDySymTab = (dysymtab_command_t *)u.pb;
+ break;
+
+ case LC_UUID:
+ memcpy(pThis->abImageUuid, u.pUuid->uuid, sizeof(pThis->abImageUuid));
+ break;
+
+ case LC_CODE_SIGNATURE:
+ pThis->offCodeSignature = u.pData->dataoff;
+ pThis->cbCodeSignature = u.pData->datasize;
+ break;
+
+ default:
+ break;
+ } /* command switch */
+ } /* while more commands */
+
+ Assert(pDstSeg == &pThis->aSegments[cSegments - pThis->fMakeGot]);
+
+ /*
+ * Adjust mapping addresses calculating the image size.
+ */
+ {
+ bool fLoadLinkEdit = RT_BOOL(pThis->fOpenFlags & RTLDR_O_MACHO_LOAD_LINKEDIT);
+ PRTLDRMODMACHOSECT pSectExtraItr;
+ RTLDRADDR uNextRVA = 0;
+ RTLDRADDR cb;
+ uint32_t cSegmentsToAdjust = cSegments - pThis->fMakeGot;
+ uint32_t c;
+
+ for (;;)
+ {
+ /* Check if there is __DWARF segment at the end and make sure it's left
+ out of the RVA negotiations and image loading. */
+ if ( cSegmentsToAdjust > 0
+ && !strcmp(pThis->aSegments[cSegmentsToAdjust - 1].SegInfo.pszName, "__DWARF"))
+ {
+ cSegmentsToAdjust--;
+ pThis->aSegments[cSegmentsToAdjust].SegInfo.RVA = NIL_RTLDRADDR;
+ pThis->aSegments[cSegmentsToAdjust].SegInfo.cbMapped = NIL_RTLDRADDR;
+ continue;
+ }
+
+ /* If we're skipping the __LINKEDIT segment, check for it and adjust
+ the number of segments we'll be messing with here. ASSUMES it's
+ last (typcially is, but not always for mach_kernel). */
+ if ( !fLoadLinkEdit
+ && cSegmentsToAdjust > 0
+ && !strcmp(pThis->aSegments[cSegmentsToAdjust - 1].SegInfo.pszName, "__LINKEDIT"))
+ {
+ cSegmentsToAdjust--;
+ pThis->aSegments[cSegmentsToAdjust].SegInfo.RVA = NIL_RTLDRADDR;
+ pThis->aSegments[cSegmentsToAdjust].SegInfo.cbMapped = NIL_RTLDRADDR;
+ continue;
+ }
+ break;
+ }
+
+ /* Adjust RVAs. */
+ c = cSegmentsToAdjust;
+ for (pDstSeg = &pThis->aSegments[0]; c-- > 0; pDstSeg++)
+ {
+ uNextRVA = RTLDR_ALIGN_ADDR(uNextRVA, pDstSeg->SegInfo.Alignment);
+ cb = pDstSeg->SegInfo.RVA - uNextRVA;
+ if (cb >= 0x00100000) /* 1MB */
+ {
+ pDstSeg->SegInfo.RVA = uNextRVA;
+ //pThis->pMod->fFlags |= KLDRMOD_FLAGS_NON_CONTIGUOUS_LINK_ADDRS;
+ }
+ uNextRVA = pDstSeg->SegInfo.RVA + pDstSeg->SegInfo.cb;
+ }
+
+ /* Calculate the cbMapping members. */
+ c = cSegmentsToAdjust;
+ for (pDstSeg = &pThis->aSegments[0]; c-- > 1; pDstSeg++)
+ {
+
+ cb = pDstSeg[1].SegInfo.RVA - pDstSeg->SegInfo.RVA;
+ pDstSeg->SegInfo.cbMapped = (size_t)cb == cb ? (size_t)cb : ~(size_t)0;
+ }
+
+ cb = RTLDR_ALIGN_ADDR(pDstSeg->SegInfo.cb, pDstSeg->SegInfo.Alignment);
+ pDstSeg->SegInfo.cbMapped = (size_t)cb == cb ? (size_t)cb : ~(size_t)0;
+
+ /* Set the image size. */
+ pThis->cbImage = pDstSeg->SegInfo.RVA + cb;
+
+ /* Fixup the section RVAs (internal). */
+ c = cSegmentsToAdjust;
+ uNextRVA = pThis->cbImage;
+ pDstSeg = &pThis->aSegments[0];
+ for (pSectExtraItr = pThis->paSections; pSectExtraItr != pSectExtra; pSectExtraItr++)
+ {
+ if (pSectExtraItr->iSegment < c)
+ pSectExtraItr->RVA += pDstSeg[pSectExtraItr->iSegment].SegInfo.RVA;
+ else
+ {
+ pSectExtraItr->RVA = uNextRVA;
+ uNextRVA += RTLDR_ALIGN_ADDR(pSectExtraItr->cb, 64);
+ }
+ }
+ }
+
+ /*
+ * Make the GOT segment if necessary.
+ */
+ if (pThis->fMakeGot)
+ {
+ uint32_t cbPtr = ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ ? sizeof(uint32_t)
+ : sizeof(uint64_t);
+ uint32_t cbGot = pThis->cSymbols * cbPtr;
+ uint32_t cbJmpStubs;
+
+ pThis->GotRVA = pThis->cbImage;
+
+ if (pThis->cbJmpStub)
+ {
+ cbGot = RT_ALIGN_Z(cbGot, 64);
+ pThis->JmpStubsRVA = pThis->GotRVA + cbGot;
+ cbJmpStubs = pThis->cbJmpStub * pThis->cSymbols;
+ }
+ else
+ {
+ pThis->JmpStubsRVA = NIL_RTLDRADDR;
+ cbJmpStubs = 0;
+ }
+
+ pDstSeg = &pThis->aSegments[cSegments - 1];
+ pDstSeg->SegInfo.pszName = "GOT";
+ pDstSeg->SegInfo.cchName = 3;
+ pDstSeg->SegInfo.SelFlat = 0;
+ pDstSeg->SegInfo.Sel16bit = 0;
+ pDstSeg->SegInfo.fFlags = 0;
+ pDstSeg->SegInfo.fProt = RTMEM_PROT_READ;
+ pDstSeg->SegInfo.cb = cbGot + cbJmpStubs;
+ pDstSeg->SegInfo.Alignment = 64;
+ pDstSeg->SegInfo.LinkAddress = pThis->LinkAddress + pThis->GotRVA;
+ pDstSeg->SegInfo.offFile = -1;
+ pDstSeg->SegInfo.cbFile = -1;
+ pDstSeg->SegInfo.RVA = pThis->GotRVA;
+ pDstSeg->SegInfo.cbMapped = (size_t)RTLDR_ALIGN_ADDR(cbGot + cbJmpStubs, pDstSeg->SegInfo.Alignment);
+
+ pDstSeg->iOrgSegNo = UINT32_MAX;
+ pDstSeg->cSections = 0;
+ pDstSeg->paSections = NULL;
+
+ pThis->cbImage += pDstSeg->SegInfo.cbMapped;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtldrMachO_Close(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ RTLDRMODMACHO_ASSERT(!pThis->pvMapping);
+
+ uint32_t i = pThis->cSegments;
+ while (i-- > 0)
+ {
+ uint32_t j = pThis->aSegments[i].cSections;
+ while (j-- > 0)
+ {
+ RTMemFree(pThis->aSegments[i].paSections[j].paFixups);
+ pThis->aSegments[i].paSections[j].paFixups = NULL;
+ RTMemFree(pThis->aSegments[i].paSections[j].pauFixupVirginData);
+ pThis->aSegments[i].paSections[j].pauFixupVirginData = NULL;
+ }
+ }
+
+ RTMemFree(pThis->pbLoadCommands);
+ pThis->pbLoadCommands = NULL;
+ RTMemFree(pThis->pchStrings);
+ pThis->pchStrings = NULL;
+ RTMemFree(pThis->pvaSymbols);
+ pThis->pvaSymbols = NULL;
+ RTMemFree(pThis->paidxIndirectSymbols);
+ pThis->paidxIndirectSymbols = NULL;
+ RTMemFree(pThis->paRelocations);
+ pThis->paRelocations = NULL;
+ RTMemFree(pThis->pauRelocationsVirginData);
+ pThis->pauRelocationsVirginData = NULL;
+ RTMemFree(pThis->PtrCodeSignature.pb);
+ pThis->PtrCodeSignature.pb = NULL;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Gets the right base address.
+ *
+ * @param pThis The interpreter module instance
+ * @param pBaseAddress The base address, IN & OUT. Optional.
+ */
+static void kldrModMachOAdjustBaseAddress(PRTLDRMODMACHO pThis, PRTLDRADDR pBaseAddress)
+{
+ /*
+ * Adjust the base address.
+ */
+ if (*pBaseAddress == RTLDR_BASEADDRESS_LINK)
+ *pBaseAddress = pThis->LinkAddress;
+}
+
+
+/**
+ * Resolves a linker generated symbol.
+ *
+ * The Apple linker generates symbols indicating the start and end of sections
+ * and segments. This function checks for these and returns the right value.
+ *
+ * @returns VINF_SUCCESS or VERR_SYMBOL_NOT_FOUND.
+ * @param pThis The interpreter module instance.
+ * @param pchSymbol The symbol.
+ * @param cchSymbol The length of the symbol.
+ * @param BaseAddress The base address to apply when calculating the
+ * value.
+ * @param puValue Where to return the symbol value.
+ */
+static int kldrModMachOQueryLinkerSymbol(PRTLDRMODMACHO pThis, const char *pchSymbol, size_t cchSymbol,
+ RTLDRADDR BaseAddress, PRTLDRADDR puValue)
+{
+ /*
+ * Match possible name prefixes.
+ */
+ static const struct
+ {
+ const char *pszPrefix;
+ uint32_t cchPrefix;
+ bool fSection;
+ bool fStart;
+ } s_aPrefixes[] =
+ {
+ { "section$start$", (uint8_t)sizeof("section$start$") - 1, true, true },
+ { "section$end$", (uint8_t)sizeof("section$end$") - 1, true, false},
+ { "segment$start$", (uint8_t)sizeof("segment$start$") - 1, false, true },
+ { "segment$end$", (uint8_t)sizeof("segment$end$") - 1, false, false},
+ };
+ size_t cchSectName = 0;
+ const char *pchSectName = "";
+ size_t cchSegName = 0;
+ const char *pchSegName = NULL;
+ uint32_t iPrefix = RT_ELEMENTS(s_aPrefixes) - 1;
+ uint32_t iSeg;
+ RTLDRADDR uValue;
+
+ for (;;)
+ {
+ uint8_t const cchPrefix = s_aPrefixes[iPrefix].cchPrefix;
+ if ( cchSymbol > cchPrefix
+ && strncmp(pchSymbol, s_aPrefixes[iPrefix].pszPrefix, cchPrefix) == 0)
+ {
+ pchSegName = pchSymbol + cchPrefix;
+ cchSegName = cchSymbol - cchPrefix;
+ break;
+ }
+
+ /* next */
+ if (!iPrefix)
+ return VERR_SYMBOL_NOT_FOUND;
+ iPrefix--;
+ }
+
+ /*
+ * Split the remainder into segment and section name, if necessary.
+ */
+ if (s_aPrefixes[iPrefix].fSection)
+ {
+ pchSectName = (const char *)memchr(pchSegName, '$', cchSegName);
+ if (!pchSectName)
+ return VERR_SYMBOL_NOT_FOUND;
+ cchSegName = pchSectName - pchSegName;
+ pchSectName++;
+ cchSectName = cchSymbol - (pchSectName - pchSymbol);
+ }
+
+ /*
+ * Locate the segment.
+ */
+ if (!pThis->cSegments)
+ return VERR_SYMBOL_NOT_FOUND;
+ for (iSeg = 0; iSeg < pThis->cSegments; iSeg++)
+ {
+ if ( pThis->aSegments[iSeg].SegInfo.cchName >= cchSegName
+ && memcmp(pThis->aSegments[iSeg].SegInfo.pszName, pchSegName, cchSegName) == 0)
+ {
+ section_32_t const *pSect;
+ if ( pThis->aSegments[iSeg].SegInfo.cchName == cchSegName
+ && pThis->Hdr.filetype != MH_OBJECT /* Good enough for __DWARF segs in MH_DHSYM, I hope. */)
+ break;
+
+ pSect = (section_32_t *)pThis->aSegments[iSeg].paSections[0].pvMachoSection;
+ if ( pThis->uEffFileType == MH_OBJECT
+ && pThis->aSegments[iSeg].SegInfo.cchName > cchSegName + 1
+ && pThis->aSegments[iSeg].SegInfo.pszName[cchSegName] == '.'
+ && strncmp(&pThis->aSegments[iSeg].SegInfo.pszName[cchSegName + 1], pSect->sectname, sizeof(pSect->sectname)) == 0
+ && pThis->aSegments[iSeg].SegInfo.cchName - cchSegName - 1 <= sizeof(pSect->sectname) )
+ break;
+ }
+ }
+ if (iSeg >= pThis->cSegments)
+ return VERR_SYMBOL_NOT_FOUND;
+
+ if (!s_aPrefixes[iPrefix].fSection)
+ {
+ /*
+ * Calculate the segment start/end address.
+ */
+ uValue = pThis->aSegments[iSeg].SegInfo.RVA;
+ if (!s_aPrefixes[iPrefix].fStart)
+ uValue += pThis->aSegments[iSeg].SegInfo.cb;
+ }
+ else
+ {
+ /*
+ * Locate the section.
+ */
+ uint32_t iSect = pThis->aSegments[iSeg].cSections;
+ if (!iSect)
+ return VERR_SYMBOL_NOT_FOUND;
+ for (;;)
+ {
+ section_32_t *pSect = (section_32_t *)pThis->aSegments[iSeg].paSections[iSect].pvMachoSection;
+ if ( cchSectName <= sizeof(pSect->sectname)
+ && memcmp(pSect->sectname, pchSectName, cchSectName) == 0
+ && ( cchSectName == sizeof(pSect->sectname)
+ || pSect->sectname[cchSectName] == '\0') )
+ break;
+ /* next */
+ if (!iSect)
+ return VERR_SYMBOL_NOT_FOUND;
+ iSect--;
+ }
+
+ uValue = pThis->aSegments[iSeg].paSections[iSect].RVA;
+ if (!s_aPrefixes[iPrefix].fStart)
+ uValue += pThis->aSegments[iSeg].paSections[iSect].cb;
+ }
+
+ /*
+ * Convert from RVA to load address.
+ */
+ uValue += BaseAddress;
+ if (puValue)
+ *puValue = uValue;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnGetSymbolEx}
+ */
+static DECLCALLBACK(int) rtldrMachO_GetSymbolEx(PRTLDRMODINTERNAL pMod, const void *pvBits, RTUINTPTR BaseAddress,
+ uint32_t iOrdinal, const char *pszSymbol, RTUINTPTR *pValue)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ RT_NOREF(pvBits);
+ //RT_NOREF(pszVersion);
+ //RT_NOREF(pfnGetForwarder);
+ //RT_NOREF(pvUser);
+ uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
+ uint32_t *pfKind = &fKind;
+ size_t cchSymbol = pszSymbol ? strlen(pszSymbol) : 0;
+
+ /*
+ * Resolve defaults.
+ */
+ kldrModMachOAdjustBaseAddress(pThis, &BaseAddress);
+
+ /*
+ * Refuse segmented requests for now.
+ */
+ RTLDRMODMACHO_CHECK_RETURN( !pfKind
+ || (*pfKind & RTLDRSYMKIND_REQ_TYPE_MASK) == RTLDRSYMKIND_REQ_FLAT,
+ VERR_LDRMACHO_TODO);
+
+ /*
+ * Take action according to file type.
+ */
+ int rc;
+ if ( pThis->Hdr.filetype == MH_OBJECT
+ || pThis->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
+ || pThis->Hdr.filetype == MH_DYLIB
+ || pThis->Hdr.filetype == MH_BUNDLE
+ || pThis->Hdr.filetype == MH_DSYM
+ || pThis->Hdr.filetype == MH_KEXT_BUNDLE)
+ {
+ rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ rc = kldrModMachODoQuerySymbol32Bit(pThis, (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols,
+ pThis->pchStrings, pThis->cchStrings, BaseAddress, iOrdinal, pszSymbol,
+ (uint32_t)cchSymbol, pValue, pfKind);
+ else
+ rc = kldrModMachODoQuerySymbol64Bit(pThis, (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols,
+ pThis->pchStrings, pThis->cchStrings, BaseAddress, iOrdinal, pszSymbol,
+ (uint32_t)cchSymbol, pValue, pfKind);
+ }
+
+ /*
+ * Check for link-editor generated symbols and supply what we can.
+ *
+ * As small service to clients that insists on adding a '_' prefix
+ * before querying symbols, we will ignore the prefix.
+ */
+ if ( rc == VERR_SYMBOL_NOT_FOUND
+ && cchSymbol > sizeof("section$end$") - 1
+ && ( pszSymbol[0] == 's'
+ || (pszSymbol[1] == 's' && pszSymbol[0] == '_') )
+ && memchr(pszSymbol, '$', cchSymbol) )
+ {
+ if (pszSymbol[0] == '_')
+ rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol + 1, cchSymbol - 1, BaseAddress, pValue);
+ else
+ rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, pValue);
+ }
+ }
+ else
+ rc = VERR_LDRMACHO_TODO;
+
+ return rc;
+}
+
+
+/**
+ * Lookup a symbol in a 32-bit symbol table.
+ *
+ * @returns IPRT status code.
+ * @param pThis
+ * @param paSyms Pointer to the symbol table.
+ * @param cSyms Number of symbols in the table.
+ * @param pchStrings Pointer to the string table.
+ * @param cchStrings Size of the string table.
+ * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol.
+ * @param iSymbol See kLdrModQuerySymbol.
+ * @param pchSymbol See kLdrModQuerySymbol.
+ * @param cchSymbol See kLdrModQuerySymbol.
+ * @param puValue See kLdrModQuerySymbol.
+ * @param pfKind See kLdrModQuerySymbol.
+ */
+static int kldrModMachODoQuerySymbol32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
+ const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol,
+ const char *pchSymbol, uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind)
+{
+ /*
+ * Find a valid symbol matching the search criteria.
+ */
+ if (iSymbol == UINT32_MAX)
+ {
+ /* simplify validation. */
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (cchStrings <= cchSymbol + 1)
+ return VERR_SYMBOL_NOT_FOUND;
+ cchStrings -= cchSymbol + 1;
+
+ /* external symbols are usually at the end, so search the other way. */
+ for (iSymbol = cSyms - 1; iSymbol != UINT32_MAX; iSymbol--)
+ {
+ const char *psz;
+
+ /* Skip irrellevant and non-public symbols. */
+ if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+ continue;
+ if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ continue;
+ if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
+ continue;
+ if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
+ continue;
+
+ /* get name */
+ if (!paSyms[iSymbol].n_un.n_strx)
+ continue;
+ if ((uint32_t)paSyms[iSymbol].n_un.n_strx >= cchStrings)
+ continue;
+ psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
+ if (psz[cchSymbol + 1])
+ continue;
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (*psz != '_' || memcmp(psz + 1, pchSymbol, cchSymbol))
+ continue;
+
+ /* match! */
+ break;
+ }
+ if (iSymbol == UINT32_MAX)
+ return VERR_SYMBOL_NOT_FOUND;
+ }
+ else
+ {
+ if (iSymbol >= cSyms)
+ return VERR_SYMBOL_NOT_FOUND;
+ if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+ return VERR_SYMBOL_NOT_FOUND;
+ if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ return VERR_SYMBOL_NOT_FOUND;
+ }
+
+ /*
+ * Calc the return values.
+ */
+ if (pfKind)
+ {
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ *pfKind = RTLDRSYMKIND_32BIT | RTLDRSYMKIND_NO_TYPE;
+ else
+ *pfKind = RTLDRSYMKIND_64BIT | RTLDRSYMKIND_NO_TYPE;
+ if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
+ *pfKind |= RTLDRSYMKIND_WEAK;
+ }
+
+ switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSect;
+ RTLDRADDR offSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSymbol].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSect = &pThis->paSections[paSyms[iSymbol].n_sect - 1];
+
+ offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
+ RTLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
+ || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
+ && offSect == 0U - pSect->RVA
+ && pThis->uEffFileType != MH_OBJECT),
+ VERR_LDRMACHO_BAD_SYMBOL);
+ if (puValue)
+ *puValue = BaseAddress + pSect->RVA + offSect;
+
+ if ( pfKind
+ && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
+ *pfKind = (*pfKind & ~RTLDRSYMKIND_TYPE_MASK) | RTLDRSYMKIND_CODE;
+ break;
+ }
+
+ case MACHO_N_ABS:
+ if (puValue)
+ *puValue = paSyms[iSymbol].n_value;
+ /*if (pfKind)
+ pfKind |= RTLDRSYMKIND_ABS;*/
+ break;
+
+ case MACHO_N_PBUD:
+ case MACHO_N_INDR:
+ /** @todo implement indirect and prebound symbols. */
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Lookup a symbol in a 64-bit symbol table.
+ *
+ * @returns IPRT status code.
+ * @param pThis
+ * @param paSyms Pointer to the symbol table.
+ * @param cSyms Number of symbols in the table.
+ * @param pchStrings Pointer to the string table.
+ * @param cchStrings Size of the string table.
+ * @param BaseAddress Adjusted base address, see kLdrModQuerySymbol.
+ * @param iSymbol See kLdrModQuerySymbol.
+ * @param pchSymbol See kLdrModQuerySymbol.
+ * @param cchSymbol See kLdrModQuerySymbol.
+ * @param puValue See kLdrModQuerySymbol.
+ * @param pfKind See kLdrModQuerySymbol.
+ */
+static int kldrModMachODoQuerySymbol64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
+ const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress, uint32_t iSymbol,
+ const char *pchSymbol, uint32_t cchSymbol, PRTLDRADDR puValue, uint32_t *pfKind)
+{
+ /*
+ * Find a valid symbol matching the search criteria.
+ */
+ if (iSymbol == UINT32_MAX)
+ {
+ /* simplify validation. */
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (cchStrings <= cchSymbol + 1)
+ return VERR_SYMBOL_NOT_FOUND;
+ cchStrings -= cchSymbol + 1;
+
+ /* external symbols are usually at the end, so search the other way. */
+ for (iSymbol = cSyms - 1; iSymbol != UINT32_MAX; iSymbol--)
+ {
+ const char *psz;
+
+ /* Skip irrellevant and non-public symbols. */
+ if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+ continue;
+ if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ continue;
+ if (!(paSyms[iSymbol].n_type & MACHO_N_EXT)) /*??*/
+ continue;
+ if (paSyms[iSymbol].n_type & MACHO_N_PEXT) /*??*/
+ continue;
+
+ /* get name */
+ if (!paSyms[iSymbol].n_un.n_strx)
+ continue;
+ if ((uint32_t)paSyms[iSymbol].n_un.n_strx >= cchStrings)
+ continue;
+ psz = &pchStrings[paSyms[iSymbol].n_un.n_strx];
+ if (psz[cchSymbol + 1])
+ continue;
+ if (*psz != '_' || memcmp(psz + 1, pchSymbol, cchSymbol))
+ continue;
+
+ /* match! */
+ break;
+ }
+ if (iSymbol == UINT32_MAX)
+ return VERR_SYMBOL_NOT_FOUND;
+ }
+ else
+ {
+ if (iSymbol >= cSyms)
+ return VERR_SYMBOL_NOT_FOUND;
+ if (paSyms[iSymbol].n_type & MACHO_N_STAB)
+ return VERR_SYMBOL_NOT_FOUND;
+ if ((paSyms[iSymbol].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ return VERR_SYMBOL_NOT_FOUND;
+ }
+
+ /*
+ * Calc the return values.
+ */
+ if (pfKind)
+ {
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ *pfKind = RTLDRSYMKIND_32BIT | RTLDRSYMKIND_NO_TYPE;
+ else
+ *pfKind = RTLDRSYMKIND_64BIT | RTLDRSYMKIND_NO_TYPE;
+ if (paSyms[iSymbol].n_desc & N_WEAK_DEF)
+ *pfKind |= RTLDRSYMKIND_WEAK;
+ }
+
+ switch (paSyms[iSymbol].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSect;
+ RTLDRADDR offSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSymbol].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSect = &pThis->paSections[paSyms[iSymbol].n_sect - 1];
+
+ offSect = paSyms[iSymbol].n_value - pSect->LinkAddress;
+ RTLDRMODMACHO_CHECK_RETURN( offSect <= pSect->cb
+ || ( paSyms[iSymbol].n_sect == 1 /* special hack for __mh_execute_header */
+ && offSect == 0U - pSect->RVA
+ && pThis->uEffFileType != MH_OBJECT),
+ VERR_LDRMACHO_BAD_SYMBOL);
+ if (puValue)
+ *puValue = BaseAddress + pSect->RVA + offSect;
+
+ if ( pfKind
+ && (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE)))
+ *pfKind = (*pfKind & ~RTLDRSYMKIND_TYPE_MASK) | RTLDRSYMKIND_CODE;
+ break;
+ }
+
+ case MACHO_N_ABS:
+ if (puValue)
+ *puValue = paSyms[iSymbol].n_value;
+ /*if (pfKind)
+ pfKind |= RTLDRSYMKIND_ABS;*/
+ break;
+
+ case MACHO_N_PBUD:
+ case MACHO_N_INDR:
+ /** @todo implement indirect and prebound symbols. */
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnEnumSymbols}
+ */
+static DECLCALLBACK(int) rtldrMachO_EnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits,
+ RTUINTPTR BaseAddress, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ RT_NOREF(pvBits);
+
+ /*
+ * Resolve defaults.
+ */
+ kldrModMachOAdjustBaseAddress(pThis, &BaseAddress);
+
+ /*
+ * Take action according to file type.
+ */
+ int rc;
+ if ( pThis->Hdr.filetype == MH_OBJECT
+ || pThis->Hdr.filetype == MH_EXECUTE /** @todo dylib, execute, dsym: symbols */
+ || pThis->Hdr.filetype == MH_DYLIB
+ || pThis->Hdr.filetype == MH_BUNDLE
+ || pThis->Hdr.filetype == MH_DSYM
+ || pThis->Hdr.filetype == MH_KEXT_BUNDLE)
+ {
+ rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ rc = kldrModMachODoEnumSymbols32Bit(pThis, (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols,
+ pThis->pchStrings, pThis->cchStrings, BaseAddress,
+ fFlags, pfnCallback, pvUser);
+ else
+ rc = kldrModMachODoEnumSymbols64Bit(pThis, (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols,
+ pThis->pchStrings, pThis->cchStrings, BaseAddress,
+ fFlags, pfnCallback, pvUser);
+ }
+ }
+ else
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+
+ return rc;
+}
+
+
+/**
+ * Enum a 32-bit symbol table.
+ *
+ * @returns IPRT status code.
+ * @param pThis
+ * @param paSyms Pointer to the symbol table.
+ * @param cSyms Number of symbols in the table.
+ * @param pchStrings Pointer to the string table.
+ * @param cchStrings Size of the string table.
+ * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols.
+ * @param fFlags See kLdrModEnumSymbols.
+ * @param pfnCallback See kLdrModEnumSymbols.
+ * @param pvUser See kLdrModEnumSymbols.
+ */
+static int kldrModMachODoEnumSymbols32Bit(PRTLDRMODMACHO pThis, const macho_nlist_32_t *paSyms, uint32_t cSyms,
+ const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
+ uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ const uint32_t fKindBase = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ ? RTLDRSYMKIND_32BIT : RTLDRSYMKIND_64BIT;
+ uint32_t iSym;
+ int rc;
+
+ /*
+ * Iterate the symbol table.
+ */
+ for (iSym = 0; iSym < cSyms; iSym++)
+ {
+ uint32_t fKind;
+ RTLDRADDR uValue;
+ const char *psz;
+ size_t cch;
+
+ /* Skip debug symbols and undefined symbols. */
+ if (paSyms[iSym].n_type & MACHO_N_STAB)
+ continue;
+ if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ continue;
+
+ /* Skip non-public symbols unless they are requested explicitly. */
+ if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_ALL))
+ {
+ if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
+ continue;
+ if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
+ continue;
+ if (!paSyms[iSym].n_un.n_strx)
+ continue;
+ }
+
+ /*
+ * Gather symbol info
+ */
+
+ /* name */
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
+ psz = &pchStrings[paSyms[iSym].n_un.n_strx];
+ cch = strlen(psz);
+ if (!cch)
+ psz = NULL;
+
+ /* kind & value */
+ fKind = fKindBase;
+ if (paSyms[iSym].n_desc & N_WEAK_DEF)
+ fKind |= RTLDRSYMKIND_WEAK;
+ switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSym].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
+
+ uValue = paSyms[iSym].n_value - pSect->LinkAddress;
+ RTLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
+ || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
+ && uValue == 0U - pSect->RVA
+ && pThis->uEffFileType != MH_OBJECT),
+ VERR_LDRMACHO_BAD_SYMBOL);
+ uValue += BaseAddress + pSect->RVA;
+
+ if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
+ fKind |= RTLDRSYMKIND_CODE;
+ else
+ fKind |= RTLDRSYMKIND_NO_TYPE;
+ break;
+ }
+
+ case MACHO_N_ABS:
+ uValue = paSyms[iSym].n_value;
+ fKind |= RTLDRSYMKIND_NO_TYPE /*RTLDRSYMKIND_ABS*/;
+ break;
+
+ case MACHO_N_PBUD:
+ case MACHO_N_INDR:
+ /** @todo implement indirect and prebound symbols. */
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ }
+
+ /*
+ * Do callback.
+ */
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (cch > 1 && *psz == '_')
+ psz++;
+ rc = pfnCallback(&pThis->Core, psz, iSym, uValue/*, fKind*/, pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Enum a 64-bit symbol table.
+ *
+ * @returns IPRT status code.
+ * @param pThis
+ * @param paSyms Pointer to the symbol table.
+ * @param cSyms Number of symbols in the table.
+ * @param pchStrings Pointer to the string table.
+ * @param cchStrings Size of the string table.
+ * @param BaseAddress Adjusted base address, see kLdrModEnumSymbols.
+ * @param fFlags See kLdrModEnumSymbols.
+ * @param pfnCallback See kLdrModEnumSymbols.
+ * @param pvUser See kLdrModEnumSymbols.
+ */
+static int kldrModMachODoEnumSymbols64Bit(PRTLDRMODMACHO pThis, const macho_nlist_64_t *paSyms, uint32_t cSyms,
+ const char *pchStrings, uint32_t cchStrings, RTLDRADDR BaseAddress,
+ uint32_t fFlags, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ const uint32_t fKindBase = pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE
+ ? RTLDRSYMKIND_64BIT : RTLDRSYMKIND_32BIT;
+ uint32_t iSym;
+ int rc;
+
+ /*
+ * Iterate the symbol table.
+ */
+ for (iSym = 0; iSym < cSyms; iSym++)
+ {
+ uint32_t fKind;
+ RTLDRADDR uValue;
+ const char *psz;
+ size_t cch;
+
+ /* Skip debug symbols and undefined symbols. */
+ if (paSyms[iSym].n_type & MACHO_N_STAB)
+ continue;
+ if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ continue;
+
+ /* Skip non-public symbols unless they are requested explicitly. */
+ if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_ALL))
+ {
+ if (!(paSyms[iSym].n_type & MACHO_N_EXT)) /*??*/
+ continue;
+ if (paSyms[iSym].n_type & MACHO_N_PEXT) /*??*/
+ continue;
+ if (!paSyms[iSym].n_un.n_strx)
+ continue;
+ }
+
+ /*
+ * Gather symbol info
+ */
+
+ /* name */
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_un.n_strx < cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
+ psz = &pchStrings[paSyms[iSym].n_un.n_strx];
+ cch = strlen(psz);
+ if (!cch)
+ psz = NULL;
+
+ /* kind & value */
+ fKind = fKindBase;
+ if (paSyms[iSym].n_desc & N_WEAK_DEF)
+ fKind |= RTLDRSYMKIND_WEAK;
+ switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)(paSyms[iSym].n_sect - 1) < pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
+
+ uValue = paSyms[iSym].n_value - pSect->LinkAddress;
+ RTLDRMODMACHO_CHECK_RETURN( uValue <= pSect->cb
+ || ( paSyms[iSym].n_sect == 1 /* special hack for __mh_execute_header */
+ && uValue == 0U - pSect->RVA
+ && pThis->uEffFileType != MH_OBJECT),
+ VERR_LDRMACHO_BAD_SYMBOL);
+ uValue += BaseAddress + pSect->RVA;
+
+ if (pSect->fFlags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE))
+ fKind |= RTLDRSYMKIND_CODE;
+ else
+ fKind |= RTLDRSYMKIND_NO_TYPE;
+ break;
+ }
+
+ case MACHO_N_ABS:
+ uValue = paSyms[iSym].n_value;
+ fKind |= RTLDRSYMKIND_NO_TYPE /*RTLDRSYMKIND_ABS*/;
+ break;
+
+ case MACHO_N_PBUD:
+ case MACHO_N_INDR:
+ /** @todo implement indirect and prebound symbols. */
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ }
+
+ /*
+ * Do callback.
+ */
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (cch > 1 && *psz == '_')
+ psz++;
+ rc = pfnCallback(&pThis->Core, psz, iSym, uValue/*, fKind*/, pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+#if 0
+
+/** @copydoc kLdrModGetImport */
+static int kldrModMachOGetImport(PRTLDRMODINTERNAL pMod, const void *pvBits, uint32_t iImport, char *pszName, size_t cchName)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ RT_NOREF(pvBits);
+ RT_NOREF(iImport);
+ RT_NOREF(pszName);
+ RT_NOREF(cchName);
+
+ if (pThis->Hdr.filetype == MH_OBJECT)
+ return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+
+ /* later */
+ return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS;
+}
+
+
+
+/** @copydoc kLdrModNumberOfImports */
+static int32_t kldrModMachONumberOfImports(PRTLDRMODINTERNAL pMod, const void *pvBits)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ RT_NOREF(pvBits);
+
+ if (pThis->Hdr.filetype == MH_OBJECT)
+ return VINF_SUCCESS;
+
+ /* later */
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModGetStackInfo */
+static int kldrModMachOGetStackInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo)
+{
+ /*PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);*/
+ RT_NOREF(pMod);
+ RT_NOREF(pvBits);
+ RT_NOREF(BaseAddress);
+
+ pStackInfo->Address = NIL_RTLDRADDR;
+ pStackInfo->LinkAddress = NIL_RTLDRADDR;
+ pStackInfo->cbStack = pStackInfo->cbStackThread = 0;
+ /* later */
+
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModQueryMainEntrypoint */
+static int kldrModMachOQueryMainEntrypoint(PRTLDRMODINTERNAL pMod, const void *pvBits, RTLDRADDR BaseAddress, PRTLDRADDR pMainEPAddress)
+{
+#if 0
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ int rc;
+
+ /*
+ * Resolve base address alias if any.
+ */
+ rc = kldrModMachOBitsAndBaseAddress(pThis, NULL, &BaseAddress);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Convert the address from the header.
+ */
+ *pMainEPAddress = pThis->Hdrs.OptionalHeader.AddressOfEntryPoint
+ ? BaseAddress + pThis->Hdrs.OptionalHeader.AddressOfEntryPoint
+ : NIL_RTLDRADDR;
+#else
+ *pMainEPAddress = NIL_RTLDRADDR;
+ RT_NOREF(pvBits);
+ RT_NOREF(BaseAddress);
+ RT_NOREF(pMod);
+#endif
+ return VINF_SUCCESS;
+}
+
+#endif
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnEnumDbgInfo}
+ */
+static DECLCALLBACK(int) rtldrMachO_EnumDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, PFNRTLDRENUMDBG pfnCallback, void *pvUser)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ int rc = VINF_SUCCESS;
+ uint32_t iSect;
+ RT_NOREF(pvBits);
+
+ for (iSect = 0; iSect < pThis->cSections; iSect++)
+ {
+ /* (32-bit & 64-bit starts the same way) */
+ section_32_t *pMachOSect = (section_32_t *)pThis->paSections[iSect].pvMachoSection;
+ char szTmp[sizeof(pMachOSect->sectname) + 1];
+
+ if (strcmp(pMachOSect->segname, "__DWARF"))
+ continue;
+
+ memcpy(szTmp, pMachOSect->sectname, sizeof(pMachOSect->sectname));
+ szTmp[sizeof(pMachOSect->sectname)] = '\0';
+
+ RTLDRDBGINFO DbgInfo;
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_DWARF;
+ DbgInfo.iDbgInfo = iSect;
+ DbgInfo.LinkAddress = pThis->paSections[iSect].LinkAddress;
+ DbgInfo.cb = pThis->paSections[iSect].cb;
+ DbgInfo.pszExtFile = NULL;
+ DbgInfo.u.Dwarf.pszSection = szTmp;
+ rc = pfnCallback(&pThis->Core, &DbgInfo, pvUser);
+ if (rc != VINF_SUCCESS)
+ break;
+ }
+
+ return rc;
+}
+
+#if 0
+
+/** @copydoc kLdrModHasDbgInfo */
+static int kldrModMachOHasDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits)
+{
+ /*PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);*/
+
+#if 0
+ /*
+ * Base this entirely on the presence of a debug directory.
+ */
+ if ( pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size
+ < sizeof(IMAGE_DEBUG_DIRECTORY) /* screw borland linkers */
+ || !pThis->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)
+ return KLDR_ERR_NO_DEBUG_INFO;
+ return VINF_SUCCESS;
+#else
+ RT_NOREF(pMod);
+ RT_NOREF(pvBits);
+ return VERR_LDR_NO_DEBUG_INFO;
+#endif
+}
+
+
+/** @copydoc kLdrModMap */
+static int kldrModMachOMap(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ unsigned fFixed;
+ uint32_t i;
+ void *pvBase;
+ int rc;
+
+ if (!pThis->fCanLoad)
+ return VERR_LDRMACHO_TODO;
+
+ /*
+ * Already mapped?
+ */
+ if (pThis->pvMapping)
+ return KLDR_ERR_ALREADY_MAPPED;
+
+ /*
+ * Map it.
+ */
+ /* fixed image? */
+ fFixed = pMod->enmType == KLDRTYPE_EXECUTABLE_FIXED
+ || pMod->enmType == KLDRTYPE_SHARED_LIBRARY_FIXED;
+ if (!fFixed)
+ pvBase = NULL;
+ else
+ {
+ pvBase = (void *)(uintptr_t)pMod->aSegments[0].LinkAddress;
+ if ((uintptr_t)pvBase != pMod->aSegments[0].LinkAddress)
+ return VERR_LDR_ADDRESS_OVERFLOW;
+ }
+
+ /* try do the prepare */
+ rc = kRdrMap(pMod->pRdr, &pvBase, pMod->cSegments, pMod->aSegments, fFixed);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Update the segments with their map addresses.
+ */
+ for (i = 0; i < pMod->cSegments; i++)
+ {
+ if (pMod->aSegments[i].RVA != NIL_RTLDRADDR)
+ pMod->aSegments[i].MapAddress = (uintptr_t)pvBase + (uintptr_t)pMod->aSegments[i].RVA;
+ }
+ pThis->pvMapping = pvBase;
+
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModUnmap */
+static int kldrModMachOUnmap(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ uint32_t i;
+ int rc;
+
+ /*
+ * Mapped?
+ */
+ if (!pThis->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /*
+ * Try unmap the image.
+ */
+ rc = kRdrUnmap(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Update the segments to reflect that they aren't mapped any longer.
+ */
+ pThis->pvMapping = NULL;
+ for (i = 0; i < pMod->cSegments; i++)
+ pMod->aSegments[i].MapAddress = 0;
+
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModAllocTLS */
+static int kldrModMachOAllocTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+
+ /*
+ * Mapped?
+ */
+ if ( pvMapping == KLDRMOD_INT_MAP
+ && !pThis->pvMapping )
+ return KLDR_ERR_NOT_MAPPED;
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModFreeTLS */
+static void kldrModMachOFreeTLS(PRTLDRMODINTERNAL pMod, void *pvMapping)
+{
+ RT_NOREF(pMod);
+ RT_NOREF(pvMapping);
+}
+
+
+
+/** @copydoc kLdrModReload */
+static int kldrModMachOReload(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+
+ /*
+ * Mapped?
+ */
+ if (!pThis->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /* the file provider does it all */
+ return kRdrRefresh(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments);
+}
+
+
+/** @copydoc kLdrModFixupMapping */
+static int kldrModMachOFixupMapping(PRTLDRMODINTERNAL pMod, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ int rc, rc2;
+
+ /*
+ * Mapped?
+ */
+ if (!pThis->pvMapping)
+ return KLDR_ERR_NOT_MAPPED;
+
+ /*
+ * Before doing anything we'll have to make all pages writable.
+ */
+ rc = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 1 /* unprotect */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Resolve imports and apply base relocations.
+ */
+ rc = rtldrMachO_RelocateBits(pMod, pThis->pvMapping, (uintptr_t)pThis->pvMapping, pThis->LinkAddress,
+ pfnGetImport, pvUser);
+
+ /*
+ * Restore protection.
+ */
+ rc2 = kRdrProtect(pMod->pRdr, pThis->pvMapping, pMod->cSegments, pMod->aSegments, 0 /* protect */);
+ if (RT_SUCCESS(rc) && RT_FAILURE(rc2)
+ rc = rc2;
+ return rc;
+}
+
+#endif
+
+
+/**
+ * Worker for resolving an undefined 32-bit symbol table entry.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pSym The symbol table entry.
+ * @param BaseAddress The module base address.
+ * @param pfnGetImport The callback for resolving an imported symbol.
+ * @param pvUser User argument to the callback.
+ */
+DECLINLINE(int) rtdlrModMachOHandleUndefinedSymbol32(PRTLDRMODMACHO pThis, macho_nlist_32_t *pSym,
+ RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ RTLDRADDR Value = NIL_RTLDRADDR;
+
+ /** @todo Implement N_REF_TO_WEAK. */
+ RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
+
+ /* Get the symbol name. */
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
+ const char *pszSymbol = &pThis->pchStrings[pSym->n_un.n_strx];
+ size_t cchSymbol = strlen(pszSymbol);
+
+ /* Check for linker defined symbols relating to sections and segments. */
+ int rc;
+ if ( cchSymbol <= sizeof("section$end$") - 1
+ || *pszSymbol != 's'
+ || memchr(pszSymbol, '$', cchSymbol) == NULL)
+ rc = VERR_SYMBOL_NOT_FOUND;
+ else
+ rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
+
+ /* Ask the user for an address to the symbol. */
+ //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (RT_FAILURE_NP(rc))
+ rc = pfnGetImport(&pThis->Core, NULL /*pszModule*/, pszSymbol + (pszSymbol[0] == '_'),
+ UINT32_MAX, &Value/*, &fKind*/, pvUser);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ /* If weak reference we can continue, otherwise fail? */
+ else if (pSym->n_desc & N_WEAK_REF)
+ Value = 0;
+ else
+ return rc;
+
+ /* Update the symbol. */
+ pSym->n_value = (uint32_t)Value;
+ if (pSym->n_value == Value)
+ return VINF_SUCCESS;
+ return VERR_LDR_ADDRESS_OVERFLOW;
+}
+
+
+/**
+ * Worker for resolving an undefined 64-bit symbol table entry.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pSym The symbol table entry.
+ * @param BaseAddress The module base address.
+ * @param pfnGetImport The callback for resolving an imported symbol.
+ * @param pvUser User argument to the callback.
+ */
+DECLINLINE(int) rtdlrModMachOHandleUndefinedSymbol64(PRTLDRMODMACHO pThis, macho_nlist_64_t *pSym,
+ RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ RTLDRADDR Value = NIL_RTLDRADDR;
+
+ /** @todo Implement N_REF_TO_WEAK. */
+ RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_desc & N_REF_TO_WEAK), VERR_LDRMACHO_TODO);
+
+ /* Get the symbol name. */
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_un.n_strx < pThis->cchStrings, VERR_LDRMACHO_BAD_SYMBOL);
+ const char *pszSymbol = &pThis->pchStrings[pSym->n_un.n_strx];
+ size_t cchSymbol = strlen(pszSymbol);
+
+ /* Check for linker defined symbols relating to sections and segments. */
+ int rc;
+ if ( cchSymbol <= sizeof("section$end$") - 1
+ || *pszSymbol != 's'
+ || memchr(pszSymbol, '$', cchSymbol) == NULL)
+ rc = VERR_SYMBOL_NOT_FOUND;
+ else
+ rc = kldrModMachOQueryLinkerSymbol(pThis, pszSymbol, cchSymbol, BaseAddress, &Value);
+
+ /* Ask the user for an address to the symbol. */
+ //uint32_t fKind = RTLDRSYMKIND_REQ_FLAT;
+ /** @todo figure out a better way to deal with underscore prefixes. sigh. */
+ if (RT_FAILURE_NP(rc))
+ rc = pfnGetImport(&pThis->Core, NULL /*pszModule*/, pszSymbol + (pszSymbol[0] == '_'),
+ UINT32_MAX, &Value/*, &fKind*/, pvUser);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ /* If weak reference we can continue, otherwise fail? */
+ else if (pSym->n_desc & N_WEAK_REF)
+ Value = 0;
+ else
+ return rc;
+
+ /* Update the symbol. */
+ pSym->n_value = (uint64_t)Value;
+ if (pSym->n_value == Value)
+ return VINF_SUCCESS;
+ return VERR_LDR_ADDRESS_OVERFLOW;
+}
+
+
+/**
+ * MH_OBJECT: Resolves undefined symbols (imports).
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param BaseAddress The module base address.
+ * @param pfnGetImport The callback for resolving an imported symbol.
+ * @param pvUser User argument to the callback.
+ */
+static int kldrModMachOObjDoImports(PRTLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+
+ /*
+ * Ensure that we've got the symbol table.
+ */
+ int rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Iterate the symbol table and resolve undefined symbols.
+ * We currently ignore REFERENCE_TYPE.
+ */
+ const uint32_t cSyms = pThis->cSymbols;
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ macho_nlist_32_t *paSyms = (macho_nlist_32_t *)pThis->pvaSymbols;
+ for (uint32_t iSym = 0; iSym < cSyms; iSym++)
+ {
+ /* skip stabs */
+ if (paSyms[iSym].n_type & MACHO_N_STAB)
+ continue;
+
+ if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ {
+ rc = rtdlrModMachOHandleUndefinedSymbol32(pThis, &paSyms[iSym], BaseAddress, pfnGetImport, pvUser);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else if (paSyms[iSym].n_desc & N_WEAK_DEF)
+ {
+ /** @todo implement weak symbols. */
+ /*return VERR_LDRMACHO_TODO; - ignored for now. */
+ }
+ }
+ }
+ else
+ {
+ /* (Identical to the 32-bit code, just different paSym type. (and n_strx is unsigned)) */
+ macho_nlist_64_t *paSyms = (macho_nlist_64_t *)pThis->pvaSymbols;
+ for (uint32_t iSym = 0; iSym < cSyms; iSym++)
+ {
+ /* skip stabs */
+ if (paSyms[iSym].n_type & MACHO_N_STAB)
+ continue;
+
+ if ((paSyms[iSym].n_type & MACHO_N_TYPE) == MACHO_N_UNDF)
+ {
+ rc = rtdlrModMachOHandleUndefinedSymbol64(pThis, &paSyms[iSym], BaseAddress, pfnGetImport, pvUser);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else if (paSyms[iSym].n_desc & N_WEAK_DEF)
+ {
+ /** @todo implement weak symbols. */
+ /*return VERR_LDRMACHO_TODO; - ignored for now. */
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Dylib: Resolves undefined symbols (imports).
+ *
+ * This is conceptually identically to kldrModMachOObjDoImports, only
+ * LC_DYSYMTAB helps us avoid working over the whole symbol table.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param BaseAddress The module base address.
+ * @param pfnGetImport The callback for resolving an imported symbol.
+ * @param pvUser User argument to the callback.
+ */
+static int kldrModMachODylibDoImports(PRTLDRMODMACHO pThis, RTLDRADDR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ /*
+ * There must be a LC_DYSYMTAB.
+ * We might be lucky, though, and not have any imports.
+ */
+ dysymtab_command_t const *pDySymTab = pThis->pDySymTab;
+ AssertReturn(pDySymTab, VERR_INTERNAL_ERROR_2);
+ if (pDySymTab->nundefsym == 0)
+ return VINF_SUCCESS;
+
+ /*
+ * Ensure that we've got the symbol table.
+ */
+ int rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Iterate the give symbol table section containing undefined symbols and resolve them.
+ */
+ uint32_t const cSyms = pDySymTab->iundefsym + pDySymTab->nundefsym;
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ macho_nlist_32_t *paSyms = (macho_nlist_32_t *)pThis->pvaSymbols;
+ for (uint32_t iSym = pDySymTab->iundefsym; RT_SUCCESS(rc) && iSym < cSyms; iSym++)
+ {
+ AssertContinue((paSyms[iSym].n_type & (MACHO_N_TYPE | MACHO_N_STAB)) == MACHO_N_UNDF);
+ rc = rtdlrModMachOHandleUndefinedSymbol32(pThis, &paSyms[iSym], BaseAddress, pfnGetImport, pvUser);
+ }
+ }
+ else
+ {
+ /* (Identical to the 32-bit code, just different paSym type. (and n_strx is unsigned)) */
+ macho_nlist_64_t *paSyms = (macho_nlist_64_t *)pThis->pvaSymbols;
+ for (uint32_t iSym = pDySymTab->iundefsym; RT_SUCCESS(rc) && iSym < cSyms; iSym++)
+ {
+ AssertContinue((paSyms[iSym].n_type & (MACHO_N_TYPE | MACHO_N_STAB)) == MACHO_N_UNDF);
+ rc = rtdlrModMachOHandleUndefinedSymbol64(pThis, &paSyms[iSym], BaseAddress, pfnGetImport, pvUser);
+ }
+ }
+
+ return rc;
+}
+
+
+static int kldrModMachODylibDoIndirectSymbols(PRTLDRMODMACHO pThis, void *pvBits, RTLDRADDR offDelta)
+{
+ /*
+ * There must be a LC_DYSYMTAB.
+ * We might be lucky, though, and not have any imports.
+ */
+ dysymtab_command_t const *pDySymTab = pThis->pDySymTab;
+ AssertReturn(pDySymTab, VERR_INTERNAL_ERROR_2);
+ uint32_t const cIndirectSymbols = pDySymTab->nindirectsymb;
+ if (cIndirectSymbols == 0)
+ return VINF_SUCCESS;
+
+ /*
+ * Ensure that we've got the symbol table.
+ */
+ int rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Load the indirect symbol table.
+ */
+ if (!pThis->paidxIndirectSymbols)
+ {
+ uint32_t *paidxIndirectSymbols = (uint32_t *)RTMemAlloc(cIndirectSymbols * sizeof(uint32_t));
+ if (!paidxIndirectSymbols)
+ return VERR_NO_MEMORY;
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paidxIndirectSymbols, cIndirectSymbols * sizeof(uint32_t),
+ pDySymTab->indirectsymboff);
+ if (RT_SUCCESS(rc))
+ pThis->paidxIndirectSymbols = paidxIndirectSymbols;
+ else
+ {
+ RTMemFree(paidxIndirectSymbols);
+ return rc;
+ }
+
+ /* Byte swap if needed. */
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ for (uint32_t i = 0; i < cIndirectSymbols; i++)
+ paidxIndirectSymbols[i] = RT_BSWAP_U32(paidxIndirectSymbols[i]);
+ }
+ uint32_t const *paidxIndirectSymbols = pThis->paidxIndirectSymbols;
+
+ /*
+ * Process the sections using indirect symbols.
+ */
+ const uint32_t cSymbols = pThis->cSymbols;
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ macho_nlist_32_t const *paSymbols = (macho_nlist_32_t *)pThis->pvaSymbols;
+ for (uint32_t iSect = 0; iSect < pThis->cSections; iSect++)
+ {
+ section_32_t const *pSect = (section_32_t const *)pThis->paSections[iSect].pvMachoSection;
+ switch (pSect->flags & SECTION_TYPE)
+ {
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ case S_LAZY_SYMBOL_POINTERS:
+ {
+ uint32_t *pauDstPtrs = (uint32_t *)((uintptr_t)pvBits + (uintptr_t)pThis->paSections[iSect].RVA);
+ uint32_t const cDstPtrs = pThis->paSections[iSect].cb / sizeof(pauDstPtrs[0]);
+ uint32_t const idxSrcSkip = pSect->reserved1;
+ if ((uint64_t)idxSrcSkip + cDstPtrs > cIndirectSymbols)
+ return VERR_BAD_EXE_FORMAT; /// @todo better error code.
+
+ for (uint32_t i = 0; i < cDstPtrs; i++)
+ {
+ uint32_t const idxSym = paidxIndirectSymbols[idxSrcSkip + i];
+ if (idxSym == INDIRECT_SYMBOL_LOCAL)
+ pauDstPtrs[i] += (int32_t)offDelta;
+ else if (idxSym != INDIRECT_SYMBOL_ABS)
+ {
+ AssertMsgReturn(idxSym < cSymbols,
+ ("i=%#x idxSym=%#x cSymbols=%#x iSect=%#x\n", i, idxSym, cSymbols, iSect),
+ VERR_BAD_EXE_FORMAT); /// @todo better error code.
+ pauDstPtrs[i] = paSymbols[idxSym].n_value;
+ }
+ }
+ break;
+ }
+
+ case S_SYMBOL_STUBS:
+ if ( pThis->Core.enmArch == RTLDRARCH_X86_32
+ && (pSect->flags & S_ATTR_SELF_MODIFYING_CODE)
+ && pSect->reserved2 == 5)
+ {
+ uint32_t uDstRva = pThis->paSections[iSect].RVA;
+ uint8_t *pbDst = (uint8_t *)((uintptr_t)pvBits + uDstRva);
+ uint32_t const cDstPtrs = pThis->paSections[iSect].cb / 5;
+ uint32_t const idxSrcSkip = pSect->reserved1;
+ if ((uint64_t)idxSrcSkip + cDstPtrs > cIndirectSymbols)
+ return VERR_BAD_EXE_FORMAT; /// @todo better error code.
+
+ for (uint32_t i = 0; i < cDstPtrs; i++, uDstRva += 5, pbDst += 5)
+ {
+ uint32_t const idxSym = paidxIndirectSymbols[idxSrcSkip + i];
+ if (idxSym != INDIRECT_SYMBOL_ABS && idxSym != INDIRECT_SYMBOL_LOCAL)
+ {
+ AssertMsgReturn(idxSym < cSymbols,
+ ("i=%#x idxSym=%#x cSymbols=%#x iSect=%#x\n", i, idxSym, cSymbols, iSect),
+ VERR_BAD_EXE_FORMAT); /// @todo better error code.
+ pbDst[0] = 0xeb; /* JMP rel32 */
+ uint32_t offDisp = paSymbols[idxSym].n_value - (uint32_t)uDstRva - 5;
+ pbDst[1] = (uint8_t)offDisp;
+ offDisp >>= 8;
+ pbDst[2] = (uint8_t)offDisp;
+ offDisp >>= 8;
+ pbDst[3] = (uint8_t)offDisp;
+ offDisp >>= 8;
+ pbDst[4] = (uint8_t)offDisp;
+ }
+ }
+ break;
+ }
+ break;
+ }
+
+ }
+ }
+ else
+ {
+ /* Exact like for 32-bit, except for 64-bit symbol table, 64-bit addresses and no need to process S_SYMBOL_STUBS. */
+ macho_nlist_64_t const *paSymbols = (macho_nlist_64_t *)pThis->pvaSymbols;
+ for (uint32_t iSect = 0; iSect < pThis->cSections; iSect++)
+ {
+ section_64_t const *pSect = (section_64_t const *)pThis->paSections[iSect].pvMachoSection;
+ switch (pSect->flags & SECTION_TYPE)
+ {
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ case S_LAZY_SYMBOL_POINTERS:
+ {
+ uint64_t *pauDstPtrs = (uint64_t *)((uintptr_t)pvBits + (uintptr_t)pThis->paSections[iSect].RVA);
+ uint32_t const cDstPtrs = pThis->paSections[iSect].cb / sizeof(pauDstPtrs[0]);
+ uint32_t const idxSrcSkip = pSect->reserved1;
+ if ((uint64_t)idxSrcSkip + cDstPtrs > cIndirectSymbols)
+ return VERR_BAD_EXE_FORMAT; /// @todo better error code.
+
+ for (uint32_t i = 0; i < cDstPtrs; i++)
+ {
+ uint32_t const idxSym = paidxIndirectSymbols[idxSrcSkip + i];
+ if (idxSym == INDIRECT_SYMBOL_LOCAL)
+ pauDstPtrs[i] += (int64_t)offDelta;
+ else if (idxSym != INDIRECT_SYMBOL_ABS)
+ {
+ AssertMsgReturn(idxSym < cSymbols,
+ ("i=%#x idxSym=%#x cSymbols=%#x iSect=%#x\n", i, idxSym, cSymbols, iSect),
+ VERR_BAD_EXE_FORMAT); /// @todo better error code.
+ pauDstPtrs[i] = paSymbols[idxSym].n_value;
+ }
+ }
+ break;
+ }
+
+ case S_SYMBOL_STUBS:
+ if ( pThis->Core.enmArch == RTLDRARCH_X86_32
+ && (pSect->flags & S_ATTR_SELF_MODIFYING_CODE)
+ && pSect->reserved2 == 5)
+ return VERR_BAD_EXE_FORMAT;
+ break;
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * MH_OBJECT: Applies base relocations to an (unprotected) image mapping.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pvMapping The mapping to fixup.
+ * @param NewBaseAddress The address to fixup the mapping to.
+ */
+static int kldrModMachOObjDoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress)
+{
+ /*
+ * Ensure that we've got the symbol table.
+ */
+ int rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Iterate over the segments and their sections and apply fixups.
+ */
+ rc = VINF_SUCCESS;
+ for (uint32_t iSeg = 0; RT_SUCCESS(rc) && iSeg < pThis->cSegments; iSeg++)
+ {
+ PRTLDRMODMACHOSEG pSeg = &pThis->aSegments[iSeg];
+ for (uint32_t iSect = 0; iSect < pSeg->cSections; iSect++)
+ {
+ PRTLDRMODMACHOSECT pSect = &pSeg->paSections[iSect];
+
+ /* skip sections without fixups. */
+ if (!pSect->cFixups)
+ continue;
+ AssertReturn(pSect->paFixups, VERR_INTERNAL_ERROR_4);
+ AssertReturn(pSect->pauFixupVirginData, VERR_INTERNAL_ERROR_4);
+
+ /*
+ * Apply the fixups.
+ */
+ uint8_t *pbSectBits = (uint8_t *)pvMapping + (uintptr_t)pSect->RVA;
+ if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE) /** @todo this aint right. */
+ rc = kldrModMachOApplyFixupsGeneric32Bit(pThis, pbSectBits, (size_t)pSect->cb, pSect->RVA, pSect->LinkAddress,
+ pSect->paFixups, pSect->cFixups, pSect->pauFixupVirginData,
+ (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols, NewBaseAddress);
+ else if ( pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
+ && pThis->Hdr.cputype == CPU_TYPE_X86_64)
+ rc = kldrModMachOApplyFixupsAMD64(pThis, pbSectBits, (size_t)pSect->cb, pSect->RVA,
+ pSect->paFixups, pSect->cFixups, pSect->pauFixupVirginData,
+ (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols, NewBaseAddress);
+ else
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Dylib: Applies base relocations to an (unprotected) image mapping.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pvMapping The mapping to fixup.
+ * @param NewBaseAddress The address to fixup the mapping to.
+ */
+static int kldrModMachODylibDoFixups(PRTLDRMODMACHO pThis, void *pvMapping, RTLDRADDR NewBaseAddress)
+{
+ /*
+ * There must be a LC_DYSYMTAB.
+ * We might be lucky, though, and not have any imports.
+ */
+ dysymtab_command_t const *pDySymTab = pThis->pDySymTab;
+ AssertReturn(pDySymTab, VERR_INTERNAL_ERROR_2);
+ uint32_t cRelocations = pDySymTab->nlocrel + pDySymTab->nextrel;
+ if (cRelocations == 0)
+ return VINF_SUCCESS;
+
+ /*
+ * Ensure that we've got the symbol table.
+ */
+ int rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Load the relocations if needed.
+ */
+ macho_relocation_union_t const *paRelocations = pThis->paRelocations;
+ if (!paRelocations)
+ {
+ uint32_t *paRawRelocs = (uint32_t *)RTMemAlloc(cRelocations * sizeof(macho_relocation_union_t));
+ if (!paRawRelocs)
+ return VERR_NO_MEMORY;
+ if (pDySymTab->nextrel)
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paRawRelocs, pDySymTab->nextrel * sizeof(macho_relocation_union_t),
+ pDySymTab->extreloff);
+ if (pDySymTab->nlocrel && RT_SUCCESS(rc))
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader,
+ (uint8_t *)paRawRelocs + pDySymTab->nextrel * sizeof(macho_relocation_union_t),
+ pDySymTab->nlocrel * sizeof(macho_relocation_union_t), pDySymTab->locreloff);
+ if (RT_SUCCESS(rc))
+ pThis->paRelocations = (macho_relocation_union_t *)paRawRelocs;
+ else
+ {
+ RTMemFree(paRawRelocs);
+ return rc;
+ }
+
+ /* Byte swap if needed. */
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ {
+ for (uint32_t i = 0; i < cRelocations; i++)
+ {
+ paRawRelocs[i * 2] = RT_BSWAP_U32(paRawRelocs[i * 2]);
+ paRawRelocs[i * 2 + 1] = RT_BSWAP_U32(paRawRelocs[i * 2 + 1]);
+ }
+ ASMCompilerBarrier();
+ }
+
+ paRelocations = pThis->paRelocations;
+ }
+
+ /*
+ * Apply the fixups.
+ */
+ if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE) /** @todo this aint right. */
+ return kldrModMachOApplyFixupsGeneric32Bit(pThis, (uint8_t *)pvMapping, (size_t)pThis->cbImage, 0, pThis->LinkAddress,
+ paRelocations, cRelocations, pThis->pauRelocationsVirginData,
+ (macho_nlist_32_t *)pThis->pvaSymbols, pThis->cSymbols, NewBaseAddress);
+ if ( pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE
+ && pThis->Hdr.cputype == CPU_TYPE_X86_64)
+ return kldrModMachOApplyFixupsAMD64(pThis, (uint8_t *)pvMapping, (size_t)pThis->cbImage, 0,
+ paRelocations, cRelocations, pThis->pauRelocationsVirginData,
+ (macho_nlist_64_t *)pThis->pvaSymbols, pThis->cSymbols, NewBaseAddress);
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+}
+
+
+/**
+ * Applies generic fixups to a section in an image of the same endian-ness
+ * as the host CPU.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pbBits Pointer to the bits to fix up.
+ * @param cbBits Size of the bits to fix up.
+ * @param uBitsRva The RVA of the bits.
+ * @param uBitsLinkAddr The link address of the bits.
+ * @param paFixups The fixups.
+ * @param cFixups Number of fixups.
+ * @param pauVirginData The virgin data / addends. Parallel to paFixups.
+ * @param paSyms Pointer to the symbol table.
+ * @param cSyms Number of symbols.
+ * @param NewBaseAddress The new base image address.
+ */
+static int kldrModMachOApplyFixupsGeneric32Bit(PRTLDRMODMACHO pThis, uint8_t *pbBits, size_t cbBits, RTLDRADDR uBitsRva,
+ RTLDRADDR uBitsLinkAddr, const macho_relocation_union_t *paFixups,
+ const uint32_t cFixups, PCRTUINT64U const pauVirginData,
+ macho_nlist_32_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
+{
+ /*
+ * Iterate the fixups and apply them.
+ */
+ for (uint32_t iFixup = 0; iFixup < cFixups; iFixup++)
+ {
+ macho_relocation_union_t Fixup = paFixups[iFixup];
+ RTLDRADDR SymAddr = ~(RTLDRADDR)0;
+ RTPTRUNION uFix;
+
+ if (!(Fixup.r.r_address & R_SCATTERED))
+ {
+ /* sanity */
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)Fixup.r.r_address + RT_BIT_32(Fixup.r.r_length) <= cbBits, VERR_LDR_BAD_FIXUP);
+
+ /* Calc the fixup address. */
+ uFix.pv = pbBits + Fixup.r.r_address;
+
+ /*
+ * Calc the symbol value.
+ */
+ /* Calc the linked symbol address / addend. */
+ switch (Fixup.r.r_length)
+ {
+ case 0: SymAddr = (int8_t)pauVirginData[iFixup].au8[0]; break;
+ case 1: SymAddr = (int16_t)pauVirginData[iFixup].au16[0]; break;
+ case 2: SymAddr = (int32_t)pauVirginData[iFixup].au32[0]; break;
+ case 3: SymAddr = (int64_t)pauVirginData[iFixup].u; break;
+ default: RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+ if (Fixup.r.r_pcrel)
+ SymAddr += Fixup.r.r_address + uBitsLinkAddr;
+
+ /* Add symbol / section address. */
+ if (Fixup.r.r_extern)
+ {
+ const macho_nlist_32_t *pSym;
+ if (Fixup.r.r_symbolnum >= cSyms)
+ return VERR_LDR_BAD_FIXUP;
+ pSym = &paSyms[Fixup.r.r_symbolnum];
+
+ if (pSym->n_type & MACHO_N_STAB)
+ return VERR_LDR_BAD_FIXUP;
+
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSymSect = &pThis->paSections[pSym->n_sect - 1];
+
+ SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ break;
+ }
+
+ case MACHO_N_UNDF:
+ case MACHO_N_ABS:
+ SymAddr += pSym->n_value;
+ break;
+
+ case MACHO_N_INDR:
+ case MACHO_N_PBUD:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
+ }
+ }
+ else if (Fixup.r.r_symbolnum != R_ABS)
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ if (Fixup.r.r_symbolnum > pThis->cSections)
+ return VERR_LDR_BAD_FIXUP;
+ pSymSect = &pThis->paSections[Fixup.r.r_symbolnum - 1];
+
+ SymAddr -= pSymSect->LinkAddress;
+ SymAddr += pSymSect->RVA + NewBaseAddress;
+ }
+
+ /* adjust for PC relative */
+ if (Fixup.r.r_pcrel)
+ SymAddr -= Fixup.r.r_address + uBitsRva + NewBaseAddress;
+ }
+ else
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ uint32_t iSymSect;
+ RTLDRADDR Value;
+
+ /* sanity */
+ RTLDRMODMACHO_ASSERT(Fixup.s.r_scattered);
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)Fixup.s.r_address + RT_BIT_32(Fixup.s.r_length) <= cbBits, VERR_LDR_BAD_FIXUP);
+
+ /* Calc the fixup address. */
+ uFix.pv = pbBits + Fixup.s.r_address;
+
+ /*
+ * Calc the symbol value.
+ */
+ /* The addend is stored in the code. */
+ switch (Fixup.s.r_length)
+ {
+ case 0: SymAddr = (int8_t)pauVirginData[iFixup].au8[0]; break;
+ case 1: SymAddr = (int16_t)pauVirginData[iFixup].au16[0]; break;
+ case 2: SymAddr = (int32_t)pauVirginData[iFixup].au32[0]; break;
+ case 3: SymAddr = (int64_t)pauVirginData[iFixup].u; break;
+ default: RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+ if (Fixup.s.r_pcrel)
+ SymAddr += Fixup.s.r_address;
+ Value = Fixup.s.r_value;
+ SymAddr -= Value; /* (-> addend only) */
+
+ /* Find the section number from the r_value. */
+ pSymSect = NULL;
+ for (iSymSect = 0; iSymSect < pThis->cSections; iSymSect++)
+ {
+ RTLDRADDR off = Value - pThis->paSections[iSymSect].LinkAddress;
+ if (off < pThis->paSections[iSymSect].cb)
+ {
+ pSymSect = &pThis->paSections[iSymSect];
+ break;
+ }
+ else if (off == pThis->paSections[iSymSect].cb) /* edge case */
+ pSymSect = &pThis->paSections[iSymSect];
+ }
+ if (!pSymSect)
+ return VERR_LDR_BAD_FIXUP;
+
+ /* Calc the symbol address. */
+ SymAddr += Value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ if (Fixup.s.r_pcrel)
+ SymAddr -= Fixup.s.r_address + uBitsRva + NewBaseAddress;
+
+ Fixup.r.r_length = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_length;
+ Fixup.r.r_type = ((scattered_relocation_info_t *)&paFixups[iFixup])->r_type;
+ }
+
+ /*
+ * Write back the fixed up value.
+ */
+ if (Fixup.r.r_type == GENERIC_RELOC_VANILLA)
+ {
+ switch (Fixup.r.r_length)
+ {
+ case 0: *uFix.pu8 = (uint8_t)SymAddr; break;
+ case 1: *uFix.pu16 = (uint16_t)SymAddr; break;
+ case 2: *uFix.pu32 = (uint32_t)SymAddr; break;
+ case 3: *uFix.pu64 = (uint64_t)SymAddr; break;
+ }
+ }
+ else if (Fixup.r.r_type <= GENERIC_RELOC_LOCAL_SECTDIFF)
+ return VERR_LDRMACHO_UNSUPPORTED_FIXUP_TYPE;
+ else
+ return VERR_LDR_BAD_FIXUP;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Applies AMD64 fixups to a section.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pbBits Pointer to the section bits.
+ * @param cbBits Size of the bits to fix up.
+ * @param uBitsRva The RVA of the bits.
+ * @param paFixups The fixups.
+ * @param cFixups Number of fixups.
+ * @param pauVirginData The virgin data / addends. Parallel to paFixups.
+ * @param paSyms Pointer to the symbol table.
+ * @param cSyms Number of symbols.
+ * @param NewBaseAddress The new base image address.
+ */
+static int kldrModMachOApplyFixupsAMD64(PRTLDRMODMACHO pThis, uint8_t *pbBits, size_t cbBits, RTLDRADDR uBitsRva,
+ const macho_relocation_union_t *paFixups,
+ const uint32_t cFixups, PCRTUINT64U const pauVirginData,
+ macho_nlist_64_t *paSyms, uint32_t cSyms, RTLDRADDR NewBaseAddress)
+{
+ /*
+ * Iterate the fixups and apply them.
+ */
+ for (uint32_t iFixup = 0; iFixup < cFixups; iFixup++)
+ {
+ macho_relocation_union_t Fixup = paFixups[iFixup];
+
+ /* AMD64 doesn't use scattered fixups. */
+ RTLDRMODMACHO_CHECK_RETURN(!(Fixup.r.r_address & R_SCATTERED), VERR_LDR_BAD_FIXUP);
+
+ /* sanity */
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)Fixup.r.r_address + RT_BIT_32(Fixup.r.r_length) <= cbBits, VERR_LDR_BAD_FIXUP);
+
+ /* calc fixup addresses. */
+ RTPTRUNION uFix;
+ uFix.pv = pbBits + Fixup.r.r_address;
+
+ /*
+ * Calc the symbol value.
+ */
+ /* Calc the linked symbol address / addend. */
+ RTLDRADDR SymAddr;
+ switch (Fixup.r.r_length)
+ {
+ case 2: SymAddr = (int32_t)pauVirginData[iFixup].au32[0]; break;
+ case 3: SymAddr = (int64_t)pauVirginData[iFixup].u; break;
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+
+ /* Add symbol / section address. */
+ if (Fixup.r.r_extern)
+ {
+ const macho_nlist_64_t *pSym;
+
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
+ pSym = &paSyms[Fixup.r.r_symbolnum];
+ RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
+
+ switch (Fixup.r.r_type)
+ {
+ /* GOT references just needs to have their symbol verified.
+ Later, we'll optimize GOT building here using a parallel sym->got array. */
+ case X86_64_RELOC_GOT_LOAD:
+ case X86_64_RELOC_GOT:
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ case MACHO_N_UNDF:
+ case MACHO_N_ABS:
+ break;
+ case MACHO_N_INDR:
+ case MACHO_N_PBUD:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
+ }
+ SymAddr = sizeof(uint64_t) * Fixup.r.r_symbolnum + pThis->GotRVA + NewBaseAddress;
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_BAD_FIXUP);
+ SymAddr -= 4;
+ break;
+
+ /* Verify the r_pcrel field for signed fixups on the way into the default case. */
+ case X86_64_RELOC_BRANCH:
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
+ RT_FALL_THRU();
+ default:
+ {
+ /* Adjust with fixup specific addend and verify unsigned/r_pcrel. */
+ switch (Fixup.r.r_type)
+ {
+ case X86_64_RELOC_UNSIGNED:
+ RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
+ break;
+ case X86_64_RELOC_BRANCH:
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_length == 2, VERR_LDR_BAD_FIXUP);
+ SymAddr -= 4;
+ break;
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ SymAddr -= 4;
+ break;
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSymSect = &pThis->paSections[pSym->n_sect - 1];
+ SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ break;
+ }
+
+ case MACHO_N_UNDF:
+ /* branch to an external symbol may have to take a short detour. */
+ if ( Fixup.r.r_type == X86_64_RELOC_BRANCH
+ && SymAddr + Fixup.r.r_address + uBitsRva + NewBaseAddress
+ - pSym->n_value
+ + UINT64_C(0x80000000)
+ >= UINT64_C(0xffffff20))
+ {
+ RTLDRMODMACHO_CHECK_RETURN(pThis->JmpStubsRVA != NIL_RTLDRADDR, VERR_LDR_ADDRESS_OVERFLOW);
+ SymAddr += pThis->cbJmpStub * Fixup.r.r_symbolnum + pThis->JmpStubsRVA + NewBaseAddress;
+ }
+ else
+ SymAddr += pSym->n_value;
+ break;
+
+ case MACHO_N_ABS:
+ SymAddr += pSym->n_value;
+ break;
+
+ case MACHO_N_INDR:
+ case MACHO_N_PBUD:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
+ }
+ break;
+ }
+
+ /*
+ * This is a weird customer, it will always be follows by an UNSIGNED fixup.
+ * The value is calculated: target - pair_target.
+ * Note! The linker generally eliminate these when linking modules rather
+ * than objects (-r).
+ */
+ case X86_64_RELOC_SUBTRACTOR:
+ {
+ /* Deal with the SUBTRACT symbol first, by subtracting it from SymAddr. */
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSymSect = &pThis->paSections[pSym->n_sect - 1];
+ SymAddr -= pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ break;
+ }
+
+ case MACHO_N_UNDF:
+ case MACHO_N_ABS:
+ SymAddr -= pSym->n_value;
+ break;
+
+ case MACHO_N_INDR:
+ case MACHO_N_PBUD:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
+ }
+
+ /* Load the 2nd fixup, check sanity. */
+ iFixup++;
+ RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel && iFixup < cFixups, VERR_LDR_BAD_FIXUP);
+ macho_relocation_info_t const Fixup2 = paFixups[iFixup].r;
+ RTLDRMODMACHO_CHECK_RETURN( Fixup2.r_address == Fixup.r.r_address
+ && Fixup2.r_length == Fixup.r.r_length
+ && Fixup2.r_type == X86_64_RELOC_UNSIGNED
+ && !Fixup2.r_pcrel
+ && Fixup2.r_symbolnum < cSyms,
+ VERR_LDR_BAD_FIXUP);
+
+ if (Fixup2.r_extern)
+ {
+ RTLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum < cSyms, VERR_LDR_BAD_FIXUP);
+ pSym = &paSyms[Fixup2.r_symbolnum];
+ RTLDRMODMACHO_CHECK_RETURN(!(pSym->n_type & MACHO_N_STAB), VERR_LDR_BAD_FIXUP);
+
+ /* Add its value to SymAddr. */
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)pSym->n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSymSect = &pThis->paSections[pSym->n_sect - 1];
+ SymAddr += pSym->n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ break;
+ }
+
+ case MACHO_N_UNDF:
+ case MACHO_N_ABS:
+ SymAddr += pSym->n_value;
+ break;
+
+ case MACHO_N_INDR:
+ case MACHO_N_PBUD:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_BAD_SYMBOL);
+ }
+ }
+ else if (Fixup2.r_symbolnum != R_ABS)
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN(Fixup2.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
+ pSymSect = &pThis->paSections[Fixup2.r_symbolnum - 1];
+ SymAddr += pSymSect->RVA - pSymSect->LinkAddress + NewBaseAddress;
+ }
+ else
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+ break;
+ }
+ }
+ else
+ {
+ /* verify against fixup type and make adjustments */
+ switch (Fixup.r.r_type)
+ {
+ case X86_64_RELOC_UNSIGNED:
+ RTLDRMODMACHO_CHECK_RETURN(!Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
+ break;
+ case X86_64_RELOC_BRANCH:
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
+ SymAddr += 4; /* dunno what the assmbler/linker really is doing here... */
+ break;
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel, VERR_LDR_BAD_FIXUP);
+ break;
+ /*case X86_64_RELOC_GOT_LOAD:*/
+ /*case X86_64_RELOC_GOT: */
+ /*case X86_64_RELOC_SUBTRACTOR: - must be r_extern=1 says as. */
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+ if (Fixup.r.r_symbolnum != R_ABS)
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_symbolnum <= pThis->cSections, VERR_LDR_BAD_FIXUP);
+ pSymSect = &pThis->paSections[Fixup.r.r_symbolnum - 1];
+
+ SymAddr -= pSymSect->LinkAddress;
+ SymAddr += pSymSect->RVA + NewBaseAddress;
+ if (Fixup.r.r_pcrel)
+ SymAddr += Fixup.r.r_address;
+ }
+ }
+
+ /* adjust for PC relative */
+ if (Fixup.r.r_pcrel)
+ SymAddr -= Fixup.r.r_address + uBitsRva + NewBaseAddress;
+
+ /*
+ * Write back the fixed up value.
+ */
+ switch (Fixup.r.r_length)
+ {
+ case 3:
+ *uFix.pu64 = (uint64_t)SymAddr;
+ break;
+ case 2:
+ RTLDRMODMACHO_CHECK_RETURN(Fixup.r.r_pcrel || Fixup.r.r_type == X86_64_RELOC_SUBTRACTOR, VERR_LDR_BAD_FIXUP);
+ RTLDRMODMACHO_CHECK_RETURN((int32_t)SymAddr == (int64_t)SymAddr, VERR_LDR_ADDRESS_OVERFLOW);
+ *uFix.pu32 = (uint32_t)SymAddr;
+ break;
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Loads the symbol table (LC_SYMTAB).
+ *
+ * The symbol table is pointed to by RTLDRMODMACHO::pvaSymbols.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ */
+static int kldrModMachOLoadObjSymTab(PRTLDRMODMACHO pThis)
+{
+ int rc = VINF_SUCCESS;
+
+ if ( !pThis->pvaSymbols
+ && pThis->cSymbols)
+ {
+ size_t cbSyms;
+ size_t cbSym;
+ void *pvSyms;
+ void *pvStrings;
+
+ /* sanity */
+ RTLDRMODMACHO_CHECK_RETURN( pThis->offSymbols
+ && (!pThis->cchStrings || pThis->offStrings),
+ VERR_LDRMACHO_BAD_OBJECT_FILE);
+
+ /* allocate */
+ cbSym = pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ ? sizeof(macho_nlist_32_t)
+ : sizeof(macho_nlist_64_t);
+ cbSyms = pThis->cSymbols * cbSym;
+ RTLDRMODMACHO_CHECK_RETURN(cbSyms / cbSym == pThis->cSymbols, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
+ rc = VERR_NO_MEMORY;
+ pvSyms = RTMemAlloc(cbSyms);
+ if (pvSyms)
+ {
+ if (pThis->cchStrings)
+ pvStrings = RTMemAlloc(pThis->cchStrings);
+ else
+ pvStrings = RTMemAllocZ(4);
+ if (pvStrings)
+ {
+ /* read */
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvSyms, cbSyms, pThis->offSymbols);
+ if (RT_SUCCESS(rc) && pThis->cchStrings)
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvStrings, pThis->cchStrings, pThis->offStrings);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pvaSymbols = pvSyms;
+ pThis->pchStrings = (char *)pvStrings;
+
+ /* perform endian conversion? */
+ if (pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ uint32_t cLeft = pThis->cSymbols;
+ macho_nlist_32_t *pSym = (macho_nlist_32_t *)pvSyms;
+ while (cLeft-- > 0)
+ {
+ pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
+ pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
+ pSym->n_value = RT_BSWAP_U32(pSym->n_value);
+ pSym++;
+ }
+ }
+ else if (pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ {
+ uint32_t cLeft = pThis->cSymbols;
+ macho_nlist_64_t *pSym = (macho_nlist_64_t *)pvSyms;
+ while (cLeft-- > 0)
+ {
+ pSym->n_un.n_strx = RT_BSWAP_U32(pSym->n_un.n_strx);
+ pSym->n_desc = (int16_t)RT_BSWAP_U16(pSym->n_desc);
+ pSym->n_value = RT_BSWAP_U64(pSym->n_value);
+ pSym++;
+ }
+ }
+
+ return VINF_SUCCESS;
+ }
+ RTMemFree(pvStrings);
+ }
+ RTMemFree(pvSyms);
+ }
+ }
+ else
+ RTLDRMODMACHO_ASSERT(pThis->pchStrings || pThis->Hdr.filetype == MH_DSYM);
+
+ return rc;
+}
+
+
+/**
+ * Loads the fixups at the given address and performs endian
+ * conversion if necessary.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param offFixups The file offset of the fixups.
+ * @param cFixups The number of fixups to load.
+ * @param ppaFixups Where to put the pointer to the allocated fixup array.
+ */
+static int kldrModMachOLoadFixups(PRTLDRMODMACHO pThis, RTFOFF offFixups, uint32_t cFixups, macho_relocation_union_t **ppaFixups)
+{
+ macho_relocation_union_t *paFixups;
+ size_t cbFixups;
+
+ /* allocate the memory. */
+ cbFixups = cFixups * sizeof(*paFixups);
+ RTLDRMODMACHO_CHECK_RETURN(cbFixups / sizeof(*paFixups) == cFixups, VERR_LDRMACHO_BAD_SYMTAB_SIZE);
+ paFixups = (macho_relocation_union_t *)RTMemAlloc(cbFixups);
+ if (!paFixups)
+ return VERR_NO_MEMORY;
+
+ /* read the fixups. */
+ int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paFixups, cbFixups, offFixups);
+ if (RT_SUCCESS(rc))
+ {
+ *ppaFixups = paFixups;
+
+ /* do endian conversion if necessary. */
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ {
+ uint32_t iFixup;
+ for (iFixup = 0; iFixup < cFixups; iFixup++)
+ {
+ uint32_t *pu32 = (uint32_t *)&paFixups[iFixup];
+ pu32[0] = RT_BSWAP_U32(pu32[0]);
+ pu32[1] = RT_BSWAP_U32(pu32[1]);
+ }
+ }
+ }
+ else
+ RTMemFree(paFixups);
+ return rc;
+}
+
+
+/**
+ * Loads virgin data (addends) for an array of fixups.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pbBits The virgin bits to lift the data from
+ * @param cbBits The number of virgin bytes.
+ * @param paFixups The fixups.
+ * @param cFixups Number of fixups
+ * @param pszName Name for logging.
+ * @param ppauVirginData Where to return the virgin data.
+ */
+static int rtldrMachOLoadVirginData(PRTLDRMODMACHO pThis, uint8_t const *pbBits, size_t cbBits,
+ macho_relocation_union_t const *paFixups, uint32_t cFixups, const char *pszName,
+ PRTUINT64U *ppauVirginData)
+{
+ /*
+ * In case we jettisoned the fixups, we will leave virgin data.
+ */
+ if (*ppauVirginData)
+ return VINF_SUCCESS;
+
+#ifdef LOG_ENABLED
+ /*
+ * Ensure that we've got the symbol table if we're logging fixups.
+ */
+ if (LogIs5Enabled())
+ {
+ int rc = kldrModMachOLoadObjSymTab(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+#endif
+
+
+ /*
+ * Allocate memory and iterate the fixups to get the data.
+ */
+ PRTUINT64U pauVirginData = *ppauVirginData = (PRTUINT64U)RTMemAllocZ(sizeof(uint64_t) * cFixups);
+ if (pauVirginData)
+ {
+ Log5(("Fixups for %s: (%u)\n", pszName, cFixups));
+ for (uint32_t i = 0; i < cFixups; i++)
+ {
+ uint32_t off;
+ uint32_t cShift;
+ if (!paFixups[i].s.r_scattered)
+ {
+ off = paFixups[i].r.r_address;
+ cShift = paFixups[i].r.r_length;
+ }
+ else
+ {
+ off = paFixups[i].s.r_address;
+ cShift = paFixups[i].s.r_length;
+ }
+ RTLDRMODMACHO_CHECK_RETURN(off + RT_BIT_32(cShift) <= cbBits, VERR_LDR_BAD_FIXUP);
+
+ /** @todo This ASSUMES same endian in the image and on the host. Would need
+ * to check target cpu (pThis->Core.enmArch) endianness against host to get
+ * it right... (outside the loop, obviously) */
+ switch (cShift)
+ {
+ case 3:
+ pauVirginData[i].u = RT_MAKE_U64_FROM_U8(pbBits[off], pbBits[off + 1], pbBits[off + 2], pbBits[off + 3],
+ pbBits[off + 4], pbBits[off + 5], pbBits[off + 6], pbBits[off + 7]);
+ break;
+ case 2:
+ pauVirginData[i].u = (int32_t)RT_MAKE_U32_FROM_U8(pbBits[off], pbBits[off + 1], pbBits[off + 2], pbBits[off + 3]);
+ break;
+ case 1:
+ pauVirginData[i].u = (int16_t)RT_MAKE_U16(pbBits[off], pbBits[off + 1]);
+ break;
+ case 0:
+ pauVirginData[i].u = (int8_t)pbBits[off];
+ break;
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDR_BAD_FIXUP);
+ }
+
+#ifdef LOG_ENABLED
+ if (LogIs5Enabled())
+ {
+ if (!paFixups[i].s.r_scattered)
+ {
+ Log5((" #%06x: %#08x LB %u: t=%#x pc=%u ex=%u sym=%#010x add=%#RX64\n",
+ i, off, RT_BIT_32(cShift), paFixups[i].r.r_type, paFixups[i].r.r_pcrel, paFixups[i].r.r_extern,
+ paFixups[i].r.r_symbolnum, pauVirginData[i].u));
+ if (paFixups[i].r.r_symbolnum < pThis->cSymbols)
+ {
+ if (pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ {
+ macho_nlist_64_t const *pSym = (macho_nlist_64_t const *)pThis->pvaSymbols + paFixups[i].r.r_symbolnum;
+ Log5((" sym: %#04x:%#018RX64 t=%#04x d=%#06x %s\n",
+ pSym->n_sect, pSym->n_value, pSym->n_type, pSym->n_desc,
+ pSym->n_un.n_strx < pThis->cchStrings ? &pThis->pchStrings[pSym->n_un.n_strx] : ""));
+
+ }
+ else
+ {
+ macho_nlist_32_t const *pSym = (macho_nlist_32_t const *)pThis->pvaSymbols + paFixups[i].r.r_symbolnum;
+ Log5((" sym: %#04x:%#010RX32 t=%#04x d=%#06x %s\n",
+ pSym->n_sect, pSym->n_value, pSym->n_type, pSym->n_desc,
+ (uint32_t)pSym->n_un.n_strx < pThis->cchStrings ? &pThis->pchStrings[pSym->n_un.n_strx] : ""));
+ }
+ }
+ }
+ else
+ Log5((" #%06x: %#08x LB %u: t=%#x pc=%u val=%#010x add=%#RX64\n", i, off, RT_BIT_32(cShift),
+ paFixups[i].s.r_type, paFixups[i].s.r_pcrel, paFixups[i].s.r_value, pauVirginData[i].u));
+ }
+#endif
+ }
+ return VINF_SUCCESS;
+ }
+ RT_NOREF(pThis, pszName);
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * MH_OBJECT: Loads fixups and addends for each section.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pbBits The image bits. First time we're called, these are
+ * ASSUMED to be in virgin state and suitable for
+ * saving addends.
+ */
+static int rtldrMachOObjLoadFixupsAndVirginData(PRTLDRMODMACHO pThis, uint8_t const *pbBits)
+{
+ PRTLDRMODMACHOSECT pSect = pThis->paSections;
+ for (uint32_t i = 0; i < pThis->cSections; i++, pSect++)
+ if ( !pSect->paFixups
+ && pSect->cFixups > 0)
+ {
+ /*
+ * Load and endian convert the fixups.
+ */
+ int rc = kldrModMachOLoadFixups(pThis, pSect->offFixups, pSect->cFixups, &pSect->paFixups);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Save virgin data (addends) for each fixup.
+ */
+ rc = rtldrMachOLoadVirginData(pThis, &pbBits[(size_t)pSect->RVA], (size_t)pSect->cb, pSect->paFixups, pSect->cFixups,
+ pThis->aSegments[pSect->iSegment].SegInfo.pszName, &pSect->pauFixupVirginData);
+ if (RT_SUCCESS(rc))
+ continue;
+
+ RTMemFree(pSect->pauFixupVirginData);
+ pSect->pauFixupVirginData = NULL;
+ RTMemFree(pSect->paFixups);
+ pSect->paFixups = NULL;
+ }
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Dylib: Loads fixups and addends.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module interpreter instance.
+ * @param pbBits The image bits. First time we're called, these are
+ * ASSUMED to be in virgin state and suitable for
+ * saving addends.
+ */
+static int rtldrMachODylibLoadFixupsAndVirginData(PRTLDRMODMACHO pThis, uint8_t const *pbBits)
+{
+ /*
+ * Don't do it again if we already loaded them.
+ */
+ if (pThis->paRelocations)
+ {
+ Assert(pThis->pauRelocationsVirginData);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * There must be a LC_DYSYMTAB. Fixups are optionals.
+ */
+ dysymtab_command_t const *pDySymTab = pThis->pDySymTab;
+ AssertReturn(pDySymTab, VERR_INTERNAL_ERROR_2);
+ uint32_t cRelocations = pDySymTab->nlocrel + pDySymTab->nextrel;
+ if (cRelocations == 0)
+ return VINF_SUCCESS;
+
+ /*
+ * Load fixups.
+ */
+ int rc = VINF_SUCCESS;
+ uint32_t *paRawRelocs = (uint32_t *)RTMemAlloc(cRelocations * sizeof(macho_relocation_union_t));
+ if (paRawRelocs)
+ {
+ pThis->paRelocations = (macho_relocation_union_t *)paRawRelocs;
+
+ if (pDySymTab->nextrel)
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, paRawRelocs,
+ pDySymTab->nextrel * sizeof(macho_relocation_union_t), pDySymTab->extreloff);
+ if (pDySymTab->nlocrel && RT_SUCCESS(rc))
+ rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader,
+ (uint8_t *)paRawRelocs + pDySymTab->nextrel * sizeof(macho_relocation_union_t),
+ pDySymTab->nlocrel * sizeof(macho_relocation_union_t), pDySymTab->locreloff);
+ if (RT_SUCCESS(rc))
+ {
+ /* Byte swap if needed. */
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pThis->Hdr.magic == IMAGE_MACHO64_SIGNATURE_OE)
+ {
+ for (uint32_t i = 0; i < cRelocations; i++)
+ {
+ paRawRelocs[i * 2] = RT_BSWAP_U32(paRawRelocs[i * 2]);
+ paRawRelocs[i * 2 + 1] = RT_BSWAP_U32(paRawRelocs[i * 2 + 1]);
+ }
+ ASMCompilerBarrier();
+ }
+
+ /*
+ * Load virgin data (addends).
+ */
+ rc = rtldrMachOLoadVirginData(pThis, pbBits, (size_t)pThis->cbImage, pThis->paRelocations, cRelocations,
+ "whole-image", &pThis->pauRelocationsVirginData);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ RTMemFree(pThis->pauRelocationsVirginData);
+ pThis->pauRelocationsVirginData = NULL;
+ }
+ RTMemFree(pThis->paRelocations);
+ pThis->paRelocations = NULL;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+#if 0
+
+/** @copydoc kLdrModCallInit */
+static int kldrModMachOCallInit(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
+{
+ /* later */
+ RT_NOREF(pMod);
+ RT_NOREF(pvMapping);
+ RT_NOREF(uHandle);
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModCallTerm */
+static int kldrModMachOCallTerm(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle)
+{
+ /* later */
+ RT_NOREF(pMod);
+ RT_NOREF(pvMapping);
+ RT_NOREF(uHandle);
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc kLdrModCallThread */
+static int kldrModMachOCallThread(PRTLDRMODINTERNAL pMod, void *pvMapping, uintptr_t uHandle, unsigned fAttachingOrDetaching)
+{
+ /* Relevant for Mach-O? */
+ RT_NOREF(pMod);
+ RT_NOREF(pvMapping);
+ RT_NOREF(uHandle);
+ RT_NOREF(fAttachingOrDetaching);
+ return VINF_SUCCESS;
+}
+
+#endif
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnGetImageSize}
+ */
+static DECLCALLBACK(size_t) rtldrMachO_GetImageSize(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ return pThis->cbImage;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnGetBits}
+ */
+static DECLCALLBACK(int) rtldrMachO_GetBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR BaseAddress,
+ PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+
+ if (!pThis->fCanLoad)
+ return VERR_LDRMACHO_TODO;
+
+ /*
+ * Zero the entire buffer first to simplify things.
+ */
+ memset(pvBits, 0, (size_t)pThis->cbImage);
+
+ /*
+ * When possible use the segment table to load the data.
+ */
+ for (uint32_t i = 0; i < pThis->cSegments; i++)
+ {
+ /* skip it? */
+ if ( pThis->aSegments[i].SegInfo.cbFile == -1
+ || pThis->aSegments[i].SegInfo.offFile == -1
+ || pThis->aSegments[i].SegInfo.RVA == NIL_RTLDRADDR
+ || pThis->aSegments[i].SegInfo.cbMapped == 0
+ || !pThis->aSegments[i].SegInfo.Alignment)
+ continue;
+ int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader,
+ (uint8_t *)pvBits + pThis->aSegments[i].SegInfo.RVA,
+ pThis->aSegments[i].SegInfo.cbFile,
+ pThis->aSegments[i].SegInfo.offFile);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Perform relocations.
+ */
+ return rtldrMachO_RelocateBits(pMod, pvBits, BaseAddress, pThis->LinkAddress, pfnGetImport, pvUser);
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnRelocate}
+ */
+static DECLCALLBACK(int) rtldrMachO_RelocateBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress,
+ RTUINTPTR OldBaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ int rc;
+
+ /*
+ * Call workers to do the jobs.
+ */
+ if (pThis->Hdr.filetype == MH_OBJECT)
+ {
+ rc = rtldrMachOObjLoadFixupsAndVirginData(pThis, (uint8_t const *)pvBits);
+ if (RT_SUCCESS(rc))
+ rc = kldrModMachOObjDoImports(pThis, NewBaseAddress, pfnGetImport, pvUser);
+ if (RT_SUCCESS(rc))
+ rc = kldrModMachOObjDoFixups(pThis, pvBits, NewBaseAddress);
+
+ }
+ else
+ {
+ rc = rtldrMachODylibLoadFixupsAndVirginData(pThis, (uint8_t const *)pvBits);
+ if (RT_SUCCESS(rc))
+ rc = kldrModMachODylibDoImports(pThis, NewBaseAddress, pfnGetImport, pvUser);
+ if (RT_SUCCESS(rc))
+ rc = kldrModMachODylibDoIndirectSymbols(pThis, pvBits, NewBaseAddress - OldBaseAddress);
+ if (RT_SUCCESS(rc))
+ rc = kldrModMachODylibDoFixups(pThis, pvBits, NewBaseAddress);
+ }
+
+ /*
+ * Construct the global offset table if necessary, it's always the last
+ * segment when present.
+ */
+ if (RT_SUCCESS(rc) && pThis->fMakeGot)
+ rc = kldrModMachOMakeGOT(pThis, pvBits, NewBaseAddress);
+
+ return rc;
+}
+
+
+/**
+ * Builds the GOT.
+ *
+ * Assumes the symbol table has all external symbols resolved correctly and that
+ * the bits has been cleared up front.
+ */
+static int kldrModMachOMakeGOT(PRTLDRMODMACHO pThis, void *pvBits, RTLDRADDR NewBaseAddress)
+{
+ uint32_t iSym = pThis->cSymbols;
+ if ( pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE
+ || pThis->Hdr.magic == IMAGE_MACHO32_SIGNATURE_OE)
+ {
+ macho_nlist_32_t const *paSyms = (macho_nlist_32_t const *)pThis->pvaSymbols;
+ uint32_t *paGOT = (uint32_t *)((uint8_t *)pvBits + pThis->GotRVA);
+ while (iSym-- > 0)
+ switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
+ paGOT[iSym] = (uint32_t)(paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress);
+ break;
+ }
+
+ case MACHO_N_UNDF:
+ case MACHO_N_ABS:
+ paGOT[iSym] = paSyms[iSym].n_value;
+ break;
+ }
+ }
+ else
+ {
+ macho_nlist_64_t const *paSyms = (macho_nlist_64_t const *)pThis->pvaSymbols;
+ uint64_t *paGOT = (uint64_t *)((uint8_t *)pvBits + pThis->GotRVA);
+ while (iSym-- > 0)
+ {
+ switch (paSyms[iSym].n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ {
+ PRTLDRMODMACHOSECT pSymSect;
+ RTLDRMODMACHO_CHECK_RETURN((uint32_t)paSyms[iSym].n_sect - 1 <= pThis->cSections, VERR_LDRMACHO_BAD_SYMBOL);
+ pSymSect = &pThis->paSections[paSyms[iSym].n_sect - 1];
+ paGOT[iSym] = paSyms[iSym].n_value - pSymSect->LinkAddress + pSymSect->RVA + NewBaseAddress;
+ break;
+ }
+
+ case MACHO_N_UNDF:
+ case MACHO_N_ABS:
+ paGOT[iSym] = paSyms[iSym].n_value;
+ break;
+ }
+ }
+
+ if (pThis->JmpStubsRVA != NIL_RTLDRADDR)
+ {
+ iSym = pThis->cSymbols;
+ switch (pThis->Hdr.cputype)
+ {
+ /*
+ * AMD64 is simple since the GOT and the indirect jmps are parallel
+ * arrays with entries of the same size. The relative offset will
+ * be the the same for each entry, kind of nice. :-)
+ */
+ case CPU_TYPE_X86_64:
+ {
+ uint64_t *paJmps = (uint64_t *)((uint8_t *)pvBits + pThis->JmpStubsRVA);
+ int32_t off;
+ uint64_t u64Tmpl;
+ union
+ {
+ uint8_t ab[8];
+ uint64_t u64;
+ } Tmpl;
+
+ /* create the template. */
+ off = (int32_t)(pThis->GotRVA - (pThis->JmpStubsRVA + 6));
+ Tmpl.ab[0] = 0xff; /* jmp [GOT-entry wrt RIP] */
+ Tmpl.ab[1] = 0x25;
+ Tmpl.ab[2] = off & 0xff;
+ Tmpl.ab[3] = (off >> 8) & 0xff;
+ Tmpl.ab[4] = (off >> 16) & 0xff;
+ Tmpl.ab[5] = (off >> 24) & 0xff;
+ Tmpl.ab[6] = 0xcc;
+ Tmpl.ab[7] = 0xcc;
+ u64Tmpl = Tmpl.u64;
+
+ /* copy the template to every jmp table entry. */
+ while (iSym-- > 0)
+ paJmps[iSym] = u64Tmpl;
+ break;
+ }
+
+ default:
+ RTLDRMODMACHO_FAILED_RETURN(VERR_LDRMACHO_TODO);
+ }
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnEnumSegments}
+ */
+static DECLCALLBACK(int) rtldrMachO_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ uint32_t const cSegments = pThis->cSegments;
+ for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
+ {
+ int rc = pfnCallback(pMod, &pThis->aSegments[iSeg].SegInfo, pvUser);
+ if (rc != VINF_SUCCESS)
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnLinkAddressToSegOffset}
+ */
+static DECLCALLBACK(int) rtldrMachO_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
+ uint32_t *piSeg, PRTLDRADDR poffSeg)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ uint32_t const cSegments = pThis->cSegments;
+ for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
+ if (pThis->aSegments[iSeg].SegInfo.RVA != NIL_RTLDRADDR)
+ {
+ Assert(pThis->aSegments[iSeg].SegInfo.cbMapped != NIL_RTLDRADDR);
+ RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
+ if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
+ || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
+ {
+ *piSeg = iSeg;
+ *poffSeg = offSeg;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_LDR_INVALID_LINK_ADDRESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnLinkAddressToRva}
+ */
+static DECLCALLBACK(int) rtldrMachO_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ uint32_t const cSegments = pThis->cSegments;
+ for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
+ if (pThis->aSegments[iSeg].SegInfo.RVA != NIL_RTLDRADDR)
+ {
+ Assert(pThis->aSegments[iSeg].SegInfo.cbMapped != NIL_RTLDRADDR);
+ RTLDRADDR offSeg = LinkAddress - pThis->aSegments[iSeg].SegInfo.LinkAddress;
+ if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
+ || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
+ {
+ *pRva = pThis->aSegments[iSeg].SegInfo.RVA + offSeg;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_LDR_INVALID_RVA;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnSegOffsetToRva}
+ */
+static DECLCALLBACK(int) rtldrMachO_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg, PRTLDRADDR pRva)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+
+ if (iSeg >= pThis->cSegments)
+ return VERR_LDR_INVALID_SEG_OFFSET;
+ RTLDRMODMACHOSEG const *pSegment = &pThis->aSegments[iSeg];
+
+ if (pSegment->SegInfo.RVA == NIL_RTLDRADDR)
+ return VERR_LDR_INVALID_SEG_OFFSET;
+
+ if ( offSeg > pSegment->SegInfo.cbMapped
+ && offSeg > pSegment->SegInfo.cb
+ && ( pSegment->SegInfo.cbFile < 0
+ || offSeg > (uint64_t)pSegment->SegInfo.cbFile))
+ return VERR_LDR_INVALID_SEG_OFFSET;
+
+ *pRva = pSegment->SegInfo.RVA + offSeg;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnRvaToSegOffset}
+ */
+static DECLCALLBACK(int) rtldrMachO_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva, uint32_t *piSeg, PRTLDRADDR poffSeg)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ uint32_t const cSegments = pThis->cSegments;
+ for (uint32_t iSeg = 0; iSeg < cSegments; iSeg++)
+ if (pThis->aSegments[iSeg].SegInfo.RVA != NIL_RTLDRADDR)
+ {
+ Assert(pThis->aSegments[iSeg].SegInfo.cbMapped != NIL_RTLDRADDR);
+ RTLDRADDR offSeg = Rva - pThis->aSegments[iSeg].SegInfo.RVA;
+ if ( offSeg < pThis->aSegments[iSeg].SegInfo.cbMapped
+ || offSeg < pThis->aSegments[iSeg].SegInfo.cb)
+ {
+ *piSeg = iSeg;
+ *poffSeg = offSeg;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_LDR_INVALID_RVA;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnReadDbgInfo}
+ */
+static DECLCALLBACK(int) rtldrMachO_ReadDbgInfo(PRTLDRMODINTERNAL pMod, uint32_t iDbgInfo, RTFOFF off, size_t cb, void *pvBuf)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+
+ /** @todo May have to apply fixups here. */
+ if (iDbgInfo < pThis->cSections)
+ return pThis->Core.pReader->pfnRead(pThis->Core.pReader, pvBuf, cb, off);
+ return VERR_OUT_OF_RANGE;
+}
+
+
+/**
+ * Loads the code signing blob if necessary (RTLDRMODMACHO::PtrCodeSignature).
+ *
+ * @returns IPRT status code.
+ * @param pThis The mach-o instance.
+ */
+static int rtldrMachO_LoadSignatureBlob(PRTLDRMODMACHO pThis)
+{
+ Assert(pThis->cbCodeSignature > 0);
+ if (pThis->PtrCodeSignature.pb != NULL)
+ return VINF_SUCCESS;
+
+ if ( pThis->cbCodeSignature > sizeof(RTCRAPLCSHDR)
+ && pThis->cbCodeSignature <= _1M)
+ {
+ /* Allocate and read. */
+ void *pv = RTMemAllocZ(RT_ALIGN_Z(pThis->cbCodeSignature, 16));
+ AssertReturn(pv, VERR_NO_MEMORY);
+ int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pv, pThis->cbCodeSignature,
+ pThis->offImage + pThis->offCodeSignature);
+ if (RT_SUCCESS(rc))
+ {
+ /* Check blob signature. */
+ PCRTCRAPLCSSUPERBLOB pSuper = (PCRTCRAPLCSSUPERBLOB)pv;
+ if (pSuper->Hdr.uMagic == RTCRAPLCS_MAGIC_EMBEDDED_SIGNATURE)
+ {
+ uint32_t cbHdr = RT_BE2H_U32(pSuper->Hdr.cb);
+ uint32_t cSlots = RT_BE2H_U32(pSuper->cSlots);
+ if ( cbHdr <= pThis->cbCodeSignature
+ && cbHdr > RT_UOFFSETOF(RTCRAPLCSSUPERBLOB, aSlots)
+ && cSlots > 0
+ && cSlots < 128
+ && RT_UOFFSETOF_DYN(RTCRAPLCSSUPERBLOB, aSlots[cSlots]) <= cbHdr)
+ {
+ pThis->PtrCodeSignature.pSuper = pSuper;
+ return VINF_SUCCESS;
+ }
+ rc = VERR_LDRVI_BAD_CERT_HDR_LENGTH;
+ }
+ else
+ rc = VERR_LDRVI_BAD_CERT_HDR_TYPE;
+ }
+ RTMemFree(pv);
+ return rc;
+ }
+ return VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY;
+}
+
+
+/**
+ * Handles a RTLDRPROP_PKCS7_SIGNED_DATA query.
+ */
+static int rtldrMachO_QueryPkcs7SignedData(PRTLDRMODMACHO pThis, void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ int rc = rtldrMachO_LoadSignatureBlob(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Locate the signature slot.
+ */
+ uint32_t iSlot = RT_BE2H_U32(pThis->PtrCodeSignature.pSuper->cSlots);
+ PCRTCRAPLCSBLOBSLOT pSlot = &pThis->PtrCodeSignature.pSuper->aSlots[iSlot];
+ while (iSlot-- > 0)
+ {
+ pSlot--;
+ if (pSlot->uType == RTCRAPLCS_SLOT_SIGNATURE)
+ {
+ /*
+ * Validate the data offset.
+ */
+ uint32_t offData = RT_BE2H_U32(pSlot->offData);
+ if ( offData < pThis->cbCodeSignature - sizeof(RTCRAPLCSHDR)
+ || !(offData & 3) )
+ {
+ /*
+ * The data is prefixed by a header with magic set to blob wrapper.
+ * Check that the size is within the bounds of the code signing blob.
+ */
+ PCRTCRAPLCSHDR pHdr = (PCRTCRAPLCSHDR)&pThis->PtrCodeSignature.pb[offData];
+ if (pHdr->uMagic == RTCRAPLCS_MAGIC_BLOBWRAPPER)
+ {
+ uint32_t cbData = RT_BE2H_U32(pHdr->cb);
+ uint32_t cbMax = pThis->cbCodeSignature - offData ;
+ if ( cbData <= cbMax
+ && cbData > sizeof(RTCRAPLCSHDR))
+ {
+ /*
+ * Copy out the requested data.
+ */
+ *pcbRet = cbData;
+ if (cbData <= cbBuf)
+ {
+ memcpy(pvBuf, pHdr + 1, cbData);
+ return VINF_SUCCESS;
+ }
+ memcpy(pvBuf, pHdr + 1, cbBuf);
+ return VERR_BUFFER_OVERFLOW;
+ }
+ }
+ }
+ return VERR_LDRVI_BAD_CERT_FORMAT;
+ }
+ }
+ rc = VERR_NOT_FOUND;
+ }
+ return rc;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnQueryProp} */
+static DECLCALLBACK(int) rtldrMachO_QueryProp(PRTLDRMODINTERNAL pMod, RTLDRPROP enmProp, void const *pvBits,
+ void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ int rc = VERR_NOT_FOUND;
+ switch (enmProp)
+ {
+ case RTLDRPROP_UUID:
+ Assert(cbBuf >= sizeof(pThis->abImageUuid));
+ if (!ASMMemIsZero(pThis->abImageUuid, sizeof(pThis->abImageUuid)))
+ {
+ *pcbRet = sizeof(pThis->abImageUuid);
+ memcpy(pvBuf, pThis->abImageUuid, sizeof(pThis->abImageUuid));
+ return VINF_SUCCESS;
+ }
+ break;
+
+ case RTLDRPROP_FILE_OFF_HEADER:
+ Assert(cbBuf == sizeof(uint32_t) || cbBuf == sizeof(uint64_t));
+ if (cbBuf == sizeof(uint32_t))
+ *(uint32_t *)pvBuf = pThis->offImage;
+ else
+ *(uint64_t *)pvBuf = pThis->offImage;
+ return VINF_SUCCESS;
+
+ case RTLDRPROP_IS_SIGNED:
+ Assert(cbBuf == sizeof(bool));
+ Assert(*pcbRet == cbBuf);
+ *(bool *)pvBuf = pThis->cbCodeSignature > 0;
+ return VINF_SUCCESS;
+
+ case RTLDRPROP_PKCS7_SIGNED_DATA:
+ if (pThis->cbCodeSignature > 0)
+ return rtldrMachO_QueryPkcs7SignedData(pThis, pvBuf, cbBuf, pcbRet);
+ break;
+
+
+#if 0 /** @todo return LC_ID_DYLIB */
+ case RTLDRPROP_INTERNAL_NAME:
+#endif
+
+ default:
+ break;
+ }
+ NOREF(cbBuf);
+ RT_NOREF_PV(pvBits);
+ return rc;
+}
+
+
+#ifndef IPRT_WITHOUT_LDR_VERIFY
+
+/**
+ * Decodes the signature blob at RTLDRMODMACHO::PtrCodeSignature.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module instance.
+ * @param ppSignature Where to return the decoded signature data.
+ * @param pErrInfo Where to supply extra error details. Optional.
+ */
+static int rtldrMachO_VerifySignatureDecode(PRTLDRMODMACHO pThis, PRTLDRMACHOSIGNATURE *ppSignature, PRTERRINFO pErrInfo)
+{
+ Assert(pThis->PtrCodeSignature.pSuper != NULL);
+
+ /*
+ * Allocate and init decoded signature data structure.
+ */
+ PRTLDRMACHOSIGNATURE pSignature = (PRTLDRMACHOSIGNATURE)RTMemTmpAllocZ(sizeof(*pSignature));
+ *ppSignature = pSignature;
+ if (!pSignature)
+ return VERR_NO_TMP_MEMORY;
+ pSignature->idxPkcs7 = UINT32_MAX;
+
+ /*
+ * Parse the slots, validating the slot headers.
+ */
+ PCRTCRAPLCSSUPERBLOB pSuper = pThis->PtrCodeSignature.pSuper;
+ uint32_t const cSlots = RT_BE2H_U32(pSuper->cSlots);
+ uint32_t const offFirst = RT_UOFFSETOF_DYN(RTCRAPLCSSUPERBLOB, aSlots[cSlots]);
+ uint32_t const cbBlob = RT_BE2H_U32(pSuper->Hdr.cb);
+ for (uint32_t iSlot = 0; iSlot < cSlots; iSlot++)
+ {
+ /*
+ * Check that the data offset is valid. There appears to be no alignment
+ * requirements here, which is a little weird consindering the PPC heritage.
+ */
+ uint32_t const offData = RT_BE2H_U32(pSuper->aSlots[iSlot].offData);
+ if ( offData < offFirst
+ || offData > cbBlob - sizeof(RTCRAPLCSHDR))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u has an invalid data offset: %#x (min %#x, max %#x-4)",
+ iSlot, offData, offFirst, cbBlob);
+ uint32_t const cbMaxData = cbBlob - offData;
+
+ /*
+ * PKCS#7/CMS signature.
+ */
+ if (pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_SIGNATURE)
+ {
+ if (pSignature->idxPkcs7 != UINT32_MAX)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Already have PKCS#7 data in slot %#u", iSlot, pSignature->idxPkcs7);
+ PCRTCRAPLCSHDR pHdr = (PCRTCRAPLCSHDR)&pThis->PtrCodeSignature.pb[offData];
+ if (pHdr->uMagic != RTCRAPLCS_MAGIC_BLOBWRAPPER)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Invalid PKCS#7 wrapper magic: %#x", iSlot, RT_BE2H_U32(pHdr->uMagic));
+ uint32_t const cb = RT_BE2H_U32(pHdr->cb);
+ if (cb > cbMaxData || cb < sizeof(*pHdr) + 2U)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Invalid PKCS#7 size is out of bound: %#x (min %#x, max %#x)",
+ iSlot, cb, sizeof(*pHdr) + 2, cbMaxData);
+ pSignature->idxPkcs7 = iSlot;
+ pSignature->pbPkcs7 = (uint8_t const *)(pHdr + 1);
+ pSignature->cbPkcs7 = cb - sizeof(*pHdr);
+ }
+ /*
+ * Code directories.
+ */
+ else if ( pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_CODEDIRECTORY
+ || ( RT_BE2H_U32(pSuper->aSlots[iSlot].uType) - RT_BE2H_U32_C(RTCRAPLCS_SLOT_ALTERNATE_CODEDIRECTORIES)
+ < RTCRAPLCS_SLOT_ALTERNATE_CODEDIRECTORIES_COUNT))
+ {
+ /* Make sure we don't get too many code directories and that the first one is a regular one. */
+ if (pSignature->cCodeDirs >= RT_ELEMENTS(pSignature->aCodeDirs))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Too many code directory slots (%u found thus far)",
+ iSlot, pSignature->cCodeDirs + 1);
+ if ( pSuper->aSlots[iSlot].uType == RTCRAPLCS_SLOT_CODEDIRECTORY
+ && pSignature->cCodeDirs > 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Already have primary code directory in slot #%u",
+ iSlot, pSignature->aCodeDirs[0].uSlot);
+ if ( pSuper->aSlots[iSlot].uType != RTCRAPLCS_SLOT_CODEDIRECTORY /* lazy bird */
+ && pSignature->cCodeDirs == 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Expected alternative code directory after the primary one", iSlot);
+
+ /* Check data header: */
+ if (cbMaxData < RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, uUnused1))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Insufficient data vailable for code directory (max %#x)", iSlot, cbMaxData);
+
+ PCRTCRAPLCSCODEDIRECTORY pCodeDir = (PCRTCRAPLCSCODEDIRECTORY)&pThis->PtrCodeSignature.pb[offData];
+ if (pCodeDir->Hdr.uMagic != RTCRAPLCS_MAGIC_CODEDIRECTORY)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Invalid code directory magic: %#x", iSlot, RT_BE2H_U32(pCodeDir->Hdr.uMagic));
+ uint32_t const cbCodeDir = RT_BE2H_U32(pCodeDir->Hdr.cb);
+ if (cbCodeDir > cbMaxData || cbCodeDir < RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, offScatter))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Code directory size is out of bound: %#x (min %#x, max %#x)",
+ iSlot, cbCodeDir, RT_UOFFSETOF(RTCRAPLCSCODEDIRECTORY, offScatter), cbMaxData);
+ pSignature->aCodeDirs[pSignature->cCodeDirs].pCodeDir = pCodeDir;
+ pSignature->aCodeDirs[pSignature->cCodeDirs].cb = cbCodeDir;
+
+ /* Check Version: */
+ uint32_t const uVersion = RT_BE2H_U32(pCodeDir->uVersion);
+ if ( uVersion < RTCRAPLCS_VER_2_0
+ || uVersion >= RT_MAKE_U32(0, 3))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Code directory version is out of bounds: %#07x", iSlot, uVersion);
+ uint32_t cbSelf = uVersion >= RTCRAPLCS_VER_SUPPORTS_EXEC_SEG ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, fExecSeg)
+ : uVersion >= RTCRAPLCS_VER_SUPPORTS_CODE_LIMIT_64 ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, cbCodeLimit64)
+ : uVersion >= RTCRAPLCS_VER_SUPPORTS_TEAMID ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offTeamId)
+ : uVersion >= RTCRAPLCS_VER_SUPPORTS_SCATTER ? RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offScatter)
+ : RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, uUnused1);
+ if (cbSelf > cbCodeDir)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Code directory size is out of bound: %#x (min %#x, max %#x)",
+ iSlot, cbCodeDir, cbSelf, cbCodeDir);
+
+ /* hash type and size. */
+ uint8_t cbHash;
+ RTDIGESTTYPE enmDigest;
+ switch (pCodeDir->bHashType)
+ {
+ case RTCRAPLCS_HASHTYPE_SHA1:
+ enmDigest = RTDIGESTTYPE_SHA1;
+ cbHash = RTSHA1_HASH_SIZE;
+ break;
+ case RTCRAPLCS_HASHTYPE_SHA256:
+ enmDigest = RTDIGESTTYPE_SHA256;
+ cbHash = RTSHA256_HASH_SIZE;
+ break;
+ case RTCRAPLCS_HASHTYPE_SHA256_TRUNCATED:
+ enmDigest = RTDIGESTTYPE_SHA256;
+ cbHash = RTSHA1_HASH_SIZE; /* truncated to SHA-1 size. */
+ break;
+ case RTCRAPLCS_HASHTYPE_SHA384:
+ enmDigest = RTDIGESTTYPE_SHA384;
+ cbHash = RTSHA384_HASH_SIZE;
+ break;
+ default:
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Slot #%u: Unknown hash type %#x (LB %#x)",
+ iSlot, pCodeDir->bHashType, pCodeDir->cbHash);
+ }
+ pSignature->aCodeDirs[pSignature->cCodeDirs].enmDigest = enmDigest;
+ if (pCodeDir->cbHash != cbHash)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Unexpected hash size for %s: %#x, expected %#x",
+ iSlot, RTCrDigestTypeToName(enmDigest), pCodeDir->cbHash, cbHash);
+
+ /* Hash slot offset and counts. Special slots are counted backwards from offHashSlots. */
+ uint32_t const cSpecialSlots = RT_BE2H_U32(pCodeDir->cSpecialSlots);
+ if (cSpecialSlots > 256)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Too many special slots: %#x", iSlot, cSpecialSlots);
+ uint32_t const cCodeSlots = RT_BE2H_U32(pCodeDir->cCodeSlots);
+ if ( cCodeSlots >= UINT32_MAX / 2
+ || cCodeSlots + cSpecialSlots > (cbCodeDir - cbHash) / cbHash)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Slot #%u: Too many code slots: %#x + %#x (max %#x)",
+ iSlot, cCodeSlots, cSpecialSlots, (cbCodeDir - cbHash) / cbHash);
+ uint32_t const offHashSlots = RT_BE2H_U32(pCodeDir->offHashSlots);
+ if ( offHashSlots > cbCodeDir - cCodeSlots * cbHash
+ || offHashSlots < cbSelf + cSpecialSlots * cbHash)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Code directory hash offset is out of bounds: %#x (min: %#x, max: %#x)",
+ iSlot, offHashSlots, cbSelf + cSpecialSlots * cbHash, cbCodeDir - cCodeSlots * cbHash);
+
+ /* page shift */
+ if (pCodeDir->cPageShift == 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Unsupported page shift of zero in code directory", iSlot);
+ uint32_t cMaxPageShift;
+ if ( pThis->Core.enmArch == RTLDRARCH_AMD64
+ || pThis->Core.enmArch == RTLDRARCH_X86_32
+ || pThis->Core.enmArch == RTLDRARCH_ARM32)
+ cMaxPageShift = 12;
+ else if (pThis->Core.enmArch == RTLDRARCH_ARM64)
+ cMaxPageShift = 16; /* 16KB */
+ else
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Unsupported architecture: %d", pThis->Core.enmArch);
+ if ( pCodeDir->cPageShift < 12 /* */
+ || pCodeDir->cPageShift > cMaxPageShift)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Page shift in code directory is out of range: %d (min: 12, max: %d)",
+ iSlot, pCodeDir->cPageShift, cMaxPageShift);
+
+ /* code limit vs page shift and code hash slots */
+ uint32_t const cbCodeLimit32 = RT_BE2H_U32(pCodeDir->cbCodeLimit32);
+ uint32_t const cExpectedCodeHashes = pCodeDir->cPageShift == 0 ? 1
+ : (cbCodeLimit32 + RT_BIT_32(pCodeDir->cPageShift) - 1) >> pCodeDir->cPageShift;
+ if (cExpectedCodeHashes != cCodeSlots)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Code limit and page shift value does not match code hash slots: cbCodeLimit32=%#x cPageShift=%u -> %#x; cCodeSlots=%#x",
+ iSlot, cbCodeLimit32, pCodeDir->cPageShift, cExpectedCodeHashes, cCodeSlots);
+
+ /* Identifier offset: */
+ if (pCodeDir->offIdentifier)
+ {
+ uint32_t const offIdentifier = RT_BE2H_U32(pCodeDir->offIdentifier);
+ if ( offIdentifier < cbSelf
+ || offIdentifier >= cbCodeDir)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Identifier offset is out of bounds: %#x (min: %#x, max: %#x)",
+ iSlot, offIdentifier, cbSelf, cbCodeDir - 1);
+ int rc = RTStrValidateEncodingEx((char const *)pCodeDir + offIdentifier, cbCodeDir - offIdentifier,
+ RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Malformed identifier string: %Rrc", iSlot, rc);
+ }
+
+ /* Team identifier: */
+ if (cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offTeamId) && pCodeDir->offTeamId)
+ {
+ uint32_t const offTeamId = RT_BE2H_U32(pCodeDir->offTeamId);
+ if ( offTeamId < cbSelf
+ || offTeamId >= cbCodeDir)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Team identifier offset is out of bounds: %#x (min: %#x, max: %#x)",
+ iSlot, offTeamId, cbSelf, cbCodeDir - 1);
+ int rc = RTStrValidateEncodingEx((char const *)pCodeDir + offTeamId, cbCodeDir - offTeamId,
+ RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Malformed team identifier string: %Rrc", iSlot, rc);
+ }
+
+ /* We don't support scatter. */
+ if (cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, offScatter) && pCodeDir->offScatter)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Scatter not supported.", iSlot);
+
+ /* We don't really support the 64-bit code limit either: */
+ if ( cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, cbCodeLimit64)
+ && pCodeDir->cbCodeLimit64
+ && RT_BE2H_U64(pCodeDir->cbCodeLimit64) != cbCodeLimit32)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: 64-bit code limit does not match 32-bit: %#RX64 vs %#RX32",
+ iSlot, RT_BE2H_U64(pCodeDir->cbCodeLimit64), cbCodeLimit32);
+
+ /* Check executable segment info if present: */
+ if ( cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, fExecSeg)
+ && ( pThis->offSeg0ForCodeSign != RT_BE2H_U64(pCodeDir->offExecSeg)
+ || pThis->cbSeg0ForCodeSign != RT_BE2H_U64(pCodeDir->cbExecSeg)
+ || pThis->fSeg0ForCodeSign != RT_BE2H_U64(pCodeDir->fExecSeg)) )
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Segment #0 info mismatch: @%#RX64 LB %#RX64 flags=%#RX64; expected @%#RX64 LB %#RX64 flags=%#RX64",
+ iSlot, RT_BE2H_U64(pCodeDir->offExecSeg), RT_BE2H_U64(pCodeDir->cbExecSeg),
+ RT_BE2H_U64(pCodeDir->fExecSeg), pThis->offSeg0ForCodeSign, pThis->cbSeg0ForCodeSign,
+ pThis->fSeg0ForCodeSign);
+
+ /* Check fields that must be zero (don't want anyone to use them to counter changes): */
+ if (pCodeDir->uUnused1 != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Unused field #1 is non-zero: %#x", iSlot, RT_BE2H_U32(pCodeDir->uUnused1));
+ if ( cbSelf >= RT_UOFFSET_AFTER(RTCRAPLCSCODEDIRECTORY, uUnused2)
+ && pCodeDir->uUnused2 != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Unused field #2 is non-zero: %#x", iSlot, RT_BE2H_U32(pCodeDir->uUnused2));
+
+ /** @todo idPlatform values. */
+ /** @todo Check for gaps if we know the version number? Alignment? */
+
+ /* If first code directory, check that the code limit covers the whole image up to the signature data. */
+ if (pSignature->cCodeDirs == 0)
+ {
+ /** @todo verify the that the signature data is at the very end... */
+ if (cbCodeLimit32 != pThis->offCodeSignature)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Unexpected code limit: %#x, expected %#x",
+ iSlot, cbCodeLimit32, pThis->offCodeSignature);
+ }
+ /* Otherwise, check that the code limit matches the previous directories. */
+ else
+ for (uint32_t i = 0; i < pSignature->cCodeDirs; i++)
+ if (pSignature->aCodeDirs[i].pCodeDir->cbCodeLimit32 != pCodeDir->cbCodeLimit32)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Slot #%u: Code limit differs from previous directory: %#x, expected %#x",
+ iSlot, cbCodeLimit32, RT_BE2H_U32(pSignature->aCodeDirs[i].pCodeDir->cbCodeLimit32));
+
+ /* Commit the code dir entry: */
+ pSignature->aCodeDirs[pSignature->cCodeDirs++].uSlot = iSlot;
+ }
+ }
+
+ /*
+ * Check that we've got at least one code directory and one PKCS#7 signature.
+ */
+ if (pSignature->cCodeDirs == 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "No code directory slot in the code signature");
+ if (pSignature->idxPkcs7 == UINT32_MAX)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "No PKCS#7 slot in the code signature");
+
+ /*
+ * Decode the PKCS#7 signature.
+ */
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pSignature->pbPkcs7, pSignature->cbPkcs7,
+ pErrInfo, &g_RTAsn1DefaultAllocator, 0, "Mach-O-BLOB");
+ int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &pSignature->ContentInfo, "CI");
+ if (RT_SUCCESS(rc))
+ {
+ if (RTCrPkcs7ContentInfo_IsSignedData(&pSignature->ContentInfo))
+ {
+ pSignature->pSignedData = pSignature->ContentInfo.u.pSignedData;
+
+ /*
+ * Check that the signedData stuff adds up.
+ */
+ if (!strcmp(pSignature->pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID))
+ {
+ rc = RTCrPkcs7SignedData_CheckSanity(pSignature->pSignedData,
+ RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE /** @todo consider not piggy-backing on auth-code */
+ | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
+ | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
+ pErrInfo, "SD");
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID,
+ "Unexpected pSignedData.ContentInfo.ContentType.szObjId value: %s (expected %s)",
+ pSignature->pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID, /** @todo error code*/
+ "PKCS#7 is not 'signedData': %s", pSignature->ContentInfo.ContentType.szObjId);
+ }
+ return rc;
+}
+
+/**
+ * Destroys the decoded signature data structure.
+ *
+ * @param pSignature The decoded signature data. Can be NULL.
+ */
+static void rtldrMachO_VerifySignatureDestroy(PRTLDRMACHOSIGNATURE pSignature)
+{
+ if (pSignature)
+ {
+ RTCrPkcs7ContentInfo_Delete(&pSignature->ContentInfo);
+ RTMemTmpFree(pSignature);
+ }
+}
+
+
+/**
+ * Worker for rtldrMachO_VerifySignatureValidatePkcs7Hashes that handles plists
+ * with code directory hashes inside them.
+ *
+ * It is assumed that these plist files was invented to handle alternative code
+ * directories.
+ *
+ * @note Putting an XML plist into the authenticated attribute list was
+ * probably not such a great idea, given all the optional and
+ * adjustable white-space padding. We should probably validate
+ * everything very strictly, limiting the elements, require certain
+ * attribute lists and even have strict expectations about the
+ * white-space, but right now let just make sure it's xml and get the
+ * data in the cdhashes array.
+ *
+ * @todo The code here is a little braindead and bulky. It should be
+ * possible to describe the expected XML structure using a tables.
+ */
+static int rtldrMachO_VerifySignatureValidateCdHashesPlist(PRTLDRMACHOSIGNATURE pSignature, char *pszPlist,
+ uint8_t *pbHash, uint32_t cbHash, PRTERRINFO pErrInfo)
+{
+ const char * const pszStart = pszPlist;
+#define CHECK_ISTR_AND_SKIP_OR_RETURN(a_szLead) \
+ do { \
+ if (!RTStrNICmp(pszPlist, RT_STR_TUPLE(a_szLead))) \
+ pszPlist += sizeof(a_szLead) - 1; \
+ else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
+ "Expected '%s' found '%.16s...' at %#zu in plist", a_szLead, pszPlist, pszPlist - pszStart); \
+ } while (0)
+#define CHECK_STR_AND_SKIP_OR_RETURN(a_szLead) \
+ do { \
+ if (!RTStrNCmp(pszPlist, RT_STR_TUPLE(a_szLead))) \
+ pszPlist += sizeof(a_szLead) - 1; \
+ else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
+ "Expected '%s' found '%.16s...' at %#zu in plist", a_szLead, pszPlist, pszPlist - pszStart); \
+ } while (0)
+#define SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN() \
+ do { /* currently only permitting spaces, tabs and newline, following char must be '<'. */ \
+ char chMacro; \
+ while ((chMacro = *pszPlist) == ' ' || chMacro == '\n' || chMacro == '\t') \
+ pszPlist++; \
+ if (chMacro == '<') { /* likely */ } \
+ else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
+ "Expected '<' found '%.16s...' at %#zu in plist", pszPlist, pszPlist - pszStart); \
+ } while (0)
+#define SKIP_SPACE_BEFORE_VALUE() \
+ do { /* currently only permitting spaces, tabs and newline. */ \
+ char chMacro; \
+ while ((chMacro = *pszPlist) == ' ' || chMacro == '\n' || chMacro == '\t') \
+ pszPlist++; \
+ } while (0)
+#define SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN() \
+ do { /* currently only permitting a single space */ \
+ if (pszPlist[0] == ' ' && pszPlist[1] != ' ') \
+ pszPlist++; \
+ else return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, \
+ "Expected ' ' found '%.16s...' at %#zu in plist", pszPlist, pszPlist - pszStart); \
+ } while (0)
+
+ /* Example:
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>cdhashes</key>
+ <array>
+ <data>
+ hul2SSkDQFRXbGlt3AmCp25MU0Y=
+ </data>
+ <data>
+ N0kvxg0CJBNuZTq135PntAaRczw=
+ </data>
+ </array>
+</dict>
+</plist>
+ */
+
+ /* <?xml version="1.0" encoding="UTF-8"?> */
+ CHECK_STR_AND_SKIP_OR_RETURN("<?xml");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("version=\"1.0\"");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("encoding=\"UTF-8\"");
+ CHECK_STR_AND_SKIP_OR_RETURN("?>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /* <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> */
+ CHECK_STR_AND_SKIP_OR_RETURN("<!DOCTYPE");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("plist");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("PUBLIC");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("\"-//Apple//DTD PLIST 1.0//EN\"");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"");
+ CHECK_STR_AND_SKIP_OR_RETURN(">");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /* <plist version="1.0"> */
+ CHECK_STR_AND_SKIP_OR_RETURN("<plist");
+ SKIP_REQUIRED_SPACE_BETWEEN_ATTRIBUTES_OR_RETURN();
+ CHECK_STR_AND_SKIP_OR_RETURN("version=\"1.0\"");
+ CHECK_STR_AND_SKIP_OR_RETURN(">");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /* <dict> */
+ CHECK_STR_AND_SKIP_OR_RETURN("<dict>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /* <key>cdhashes</key> */
+ CHECK_STR_AND_SKIP_OR_RETURN("<key>cdhashes</key>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /* <array> */
+ CHECK_STR_AND_SKIP_OR_RETURN("<array>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /*
+ * Repeated: <data>hul2SSkDQFRXbGlt3AmCp25MU0Y=</data>
+ */
+ uint32_t iCodeDir = 0;
+ for (;;)
+ {
+ /* Decode the binary data (base64) and skip it. */
+ CHECK_STR_AND_SKIP_OR_RETURN("<data>");
+ SKIP_SPACE_BEFORE_VALUE();
+
+ char ch;
+ size_t cchBase64 = 0;
+ while (RT_C_IS_ALNUM(ch = pszPlist[cchBase64]) || ch == '+' || ch == '/' || ch == '=')
+ cchBase64++;
+ size_t cbActualHash = cbHash;
+ char *pszEnd = NULL;
+ int rc = RTBase64DecodeEx(pszPlist, cchBase64, pbHash, cbHash, &cbActualHash, &pszEnd);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Failed to decode hash #%u in authenticated plist attribute: %Rrc (%.*s)",
+ iCodeDir, rc, cchBase64, pszPlist);
+ pszPlist += cchBase64;
+ AssertReturn(pszPlist == pszEnd, VERR_INTERNAL_ERROR_2);
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /* The binary hash data must be exactly the size of SHA1, larger
+ hash like SHA-256 and SHA-384 are truncated for some reason. */
+ if (cbActualHash != RTSHA1_HASH_SIZE)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Hash #%u in authenticated plist attribute has the wrong length: %u, exepcted %u",
+ iCodeDir, cbActualHash, RTSHA1_HASH_SIZE);
+
+ /* Skip closing tag. */
+ CHECK_STR_AND_SKIP_OR_RETURN("</data>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+
+ /* Calculate the hash and compare. */
+ RTCRDIGEST hDigest;
+ rc = RTCrDigestCreateByType(&hDigest, pSignature->aCodeDirs[iCodeDir].enmDigest);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrDigestUpdate(hDigest, pSignature->aCodeDirs[iCodeDir].pCodeDir, pSignature->aCodeDirs[iCodeDir].cb);
+ if (RT_SUCCESS(rc))
+ {
+ if (memcmp(pbHash, RTCrDigestGetHash(hDigest), cbActualHash) == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_IMAGE_HASH_MISMATCH,
+ "Code directory #%u hash mismatch (plist):\n"
+ "signed: %.*Rhxs\n"
+ "our: %.*Rhxs\n",
+ iCodeDir, cbActualHash, pbHash,
+ RTCrDigestGetHashSize(hDigest), RTCrDigestGetHash(hDigest));
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTCrDigestUpdate failed: %Rrc", rc);
+ RTCrDigestRelease(hDigest);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "Failed to create a digest of type %u verifying code dir #%u: %Rrc",
+ pSignature->aCodeDirs[iCodeDir].enmDigest, iCodeDir, rc);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Advance.
+ */
+ iCodeDir++;
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+ if (RTStrNCmp(pszPlist, RT_STR_TUPLE("<data>")) == 0)
+ {
+ if (iCodeDir >= pSignature->cCodeDirs)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Authenticated plist attribute has too many code directories (%u in blob)",
+ pSignature->cCodeDirs);
+ }
+ else if (iCodeDir == pSignature->cCodeDirs)
+ break;
+ else
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Authenticated plist attribute does not include all code directors: %u out of %u",
+ iCodeDir, pSignature->cCodeDirs);
+ }
+
+ /*</array>*/
+ CHECK_STR_AND_SKIP_OR_RETURN("</array>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /*</dict>*/
+ CHECK_STR_AND_SKIP_OR_RETURN("</dict>");
+ SKIP_SPACE_BETWEEN_ELEMENTS_OR_RETURN();
+
+ /*</plist>*/
+ CHECK_STR_AND_SKIP_OR_RETURN("</plist>");
+ SKIP_SPACE_BEFORE_VALUE();
+
+ if (*pszPlist == '\0')
+ return VINF_SUCCESS;
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Authenticated plist attribute has unexpected trailing content: %.32s", pszPlist);
+}
+
+
+/**
+ * Verifies the code directory hashes embedded in the PKCS\#7 data.
+ *
+ * @returns IPRT status code.
+ * @param pSignature The decoded signature data.
+ * @param pErrInfo Where to supply extra error details. Optional.
+ */
+static int rtldrMachO_VerifySignatureValidatePkcs7Hashes(PRTLDRMACHOSIGNATURE pSignature, PRTERRINFO pErrInfo)
+{
+ /*
+ * Look thru the authenticated attributes in the signer info array.
+ */
+ PRTCRPKCS7SIGNEDDATA pSignedData = pSignature->pSignedData;
+ for (uint32_t iSignerInfo = 0; iSignerInfo < pSignedData->SignerInfos.cItems; iSignerInfo++)
+ {
+ PCRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[iSignerInfo];
+ bool fMsgDigest = false;
+ bool fPlist = false;
+ for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->AuthenticatedAttributes.cItems; iAttrib++)
+ {
+ PCRTCRPKCS7ATTRIBUTE pAttrib = pSignerInfo->AuthenticatedAttributes.papItems[iAttrib];
+ if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_MESSAGE_DIGEST_OID) == 0)
+ {
+ /*
+ * Validate the message digest while we're here.
+ */
+ AssertReturn(pAttrib->uValues.pOctetStrings && pAttrib->uValues.pOctetStrings->cItems == 1, VERR_INTERNAL_ERROR_5);
+
+ RTCRDIGEST hDigest;
+ int rc = RTCrDigestCreateByObjId(&hDigest, &pSignerInfo->DigestAlgorithm.Algorithm);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrDigestUpdate(hDigest, pSignature->aCodeDirs[0].pCodeDir, pSignature->aCodeDirs[0].cb);
+ if (RT_SUCCESS(rc))
+ {
+ if (!RTCrDigestMatch(hDigest,
+ pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv,
+ pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb))
+ rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MESSAGE_DIGEST_ATTRIB_MISMATCH,
+ "Authenticated message-digest attribute mismatch:\n"
+ "signed: %.*Rhxs\n"
+ "our: %.*Rhxs\n",
+ pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb,
+ pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv,
+ RTCrDigestGetHashSize(hDigest), RTCrDigestGetHash(hDigest));
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTCrDigestUpdate failed: %Rrc", rc);
+ RTCrDigestRelease(hDigest);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "Failed to create a digest for OID %s: %Rrc",
+ pSignerInfo->DigestAlgorithm.Algorithm.szObjId, rc);
+ if (RT_FAILURE(rc))
+ return rc;
+ fMsgDigest = true;
+ }
+ else if (pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_APPLE_MULTI_CD_PLIST)
+ {
+ /*
+ * An XML (better be) property list with code directory hashes in it.
+ */
+ if (!pAttrib->uValues.pOctetStrings || pAttrib->uValues.pOctetStrings->cItems != 1)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Bad authenticated plist attribute");
+
+ uint32_t cch = pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb;
+ char const *pch = pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pch;
+ int rc = RTStrValidateEncodingEx(pch, cch, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Authenticated plist attribute is not valid UTF-8: %Rrc", rc);
+ uint32_t const cchMin = sizeof("<?xml?><plist><dict><key>cdhashes</key><array><data>hul2SSkDQFRXbGlt3AmCp25MU0Y=</data></array></dict></plist>") - 1;
+ if (cch < cchMin)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Authenticated plist attribute is too short: %#x, min: %#x", cch, cchMin);
+ if (cch > _64K)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT,
+ "Authenticated plist attribute is too long: %#x, max: 64KB", cch);
+
+ /* Copy the plist into a buffer and zero terminate it. Also allocate room for decoding a hash. */
+ const uint32_t cbMaxHash = 128;
+ char *pszTmp = (char *)RTMemTmpAlloc(cbMaxHash + cch + 3);
+ if (!pszTmp)
+ return VERR_NO_TMP_MEMORY;
+ pszTmp[cbMaxHash + cch] = '\0';
+ pszTmp[cbMaxHash + cch + 1] = '\0';
+ pszTmp[cbMaxHash + cch + 2] = '\0';
+ rc = rtldrMachO_VerifySignatureValidateCdHashesPlist(pSignature, (char *)memcpy(pszTmp + cbMaxHash, pch, cch),
+ (uint8_t *)pszTmp, cbMaxHash, pErrInfo);
+ RTMemTmpFree(pszTmp);
+ if (RT_FAILURE(rc))
+ return rc;
+ fPlist = true;
+ }
+ }
+ if (!fMsgDigest && pSignature->cCodeDirs > 1)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Missing authenticated message-digest attribute");
+ if (!fPlist && pSignature->cCodeDirs > 1)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "Missing authenticated code directory hash plist attribute");
+ }
+ if (pSignedData->SignerInfos.cItems < 1)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_FORMAT, "PKCS#7 signed data contains no signatures");
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Verifies the page hashes of the given code directory.
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module instance.
+ * @param pEntry The data entry for the code directory to validate.
+ * @param pbBuf Read buffer.
+ * @param cbBuf Buffer size.
+ * @param pErrInfo Where to supply extra error details. Optional.
+ */
+static int rtldrMachO_VerifySignatureValidateCodeDir(PRTLDRMODMACHO pThis, PRTLDRMACHCODEDIR pEntry,
+ uint8_t *pbBuf, uint32_t cbBuf, PRTERRINFO pErrInfo)
+{
+ RTCRDIGEST hDigest;
+ int rc = RTCrDigestCreateByType(&hDigest, pEntry->enmDigest);
+ if (RT_SUCCESS(rc))
+ {
+ PCRTCRAPLCSCODEDIRECTORY pCodeDir = pEntry->pCodeDir;
+ PRTLDRREADER const pRdr = pThis->Core.pReader;
+ uint32_t cbCodeLimit = RT_BE2H_U32(pCodeDir->cbCodeLimit32);
+ uint32_t const cbPage = RT_BIT_32(pCodeDir->cPageShift);
+ uint32_t const cHashes = RT_BE2H_U32(pCodeDir->cCodeSlots);
+ uint8_t const cbHash = pCodeDir->cbHash;
+ uint8_t const *pbHash = (uint8_t const *)pCodeDir + RT_BE2H_U32(pCodeDir->offHashSlots);
+ RTFOFF offFile = pThis->offImage;
+ if ( RT_BE2H_U32(pCodeDir->uVersion) < RTCRAPLCS_VER_SUPPORTS_SCATTER
+ || pCodeDir->offScatter == 0)
+ {
+ /*
+ * Work the image in linear fashion.
+ */
+ for (uint32_t iHash = 0; iHash < cHashes; iHash++, pbHash += cbHash, cbCodeLimit -= cbPage)
+ {
+ RTFOFF const offPage = offFile;
+
+ /*
+ * Read and digest the data for the current hash page.
+ */
+ rc = RTCrDigestReset(hDigest);
+ AssertRCBreak(rc);
+ Assert(cbCodeLimit > cbPage || iHash + 1 == cHashes);
+ uint32_t cbLeft = iHash + 1 < cHashes ? cbPage : cbCodeLimit;
+ while (cbLeft > 0)
+ {
+ uint32_t const cbToRead = RT_MIN(cbBuf, cbLeft);
+ rc = pRdr->pfnRead(pRdr, pbBuf, cbToRead, offFile);
+ AssertRCBreak(rc);
+
+ rc = RTCrDigestUpdate(hDigest, pbBuf, cbToRead);
+ AssertRCBreak(rc);
+
+ offFile += cbToRead;
+ cbLeft -= cbToRead;
+ }
+ AssertRCBreak(rc);
+ rc = RTCrDigestFinal(hDigest, NULL, 0);
+ AssertRCBreak(rc);
+
+ /*
+ * Compare it.
+ * Note! Don't use RTCrDigestMatch here as there is a truncated SHA-256 variant.
+ */
+ if (memcmp(pbHash, RTCrDigestGetHash(hDigest), cbHash) != 0)
+ {
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_MISMATCH,
+ "Hash #%u (@%RX64 LB %#x) mismatch in code dir #%u: %.*Rhxs, expected %.*Rhxs",
+ iHash, offPage, cbPage, pEntry->uSlot, (int)cbHash, pbHash,
+ (int)cbHash, RTCrDigestGetHash(hDigest));
+ break;
+ }
+
+ }
+ }
+ /*
+ * Work the image in scattered fashion.
+ */
+ else
+ rc = VERR_INTERNAL_ERROR_4;
+
+ RTCrDigestRelease(hDigest);
+ }
+ return rc;
+}
+
+
+/**
+ * Verifies the page hashes of all the code directories
+ *
+ * @returns IPRT status code.
+ * @param pThis The Mach-O module instance.
+ * @param pSignature The decoded signature data.
+ * @param pErrInfo Where to supply extra error details. Optional.
+ */
+static int rtldrMachO_VerifySignatureValidateCodeDirs(PRTLDRMODMACHO pThis, PRTLDRMACHOSIGNATURE pSignature, PRTERRINFO pErrInfo)
+{
+ void *pvBuf = RTMemTmpAllocZ(_4K);
+ if (pvBuf)
+ {
+ int rc = VERR_INTERNAL_ERROR_3;
+ for (uint32_t i = 0; i < pSignature->cCodeDirs; i++)
+ {
+ rc = rtldrMachO_VerifySignatureValidateCodeDir(pThis, &pSignature->aCodeDirs[i], (uint8_t *)pvBuf, _4K, pErrInfo);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ RTMemTmpFree(pvBuf);
+ return rc;
+ }
+ return VERR_NO_TMP_MEMORY;
+}
+
+#endif /* !IPRT_WITHOUT_LDR_VERIFY*/
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnVerifySignature}
+ */
+static DECLCALLBACK(int)
+rtldrMachO_VerifySignature(PRTLDRMODINTERNAL pMod, PFNRTLDRVALIDATESIGNEDDATA pfnCallback, void *pvUser, PRTERRINFO pErrInfo)
+{
+#ifndef IPRT_WITHOUT_LDR_VERIFY
+ PRTLDRMODMACHO pThis = RT_FROM_MEMBER(pMod, RTLDRMODMACHO, Core);
+ int rc = rtldrMachO_LoadSignatureBlob(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ PRTLDRMACHOSIGNATURE pSignature = NULL;
+ rc = rtldrMachO_VerifySignatureDecode(pThis, &pSignature, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtldrMachO_VerifySignatureValidatePkcs7Hashes(pSignature, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtldrMachO_VerifySignatureValidateCodeDirs(pThis, pSignature, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Finally, let the caller verify the certificate chain for the PKCS#7 bit.
+ */
+ RTLDRSIGNATUREINFO Info;
+ Info.iSignature = 0;
+ Info.cSignatures = 1;
+ Info.enmType = RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA;
+ Info.pvSignature = &pSignature->ContentInfo;
+ Info.cbSignature = sizeof(pSignature->ContentInfo);
+ Info.pvExternalData = pSignature->aCodeDirs[0].pCodeDir;
+ Info.cbExternalData = pSignature->aCodeDirs[0].cb;
+ rc = pfnCallback(&pThis->Core, &Info, pErrInfo, pvUser);
+ }
+ }
+ }
+ rtldrMachO_VerifySignatureDestroy(pSignature);
+ }
+ return rc;
+#else
+ RT_NOREF_PV(pMod); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(pvUser); RT_NOREF_PV(pErrInfo);
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+/**
+ * Operations for a Mach-O module interpreter.
+ */
+static const RTLDROPS s_rtldrMachOOps=
+{
+ "mach-o",
+ rtldrMachO_Close,
+ NULL,
+ NULL /*pfnDone*/,
+ rtldrMachO_EnumSymbols,
+ /* ext */
+ rtldrMachO_GetImageSize,
+ rtldrMachO_GetBits,
+ rtldrMachO_RelocateBits,
+ rtldrMachO_GetSymbolEx,
+ NULL /*pfnQueryForwarderInfo*/,
+ rtldrMachO_EnumDbgInfo,
+ rtldrMachO_EnumSegments,
+ rtldrMachO_LinkAddressToSegOffset,
+ rtldrMachO_LinkAddressToRva,
+ rtldrMachO_SegOffsetToRva,
+ rtldrMachO_RvaToSegOffset,
+ rtldrMachO_ReadDbgInfo,
+ rtldrMachO_QueryProp,
+ rtldrMachO_VerifySignature,
+ NULL /*pfnHashImage*/,
+ NULL /*pfnUnwindFrame*/,
+ 42
+};
+
+
+/**
+ * Handles opening Mach-O images (non-fat).
+ */
+DECLHIDDEN(int) rtldrMachOOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offImage,
+ PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
+{
+
+ /*
+ * Create the instance data and do a minimal header validation.
+ */
+ PRTLDRMODMACHO pThis = NULL;
+ int rc = kldrModMachODoCreate(pReader, offImage, fFlags, &pThis, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Match up against the requested CPU architecture.
+ */
+ if ( enmArch == RTLDRARCH_WHATEVER
+ || pThis->Core.enmArch == enmArch)
+ {
+ pThis->Core.pOps = &s_rtldrMachOOps;
+ pThis->Core.u32Magic = RTLDRMOD_MAGIC;
+ *phLdrMod = &pThis->Core;
+ return VINF_SUCCESS;
+ }
+ rc = VERR_LDR_ARCH_MISMATCH;
+ }
+ if (pThis)
+ {
+ RTMemFree(pThis->pbLoadCommands);
+ RTMemFree(pThis);
+ }
+ return rc;
+
+}
+
+
+/**
+ * Handles opening FAT Mach-O image.
+ */
+DECLHIDDEN(int) rtldrFatOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
+{
+ fat_header_t FatHdr;
+ int rc = pReader->pfnRead(pReader, &FatHdr, sizeof(FatHdr), 0);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
+
+ if (FatHdr.magic == IMAGE_FAT_SIGNATURE)
+ { /* likely */ }
+ else if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
+ FatHdr.nfat_arch = RT_BSWAP_U32(FatHdr.nfat_arch);
+ else
+ return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "magic=%#x", FatHdr.magic);
+ if (FatHdr.nfat_arch < 64)
+ return RTErrInfoSetF(pErrInfo, VERR_INVALID_EXE_SIGNATURE, "Bad nfat_arch value: %#x", FatHdr.nfat_arch);
+
+ uint32_t offEntry = sizeof(FatHdr);
+ for (uint32_t i = 0; i < FatHdr.nfat_arch; i++, offEntry += sizeof(fat_arch_t))
+ {
+ fat_arch_t FatEntry;
+ rc = pReader->pfnRead(pReader, &FatEntry, sizeof(FatEntry), offEntry);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, rc, "Read error at offset 0: %Rrc", rc);
+ if (FatHdr.magic == IMAGE_FAT_SIGNATURE_OE)
+ {
+ FatEntry.cputype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cputype);
+ //FatEntry.cpusubtype = (int32_t)RT_BSWAP_U32((uint32_t)FatEntry.cpusubtype);
+ FatEntry.offset = RT_BSWAP_U32(FatEntry.offset);
+ //FatEntry.size = RT_BSWAP_U32(FatEntry.size);
+ //FatEntry.align = RT_BSWAP_U32(FatEntry.align);
+ }
+
+ /*
+ * Match enmArch.
+ */
+ bool fMatch = false;
+ switch (enmArch)
+ {
+ case RTLDRARCH_WHATEVER:
+ fMatch = true;
+ break;
+
+ case RTLDRARCH_X86_32:
+ fMatch = FatEntry.cputype == CPU_TYPE_X86;
+ break;
+
+ case RTLDRARCH_AMD64:
+ fMatch = FatEntry.cputype == CPU_TYPE_X86_64;
+ break;
+
+ case RTLDRARCH_ARM32:
+ fMatch = FatEntry.cputype == CPU_TYPE_ARM32;
+ break;
+
+ case RTLDRARCH_ARM64:
+ fMatch = FatEntry.cputype == CPU_TYPE_ARM64;
+ break;
+
+ case RTLDRARCH_X86_16:
+ fMatch = false;
+ break;
+
+ case RTLDRARCH_INVALID:
+ case RTLDRARCH_HOST:
+ case RTLDRARCH_END:
+ case RTLDRARCH_32BIT_HACK:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+ if (fMatch)
+ return rtldrMachOOpen(pReader, fFlags, enmArch, FatEntry.offset, phLdrMod, pErrInfo);
+ }
+
+ return VERR_LDR_ARCH_MISMATCH;
+
+}
+
diff --git a/src/VBox/Runtime/common/ldr/ldrMemory.cpp b/src/VBox/Runtime/common/ldr/ldrMemory.cpp
new file mode 100644
index 00000000..80e8dce2
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/ldrMemory.cpp
@@ -0,0 +1,336 @@
+
+/* $Id: ldrMemory.cpp $ */
+/** @file
+ * IPRT - Binary Image Loader, The Memory/Debugger Oriented Parts.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include "internal/ldr.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Memory reader (for debuggers) instance.
+ */
+typedef struct RTLDRRDRMEM
+{
+ /** The core. */
+ RTLDRREADER Core;
+ /** The size of the image. */
+ size_t cbImage;
+ /** The current offset. */
+ size_t offCur;
+
+ /** User parameter for the reader and destructor functions.*/
+ void *pvUser;
+ /** Read function. */
+ PFNRTLDRRDRMEMREAD pfnRead;
+ /** Destructor callback. */
+ PFNRTLDRRDRMEMDTOR pfnDtor;
+
+ /** Mapping of the file. */
+ void *pvMapping;
+ /** Mapping usage counter. */
+ uint32_t cMappings;
+
+ /** The fake filename (variable size). */
+ char szName[1];
+} RTLDRRDRMEM;
+/** Memory based loader reader instance data. */
+typedef RTLDRRDRMEM *PRTLDRRDRMEM;
+
+
+/**
+ * @callback_method_impl{FNRTLDRRDRMEMDTOR,
+ * Default destructor - pvUser points to the image memory block}
+ */
+static DECLCALLBACK(void) rtldrRdrMemDefaultDtor(void *pvUser, size_t cbImage)
+{
+ RT_NOREF(cbImage);
+ RTMemFree(pvUser);
+}
+
+
+/**
+ * @callback_method_impl{FNRTLDRRDRMEMREAD,
+ * Default memory reader - pvUser points to the image memory block}
+ */
+static DECLCALLBACK(int) rtldrRdrMemDefaultReader(void *pvBuf, size_t cb, size_t off, void *pvUser)
+{
+ memcpy(pvBuf, (uint8_t *)pvUser + off, cb);
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTLDRREADER,pfnRead} */
+static DECLCALLBACK(int) rtldrRdrMem_Read(PRTLDRREADER pReader, void *pvBuf, size_t cb, RTFOFF off)
+{
+ PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
+
+ AssertReturn(off >= 0, VERR_INVALID_PARAMETER);
+ if ( cb > pThis->cbImage
+ || off > (RTFOFF)pThis->cbImage
+ || off + (RTFOFF)cb > (RTFOFF)pThis->cbImage)
+ {
+ pThis->offCur = pThis->cbImage;
+ return VERR_EOF;
+ }
+
+ int rc = pThis->pfnRead(pvBuf, cb, (size_t)off, pThis->pvUser);
+ if (RT_SUCCESS(rc))
+ pThis->offCur = (size_t)off + cb;
+ else
+ pThis->offCur = ~(size_t)0;
+ return rc;
+}
+
+
+/** @interface_method_impl{RTLDRREADER,pfnTell} */
+static DECLCALLBACK(RTFOFF) rtldrRdrMem_Tell(PRTLDRREADER pReader)
+{
+ PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
+ return pThis->offCur;
+}
+
+
+/** @interface_method_impl{RTLDRREADER,pfnSize} */
+static DECLCALLBACK(uint64_t) rtldrRdrMem_Size(PRTLDRREADER pReader)
+{
+ PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
+ return pThis->cbImage;
+}
+
+
+/** @interface_method_impl{RTLDRREADER,pfnLogName} */
+static DECLCALLBACK(const char *) rtldrRdrMem_LogName(PRTLDRREADER pReader)
+{
+ PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
+ return pThis->szName;
+}
+
+
+/** @interface_method_impl{RTLDRREADER,pfnMap} */
+static DECLCALLBACK(int) rtldrRdrMem_Map(PRTLDRREADER pReader, const void **ppvBits)
+{
+ PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
+
+ /*
+ * Already mapped?
+ */
+ if (pThis->pvMapping)
+ {
+ pThis->cMappings++;
+ *ppvBits = pThis->pvMapping;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Allocate memory.
+ */
+ pThis->pvMapping = RTMemAlloc(pThis->cbImage);
+ if (!pThis->pvMapping)
+ return VERR_NO_MEMORY;
+ int rc = rtldrRdrMem_Read(pReader, pThis->pvMapping, pThis->cbImage, 0);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->cMappings = 1;
+ *ppvBits = pThis->pvMapping;
+ }
+ else
+ {
+ RTMemFree(pThis->pvMapping);
+ pThis->pvMapping = NULL;
+ }
+
+ return rc;
+}
+
+
+/** @interface_method_impl{RTLDRREADER,pfnUnmap} */
+static DECLCALLBACK(int) rtldrRdrMem_Unmap(PRTLDRREADER pReader, const void *pvBits)
+{
+ PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
+ AssertReturn(pThis->cMappings > 0, VERR_INVALID_PARAMETER);
+
+ if (!--pThis->cMappings)
+ {
+ RTMemFree(pThis->pvMapping);
+ pThis->pvMapping = NULL;
+ }
+
+ NOREF(pvBits);
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTLDRREADER,pfnDestroy} */
+static DECLCALLBACK(int) rtldrRdrMem_Destroy(PRTLDRREADER pReader)
+{
+ PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)pReader;
+ pThis->pfnDtor(pThis->pvUser, pThis->cbImage);
+ pThis->pfnDtor = NULL;
+ pThis->pvUser = NULL;
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Opens a memory based loader reader.
+ *
+ * @returns iprt status code.
+ * @param ppReader Where to store the reader instance on success.
+ * @param pszName The name to give the image.
+ * @param cbImage The image size.
+ * @param pfnRead The reader function. If NULL, a default reader is
+ * used that assumes pvUser points to a memory buffer
+ * of at least @a cbImage size.
+ * @param pfnDtor The destructor. If NULL, a default destructore is
+ * used that will call RTMemFree on @a pvUser.
+ * @param pvUser User argument. If either @a pfnRead or @a pfnDtor
+ * is NULL, this must be a pointer to readable memory
+ * (see above).
+ */
+static int rtldrRdrMem_Create(PRTLDRREADER *ppReader, const char *pszName, size_t cbImage,
+ PFNRTLDRRDRMEMREAD pfnRead, PFNRTLDRRDRMEMDTOR pfnDtor, void *pvUser)
+{
+#if ARCH_BITS > 32 /* 'ing gcc. */
+ AssertReturn(cbImage < RTFOFF_MAX, VERR_INVALID_PARAMETER);
+#endif
+ AssertReturn((RTFOFF)cbImage > 0, VERR_INVALID_PARAMETER);
+
+ size_t cchName = strlen(pszName);
+ int rc = VERR_NO_MEMORY;
+ PRTLDRRDRMEM pThis = (PRTLDRRDRMEM)RTMemAlloc(sizeof(*pThis) + cchName);
+ if (pThis)
+ {
+ memcpy(pThis->szName, pszName, cchName + 1);
+ pThis->cbImage = cbImage;
+ pThis->pvUser = pvUser;
+ pThis->offCur = 0;
+ pThis->pvUser = pvUser;
+ pThis->pfnRead = pfnRead ? pfnRead : rtldrRdrMemDefaultReader;
+ pThis->pfnDtor = pfnDtor ? pfnDtor : rtldrRdrMemDefaultDtor;
+ pThis->pvMapping = NULL;
+ pThis->cMappings = 0;
+ pThis->Core.uMagic = RTLDRREADER_MAGIC;
+ pThis->Core.pfnRead = rtldrRdrMem_Read;
+ pThis->Core.pfnTell = rtldrRdrMem_Tell;
+ pThis->Core.pfnSize = rtldrRdrMem_Size;
+ pThis->Core.pfnLogName = rtldrRdrMem_LogName;
+ pThis->Core.pfnMap = rtldrRdrMem_Map;
+ pThis->Core.pfnUnmap = rtldrRdrMem_Unmap;
+ pThis->Core.pfnDestroy = rtldrRdrMem_Destroy;
+ *ppReader = &pThis->Core;
+ return VINF_SUCCESS;
+ }
+
+ *ppReader = NULL;
+ return rc;
+}
+
+
+RTDECL(int) RTLdrOpenInMemory(const char *pszName, uint32_t fFlags, RTLDRARCH enmArch, size_t cbImage,
+ PFNRTLDRRDRMEMREAD pfnRead, PFNRTLDRRDRMEMDTOR pfnDtor, void *pvUser,
+ PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
+{
+ LogFlow(("RTLdrOpenInMemory: pszName=%p:{%s} fFlags=%#x enmArch=%d cbImage=%#zx pfnRead=%p pfnDtor=%p pvUser=%p phLdrMod=%p pErrInfo=%p\n",
+ pszName, pszName, fFlags, enmArch, cbImage, pfnRead, pfnDtor, pvUser, phLdrMod, pErrInfo));
+
+ if (!pfnRead || !pfnDtor)
+ AssertPtrReturn(pvUser, VERR_INVALID_POINTER);
+ if (!pfnDtor)
+ pfnDtor = rtldrRdrMemDefaultDtor;
+ else
+ AssertPtrReturn(pfnDtor, VERR_INVALID_POINTER);
+
+ /* The rest of the validations will call the destructor. */
+ AssertMsgReturnStmt(!(fFlags & ~RTLDR_O_VALID_MASK), ("%#x\n", fFlags),
+ pfnDtor(pvUser, cbImage), VERR_INVALID_PARAMETER);
+ AssertMsgReturnStmt(enmArch > RTLDRARCH_INVALID && enmArch < RTLDRARCH_END, ("%d\n", enmArch),
+ pfnDtor(pvUser, cbImage), VERR_INVALID_PARAMETER);
+ if (!pfnRead)
+ pfnRead = rtldrRdrMemDefaultReader;
+ else
+ AssertReturnStmt(RT_VALID_PTR(pfnRead), pfnDtor(pvUser, cbImage), VERR_INVALID_POINTER);
+ AssertReturnStmt(cbImage > 0, pfnDtor(pvUser, cbImage), VERR_INVALID_PARAMETER);
+
+ /*
+ * Resolve RTLDRARCH_HOST.
+ */
+ if (enmArch == RTLDRARCH_HOST)
+ enmArch = RTLdrGetHostArch();
+
+ /*
+ * Create file reader & invoke worker which identifies and calls the image interpreter.
+ */
+ PRTLDRREADER pReader = NULL; /* gcc may be wrong */
+ int rc = rtldrRdrMem_Create(&pReader, pszName, cbImage, pfnRead, pfnDtor, pvUser);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrOpenWithReader(pReader, fFlags, enmArch, phLdrMod, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlow(("RTLdrOpen: return %Rrc *phLdrMod=%p\n", rc, *phLdrMod));
+ return rc;
+ }
+
+ pReader->pfnDestroy(pReader);
+ }
+ else
+ {
+ pfnDtor(pvUser, cbImage);
+ rc = RTErrInfoSetF(pErrInfo, rc, "rtldrRdrMem_Create failed: %Rrc", rc);
+ }
+ *phLdrMod = NIL_RTLDRMOD;
+
+ LogFlow(("RTLdrOpen: return %Rrc\n", rc));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrOpenInMemory);
+
diff --git a/src/VBox/Runtime/common/ldr/ldrNative.cpp b/src/VBox/Runtime/common/ldr/ldrNative.cpp
new file mode 100644
index 00000000..8675a5ae
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/ldrNative.cpp
@@ -0,0 +1,336 @@
+/* $Id: ldrNative.cpp $ */
+/** @file
+ * IPRT - Binary Image Loader, Native interface.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include "internal/ldr.h"
+
+
+/** @copydoc RTLDROPS::pfnEnumSymbols */
+static DECLCALLBACK(int) rtldrNativeEnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits,
+ RTUINTPTR BaseAddress, PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ NOREF(pMod); NOREF(fFlags); NOREF(pvBits); NOREF(BaseAddress); NOREF(pfnCallback); NOREF(pvUser);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/** @copydoc RTLDROPS::pfnDone */
+static DECLCALLBACK(int) rtldrNativeDone(PRTLDRMODINTERNAL pMod)
+{
+ NOREF(pMod);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Operations for a native module.
+ */
+static const RTLDROPS g_rtldrNativeOps =
+{
+ "native",
+ rtldrNativeClose,
+ rtldrNativeGetSymbol,
+ rtldrNativeDone,
+ rtldrNativeEnumSymbols,
+ /* ext: */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL /*pfnQueryForwarderInfo*/,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL /*pfnUnwindFrame*/,
+ 42
+};
+
+
+
+RTDECL(int) RTLdrLoad(const char *pszFilename, PRTLDRMOD phLdrMod)
+{
+ return RTLdrLoadEx(pszFilename, phLdrMod, RTLDRLOAD_FLAGS_LOCAL, NULL);
+}
+RT_EXPORT_SYMBOL(RTLdrLoad);
+
+
+RTDECL(int) RTLdrLoadEx(const char *pszFilename, PRTLDRMOD phLdrMod, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ LogFlow(("RTLdrLoadEx: pszFilename=%p:{%s} phLdrMod=%p fFlags=%#x pErrInfo=%p\n", pszFilename, pszFilename, phLdrMod, fFlags, pErrInfo));
+
+ /*
+ * Validate and massage the input.
+ */
+ RTErrInfoClear(pErrInfo);
+ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
+ AssertPtrReturn(phLdrMod, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTLDRLOAD_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate and initialize module structure.
+ */
+ int rc = VERR_NO_MEMORY;
+ PRTLDRMODNATIVE pMod = (PRTLDRMODNATIVE)RTMemAlloc(sizeof(*pMod));
+ if (pMod)
+ {
+ pMod->Core.u32Magic = RTLDRMOD_MAGIC;
+ pMod->Core.eState = LDR_STATE_LOADED;
+ pMod->Core.pOps = &g_rtldrNativeOps;
+ pMod->Core.pReader = NULL;
+ pMod->Core.enmFormat = RTLDRFMT_NATIVE;
+ pMod->Core.enmType = RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE; /* approx */
+#ifdef RT_BIG_ENDIAN
+ pMod->Core.enmEndian = RTLDRENDIAN_BIG;
+#else
+ pMod->Core.enmEndian = RTLDRENDIAN_LITTLE;
+#endif
+#ifdef RT_ARCH_AMD64
+ pMod->Core.enmArch = RTLDRARCH_AMD64;
+#elif defined(RT_ARCH_X86)
+ pMod->Core.enmArch = RTLDRARCH_X86_32;
+#else
+ pMod->Core.enmArch = RTLDRARCH_HOST;
+#endif
+ pMod->hNative = ~(uintptr_t)0;
+ pMod->fFlags = fFlags;
+
+ /*
+ * Attempt to open the module.
+ */
+ rc = rtldrNativeLoad(pszFilename, &pMod->hNative, fFlags, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (fFlags & RTLDRLOAD_FLAGS_NO_UNLOAD)
+ RTMEM_MAY_LEAK(pMod);
+
+ *phLdrMod = &pMod->Core;
+ LogFlow(("RTLdrLoad: returns %Rrc *phLdrMod=%RTldrm\n", rc,*phLdrMod));
+ return rc;
+ }
+
+ RTMemFree(pMod);
+ }
+ else
+ RTErrInfoSetF(pErrInfo, rc, "Failed to allocate %zu bytes for the module handle", sizeof(*pMod));
+ *phLdrMod = NIL_RTLDRMOD;
+ LogFlow(("RTLdrLoad: returns %Rrc\n", rc));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrLoadEx);
+
+
+RTDECL(int) RTLdrLoadSystem(const char *pszFilename, bool fNoUnload, PRTLDRMOD phLdrMod)
+{
+ return RTLdrLoadSystemEx(pszFilename, fNoUnload ? RTLDRLOAD_FLAGS_NO_UNLOAD : 0, phLdrMod);
+}
+
+
+RTDECL(int) RTLdrLoadSystemEx(const char *pszFilename, uint32_t fFlags, PRTLDRMOD phLdrMod)
+{
+ LogFlow(("RTLdrLoadSystemEx: pszFilename=%p:{%s} fFlags=%#RX32 phLdrMod=%p\n", pszFilename, pszFilename, fFlags, phLdrMod));
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phLdrMod, VERR_INVALID_PARAMETER);
+ *phLdrMod = NIL_RTLDRMOD;
+ AssertPtrReturn(pszFilename, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!RTPathHasPath(pszFilename), ("%s\n", pszFilename), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!(fFlags & ~(RTLDRLOAD_FLAGS_VALID_MASK | RTLDRLOAD_FLAGS_SO_VER_BEGIN_MASK | RTLDRLOAD_FLAGS_SO_VER_END_MASK)),
+ ("fFlags=%#RX32\n", fFlags), VERR_INVALID_FLAGS);
+
+ /*
+ * Check the filename.
+ */
+ size_t cchFilename = strlen(pszFilename);
+ AssertMsgReturn(cchFilename < (RTPATH_MAX / 4) * 3, ("%zu\n", cchFilename), VERR_INVALID_PARAMETER);
+
+ const char *pszSuffix = NULL;
+ if (!RTPathHasSuffix(pszFilename))
+ pszSuffix = RTLdrGetSuff();
+
+ /*
+ * Let the platform specific code do the rest.
+ */
+ int rc = rtldrNativeLoadSystem(pszFilename, pszSuffix, fFlags, phLdrMod);
+ LogFlow(("RTLdrLoadSystem: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+RTDECL(void *) RTLdrGetSystemSymbol(const char *pszFilename, const char *pszSymbol)
+{
+ return RTLdrGetSystemSymbolEx(pszFilename, pszSymbol, RTLDRLOAD_FLAGS_NO_UNLOAD);
+}
+
+
+RTDECL(void *) RTLdrGetSystemSymbolEx(const char *pszFilename, const char *pszSymbol, uint32_t fFlags)
+{
+ void *pvRet = NULL;
+ RTLDRMOD hLdrMod;
+ int rc = RTLdrLoadSystemEx(pszFilename, fFlags | RTLDRLOAD_FLAGS_NO_UNLOAD, &hLdrMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hLdrMod, pszSymbol, &pvRet);
+ if (RT_FAILURE(rc))
+ pvRet = NULL; /* paranoia */
+ RTLdrClose(hLdrMod);
+ }
+ return pvRet;
+}
+
+
+RTDECL(int) RTLdrLoadAppPriv(const char *pszFilename, PRTLDRMOD phLdrMod)
+{
+ LogFlow(("RTLdrLoadAppPriv: pszFilename=%p:{%s} phLdrMod=%p\n", pszFilename, pszFilename, phLdrMod));
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phLdrMod, VERR_INVALID_PARAMETER);
+ *phLdrMod = NIL_RTLDRMOD;
+ AssertPtrReturn(pszFilename, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!RTPathHasPath(pszFilename), ("%s\n", pszFilename), VERR_INVALID_PARAMETER);
+
+ /*
+ * Check the filename.
+ */
+ size_t cchFilename = strlen(pszFilename);
+ AssertMsgReturn(cchFilename < (RTPATH_MAX / 4) * 3, ("%zu\n", cchFilename), VERR_INVALID_PARAMETER);
+
+ const char *pszSuffix = "";
+ size_t cchSuffix = 0;
+ if (!RTPathHasSuffix(pszFilename))
+ {
+ pszSuffix = RTLdrGetSuff();
+ cchSuffix = strlen(pszSuffix);
+ }
+
+ /*
+ * Construct the private arch path and check if the file exists.
+ */
+ char szPath[RTPATH_MAX];
+ int rc = RTPathAppPrivateArch(szPath, sizeof(szPath) - 1 - cchSuffix - cchFilename);
+ AssertRCReturn(rc, rc);
+
+ char *psz = strchr(szPath, '\0');
+ *psz++ = RTPATH_SLASH;
+ memcpy(psz, pszFilename, cchFilename);
+ psz += cchFilename;
+ memcpy(psz, pszSuffix, cchSuffix + 1);
+
+ if (!RTPathExists(szPath))
+ {
+ LogRel(("RTLdrLoadAppPriv: \"%s\" not found\n", szPath));
+ return VERR_FILE_NOT_FOUND;
+ }
+
+ /*
+ * Pass it on to RTLdrLoad.
+ */
+ rc = RTLdrLoad(szPath, phLdrMod);
+
+ LogFlow(("RTLdrLoadAppPriv: returns %Rrc\n", rc));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrLoadAppPriv);
+
+
+RTDECL(const char *) RTLdrGetSuff(void)
+{
+#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
+ static const char s_szSuff[] = ".DLL";
+#elif defined(RT_OS_L4)
+ static const char s_szSuff[] = ".s.so";
+#elif defined(RT_OS_DARWIN)
+ static const char s_szSuff[] = ".dylib";
+#else
+ static const char s_szSuff[] = ".so";
+#endif
+
+ return s_szSuff;
+}
+RT_EXPORT_SYMBOL(RTLdrGetSuff);
+
+
+RTDECL(uintptr_t) RTLdrGetNativeHandle(RTLDRMOD hLdrMod)
+{
+ PRTLDRMODNATIVE pThis = (PRTLDRMODNATIVE)hLdrMod;
+ AssertPtrReturn(pThis, ~(uintptr_t)0);
+ AssertReturn(pThis->Core.u32Magic == RTLDRMOD_MAGIC, ~(uintptr_t)0);
+ AssertReturn(pThis->Core.pOps == &g_rtldrNativeOps, ~(uintptr_t)0);
+ return pThis->hNative;
+}
+RT_EXPORT_SYMBOL(RTLdrGetNativeHandle);
+
+
+RTDECL(bool) RTLdrIsLoadable(const char *pszFilename)
+{
+ /*
+ * Try to load the library.
+ */
+ RTLDRMOD hLib;
+ int rc = RTLdrLoad(pszFilename, &hLib);
+ if (RT_SUCCESS(rc))
+ {
+ RTLdrClose(hLib);
+ return true;
+ }
+ return false;
+}
+RT_EXPORT_SYMBOL(RTLdrIsLoadable);
+
diff --git a/src/VBox/Runtime/common/ldr/ldrPE.cpp b/src/VBox/Runtime/common/ldr/ldrPE.cpp
new file mode 100644
index 00000000..486be79e
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/ldrPE.cpp
@@ -0,0 +1,5194 @@
+/* $Id: ldrPE.cpp $ */
+/** @file
+ * IPRT - Binary Image Loader, Portable Executable (PE).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/dbg.h>
+#include <iprt/err.h>
+#include <iprt/latin1.h>
+#include <iprt/log.h>
+#include <iprt/md5.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/sha.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <iprt/x86.h>
+#if !defined(IPRT_WITHOUT_LDR_VERIFY) || !defined(IPRT_WITHOUT_LDR_PAGE_HASHING)
+# include <iprt/zero.h>
+#endif
+#ifndef IPRT_WITHOUT_LDR_VERIFY
+# include <iprt/crypto/pkcs7.h>
+# include <iprt/crypto/spc.h>
+# include <iprt/crypto/x509.h>
+#endif
+#include <iprt/formats/codeview.h>
+#include <iprt/formats/pecoff.h>
+#include "internal/ldr.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Converts rva to a type.
+ * @param pvBits Pointer to base of image bits.
+ * @param rva Relative virtual address.
+ * @param type Type.
+ */
+#define PE_RVA2TYPE(pvBits, rva, type) ((type) ((uintptr_t)pvBits + (uintptr_t)(rva)) )
+
+/** The max size of the security directory. */
+#ifdef IN_RING3
+# define RTLDRMODPE_MAX_SECURITY_DIR_SIZE _4M
+#else
+# define RTLDRMODPE_MAX_SECURITY_DIR_SIZE _1M
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The PE loader structure.
+ */
+typedef struct RTLDRMODPE
+{
+ /** Core module structure. */
+ RTLDRMODINTERNAL Core;
+ /** Pointer to internal copy of image bits.
+ * @todo the reader should take care of this. */
+ void *pvBits;
+ /** The offset of the NT headers. */
+ RTFOFF offNtHdrs;
+ /** The offset of the first byte after the section table. */
+ RTFOFF offEndOfHdrs;
+
+ /** The machine type (IMAGE_FILE_HEADER::Machine). */
+ uint16_t u16Machine;
+ /** The file flags (IMAGE_FILE_HEADER::Characteristics). */
+ uint16_t fFile;
+ /** Number of sections (IMAGE_FILE_HEADER::NumberOfSections). */
+ unsigned cSections;
+ /** Pointer to an array of the section headers related to the file. */
+ PIMAGE_SECTION_HEADER paSections;
+
+ /** The RVA of the entry point (IMAGE_OPTIONAL_HEADER32::AddressOfEntryPoint). */
+ RTUINTPTR uEntryPointRVA;
+ /** The base address of the image at link time (IMAGE_OPTIONAL_HEADER32::ImageBase). */
+ RTUINTPTR uImageBase;
+ /** The size of the loaded image (IMAGE_OPTIONAL_HEADER32::SizeOfImage). */
+ uint32_t cbImage;
+ /** Size of the header (IMAGE_OPTIONAL_HEADER32::SizeOfHeaders). */
+ uint32_t cbHeaders;
+ /** Section alignment (IMAGE_OPTIONAL_HEADER32::SectionAlignment). */
+ uint32_t uSectionAlign;
+ /** The image timestamp. */
+ uint32_t uTimestamp;
+ /** The number of imports. UINT32_MAX if not determined. */
+ uint32_t cImports;
+ /** Set if the image is 64-bit, clear if 32-bit. */
+ bool f64Bit;
+ /** The import data directory entry. */
+ IMAGE_DATA_DIRECTORY ImportDir;
+ /** The base relocation data directory entry. */
+ IMAGE_DATA_DIRECTORY RelocDir;
+ /** The export data directory entry. */
+ IMAGE_DATA_DIRECTORY ExportDir;
+ /** The debug directory entry. */
+ IMAGE_DATA_DIRECTORY DebugDir;
+ /** The security directory entry. */
+ IMAGE_DATA_DIRECTORY SecurityDir;
+ /** The exception data directory entry. */
+ IMAGE_DATA_DIRECTORY ExceptionDir;
+
+ /** Offset of the first PKCS \#7 SignedData signature if present. */
+ uint32_t offPkcs7SignedData;
+ /** Size of the first PKCS \#7 SignedData. */
+ uint32_t cbPkcs7SignedData;
+
+ /** Copy of the optional header field DllCharacteristics. */
+ uint16_t fDllCharacteristics;
+} RTLDRMODPE;
+/** Pointer to the instance data for a PE loader module. */
+typedef RTLDRMODPE *PRTLDRMODPE;
+
+
+/**
+ * PE Loader module operations.
+ *
+ * The PE loader has one operation which is a bit different between 32-bit and 64-bit PE images,
+ * and for historical and performance reasons have been split into separate functions. Thus the
+ * PE loader extends the RTLDROPS structure with this one entry.
+ */
+typedef struct RTLDROPSPE
+{
+ /** The usual ops. */
+ RTLDROPS Core;
+
+ /**
+ * Resolves all imports.
+ *
+ * @returns iprt status code.
+ * @param pModPe Pointer to the PE loader module structure.
+ * @param pvBitsR Where to read raw image bits. (optional)
+ * @param pvBitsW Where to store the imports. The size of this buffer is equal or
+ * larger to the value returned by pfnGetImageSize().
+ * @param pfnGetImport The callback function to use to resolve imports (aka unresolved externals).
+ * @param pvUser User argument to pass to the callback.
+ */
+ DECLCALLBACKMEMBER(int, pfnResolveImports,(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, PFNRTLDRIMPORT pfnGetImport, void *pvUser));
+
+ /** Dummy entry to make sure we've initialized it all. */
+ RTUINT uDummy;
+} RTLDROPSPE, *PRTLDROPSPE;
+
+
+/**
+ * PE hash context union.
+ */
+typedef union RTLDRPEHASHCTXUNION
+{
+ RTSHA512CONTEXT Sha512;
+ RTSHA384CONTEXT Sha384;
+ RTSHA256CONTEXT Sha256;
+ RTSHA1CONTEXT Sha1;
+ RTMD5CONTEXT Md5;
+} RTLDRPEHASHCTXUNION;
+/** Pointer to a PE hash context union. */
+typedef RTLDRPEHASHCTXUNION *PRTLDRPEHASHCTXUNION;
+
+
+/**
+ * PE hash digests
+ */
+typedef union RTLDRPEHASHRESUNION
+{
+ uint8_t abSha512[RTSHA512_HASH_SIZE];
+ uint8_t abSha384[RTSHA384_HASH_SIZE];
+ uint8_t abSha256[RTSHA256_HASH_SIZE];
+ uint8_t abSha1[RTSHA1_HASH_SIZE];
+ uint8_t abMd5[RTMD5_HASH_SIZE];
+} RTLDRPEHASHRESUNION;
+/** Pointer to a PE hash work set. */
+typedef RTLDRPEHASHRESUNION *PRTLDRPEHASHRESUNION;
+
+/**
+ * Special places to watch out for when hashing a PE image.
+ */
+typedef struct RTLDRPEHASHSPECIALS
+{
+ uint32_t cbToHash;
+ uint32_t offCksum;
+ uint32_t cbCksum;
+ uint32_t offSecDir;
+ uint32_t cbSecDir;
+ uint32_t offEndSpecial;
+} RTLDRPEHASHSPECIALS;
+/** Pointer to the structure with the special hash places. */
+typedef RTLDRPEHASHSPECIALS *PRTLDRPEHASHSPECIALS;
+
+
+#ifndef IPRT_WITHOUT_LDR_VERIFY
+/**
+ * Parsed data for one signature.
+ */
+typedef struct RTLDRPESIGNATUREONE
+{
+ /** The outer content info wrapper. */
+ PRTCRPKCS7CONTENTINFO pContentInfo;
+ /** Pointer to the decoded SignedData inside the ContentInfo member. */
+ PRTCRPKCS7SIGNEDDATA pSignedData;
+ /** Pointer to the indirect data content. */
+ PRTCRSPCINDIRECTDATACONTENT pIndData;
+ /** The digest type employed by the signature. */
+ RTDIGESTTYPE enmDigest;
+ /** Set if we've already validate the image hash. */
+ bool fValidatedImageHash;
+ /** The signature number. */
+ uint16_t iSignature;
+ /** Hash result. */
+ RTLDRPEHASHRESUNION HashRes;
+} RTLDRPESIGNATUREONE;
+/** Pointer to the parsed data of one signature. */
+typedef RTLDRPESIGNATUREONE *PRTLDRPESIGNATUREONE;
+
+/**
+ * Parsed signature data.
+ */
+typedef struct RTLDRPESIGNATURE
+{
+ /** Pointer to the raw signatures. This is allocated in the continuation of
+ * this structure to keep things simple. The size is given by the security
+ * export directory. */
+ WIN_CERTIFICATE const *pRawData;
+ /** The outer content info wrapper (primary signature). */
+ RTCRPKCS7CONTENTINFO PrimaryContentInfo;
+ /** The info for the primary signature. */
+ RTLDRPESIGNATUREONE Primary;
+ /** Number of nested signatures (zero if none). */
+ uint16_t cNested;
+ /** Pointer to an array of nested signatures (NULL if none). */
+ PRTLDRPESIGNATUREONE paNested;
+ /** Hash scratch data. */
+ RTLDRPEHASHCTXUNION HashCtx;
+} RTLDRPESIGNATURE;
+/** Pointed to SigneData parsing stat and output. */
+typedef RTLDRPESIGNATURE *PRTLDRPESIGNATURE;
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtldrPEConvert32BitOptionalHeaderTo64Bit(PIMAGE_OPTIONAL_HEADER64 pOptHdr);
+static void rtldrPEConvert32BitLoadConfigTo64Bit(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg);
+static int rtldrPEApplyFixups(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, RTUINTPTR BaseAddress, RTUINTPTR OldBaseAddress);
+#ifndef IPRT_WITHOUT_LDR_PAGE_HASHING
+static int rtLdrPE_QueryPageHashes(PRTLDRMODPE pModPe, RTDIGESTTYPE enmDigest, void *pvBuf, size_t cbBuf, size_t *pcbRet);
+static uint32_t rtLdrPE_GetHashablePages(PRTLDRMODPE pModPe);
+#endif
+
+
+
+/**
+ * Reads a section of a PE image given by RVA + size, using mapped bits if
+ * available or allocating heap memory and reading from the file.
+ *
+ * @returns IPRT status code.
+ * @param pThis Pointer to the PE loader module structure.
+ * @param pvBits Read only bits if available. NULL if not.
+ * @param uRva The RVA to read at.
+ * @param cbMem The number of bytes to read.
+ * @param ppvMem Where to return the memory on success (heap or
+ * inside pvBits).
+ */
+static int rtldrPEReadPartByRva(PRTLDRMODPE pThis, const void *pvBits, uint32_t uRva, uint32_t cbMem, void const **ppvMem)
+{
+ *ppvMem = NULL;
+ if (!cbMem)
+ return VINF_SUCCESS;
+
+ /*
+ * Use bits if we've got some.
+ */
+ if (pvBits)
+ {
+ *ppvMem = (uint8_t const *)pvBits + uRva;
+ return VINF_SUCCESS;
+ }
+ if (pThis->pvBits)
+ {
+ *ppvMem = (uint8_t const *)pThis->pvBits + uRva;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Allocate a buffer and read the bits from the file (or whatever).
+ */
+ if (!pThis->Core.pReader)
+ return VERR_ACCESS_DENIED;
+
+ uint8_t *pbMem = (uint8_t *)RTMemAllocZ(cbMem);
+ if (!pbMem)
+ return VERR_NO_MEMORY;
+ *ppvMem = pbMem;
+
+ /* Do the reading on a per section base. */
+ uint64_t const cbFile = pThis->Core.pReader->pfnSize(pThis->Core.pReader);
+ for (;;)
+ {
+ /* Translate the RVA into a file offset. */
+ uint32_t offFile = uRva;
+ uint32_t cbToRead = cbMem;
+ uint32_t cbToAdv = cbMem;
+
+ if (uRva < pThis->paSections[0].VirtualAddress)
+ {
+ /* Special header section. */
+ cbToRead = pThis->paSections[0].VirtualAddress - uRva;
+ if (cbToRead > cbMem)
+ cbToRead = cbMem;
+ cbToAdv = cbToRead;
+
+ /* The following capping is an approximation. */
+ uint32_t offFirstRawData = RT_ALIGN(pThis->cbHeaders, _4K);
+ if ( pThis->paSections[0].PointerToRawData > 0
+ && pThis->paSections[0].SizeOfRawData > 0)
+ offFirstRawData = pThis->paSections[0].PointerToRawData;
+ if (offFile >= offFirstRawData)
+ cbToRead = 0;
+ else if (offFile + cbToRead > offFirstRawData)
+ cbToRead = offFile - offFirstRawData;
+ }
+ else
+ {
+ /* Find the matching section and its mapping size. */
+ uint32_t j = 0;
+ uint32_t cbMapping = 0;
+ uint32_t offSection = 0;
+ while (j < pThis->cSections)
+ {
+ cbMapping = (j + 1 < pThis->cSections ? pThis->paSections[j + 1].VirtualAddress : pThis->cbImage)
+ - pThis->paSections[j].VirtualAddress;
+ offSection = uRva - pThis->paSections[j].VirtualAddress;
+ if (offSection < cbMapping)
+ break;
+ j++;
+ }
+ if (j >= cbMapping)
+ break; /* This shouldn't happen, just return zeros if it does. */
+
+ /* Adjust the sizes and calc the file offset. */
+ if (offSection + cbToAdv > cbMapping)
+ cbToAdv = cbToRead = cbMapping - offSection;
+
+ if ( pThis->paSections[j].PointerToRawData > 0
+ && pThis->paSections[j].SizeOfRawData > 0)
+ {
+ offFile = offSection;
+ if (offFile + cbToRead > pThis->paSections[j].SizeOfRawData)
+ cbToRead = pThis->paSections[j].SizeOfRawData - offFile;
+ offFile += pThis->paSections[j].PointerToRawData;
+ }
+ else
+ {
+ offFile = UINT32_MAX;
+ cbToRead = 0;
+ }
+ }
+
+ /* Perform the read after adjusting a little (paranoia). */
+ if (offFile > cbFile)
+ cbToRead = 0;
+ if (cbToRead)
+ {
+ if ((uint64_t)offFile + cbToRead > cbFile)
+ cbToRead = (uint32_t)(cbFile - (uint64_t)offFile);
+ int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pbMem, cbToRead, offFile);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree((void *)*ppvMem);
+ *ppvMem = NULL;
+ return rc;
+ }
+ }
+
+ /* Advance */
+ if (cbMem <= cbToAdv)
+ break;
+ cbMem -= cbToAdv;
+ pbMem += cbToAdv;
+ uRva += cbToAdv;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Reads a part of a PE file from the file and into a heap block.
+ *
+ * @returns IRPT status code.
+ * @param pThis Pointer to the PE loader module structure..
+ * @param offFile The file offset.
+ * @param cbMem The number of bytes to read.
+ * @param ppvMem Where to return the heap block with the bytes on
+ * success.
+ */
+static int rtldrPEReadPartFromFile(PRTLDRMODPE pThis, uint32_t offFile, uint32_t cbMem, void const **ppvMem)
+{
+ *ppvMem = NULL;
+ if (!cbMem)
+ return VINF_SUCCESS;
+
+ /*
+ * Allocate a buffer and read the bits from the file (or whatever).
+ */
+ if (!pThis->Core.pReader)
+ return VERR_ACCESS_DENIED;
+
+ uint8_t *pbMem = (uint8_t *)RTMemAlloc(cbMem);
+ if (!pbMem)
+ return VERR_NO_MEMORY;
+
+ int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pbMem, cbMem, offFile);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree((void *)*ppvMem);
+ return rc;
+ }
+
+ *ppvMem = pbMem;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Reads a part of a PE image into memory one way or another.
+ *
+ * Either the RVA or the offFile must be valid. We'll prefer the RVA if
+ * possible.
+ *
+ * @returns IPRT status code.
+ * @param pThis Pointer to the PE loader module structure.
+ * @param pvBits Read only bits if available. NULL if not.
+ * @param uRva The RVA to read at.
+ * @param offFile The file offset.
+ * @param cbMem The number of bytes to read.
+ * @param ppvMem Where to return the memory on success (heap or
+ * inside pvBits).
+ */
+static int rtldrPEReadPart(PRTLDRMODPE pThis, const void *pvBits, RTFOFF offFile, RTLDRADDR uRva,
+ uint32_t cbMem, void const **ppvMem)
+{
+ if ( uRva == NIL_RTLDRADDR
+ || uRva > pThis->cbImage
+ || cbMem > pThis->cbImage
+ || uRva + cbMem > pThis->cbImage)
+ {
+ if (offFile < 0 || offFile >= UINT32_MAX)
+ return VERR_INVALID_PARAMETER;
+ return rtldrPEReadPartFromFile(pThis, (uint32_t)offFile, cbMem, ppvMem);
+ }
+ return rtldrPEReadPartByRva(pThis, pvBits, (uint32_t)uRva, cbMem, ppvMem);
+}
+
+
+/**
+ * Frees up memory returned by rtldrPEReadPart*.
+ *
+ * @param pThis Pointer to the PE loader module structure..
+ * @param pvBits Read only bits if available. NULL if not..
+ * @param pvMem The memory we were given by the reader method.
+ */
+static void rtldrPEFreePart(PRTLDRMODPE pThis, const void *pvBits, void const *pvMem)
+{
+ if (!pvMem)
+ return;
+
+ if (pvBits && (uintptr_t)pvMem - (uintptr_t)pvBits < pThis->cbImage)
+ return;
+ if (pThis->pvBits && (uintptr_t)pvMem - (uintptr_t)pThis->pvBits < pThis->cbImage)
+ return;
+
+ RTMemFree((void *)pvMem);
+}
+
+
+/**
+ * Reads a section of a PE image given by RVA + size.
+ *
+ * @returns IPRT status code.
+ * @param pThis Pointer to the PE loader module structure.
+ * @param pvBits Read only bits if available. NULL if not.
+ * @param uRva The RVA to read at.
+ * @param cbMem The number of bytes to read.
+ * @param pvDst The destination buffer.
+ */
+static int rtldrPEReadPartByRvaInfoBuf(PRTLDRMODPE pThis, const void *pvBits, uint32_t uRva, uint32_t cbMem, void *pvDst)
+{
+ /** @todo consider optimizing this. */
+ const void *pvSrc = NULL;
+ int rc = rtldrPEReadPartByRva(pThis, pvBits, uRva, cbMem, &pvSrc);
+ if (RT_SUCCESS(rc))
+ {
+ memcpy(pvDst, pvSrc, cbMem);
+ rtldrPEFreePart(pThis, NULL, pvSrc);
+ }
+ return rc;
+}
+
+
+
+
+
+/** @interface_method_impl{RTLDROPS,pfnGetImageSize} */
+static DECLCALLBACK(size_t) rtldrPEGetImageSize(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+ return pModPe->cbImage;
+}
+
+
+/**
+ * Reads the image into memory.
+ *
+ * @returns iprt status code.
+ * @param pModPe The PE module.
+ * @param pvBits Where to store the bits, this buffer is at least pItem->Core.cbImage in size.
+ */
+static int rtldrPEGetBitsNoImportsNorFixups(PRTLDRMODPE pModPe, void *pvBits)
+{
+ /*
+ * Both these checks are related to pfnDone().
+ */
+ PRTLDRREADER pReader = pModPe->Core.pReader;
+ if (!pReader)
+ {
+ AssertMsgFailed(("You've called done!\n"));
+ return VERR_WRONG_ORDER;
+ }
+ if (!pvBits)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Zero everything (could be done per section).
+ */
+ memset(pvBits, 0, pModPe->cbImage);
+
+#ifdef PE_FILE_OFFSET_EQUALS_RVA
+ /*
+ * Read the entire image / file.
+ */
+ const uint64_t cbRawImage = pReader->pfnSize(pReader)
+ rc = pReader->pfnRead(pReader, pvBits, RT_MIN(pModPe->cbImage, cbRawImage), 0);
+ if (RT_FAILURE(rc))
+ Log(("rtldrPE: %s: Reading %#x bytes at offset %#x failed, %Rrc!!! (the entire image)\n",
+ pReader->pfnLogName(pReader), RT_MIN(pModPe->cbImage, cbRawImage), 0, rc));
+#else
+
+ /*
+ * Read the headers.
+ */
+ int rc = pReader->pfnRead(pReader, pvBits, pModPe->cbHeaders, 0);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Read the sections.
+ */
+ PIMAGE_SECTION_HEADER pSH = pModPe->paSections;
+ for (unsigned cLeft = pModPe->cSections; cLeft > 0; cLeft--, pSH++)
+ if ( pSH->SizeOfRawData
+ && pSH->Misc.VirtualSize
+ && !(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD))
+ {
+ uint32_t const cbToRead = RT_MIN(pSH->SizeOfRawData, pModPe->cbImage - pSH->VirtualAddress);
+ Assert(pSH->VirtualAddress <= pModPe->cbImage);
+
+ rc = pReader->pfnRead(pReader, (uint8_t *)pvBits + pSH->VirtualAddress, cbToRead, pSH->PointerToRawData);
+ if (RT_FAILURE(rc))
+ {
+ Log(("rtldrPE: %s: Reading %#x bytes at offset %#x failed, %Rrc - section #%d '%.*s'!!!\n",
+ pReader->pfnLogName(pReader), pSH->SizeOfRawData, pSH->PointerToRawData, rc,
+ pSH - pModPe->paSections, sizeof(pSH->Name), pSH->Name));
+ break;
+ }
+ }
+ }
+ else
+ Log(("rtldrPE: %s: Reading %#x bytes at offset %#x failed, %Rrc!!!\n",
+ pReader->pfnLogName(pReader), pModPe->cbHeaders, 0, rc));
+#endif
+ return rc;
+}
+
+
+/**
+ * Reads the bits into the internal buffer pointed to by PRTLDRMODPE::pvBits.
+ *
+ * @returns iprt status code.
+ * @param pModPe The PE module.
+ */
+static int rtldrPEReadBits(PRTLDRMODPE pModPe)
+{
+ Assert(!pModPe->pvBits);
+ void *pvBitsW = RTMemAllocZ(pModPe->cbImage);
+ if (!pvBitsW)
+ return VERR_NO_MEMORY;
+ int rc = rtldrPEGetBitsNoImportsNorFixups(pModPe, pvBitsW);
+ if (RT_SUCCESS(rc))
+ pModPe->pvBits = pvBitsW;
+ else
+ RTMemFree(pvBitsW);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnGetBits} */
+static DECLCALLBACK(int) rtldrPEGetBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+
+ /*
+ * Read the image.
+ */
+ int rc = rtldrPEGetBitsNoImportsNorFixups(pModPe, pvBits);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Resolve imports.
+ */
+ if (pfnGetImport)
+ rc = ((PRTLDROPSPE)pMod->pOps)->pfnResolveImports(pModPe, pvBits, pvBits, pfnGetImport, pvUser);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Apply relocations.
+ */
+ rc = rtldrPEApplyFixups(pModPe, pvBits, pvBits, BaseAddress, pModPe->uImageBase);
+ if (RT_SUCCESS(rc))
+ return rc;
+ AssertMsgFailed(("Failed to apply fixups. rc=%Rrc\n", rc));
+ }
+#ifndef IN_SUP_HARDENED_R3
+ else
+ AssertMsgFailed(("Failed to resolve imports. rc=%Rrc\n", rc));
+#endif
+ }
+ return rc;
+}
+
+
+/* The image_thunk_data32/64 structures are not very helpful except for getting RSI. keep them around till all the code has been converted. */
+typedef struct _IMAGE_THUNK_DATA32
+{
+ union
+ {
+ uint32_t ForwarderString;
+ uint32_t Function;
+ uint32_t Ordinal;
+ uint32_t AddressOfData;
+ } u1;
+} IMAGE_THUNK_DATA32;
+typedef IMAGE_THUNK_DATA32 *PIMAGE_THUNK_DATA32;
+
+
+/** @copydoc RTLDROPSPE::pfnResolveImports */
+static DECLCALLBACK(int) rtldrPEResolveImports32(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ /*
+ * Check if there is actually anything to work on.
+ */
+ if ( !pModPe->ImportDir.VirtualAddress
+ || !pModPe->ImportDir.Size)
+ return 0;
+
+ /*
+ * Walk the IMAGE_IMPORT_DESCRIPTOR table.
+ */
+ int rc = VINF_SUCCESS;
+ PIMAGE_IMPORT_DESCRIPTOR pImps;
+ for (pImps = PE_RVA2TYPE(pvBitsR, pModPe->ImportDir.VirtualAddress, PIMAGE_IMPORT_DESCRIPTOR);
+ !rc && pImps->Name != 0 && pImps->FirstThunk != 0;
+ pImps++)
+ {
+ AssertReturn(pImps->Name < pModPe->cbImage, VERR_BAD_EXE_FORMAT);
+ const char *pszModName = PE_RVA2TYPE(pvBitsR, pImps->Name, const char *);
+ AssertReturn(pImps->FirstThunk < pModPe->cbImage, VERR_BAD_EXE_FORMAT);
+ AssertReturn(pImps->u.OriginalFirstThunk < pModPe->cbImage, VERR_BAD_EXE_FORMAT);
+
+ Log3(("RTLdrPE: Import descriptor: %s\n", pszModName));
+ Log4(("RTLdrPE: OriginalFirstThunk = %#RX32\n"
+ "RTLdrPE: TimeDateStamp = %#RX32\n"
+ "RTLdrPE: ForwarderChain = %#RX32\n"
+ "RTLdrPE: Name = %#RX32\n"
+ "RTLdrPE: FirstThunk = %#RX32\n",
+ pImps->u.OriginalFirstThunk, pImps->TimeDateStamp,
+ pImps->ForwarderChain, pImps->Name, pImps->FirstThunk));
+
+ /*
+ * Walk the thunks table(s).
+ */
+ PIMAGE_THUNK_DATA32 pFirstThunk = PE_RVA2TYPE(pvBitsW, pImps->FirstThunk, PIMAGE_THUNK_DATA32); /* update this. */
+ PIMAGE_THUNK_DATA32 pThunk = pImps->u.OriginalFirstThunk == 0 /* read from this. */
+ ? PE_RVA2TYPE(pvBitsR, pImps->FirstThunk, PIMAGE_THUNK_DATA32)
+ : PE_RVA2TYPE(pvBitsR, pImps->u.OriginalFirstThunk, PIMAGE_THUNK_DATA32);
+ while (!rc && pThunk->u1.Ordinal != 0)
+ {
+ RTUINTPTR Value = 0;
+ if (pThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32)
+ {
+ rc = pfnGetImport(&pModPe->Core, pszModName, NULL, IMAGE_ORDINAL32(pThunk->u1.Ordinal), &Value, pvUser);
+ Log4((RT_SUCCESS(rc) ? "RTLdrPE: %RTptr #%u\n" : "RTLdrPE: %08RX32 #%u rc=%Rrc\n",
+ (uint32_t)Value, IMAGE_ORDINAL32(pThunk->u1.Ordinal), rc));
+ }
+ else if ( pThunk->u1.Ordinal > 0
+ && pThunk->u1.Ordinal < pModPe->cbImage)
+ {
+ rc = pfnGetImport(&pModPe->Core, pszModName,
+ PE_RVA2TYPE(pvBitsR, (char*)(uintptr_t)pThunk->u1.AddressOfData + 2, const char *),
+ ~0U, &Value, pvUser);
+ Log4((RT_SUCCESS(rc) ? "RTLdrPE: %RTptr %s\n" : "RTLdrPE: %08RX32 %s rc=%Rrc\n",
+ (uint32_t)Value, PE_RVA2TYPE(pvBitsR, (char*)(uintptr_t)pThunk->u1.AddressOfData + 2, const char *), rc));
+ }
+ else
+ {
+ AssertMsgFailed(("bad import data thunk!\n"));
+ rc = VERR_BAD_EXE_FORMAT;
+ }
+ pFirstThunk->u1.Function = (uint32_t)Value;
+ if (pFirstThunk->u1.Function != Value)
+ {
+ AssertMsgFailed(("external symbol address to big!\n"));
+ rc = VERR_ADDRESS_CONFLICT; /** @todo get me a better error status code. */
+ }
+ pThunk++;
+ pFirstThunk++;
+ }
+ }
+
+ return rc;
+}
+
+
+/* The image_thunk_data32/64 structures are not very helpful except for getting RSI. keep them around till all the code has been converted. */
+typedef struct _IMAGE_THUNK_DATA64
+{
+ union
+ {
+ uint64_t ForwarderString;
+ uint64_t Function;
+ uint64_t Ordinal;
+ uint64_t AddressOfData;
+ } u1;
+} IMAGE_THUNK_DATA64;
+typedef IMAGE_THUNK_DATA64 *PIMAGE_THUNK_DATA64;
+
+
+/** @copydoc RTLDROPSPE::pfnResolveImports */
+static DECLCALLBACK(int) rtldrPEResolveImports64(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW,
+ PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ /*
+ * Check if there is actually anything to work on.
+ */
+ if ( !pModPe->ImportDir.VirtualAddress
+ || !pModPe->ImportDir.Size)
+ return 0;
+
+ /*
+ * Walk the IMAGE_IMPORT_DESCRIPTOR table.
+ */
+ int rc = VINF_SUCCESS;
+ PIMAGE_IMPORT_DESCRIPTOR pImps;
+ for (pImps = PE_RVA2TYPE(pvBitsR, pModPe->ImportDir.VirtualAddress, PIMAGE_IMPORT_DESCRIPTOR);
+ !rc && pImps->Name != 0 && pImps->FirstThunk != 0;
+ pImps++)
+ {
+ AssertReturn(pImps->Name < pModPe->cbImage, VERR_BAD_EXE_FORMAT);
+ const char *pszModName = PE_RVA2TYPE(pvBitsR, pImps->Name, const char *);
+ AssertReturn(pImps->FirstThunk < pModPe->cbImage, VERR_BAD_EXE_FORMAT);
+ AssertReturn(pImps->u.OriginalFirstThunk < pModPe->cbImage, VERR_BAD_EXE_FORMAT);
+
+ Log3(("RTLdrPE: Import descriptor: %s\n", pszModName));
+ Log4(("RTLdrPE: OriginalFirstThunk = %#RX32\n"
+ "RTLdrPE: TimeDateStamp = %#RX32\n"
+ "RTLdrPE: ForwarderChain = %#RX32\n"
+ "RTLdrPE: Name = %#RX32\n"
+ "RTLdrPE: FirstThunk = %#RX32\n",
+ pImps->u.OriginalFirstThunk, pImps->TimeDateStamp,
+ pImps->ForwarderChain, pImps->Name, pImps->FirstThunk));
+
+ /*
+ * Walk the thunks table(s).
+ */
+ PIMAGE_THUNK_DATA64 pFirstThunk = PE_RVA2TYPE(pvBitsW, pImps->FirstThunk, PIMAGE_THUNK_DATA64); /* update this. */
+ PIMAGE_THUNK_DATA64 pThunk = pImps->u.OriginalFirstThunk == 0 /* read from this. */
+ ? PE_RVA2TYPE(pvBitsR, pImps->FirstThunk, PIMAGE_THUNK_DATA64)
+ : PE_RVA2TYPE(pvBitsR, pImps->u.OriginalFirstThunk, PIMAGE_THUNK_DATA64);
+ while (!rc && pThunk->u1.Ordinal != 0)
+ {
+ RTUINTPTR Value = 0;
+ if (pThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64)
+ {
+ rc = pfnGetImport(&pModPe->Core, pszModName, NULL, (unsigned)IMAGE_ORDINAL64(pThunk->u1.Ordinal), &Value, pvUser);
+ Log4((RT_SUCCESS(rc) ? "RTLdrPE: %016RX64 #%u\n" : "RTLdrPE: %016RX64 #%u rc=%Rrc\n",
+ (uint64_t)Value, (unsigned)IMAGE_ORDINAL64(pThunk->u1.Ordinal), rc));
+ }
+ else if ( pThunk->u1.Ordinal > 0
+ && pThunk->u1.Ordinal < pModPe->cbImage)
+ {
+ /** @todo add validation of the string pointer! */
+ rc = pfnGetImport(&pModPe->Core, pszModName, PE_RVA2TYPE(pvBitsR, (uintptr_t)pThunk->u1.AddressOfData + 2, const char *),
+ ~0U, &Value, pvUser);
+ Log4((RT_SUCCESS(rc) ? "RTLdrPE: %016RX64 %s\n" : "RTLdrPE: %016RX64 %s rc=%Rrc\n",
+ (uint64_t)Value, PE_RVA2TYPE(pvBitsR, (uintptr_t)pThunk->u1.AddressOfData + 2, const char *), rc));
+ }
+ else
+ {
+ AssertMsgFailed(("bad import data thunk!\n"));
+ rc = VERR_BAD_EXE_FORMAT;
+ }
+ pFirstThunk->u1.Function = Value;
+ pThunk++;
+ pFirstThunk++;
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Applies fixups.
+ */
+static int rtldrPEApplyFixups(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, RTUINTPTR BaseAddress,
+ RTUINTPTR OldBaseAddress)
+{
+ if ( !pModPe->RelocDir.VirtualAddress
+ || !pModPe->RelocDir.Size)
+ return 0;
+
+ /*
+ * Apply delta fixups iterating fixup chunks.
+ */
+ PIMAGE_BASE_RELOCATION pbr = PE_RVA2TYPE(pvBitsR, pModPe->RelocDir.VirtualAddress, PIMAGE_BASE_RELOCATION);
+ PIMAGE_BASE_RELOCATION pBaseRelocs = pbr;
+ unsigned cbBaseRelocs = pModPe->RelocDir.Size;
+ RTUINTPTR uDelta = BaseAddress - OldBaseAddress;
+ Log2(("RTLdrPE: Fixups: uDelta=%#RTptr BaseAddress=%#RTptr OldBaseAddress=%#RTptr\n", uDelta, BaseAddress, OldBaseAddress));
+ Log4(("RTLdrPE: BASERELOC: VirtualAddres=%RX32 Size=%RX32\n", pModPe->RelocDir.VirtualAddress, pModPe->RelocDir.Size));
+ Assert(sizeof(*pbr) == sizeof(uint32_t) * 2);
+
+ while ( (uintptr_t)pbr - (uintptr_t)pBaseRelocs + 8 < cbBaseRelocs /* 8= VirtualAddress and SizeOfBlock members */
+ && pbr->SizeOfBlock >= 8)
+ {
+ uint16_t *pwoffFixup = (uint16_t *)(pbr + 1);
+ uint32_t cRelocations = (pbr->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(uint16_t);
+ Log3(("RTLdrPE: base relocs for %#010x, size %#06x (%d relocs)\n", pbr->VirtualAddress, pbr->SizeOfBlock, cRelocations));
+
+ /* Some bound checking just to be sure it works... */
+ if ((uintptr_t)pbr - (uintptr_t)pBaseRelocs + pbr->SizeOfBlock > cbBaseRelocs)
+ cRelocations = (uint32_t)( (((uintptr_t)pBaseRelocs + cbBaseRelocs) - (uintptr_t)pbr - sizeof(IMAGE_BASE_RELOCATION))
+ / sizeof(uint16_t) );
+
+ /*
+ * Loop thru the fixups in this chunk.
+ */
+ while (cRelocations != 0)
+ {
+ /*
+ * Common fixup
+ */
+ static const char * const s_apszReloc[16] =
+ {
+ "ABS", "HIGH", "LOW", "HIGHLOW", "HIGHADJ", "MIPS_JMPADDR", "RES6", "RES7",
+ "RES8", "IA64_IMM64", "DIR64", "HIGH3ADJ", "RES12", "RES13", "RES14", "RES15"
+ }; NOREF(s_apszReloc);
+ union
+ {
+ uint16_t *pu16;
+ uint32_t *pu32;
+ uint64_t *pu64;
+ } u;
+ const int offFixup = *pwoffFixup & 0xfff;
+ u.pu32 = PE_RVA2TYPE(pvBitsW, offFixup + pbr->VirtualAddress, uint32_t *);
+ const int fType = *pwoffFixup >> 12;
+ Log4(("RTLdrPE: %08x %s\n", offFixup + pbr->VirtualAddress, s_apszReloc[fType]));
+ switch (fType)
+ {
+ case IMAGE_REL_BASED_HIGHLOW: /* 32-bit, add delta. */
+ *u.pu32 += (uint32_t)uDelta;
+ break;
+ case IMAGE_REL_BASED_DIR64: /* 64-bit, add delta. */
+ *u.pu64 += (RTINTPTR)uDelta;
+ break;
+ case IMAGE_REL_BASED_ABSOLUTE: /* Alignment placeholder. */
+ break;
+ /* odd ones */
+ case IMAGE_REL_BASED_LOW: /* 16-bit, add 1st 16-bit part of the delta. */
+ *u.pu16 += (uint16_t)uDelta;
+ break;
+ case IMAGE_REL_BASED_HIGH: /* 16-bit, add 2nd 16-bit part of the delta. */
+ *u.pu16 += (uint16_t)(uDelta >> 16);
+ break;
+ /* never ever seen these next two, and I'm not 100% sure they are correctly implemented here. */
+ case IMAGE_REL_BASED_HIGHADJ:
+ {
+ if (cRelocations <= 1)
+ {
+ AssertMsgFailed(("HIGHADJ missing 2nd record!\n"));
+ return VERR_BAD_EXE_FORMAT;
+ }
+ cRelocations--;
+ pwoffFixup++;
+ int32_t i32 = (uint32_t)(*u.pu16 << 16) | *pwoffFixup;
+ i32 += (uint32_t)uDelta;
+ i32 += 0x8000; //??
+ *u.pu16 = (uint16_t)(i32 >> 16);
+ break;
+ }
+ case IMAGE_REL_BASED_HIGH3ADJ:
+ {
+ if (cRelocations <= 2)
+ {
+ AssertMsgFailed(("HIGHADJ3 missing 2nd record!\n"));
+ return VERR_BAD_EXE_FORMAT;
+ }
+ cRelocations -= 2;
+ pwoffFixup++;
+ int64_t i64 = ((uint64_t)*u.pu16 << 32) | *(uint32_t *)pwoffFixup++;
+ i64 += (int64_t)uDelta << 16; //??
+ i64 += 0x80000000;//??
+ *u.pu16 = (uint16_t)(i64 >> 32);
+ break;
+ }
+ default:
+ AssertMsgFailed(("Unknown fixup type %d offset=%#x\n", fType, offFixup));
+ break;
+ }
+
+ /*
+ * Next offset/type
+ */
+ pwoffFixup++;
+ cRelocations--;
+ } /* while loop */
+
+ /*
+ * Next Fixup chunk. (i.e. next page)
+ */
+ pbr = (PIMAGE_BASE_RELOCATION)((uintptr_t)pbr + pbr->SizeOfBlock);
+ } /* while loop */
+
+ return 0;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnRelocate} */
+static DECLCALLBACK(int) rtldrPERelocate(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress, RTUINTPTR OldBaseAddress,
+ PFNRTLDRIMPORT pfnGetImport, void *pvUser)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+
+ /*
+ * Do we have to read the image bits?
+ */
+ if (!pModPe->pvBits)
+ {
+ int rc = rtldrPEReadBits(pModPe);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Process imports.
+ */
+ int rc = ((PRTLDROPSPE)pMod->pOps)->pfnResolveImports(pModPe, pModPe->pvBits, pvBits, pfnGetImport, pvUser);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Apply relocations.
+ */
+ rc = rtldrPEApplyFixups(pModPe, pModPe->pvBits, pvBits, NewBaseAddress, OldBaseAddress);
+ AssertRC(rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Internal worker for pfnGetSymbolEx and pfnQueryForwarderInfo.
+ *
+ * @returns IPRT status code.
+ * @param pModPe The PE module instance.
+ * @param iOrdinal The symbol ordinal, UINT32_MAX if named symbol.
+ * @param pszSymbol The symbol name.
+ * @param ppvBits The image bits pointer (input/output).
+ * @param puRvaExport Where to return the symbol RVA.
+ * @param puOrdinal Where to return the ordinal number. Optional.
+ */
+static int rtLdrPE_ExportToRva(PRTLDRMODPE pModPe, uint32_t iOrdinal, const char *pszSymbol,
+ const void **ppvBits, uint32_t *puRvaExport, uint32_t *puOrdinal)
+{
+ /*
+ * Check if there is actually anything to work on.
+ */
+ if ( !pModPe->ExportDir.VirtualAddress
+ || !pModPe->ExportDir.Size)
+ return VERR_SYMBOL_NOT_FOUND;
+
+ /*
+ * No bits supplied? Do we need to read the bits?
+ */
+ void const *pvBits = *ppvBits;
+ if (!pvBits)
+ {
+ if (!pModPe->pvBits)
+ {
+ int rc = rtldrPEReadBits(pModPe);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ *ppvBits = pvBits = pModPe->pvBits;
+ }
+
+ PIMAGE_EXPORT_DIRECTORY pExpDir = PE_RVA2TYPE(pvBits, pModPe->ExportDir.VirtualAddress, PIMAGE_EXPORT_DIRECTORY);
+ int iExpOrdinal = 0; /* index into address table. */
+ if (iOrdinal != UINT32_MAX)
+ {
+ /*
+ * Find ordinal export: Simple table lookup.
+ */
+ if ( iOrdinal >= pExpDir->Base + RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions)
+ || iOrdinal < pExpDir->Base)
+ return VERR_SYMBOL_NOT_FOUND;
+ iExpOrdinal = iOrdinal - pExpDir->Base;
+ }
+ else
+ {
+ /*
+ * Find Named Export: Do binary search on the name table.
+ */
+ uint32_t *paRVANames = PE_RVA2TYPE(pvBits, pExpDir->AddressOfNames, uint32_t *);
+ uint16_t *paOrdinals = PE_RVA2TYPE(pvBits, pExpDir->AddressOfNameOrdinals, uint16_t *);
+ int iStart = 1;
+ int iEnd = pExpDir->NumberOfNames;
+
+ for (;;)
+ {
+ /* end of search? */
+ if (iStart > iEnd)
+ {
+#ifdef RT_STRICT
+ /* do a linear search just to verify the correctness of the above algorithm */
+ for (unsigned i = 0; i < pExpDir->NumberOfNames; i++)
+ {
+ AssertMsg(i == 0 || strcmp(PE_RVA2TYPE(pvBits, paRVANames[i], const char *), PE_RVA2TYPE(pvBits, paRVANames[i - 1], const char *)) > 0,
+ ("bug in binary export search!!!\n"));
+ AssertMsg(strcmp(PE_RVA2TYPE(pvBits, paRVANames[i], const char *), pszSymbol) != 0,
+ ("bug in binary export search!!!\n"));
+ }
+#endif
+ return VERR_SYMBOL_NOT_FOUND;
+ }
+
+ int i = (iEnd - iStart) / 2 + iStart;
+ const char *pszExpName = PE_RVA2TYPE(pvBits, paRVANames[i - 1], const char *);
+ int diff = strcmp(pszExpName, pszSymbol);
+ if (diff > 0) /* pszExpName > pszSymbol: search chunck before i */
+ iEnd = i - 1;
+ else if (diff) /* pszExpName < pszSymbol: search chunk after i */
+ iStart = i + 1;
+ else /* pszExpName == pszSymbol */
+ {
+ iExpOrdinal = paOrdinals[i - 1];
+ break;
+ }
+ } /* binary search thru name table */
+ }
+
+ /*
+ * Found export (iExpOrdinal).
+ */
+ uint32_t *paAddress = PE_RVA2TYPE(pvBits, pExpDir->AddressOfFunctions, uint32_t *);
+ *puRvaExport = paAddress[iExpOrdinal];
+ if (puOrdinal)
+ *puOrdinal = iExpOrdinal;
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnGetSymbolEx} */
+static DECLCALLBACK(int) rtldrPEGetSymbolEx(PRTLDRMODINTERNAL pMod, const void *pvBits, RTUINTPTR BaseAddress,
+ uint32_t iOrdinal, const char *pszSymbol, RTUINTPTR *pValue)
+{
+ PRTLDRMODPE pThis = (PRTLDRMODPE)pMod;
+ uint32_t uRvaExport;
+ int rc = rtLdrPE_ExportToRva(pThis, iOrdinal, pszSymbol, &pvBits, &uRvaExport, NULL);
+ if (RT_SUCCESS(rc))
+ {
+
+ uint32_t offForwarder = uRvaExport - pThis->ExportDir.VirtualAddress;
+ if (offForwarder >= pThis->ExportDir.Size)
+ /* Get plain export address */
+ *pValue = PE_RVA2TYPE(BaseAddress, uRvaExport, RTUINTPTR);
+ else
+ {
+ /* Return the approximate length of the forwarder buffer. */
+ const char *pszForwarder = PE_RVA2TYPE(pvBits, uRvaExport, const char *);
+ *pValue = sizeof(RTLDRIMPORTINFO) + RTStrNLen(pszForwarder, offForwarder - pThis->ExportDir.Size);
+ rc = VERR_LDR_FORWARDER;
+ }
+ }
+ return rc;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnQueryForwarderInfo} */
+static DECLCALLBACK(int) rtldrPE_QueryForwarderInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, uint32_t iOrdinal,
+ const char *pszSymbol, PRTLDRIMPORTINFO pInfo, size_t cbInfo)
+{
+ AssertReturn(cbInfo >= sizeof(*pInfo), VERR_INVALID_PARAMETER);
+
+ PRTLDRMODPE pThis = (PRTLDRMODPE)pMod;
+ uint32_t uRvaExport;
+ int rc = rtLdrPE_ExportToRva(pThis, iOrdinal, pszSymbol, &pvBits, &uRvaExport, &iOrdinal);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t offForwarder = uRvaExport - pThis->ExportDir.VirtualAddress;
+ if (offForwarder < pThis->ExportDir.Size)
+ {
+ const char *pszForwarder = PE_RVA2TYPE(pvBits, uRvaExport, const char *);
+
+ /*
+ * Parse and validate the string. We must make sure it's valid
+ * UTF-8, so we restrict it to ASCII.
+ */
+ const char *pszEnd = RTStrEnd(pszForwarder, offForwarder - pThis->ExportDir.Size);
+ if (pszEnd)
+ {
+ /* The module name. */
+ char ch;
+ uint32_t off = 0;
+ while ((ch = pszForwarder[off]) != '.' && ch != '\0')
+ {
+ if (RT_UNLIKELY((uint8_t)ch >= 0x80))
+ return VERR_LDR_BAD_FORWARDER;
+ off++;
+ }
+ if (RT_UNLIKELY(ch != '.'))
+ return VERR_LDR_BAD_FORWARDER;
+ uint32_t const offDot = off;
+ off++;
+
+ /* The function name or ordinal number. Ordinals starts with a hash. */
+ uint32_t iImpOrdinal;
+ if (pszForwarder[off] != '#')
+ {
+ iImpOrdinal = UINT32_MAX;
+ while ((ch = pszForwarder[off]) != '\0')
+ {
+ if (RT_UNLIKELY((uint8_t)ch >= 0x80))
+ return VERR_LDR_BAD_FORWARDER;
+ off++;
+ }
+ if (RT_UNLIKELY(off == offDot + 1))
+ return VERR_LDR_BAD_FORWARDER;
+ }
+ else
+ {
+ rc = RTStrToUInt32Full(&pszForwarder[off + 1], 10, &iImpOrdinal);
+ if (RT_UNLIKELY(rc != VINF_SUCCESS || iImpOrdinal > UINT16_MAX))
+ return VERR_LDR_BAD_FORWARDER;
+ }
+
+ /*
+ * Enough buffer?
+ */
+ uint32_t cbNeeded = RT_UOFFSETOF_DYN(RTLDRIMPORTINFO, szModule[iImpOrdinal != UINT32_MAX ? offDot + 1 : off + 1]);
+ if (cbNeeded > cbInfo)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Fill in the return buffer.
+ */
+ pInfo->iSelfOrdinal = iOrdinal;
+ pInfo->iOrdinal = iImpOrdinal;
+ if (iImpOrdinal == UINT32_MAX)
+ {
+ pInfo->pszSymbol = &pInfo->szModule[offDot + 1];
+ memcpy(&pInfo->szModule[0], pszForwarder, off + 1);
+ }
+ else
+ {
+ pInfo->pszSymbol = NULL;
+ memcpy(&pInfo->szModule[0], pszForwarder, offDot);
+ }
+ pInfo->szModule[offDot] = '\0';
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_LDR_BAD_FORWARDER;
+ }
+ else
+ rc = VERR_LDR_NOT_FORWARDER;
+ }
+ return rc;
+}
+
+
+/**
+ * Slow version of rtldrPEEnumSymbols that'll work without all of the image
+ * being accessible.
+ *
+ * This is mainly for use in debuggers and similar.
+ */
+static int rtldrPEEnumSymbolsSlow(PRTLDRMODPE pThis, unsigned fFlags, RTUINTPTR BaseAddress,
+ PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ /*
+ * We enumerates by ordinal, which means using a slow linear search for
+ * getting any name
+ */
+ PCIMAGE_EXPORT_DIRECTORY pExpDir = NULL;
+ int rc = rtldrPEReadPartByRva(pThis, NULL, pThis->ExportDir.VirtualAddress, pThis->ExportDir.Size,
+ (void const **)&pExpDir);
+ if (RT_FAILURE(rc))
+ return rc;
+ uint32_t const cOrdinals = RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions);
+
+ uint32_t const *paAddress = NULL;
+ rc = rtldrPEReadPartByRva(pThis, NULL, pExpDir->AddressOfFunctions, cOrdinals * sizeof(uint32_t),
+ (void const **)&paAddress);
+ uint32_t const *paRVANames = NULL;
+ if (RT_SUCCESS(rc) && pExpDir->NumberOfNames)
+ rc = rtldrPEReadPartByRva(pThis, NULL, pExpDir->AddressOfNames, pExpDir->NumberOfNames * sizeof(uint32_t),
+ (void const **)&paRVANames);
+ uint16_t const *paOrdinals = NULL;
+ if (RT_SUCCESS(rc) && pExpDir->NumberOfNames)
+ rc = rtldrPEReadPartByRva(pThis, NULL, pExpDir->AddressOfNameOrdinals, pExpDir->NumberOfNames * sizeof(uint16_t),
+ (void const **)&paOrdinals);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t uNamePrev = 0;
+ for (uint32_t uOrdinal = 0; uOrdinal < cOrdinals; uOrdinal++)
+ {
+ if (paAddress[uOrdinal] /* needed? */)
+ {
+ /*
+ * Look for name.
+ */
+ uint32_t uRvaName = UINT32_MAX;
+ /* Search from previous + 1 to the end. */
+ unsigned uName = uNamePrev + 1;
+ while (uName < pExpDir->NumberOfNames)
+ {
+ if (paOrdinals[uName] == uOrdinal)
+ {
+ uRvaName = paRVANames[uName];
+ uNamePrev = uName;
+ break;
+ }
+ uName++;
+ }
+ if (uRvaName == UINT32_MAX)
+ {
+ /* Search from start to the previous. */
+ uName = 0;
+ for (uName = 0 ; uName <= uNamePrev; uName++)
+ {
+ if (paOrdinals[uName] == uOrdinal)
+ {
+ uRvaName = paRVANames[uName];
+ uNamePrev = uName;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Get address.
+ */
+ uint32_t uRVAExport = paAddress[uOrdinal];
+ RTUINTPTR Value;
+ if (uRVAExport - pThis->ExportDir.VirtualAddress >= pThis->ExportDir.Size)
+ Value = PE_RVA2TYPE(BaseAddress, uRVAExport, RTUINTPTR);
+ else if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_NO_FWD))
+ Value = RTLDR_ENUM_SYMBOL_FWD_ADDRESS;
+ else
+ continue;
+
+ /* Read in the name if found one. */
+ char szAltName[32];
+ const char *pszName = NULL;
+ if (uRvaName != UINT32_MAX)
+ {
+ uint32_t cbName = 0x1000 - (uRvaName & 0xfff);
+ if (cbName < 10 || cbName > 512)
+ cbName = 128;
+ rc = rtldrPEReadPartByRva(pThis, NULL, uRvaName, cbName, (void const **)&pszName);
+ while (RT_SUCCESS(rc) && RTStrNLen(pszName, cbName) == cbName)
+ {
+ rtldrPEFreePart(pThis, NULL, pszName);
+ pszName = NULL;
+ if (cbName >= _4K)
+ break;
+ cbName += 128;
+ rc = rtldrPEReadPartByRva(pThis, NULL, uRvaName, cbName, (void const **)&pszName);
+ }
+ }
+ if (!pszName)
+ {
+ RTStrPrintf(szAltName, sizeof(szAltName), "Ordinal%#x", uOrdinal);
+ pszName = szAltName;
+ }
+
+ /*
+ * Call back.
+ */
+ rc = pfnCallback(&pThis->Core, pszName, uOrdinal + pExpDir->Base, Value, pvUser);
+ if (pszName != szAltName && pszName)
+ rtldrPEFreePart(pThis, NULL, pszName);
+ if (rc)
+ break;
+ }
+ }
+ }
+
+ rtldrPEFreePart(pThis, NULL, paOrdinals);
+ rtldrPEFreePart(pThis, NULL, paRVANames);
+ rtldrPEFreePart(pThis, NULL, paAddress);
+ rtldrPEFreePart(pThis, NULL, pExpDir);
+ return rc;
+
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnEnumSymbols} */
+static DECLCALLBACK(int) rtldrPEEnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits, RTUINTPTR BaseAddress,
+ PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+ NOREF(fFlags); /* ignored ... */
+
+ /*
+ * Check if there is actually anything to work on.
+ */
+ if ( !pModPe->ExportDir.VirtualAddress
+ || !pModPe->ExportDir.Size)
+ return VERR_SYMBOL_NOT_FOUND;
+
+ /*
+ * No bits supplied? Do we need to read the bits?
+ */
+ if (!pvBits)
+ {
+ if (!pModPe->pvBits)
+ {
+ int rc = rtldrPEReadBits(pModPe);
+ if (RT_FAILURE(rc))
+ return rtldrPEEnumSymbolsSlow(pModPe, fFlags, BaseAddress, pfnCallback, pvUser);
+ }
+ pvBits = pModPe->pvBits;
+ }
+
+ /*
+ * We enumerates by ordinal, which means using a slow linear search for
+ * getting any name
+ */
+ PIMAGE_EXPORT_DIRECTORY pExpDir = PE_RVA2TYPE(pvBits, pModPe->ExportDir.VirtualAddress, PIMAGE_EXPORT_DIRECTORY);
+ uint32_t *paAddress = PE_RVA2TYPE(pvBits, pExpDir->AddressOfFunctions, uint32_t *);
+ uint32_t *paRVANames = PE_RVA2TYPE(pvBits, pExpDir->AddressOfNames, uint32_t *);
+ uint16_t *paOrdinals = PE_RVA2TYPE(pvBits, pExpDir->AddressOfNameOrdinals, uint16_t *);
+ uint32_t uNamePrev = 0;
+ unsigned cOrdinals = RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions);
+ for (unsigned uOrdinal = 0; uOrdinal < cOrdinals; uOrdinal++)
+ {
+ if (paAddress[uOrdinal] /* needed? */)
+ {
+ /*
+ * Look for name.
+ */
+ const char *pszName = NULL;
+ /* Search from previous + 1 to the end. */
+ uint32_t uName = uNamePrev + 1;
+ while (uName < pExpDir->NumberOfNames)
+ {
+ if (paOrdinals[uName] == uOrdinal)
+ {
+ pszName = PE_RVA2TYPE(pvBits, paRVANames[uName], const char *);
+ uNamePrev = uName;
+ break;
+ }
+ uName++;
+ }
+ if (!pszName)
+ {
+ /* Search from start to the previous. */
+ uName = 0;
+ for (uName = 0 ; uName <= uNamePrev; uName++)
+ {
+ if (paOrdinals[uName] == uOrdinal)
+ {
+ pszName = PE_RVA2TYPE(pvBits, paRVANames[uName], const char *);
+ uNamePrev = uName;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Get address.
+ */
+ uint32_t uRVAExport = paAddress[uOrdinal];
+ RTUINTPTR Value;
+ if (uRVAExport - pModPe->ExportDir.VirtualAddress >= pModPe->ExportDir.Size)
+ Value = PE_RVA2TYPE(BaseAddress, uRVAExport, RTUINTPTR);
+ else if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_NO_FWD))
+ Value = RTLDR_ENUM_SYMBOL_FWD_ADDRESS;
+ else
+ continue;
+
+ /*
+ * Call back.
+ */
+ int rc = pfnCallback(pMod, pszName, uOrdinal + pExpDir->Base, Value, pvUser);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnEnumDbgInfo} */
+static DECLCALLBACK(int) rtldrPE_EnumDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits,
+ PFNRTLDRENUMDBG pfnCallback, void *pvUser)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+ int rc;
+
+ /*
+ * Debug info directory empty?
+ */
+ if ( !pModPe->DebugDir.VirtualAddress
+ || !pModPe->DebugDir.Size)
+ return VINF_SUCCESS;
+
+ /*
+ * Allocate temporary memory for a path buffer (this code is also compiled
+ * and maybe even used in stack starved environments).
+ */
+ char *pszPath = (char *)RTMemTmpAlloc(RTPATH_MAX);
+ if (!pszPath)
+ return VERR_NO_TMP_MEMORY;
+
+ /*
+ * Get the debug directory.
+ */
+ if (!pvBits)
+ pvBits = pModPe->pvBits;
+
+ PCIMAGE_DEBUG_DIRECTORY paDbgDir;
+ int rcRet = rtldrPEReadPartByRva(pModPe, pvBits, pModPe->DebugDir.VirtualAddress, pModPe->DebugDir.Size,
+ (void const **)&paDbgDir);
+ if (RT_FAILURE(rcRet))
+ {
+ RTMemTmpFree(pszPath);
+ return rcRet;
+ }
+
+ /*
+ * Enumerate the debug directory.
+ */
+ uint32_t const cEntries = pModPe->DebugDir.Size / sizeof(paDbgDir[0]);
+ for (uint32_t i = 0; i < cEntries; i++)
+ {
+ if (paDbgDir[i].PointerToRawData < pModPe->offEndOfHdrs)
+ continue;
+ if (paDbgDir[i].SizeOfData < 4)
+ continue;
+
+ void const *pvPart = NULL;
+ RTLDRDBGINFO DbgInfo;
+ RT_ZERO(DbgInfo.u);
+ DbgInfo.iDbgInfo = i;
+ DbgInfo.offFile = paDbgDir[i].PointerToRawData;
+ DbgInfo.LinkAddress = paDbgDir[i].AddressOfRawData < pModPe->cbImage
+ && paDbgDir[i].AddressOfRawData >= pModPe->offEndOfHdrs
+ ? paDbgDir[i].AddressOfRawData : NIL_RTLDRADDR;
+ DbgInfo.cb = paDbgDir[i].SizeOfData;
+ DbgInfo.pszExtFile = NULL;
+
+ rc = VINF_SUCCESS;
+ switch (paDbgDir[i].Type)
+ {
+ case IMAGE_DEBUG_TYPE_CODEVIEW:
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW;
+ DbgInfo.u.Cv.cbImage = pModPe->cbImage;
+ DbgInfo.u.Cv.uMajorVer = paDbgDir[i].MajorVersion;
+ DbgInfo.u.Cv.uMinorVer = paDbgDir[i].MinorVersion;
+ DbgInfo.u.Cv.uTimestamp = paDbgDir[i].TimeDateStamp;
+ if ( paDbgDir[i].SizeOfData < RTPATH_MAX
+ && paDbgDir[i].SizeOfData > 16
+ && ( DbgInfo.LinkAddress != NIL_RTLDRADDR
+ || DbgInfo.offFile > 0)
+ )
+ {
+ rc = rtldrPEReadPart(pModPe, pvBits, DbgInfo.offFile, DbgInfo.LinkAddress, paDbgDir[i].SizeOfData, &pvPart);
+ if (RT_SUCCESS(rc))
+ {
+ PCCVPDB20INFO pCv20 = (PCCVPDB20INFO)pvPart;
+ if ( pCv20->u32Magic == CVPDB20INFO_MAGIC
+ && pCv20->offDbgInfo == 0
+ && paDbgDir[i].SizeOfData > RT_UOFFSETOF(CVPDB20INFO, szPdbFilename) )
+ {
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB20;
+ DbgInfo.u.Pdb20.cbImage = pModPe->cbImage;
+ DbgInfo.u.Pdb20.uTimestamp = pCv20->uTimestamp;
+ DbgInfo.u.Pdb20.uAge = pCv20->uAge;
+ DbgInfo.pszExtFile = (const char *)&pCv20->szPdbFilename[0];
+ }
+ else if ( pCv20->u32Magic == CVPDB70INFO_MAGIC
+ && paDbgDir[i].SizeOfData > RT_UOFFSETOF(CVPDB70INFO, szPdbFilename) )
+ {
+ PCCVPDB70INFO pCv70 = (PCCVPDB70INFO)pCv20;
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB70;
+ DbgInfo.u.Pdb70.cbImage = pModPe->cbImage;
+ DbgInfo.u.Pdb70.Uuid = pCv70->PdbUuid;
+ DbgInfo.u.Pdb70.uAge = pCv70->uAge;
+ DbgInfo.pszExtFile = (const char *)&pCv70->szPdbFilename[0];
+ }
+ }
+ else
+ rcRet = rc;
+ }
+ break;
+
+ case IMAGE_DEBUG_TYPE_MISC:
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_UNKNOWN;
+ if ( paDbgDir[i].SizeOfData < RTPATH_MAX
+ && paDbgDir[i].SizeOfData > RT_UOFFSETOF(IMAGE_DEBUG_MISC, Data))
+ {
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_DBG;
+ DbgInfo.u.Dbg.cbImage = pModPe->cbImage;
+ if (DbgInfo.LinkAddress != NIL_RTLDRADDR)
+ DbgInfo.u.Dbg.uTimestamp = paDbgDir[i].TimeDateStamp;
+ else
+ DbgInfo.u.Dbg.uTimestamp = pModPe->uTimestamp; /* NT4 SP1 ntfs.sys hack. Generic? */
+
+ rc = rtldrPEReadPart(pModPe, pvBits, DbgInfo.offFile, DbgInfo.LinkAddress, paDbgDir[i].SizeOfData, &pvPart);
+ if (RT_SUCCESS(rc))
+ {
+ PCIMAGE_DEBUG_MISC pMisc = (PCIMAGE_DEBUG_MISC)pvPart;
+ if ( pMisc->DataType == IMAGE_DEBUG_MISC_EXENAME
+ && pMisc->Length == paDbgDir[i].SizeOfData)
+ {
+ if (!pMisc->Unicode)
+ DbgInfo.pszExtFile = (const char *)&pMisc->Data[0];
+ else
+ {
+ rc = RTUtf16ToUtf8Ex((PCRTUTF16)&pMisc->Data[0],
+ (pMisc->Length - RT_UOFFSETOF(IMAGE_DEBUG_MISC, Data)) / sizeof(RTUTF16),
+ &pszPath, RTPATH_MAX, NULL);
+ if (RT_SUCCESS(rc))
+ DbgInfo.pszExtFile = pszPath;
+ else
+ rcRet = rc; /* continue without a filename. */
+ }
+ }
+ }
+ else
+ rcRet = rc; /* continue without a filename. */
+ }
+ break;
+
+ case IMAGE_DEBUG_TYPE_COFF:
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_COFF;
+ DbgInfo.u.Coff.cbImage = pModPe->cbImage;
+ DbgInfo.u.Coff.uMajorVer = paDbgDir[i].MajorVersion;
+ DbgInfo.u.Coff.uMinorVer = paDbgDir[i].MinorVersion;
+ DbgInfo.u.Coff.uTimestamp = paDbgDir[i].TimeDateStamp;
+ break;
+
+ default:
+ DbgInfo.enmType = RTLDRDBGINFOTYPE_UNKNOWN;
+ break;
+ }
+
+ /* Fix (hack) the file name encoding. We don't have Windows-1252 handy,
+ so we'll be using Latin-1 as a reasonable approximation.
+ (I don't think we know exactly which encoding this is anyway, as
+ it's probably the current ANSI/Windows code page for the process
+ generating the image anyways.) */
+ if (DbgInfo.pszExtFile && DbgInfo.pszExtFile != pszPath)
+ {
+ rc = RTLatin1ToUtf8Ex(DbgInfo.pszExtFile,
+ paDbgDir[i].SizeOfData - ((uintptr_t)DbgInfo.pszExtFile - (uintptr_t)pvBits),
+ &pszPath, RTPATH_MAX, NULL);
+ if (RT_FAILURE(rc))
+ {
+ rcRet = rc;
+ DbgInfo.pszExtFile = NULL;
+ }
+ }
+ if (DbgInfo.pszExtFile)
+ RTPathChangeToUnixSlashes(pszPath, true /*fForce*/);
+
+ rc = pfnCallback(pMod, &DbgInfo, pvUser);
+ rtldrPEFreePart(pModPe, pvBits, pvPart);
+ if (rc != VINF_SUCCESS)
+ {
+ rcRet = rc;
+ break;
+ }
+ }
+
+ rtldrPEFreePart(pModPe, pvBits, paDbgDir);
+ RTMemTmpFree(pszPath);
+ return rcRet;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnEnumSegments} */
+static DECLCALLBACK(int) rtldrPE_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+ RTLDRSEG SegInfo;
+
+ /*
+ * The first section is a fake one covering the headers.
+ */
+ SegInfo.pszName = "NtHdrs";
+ SegInfo.cchName = 6;
+ SegInfo.SelFlat = 0;
+ SegInfo.Sel16bit = 0;
+ SegInfo.fFlags = 0;
+ SegInfo.fProt = RTMEM_PROT_READ;
+ SegInfo.Alignment = 1;
+ SegInfo.LinkAddress = pModPe->uImageBase;
+ SegInfo.RVA = 0;
+ SegInfo.offFile = 0;
+ SegInfo.cb = pModPe->cbHeaders;
+ SegInfo.cbFile = pModPe->cbHeaders;
+ SegInfo.cbMapped = pModPe->cbHeaders;
+ if (!(pModPe->paSections[0].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
+ SegInfo.cbMapped = pModPe->paSections[0].VirtualAddress;
+ int rc = pfnCallback(pMod, &SegInfo, pvUser);
+
+ /*
+ * Then all the normal sections.
+ */
+ PCIMAGE_SECTION_HEADER pSh = pModPe->paSections;
+ for (uint32_t i = 0; i < pModPe->cSections && rc == VINF_SUCCESS; i++, pSh++)
+ {
+ char szName[32];
+ SegInfo.pszName = (const char *)&pSh->Name[0];
+ SegInfo.cchName = (uint32_t)RTStrNLen(SegInfo.pszName, sizeof(pSh->Name));
+ if (SegInfo.cchName >= sizeof(pSh->Name))
+ {
+ memcpy(szName, &pSh->Name[0], sizeof(pSh->Name));
+ szName[sizeof(pSh->Name)] = '\0';
+ SegInfo.pszName = szName;
+ }
+ else if (SegInfo.cchName == 0)
+ {
+ SegInfo.pszName = szName;
+ SegInfo.cchName = (uint32_t)RTStrPrintf(szName, sizeof(szName), "UnamedSect%02u", i);
+ }
+ SegInfo.SelFlat = 0;
+ SegInfo.Sel16bit = 0;
+ SegInfo.fFlags = 0;
+ SegInfo.fProt = RTMEM_PROT_NONE;
+ if (pSh->Characteristics & IMAGE_SCN_MEM_READ)
+ SegInfo.fProt |= RTMEM_PROT_READ;
+ if (pSh->Characteristics & IMAGE_SCN_MEM_WRITE)
+ SegInfo.fProt |= RTMEM_PROT_WRITE;
+ if (pSh->Characteristics & IMAGE_SCN_MEM_EXECUTE)
+ SegInfo.fProt |= RTMEM_PROT_EXEC;
+ SegInfo.Alignment = (pSh->Characteristics & IMAGE_SCN_ALIGN_MASK) >> IMAGE_SCN_ALIGN_SHIFT;
+ if (SegInfo.Alignment > 0)
+ SegInfo.Alignment = RT_BIT_64(SegInfo.Alignment - 1);
+ else
+ SegInfo.Alignment = pModPe->uSectionAlign;
+ if (pSh->Characteristics & IMAGE_SCN_TYPE_NOLOAD)
+ {
+ SegInfo.LinkAddress = NIL_RTLDRADDR;
+ SegInfo.RVA = NIL_RTLDRADDR;
+ SegInfo.cbMapped = 0;
+ }
+ else
+ {
+ SegInfo.LinkAddress = pSh->VirtualAddress + pModPe->uImageBase;
+ SegInfo.RVA = pSh->VirtualAddress;
+ SegInfo.cbMapped = RT_ALIGN(pSh->Misc.VirtualSize, SegInfo.Alignment);
+ if (i + 1 < pModPe->cSections && !(pSh[1].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
+ SegInfo.cbMapped = pSh[1].VirtualAddress - pSh->VirtualAddress;
+ }
+ SegInfo.cb = pSh->Misc.VirtualSize;
+ if (pSh->PointerToRawData == 0 || pSh->SizeOfRawData == 0)
+ {
+ SegInfo.offFile = -1;
+ SegInfo.cbFile = 0;
+ }
+ else
+ {
+ SegInfo.offFile = pSh->PointerToRawData;
+ SegInfo.cbFile = pSh->SizeOfRawData;
+ }
+
+ rc = pfnCallback(pMod, &SegInfo, pvUser);
+ }
+
+ return rc;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnLinkAddressToSegOffset} */
+static DECLCALLBACK(int) rtldrPE_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
+ uint32_t *piSeg, PRTLDRADDR poffSeg)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+
+ LinkAddress -= pModPe->uImageBase;
+
+ /* Special header segment. */
+ if (LinkAddress < pModPe->paSections[0].VirtualAddress)
+ {
+ *piSeg = 0;
+ *poffSeg = LinkAddress;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Search the normal sections. (Could do this in binary fashion, they're
+ * sorted, but too much bother right now.)
+ */
+ if (LinkAddress > pModPe->cbImage)
+ return VERR_LDR_INVALID_LINK_ADDRESS;
+ uint32_t i = pModPe->cSections;
+ PCIMAGE_SECTION_HEADER paShs = pModPe->paSections;
+ while (i-- > 0)
+ if (!(paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
+ {
+ uint32_t uAddr = paShs[i].VirtualAddress;
+ if (LinkAddress >= uAddr)
+ {
+ *poffSeg = LinkAddress - uAddr;
+ *piSeg = i + 1;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_LDR_INVALID_LINK_ADDRESS;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnLinkAddressToRva} */
+static DECLCALLBACK(int) rtldrPE_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+
+ LinkAddress -= pModPe->uImageBase;
+ if (LinkAddress > pModPe->cbImage)
+ return VERR_LDR_INVALID_LINK_ADDRESS;
+ *pRva = LinkAddress;
+
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnSegOffsetToRva} */
+static DECLCALLBACK(int) rtldrPE_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg,
+ PRTLDRADDR pRva)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+
+ if (iSeg > pModPe->cSections)
+ return VERR_LDR_INVALID_SEG_OFFSET;
+
+ /** @todo should validate offSeg here... too lazy right now. */
+ if (iSeg == 0)
+ *pRva = offSeg;
+ else if (!(pModPe->paSections[iSeg - 1].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
+ *pRva = offSeg + pModPe->paSections[iSeg - 1].VirtualAddress;
+ else
+ return VERR_LDR_INVALID_SEG_OFFSET;
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnRvaToSegOffset} */
+static DECLCALLBACK(int) rtldrPE_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva,
+ uint32_t *piSeg, PRTLDRADDR poffSeg)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+ int rc = rtldrPE_LinkAddressToSegOffset(pMod, Rva + pModPe->uImageBase, piSeg, poffSeg);
+ if (RT_FAILURE(rc))
+ rc = VERR_LDR_INVALID_RVA;
+ return rc;
+}
+
+
+/**
+ * Worker for rtLdrPE_QueryProp and rtLdrPE_QueryImportModule that counts the
+ * number of imports, storing the result in RTLDRMODPE::cImports.
+ *
+ * @returns IPRT status code.
+ * @param pThis The PE module instance.
+ * @param pvBits Image bits if the caller had them available, NULL if
+ * not. Saves a couple of file accesses.
+ */
+static int rtLdrPE_CountImports(PRTLDRMODPE pThis, void const *pvBits)
+{
+ PCIMAGE_IMPORT_DESCRIPTOR paImpDescs;
+ int rc = rtldrPEReadPartByRva(pThis, pvBits, pThis->ImportDir.VirtualAddress, pThis->ImportDir.Size,
+ (void const **)&paImpDescs);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t const cMax = pThis->ImportDir.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
+ uint32_t i = 0;
+ while ( i < cMax
+ && paImpDescs[i].Name > pThis->offNtHdrs
+ && paImpDescs[i].Name < pThis->cbImage
+ && paImpDescs[i].FirstThunk > pThis->offNtHdrs
+ && paImpDescs[i].FirstThunk < pThis->cbImage)
+ i++;
+ pThis->cImports = i;
+
+ rtldrPEFreePart(pThis, pvBits, paImpDescs);
+ }
+ return rc;
+}
+
+/**
+ * Worker for rtLdrPE_QueryImportModule and rtLdrPE_QueryInternalName that
+ * copies a zero termianted string at the given RVA into the RTLdrQueryPropEx
+ * output buffer.
+ *
+ * @returns IPRT status code. If VERR_BUFFER_OVERFLOW, pcbBuf is required size.
+ * @param pThis The PE module instance.
+ * @param pvBits Image bits if the caller had them available, NULL if
+ * not. Saves a couple of file accesses.
+ * @param uRvaString The RVA of the string to copy.
+ * @param cbMaxString The max string length.
+ * @param pvBuf The output buffer.
+ * @param cbBuf The buffer size.
+ * @param pcbRet Where to return the number of bytes we've returned
+ * (or in case of VERR_BUFFER_OVERFLOW would have).
+ */
+static int rtLdrPE_QueryNameAtRva(PRTLDRMODPE pThis, void const *pvBits, uint32_t uRvaString, uint32_t cbMaxString,
+ void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ int rc;
+ if ( uRvaString >= pThis->cbHeaders
+ && uRvaString < pThis->cbImage)
+ {
+ /*
+ * Limit the string.
+ */
+ uint32_t cbMax = pThis->cbImage - uRvaString;
+ if (cbMax > cbMaxString)
+ cbMax = cbMaxString;
+ char *pszString;
+ rc = rtldrPEReadPartByRva(pThis, pvBits, uRvaString, cbMax, (void const **)&pszString);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Make sure it's null terminated and valid UTF-8 encoding.
+ *
+ * Which encoding this really is isn't defined, I think,
+ * but we need to make sure we don't get bogus UTF-8 into
+ * the process, so making sure it's valid UTF-8 is a good
+ * as anything else since it covers ASCII.
+ */
+ size_t cchString = RTStrNLen(pszString, cbMaxString);
+ if (cchString < cbMaxString)
+ {
+ rc = RTStrValidateEncodingEx(pszString, cchString, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Copy out the result and we're done.
+ * (We have to do all the cleanup code though, so no return success here.)
+ */
+ *pcbRet = cchString + 1;
+ if (cbBuf >= cchString + 1)
+ memcpy(pvBuf, pszString, cchString + 1);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ rc = VERR_BAD_EXE_FORMAT;
+ rtldrPEFreePart(pThis, pvBits, pszString);
+ }
+ }
+ else
+ rc = VERR_BAD_EXE_FORMAT;
+ return rc;
+}
+
+
+/**
+ * Worker for rtLdrPE_QueryProp that retrievs the name of an import DLL.
+ *
+ * @returns IPRT status code. If VERR_BUFFER_OVERFLOW, pcbBuf is required size.
+ * @param pThis The PE module instance.
+ * @param pvBits Image bits if the caller had them available, NULL if
+ * not. Saves a couple of file accesses.
+ * @param iImport The index of the import table descriptor to fetch
+ * the name from.
+ * @param pvBuf The output buffer.
+ * @param cbBuf The buffer size.
+ * @param pcbRet Where to return the number of bytes we've returned
+ * (or in case of VERR_BUFFER_OVERFLOW would have).
+ */
+static int rtLdrPE_QueryImportModule(PRTLDRMODPE pThis, void const *pvBits, uint32_t iImport,
+ void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ /*
+ * Make sure we got the import count.
+ */
+ int rc;
+ if (pThis->cImports == UINT32_MAX)
+ {
+ rc = rtLdrPE_CountImports(pThis, pvBits);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Check the index first, converting it to an RVA.
+ */
+ if (iImport < pThis->cImports)
+ {
+ uint32_t offEntry = iImport * sizeof(IMAGE_IMPORT_DESCRIPTOR) + pThis->ImportDir.VirtualAddress;
+
+ /*
+ * Retrieve the import table descriptor.
+ * Using 1024 as the max name length (should be more than enough).
+ */
+ PCIMAGE_IMPORT_DESCRIPTOR pImpDesc;
+ rc = rtldrPEReadPartByRva(pThis, pvBits, offEntry, sizeof(*pImpDesc), (void const **)&pImpDesc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtLdrPE_QueryNameAtRva(pThis, pvBits, pImpDesc->Name, 1024 /*cchMaxString*/, pvBuf, cbBuf, pcbRet);
+ rtldrPEFreePart(pThis, pvBits, pImpDesc);
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ *pcbRet = 0;
+ return rc;
+}
+
+
+/**
+ * Worker for rtLdrPE_QueryProp that retrieves the internal module name.
+ *
+ * @returns IPRT status code. If VERR_BUFFER_OVERFLOW, pcbBuf is required size.
+ * @param pThis The PE module instance.
+ * @param pvBits Image bits if the caller had them available, NULL if
+ * not. Saves a couple of file accesses.
+ * @param pvBuf The output buffer.
+ * @param cbBuf The buffer size.
+ * @param pcbRet Where to return the number of bytes we've returned
+ * (or in case of VERR_BUFFER_OVERFLOW would have).
+ */
+static int rtLdrPE_QueryInternalName(PRTLDRMODPE pThis, void const *pvBits, void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ *pcbRet = 0;
+
+ if ( pThis->ExportDir.Size < sizeof(IMAGE_EXPORT_DIRECTORY)
+ || pThis->ExportDir.VirtualAddress == 0)
+ return VERR_NOT_FOUND;
+
+ PCIMAGE_EXPORT_DIRECTORY pExpDir;
+ int rc = rtldrPEReadPartByRva(pThis, pvBits, pThis->ExportDir.VirtualAddress, sizeof(*pExpDir), (void const **)&pExpDir);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtLdrPE_QueryNameAtRva(pThis, pvBits, pExpDir->Name, 1024 /*cchMaxString*/, pvBuf, cbBuf, pcbRet);
+ rtldrPEFreePart(pThis, pvBits, pExpDir);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Worker for rtLdrPE_QueryProp that retrieves unwind information.
+ *
+ * @returns IPRT status code. If VERR_BUFFER_OVERFLOW, pcbBuf is required size.
+ * @param pThis The PE module instance.
+ * @param pvBits Image bits if the caller had them available, NULL if
+ * not. Saves a couple of file accesses.
+ * @param pvBuf The output buffer.
+ * @param cbBuf The buffer size.
+ * @param pcbRet Where to return the number of bytes we've returned
+ * (or in case of VERR_BUFFER_OVERFLOW would have).
+ */
+static int rtLdrPE_QueryUnwindTable(PRTLDRMODPE pThis, void const *pvBits, void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ int rc;
+ uint32_t const cbSrc = pThis->ExceptionDir.Size;
+ if ( cbSrc > 0
+ && pThis->ExceptionDir.VirtualAddress > 0)
+ {
+ *pcbRet = cbSrc;
+ if (cbBuf >= cbSrc)
+ rc = rtldrPEReadPartByRvaInfoBuf(pThis, pvBits, pThis->ExceptionDir.VirtualAddress, cbSrc, pvBuf);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ else
+ {
+ *pcbRet = 0;
+ rc = VERR_NOT_FOUND;
+ }
+ return rc;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnQueryProp} */
+static DECLCALLBACK(int) rtldrPE_QueryProp(PRTLDRMODINTERNAL pMod, RTLDRPROP enmProp, void const *pvBits,
+ void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+ switch (enmProp)
+ {
+ case RTLDRPROP_TIMESTAMP_SECONDS:
+ Assert(*pcbRet == cbBuf);
+ if (cbBuf == sizeof(int32_t))
+ *(int32_t *)pvBuf = pModPe->uTimestamp;
+ else if (cbBuf == sizeof(int64_t))
+ *(int64_t *)pvBuf = pModPe->uTimestamp;
+ else
+ AssertFailedReturn(VERR_INTERNAL_ERROR_3);
+ break;
+
+ case RTLDRPROP_IS_SIGNED:
+ Assert(cbBuf == sizeof(bool));
+ Assert(*pcbRet == cbBuf);
+ *(bool *)pvBuf = pModPe->offPkcs7SignedData != 0;
+ break;
+
+ case RTLDRPROP_PKCS7_SIGNED_DATA:
+ {
+ if (pModPe->cbPkcs7SignedData == 0)
+ return VERR_NOT_FOUND;
+ Assert(pModPe->offPkcs7SignedData > pModPe->SecurityDir.VirtualAddress);
+
+ *pcbRet = pModPe->cbPkcs7SignedData;
+ if (cbBuf < pModPe->cbPkcs7SignedData)
+ return VERR_BUFFER_OVERFLOW;
+ return pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pvBuf, pModPe->cbPkcs7SignedData,
+ pModPe->offPkcs7SignedData);
+ }
+
+#ifndef IPRT_WITHOUT_LDR_PAGE_HASHING
+ case RTLDRPROP_HASHABLE_PAGES:
+ *pcbRet = sizeof(uint32_t);
+ *(uint32_t *)pvBuf = rtLdrPE_GetHashablePages(pModPe);
+ return VINF_SUCCESS;
+
+ case RTLDRPROP_SHA1_PAGE_HASHES:
+ return rtLdrPE_QueryPageHashes(pModPe, RTDIGESTTYPE_SHA1, pvBuf, cbBuf, pcbRet);
+
+ case RTLDRPROP_SHA256_PAGE_HASHES:
+ return rtLdrPE_QueryPageHashes(pModPe, RTDIGESTTYPE_SHA256, pvBuf, cbBuf, pcbRet);
+#endif
+
+ case RTLDRPROP_SIGNATURE_CHECKS_ENFORCED:
+ Assert(cbBuf == sizeof(bool));
+ Assert(*pcbRet == cbBuf);
+ *(bool *)pvBuf = pModPe->offPkcs7SignedData > 0
+ && (pModPe->fDllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY);
+ break;
+
+ case RTLDRPROP_IMPORT_COUNT:
+ Assert(cbBuf == sizeof(uint32_t));
+ Assert(*pcbRet == cbBuf);
+ if (pModPe->cImports == UINT32_MAX)
+ {
+ int rc = rtLdrPE_CountImports(pModPe, pvBits);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ *(uint32_t *)pvBuf = pModPe->cImports;
+ break;
+
+ case RTLDRPROP_IMPORT_MODULE:
+ Assert(cbBuf >= sizeof(uint32_t));
+ return rtLdrPE_QueryImportModule(pModPe, pvBits, *(uint32_t *)pvBuf, pvBuf, cbBuf, pcbRet);
+
+ case RTLDRPROP_FILE_OFF_HEADER:
+ Assert(cbBuf == sizeof(uint32_t) || cbBuf == sizeof(uint64_t));
+ if (cbBuf == sizeof(uint32_t))
+ *(uint32_t *)pvBuf = pModPe->offNtHdrs;
+ else
+ *(uint64_t *)pvBuf = pModPe->offNtHdrs;
+ return VINF_SUCCESS;
+
+ case RTLDRPROP_INTERNAL_NAME:
+ return rtLdrPE_QueryInternalName(pModPe, pvBits, pvBuf, cbBuf, pcbRet);
+
+ case RTLDRPROP_UNWIND_TABLE:
+ return rtLdrPE_QueryUnwindTable(pModPe, pvBits, pvBuf, cbBuf, pcbRet);
+
+ case RTLDRPROP_UNWIND_INFO:
+ {
+ uint32_t uRva = *(uint32_t const *)pvBuf;
+ if (uRva < pModPe->cbImage)
+ {
+ uint32_t cbLeft = pModPe->cbImage - uRva;
+ uint32_t cbToRead = (uint32_t)RT_MIN(cbLeft, cbBuf);
+ *pcbRet = cbToRead;
+ return rtldrPEReadPartByRvaInfoBuf(pModPe, pvBits, uRva, cbToRead, pvBuf);
+ }
+ *pcbRet = 0;
+ return VINF_SUCCESS;
+ }
+
+ default:
+ return VERR_NOT_FOUND;
+ }
+ return VINF_SUCCESS;
+}
+
+
+
+/*
+ * Lots of Authenticode fun ahead.
+ */
+
+
+/**
+ * Initializes the hash context.
+ *
+ * @returns VINF_SUCCESS or VERR_NOT_SUPPORTED.
+ * @param pHashCtx The hash context union.
+ * @param enmDigest The hash type we're calculating..
+ */
+static int rtLdrPE_HashInit(PRTLDRPEHASHCTXUNION pHashCtx, RTDIGESTTYPE enmDigest)
+{
+ switch (enmDigest)
+ {
+ case RTDIGESTTYPE_SHA512: RTSha512Init(&pHashCtx->Sha512); break;
+ case RTDIGESTTYPE_SHA384: RTSha384Init(&pHashCtx->Sha384); break;
+ case RTDIGESTTYPE_SHA256: RTSha256Init(&pHashCtx->Sha256); break;
+ case RTDIGESTTYPE_SHA1: RTSha1Init(&pHashCtx->Sha1); break;
+ case RTDIGESTTYPE_MD5: RTMd5Init(&pHashCtx->Md5); break;
+ default: AssertFailedReturn(VERR_NOT_SUPPORTED);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Updates the hash with more data.
+ *
+ * @param pHashCtx The hash context union.
+ * @param enmDigest The hash type we're calculating..
+ * @param pvBuf Pointer to a buffer with bytes to add to thash.
+ * @param cbBuf How many bytes to add from @a pvBuf.
+ */
+static void rtLdrPE_HashUpdate(PRTLDRPEHASHCTXUNION pHashCtx, RTDIGESTTYPE enmDigest, void const *pvBuf, size_t cbBuf)
+{
+ switch (enmDigest)
+ {
+ case RTDIGESTTYPE_SHA512: RTSha512Update(&pHashCtx->Sha512, pvBuf, cbBuf); break;
+ case RTDIGESTTYPE_SHA384: RTSha384Update(&pHashCtx->Sha384, pvBuf, cbBuf); break;
+ case RTDIGESTTYPE_SHA256: RTSha256Update(&pHashCtx->Sha256, pvBuf, cbBuf); break;
+ case RTDIGESTTYPE_SHA1: RTSha1Update(&pHashCtx->Sha1, pvBuf, cbBuf); break;
+ case RTDIGESTTYPE_MD5: RTMd5Update(&pHashCtx->Md5, pvBuf, cbBuf); break;
+ default: AssertReleaseFailed();
+ }
+}
+
+
+/**
+ * Finalizes the hash calculations.
+ *
+ * @param pHashCtx The hash context union.
+ * @param enmDigest The hash type we're calculating..
+ * @param pHashRes The hash result union.
+ */
+static void rtLdrPE_HashFinalize(PRTLDRPEHASHCTXUNION pHashCtx, RTDIGESTTYPE enmDigest, PRTLDRPEHASHRESUNION pHashRes)
+{
+ switch (enmDigest)
+ {
+ case RTDIGESTTYPE_SHA512: RTSha512Final(&pHashCtx->Sha512, pHashRes->abSha512); break;
+ case RTDIGESTTYPE_SHA384: RTSha384Final(&pHashCtx->Sha384, pHashRes->abSha384); break;
+ case RTDIGESTTYPE_SHA256: RTSha256Final(&pHashCtx->Sha256, pHashRes->abSha256); break;
+ case RTDIGESTTYPE_SHA1: RTSha1Final(&pHashCtx->Sha1, pHashRes->abSha1); break;
+ case RTDIGESTTYPE_MD5: RTMd5Final(pHashRes->abMd5, &pHashCtx->Md5); break;
+ default: AssertReleaseFailed();
+ }
+}
+
+
+/**
+ * Returns the digest size for the given digest type.
+ *
+ * @returns Size in bytes.
+ * @param enmDigest The hash type in question.
+ */
+static uint32_t rtLdrPE_HashGetHashSize(RTDIGESTTYPE enmDigest)
+{
+ switch (enmDigest)
+ {
+ case RTDIGESTTYPE_SHA512: return RTSHA512_HASH_SIZE;
+ case RTDIGESTTYPE_SHA384: return RTSHA384_HASH_SIZE;
+ case RTDIGESTTYPE_SHA256: return RTSHA256_HASH_SIZE;
+ case RTDIGESTTYPE_SHA1: return RTSHA1_HASH_SIZE;
+ case RTDIGESTTYPE_MD5: return RTMD5_HASH_SIZE;
+ default: AssertReleaseFailedReturn(0);
+ }
+}
+
+
+#ifndef IPRT_WITHOUT_LDR_VERIFY
+/**
+ * Checks if the hash type is supported.
+ *
+ * @returns true/false.
+ * @param enmDigest The hash type in question.
+ */
+static bool rtLdrPE_HashIsSupported(RTDIGESTTYPE enmDigest)
+{
+ switch (enmDigest)
+ {
+ case RTDIGESTTYPE_SHA512:
+ case RTDIGESTTYPE_SHA384:
+ case RTDIGESTTYPE_SHA256:
+ case RTDIGESTTYPE_SHA1:
+ case RTDIGESTTYPE_MD5:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+
+/**
+ * Calculate the special too watch out for when hashing the image.
+ *
+ * @returns IPRT status code.
+ * @param pModPe The PE module.
+ * @param pPlaces The structure where to store the special places.
+ * @param pErrInfo Optional error info.
+ */
+static int rtldrPe_CalcSpecialHashPlaces(PRTLDRMODPE pModPe, PRTLDRPEHASHSPECIALS pPlaces, PRTERRINFO pErrInfo)
+{
+ /*
+ * If we're here despite a missing signature, we need to get the file size.
+ */
+ pPlaces->cbToHash = pModPe->SecurityDir.VirtualAddress;
+ if (pPlaces->cbToHash == 0)
+ {
+ uint64_t cbFile = pModPe->Core.pReader->pfnSize(pModPe->Core.pReader);
+ pPlaces->cbToHash = (uint32_t)cbFile;
+ if (pPlaces->cbToHash != (uint64_t)cbFile)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_FILE_LENGTH_ERROR, "File is too large: %RTfoff", cbFile);
+ }
+
+ /*
+ * Calculate the special places.
+ */
+ pPlaces->offCksum = (uint32_t)pModPe->offNtHdrs
+ + (pModPe->f64Bit
+ ? RT_UOFFSETOF(IMAGE_NT_HEADERS64, OptionalHeader.CheckSum)
+ : RT_UOFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader.CheckSum));
+ pPlaces->cbCksum = RT_SIZEOFMEMB(IMAGE_NT_HEADERS32, OptionalHeader.CheckSum);
+ pPlaces->offSecDir = (uint32_t)pModPe->offNtHdrs
+ + (pModPe->f64Bit
+ ? RT_UOFFSETOF(IMAGE_NT_HEADERS64, OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY])
+ : RT_UOFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]));
+ pPlaces->cbSecDir = sizeof(IMAGE_DATA_DIRECTORY);
+ pPlaces->offEndSpecial = pPlaces->offSecDir + pPlaces->cbSecDir;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Calculates the whole image hash.
+ *
+ * The Authenticode_PE.docx version 1.0 explains how the hash is calculated,
+ * points 8 thru 14 are bogus. If you study them a little carefully, it is
+ * clear that the algorithm will only work if the raw data for the section have
+ * no gaps between them or in front of them. So, this elaborate section sorting
+ * by PointerToRawData and working them section by section could simply be
+ * replaced by one point:
+ *
+ * 8. Add all the file content between SizeOfHeaders and the
+ * attribute certificate table to the hash. Then finalize
+ * the hash.
+ *
+ * Not sure if Microsoft is screwing with us on purpose here or whether they
+ * assigned some of this work to less talented engineers and tech writers. I
+ * love fact that they say it's "simplified" and should yield the correct hash
+ * for "almost all" files. Stupid, Stupid, Microsofties!!
+ *
+ * My simplified implementation that just hashes the entire file up to the
+ * signature or end of the file produces the same SHA1 values as "signtool
+ * verify /v" does both for edited executables with gaps between/before/after
+ * sections raw data and normal executables without any gaps.
+ *
+ * @returns IPRT status code.
+ * @param pModPe The PE module.
+ * @param pvScratch Scratch buffer.
+ * @param cbScratch Size of the scratch buffer.
+ * @param enmDigest The hash digest type we're calculating.
+ * @param pHashCtx Hash context scratch area.
+ * @param pHashRes Hash result buffer.
+ * @param pErrInfo Optional error info buffer.
+ */
+static int rtldrPE_HashImageCommon(PRTLDRMODPE pModPe, void *pvScratch, uint32_t cbScratch, RTDIGESTTYPE enmDigest,
+ PRTLDRPEHASHCTXUNION pHashCtx, PRTLDRPEHASHRESUNION pHashRes, PRTERRINFO pErrInfo)
+{
+ int rc = rtLdrPE_HashInit(pHashCtx, enmDigest);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Calculate the special places.
+ */
+ RTLDRPEHASHSPECIALS SpecialPlaces = { 0, 0, 0, 0, 0, 0 }; /* shut up gcc */
+ rc = rtldrPe_CalcSpecialHashPlaces(pModPe, &SpecialPlaces, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Work our way thru the image data.
+ */
+ uint32_t off = 0;
+ while (off < SpecialPlaces.cbToHash)
+ {
+ uint32_t cbRead = RT_MIN(SpecialPlaces.cbToHash - off, cbScratch);
+ uint8_t *pbCur = (uint8_t *)pvScratch;
+ rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pbCur, cbRead, off);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_READ_ERROR_HASH, "Hash read error at %#x: %Rrc (cbRead=%#zx)",
+ off, rc, cbRead);
+
+ if (off < SpecialPlaces.offEndSpecial)
+ {
+ if (off < SpecialPlaces.offCksum)
+ {
+ /* Hash everything up to the checksum. */
+ uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum - off, cbRead);
+ rtLdrPE_HashUpdate(pHashCtx, enmDigest, pbCur, cbChunk);
+ pbCur += cbChunk;
+ cbRead -= cbChunk;
+ off += cbChunk;
+ }
+
+ if (off < SpecialPlaces.offCksum + SpecialPlaces.cbCksum && off >= SpecialPlaces.offCksum)
+ {
+ /* Skip the checksum */
+ uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum + SpecialPlaces.cbCksum - off, cbRead);
+ pbCur += cbChunk;
+ cbRead -= cbChunk;
+ off += cbChunk;
+ }
+
+ if (off < SpecialPlaces.offSecDir && off >= SpecialPlaces.offCksum + SpecialPlaces.cbCksum)
+ {
+ /* Hash everything between the checksum and the data dir entry. */
+ uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir - off, cbRead);
+ rtLdrPE_HashUpdate(pHashCtx, enmDigest, pbCur, cbChunk);
+ pbCur += cbChunk;
+ cbRead -= cbChunk;
+ off += cbChunk;
+ }
+
+ if (off < SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir && off >= SpecialPlaces.offSecDir)
+ {
+ /* Skip the security data directory entry. */
+ uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir - off, cbRead);
+ pbCur += cbChunk;
+ cbRead -= cbChunk;
+ off += cbChunk;
+ }
+ }
+
+ rtLdrPE_HashUpdate(pHashCtx, enmDigest, pbCur, cbRead);
+
+ /* Advance */
+ off += cbRead;
+ }
+
+ /*
+ * If there isn't a signature, experiments with signtool indicates that we
+ * have to zero padd the file size until it's a multiple of 8. (This is
+ * most likely to give 64-bit values in the certificate a natural alignment
+ * when memory mapped.)
+ */
+ if ( pModPe->SecurityDir.VirtualAddress != SpecialPlaces.cbToHash
+ && SpecialPlaces.cbToHash != RT_ALIGN_32(SpecialPlaces.cbToHash, WIN_CERTIFICATE_ALIGNMENT))
+ {
+ static const uint8_t s_abZeros[WIN_CERTIFICATE_ALIGNMENT] = { 0,0,0,0, 0,0,0,0 };
+ rtLdrPE_HashUpdate(pHashCtx, enmDigest, s_abZeros,
+ RT_ALIGN_32(SpecialPlaces.cbToHash, WIN_CERTIFICATE_ALIGNMENT) - SpecialPlaces.cbToHash);
+ }
+
+ /*
+ * Done. Finalize the hashes.
+ */
+ rtLdrPE_HashFinalize(pHashCtx, enmDigest, pHashRes);
+ return VINF_SUCCESS;
+}
+
+#ifndef IPRT_WITHOUT_LDR_PAGE_HASHING
+
+/**
+ * Returns the size of the page hashes, including the terminator entry.
+ *
+ * Used for handling RTLDRPROP_HASHABLE_PAGES.
+ *
+ * @returns Number of page hashes.
+ * @param pModPe The PE module.
+ */
+static uint32_t rtLdrPE_GetHashablePages(PRTLDRMODPE pModPe)
+{
+ uint32_t const cbPage = _4K;
+ uint32_t cPages = 1; /* termination entry */
+
+ /* Add implicit header section: */
+ cPages += (pModPe->cbHeaders + cbPage - 1) / cbPage;
+
+ /* Add on disk pages for each section. Each starts with a fresh page and
+ we ASSUMES that it is page aligned (in memory). */
+ for (uint32_t i = 0; i < pModPe->cSections; i++)
+ {
+ uint32_t const cbRawData = pModPe->paSections[i].SizeOfRawData;
+ if (cbRawData > 0)
+ cPages += (cbRawData + cbPage - 1) / cbPage;
+ }
+
+ return cPages;
+}
+
+
+/**
+ * Worker for rtLdrPE_QueryPageHashes.
+ *
+ * Keep in mind that rtldrPE_VerifyAllPageHashes does similar work, so some
+ * fixes may apply both places.
+ */
+static int rtLdrPE_CalcPageHashes(PRTLDRMODPE pModPe, RTDIGESTTYPE const enmDigest, uint32_t const cbHash,
+ uint8_t *pbDst, uint8_t *pbScratch, uint32_t cbScratch, uint32_t const cbPage)
+{
+ /*
+ * Calculate the special places.
+ */
+ RTLDRPEHASHSPECIALS SpecialPlaces = { 0, 0, 0, 0, 0, 0 }; /* shut up gcc */
+ int rc = rtldrPe_CalcSpecialHashPlaces(pModPe, &SpecialPlaces, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Walk section table and hash the pages in each. Because the headers are
+ * in an implicit section, the loop advancing is a little funky.
+ */
+ int32_t const cSections = pModPe->cSections;
+ int32_t iSection = -1;
+ uint32_t offRawData = 0;
+ uint32_t cbRawData = pModPe->cbHeaders;
+ uint32_t offLastPage = 0;
+
+ uint32_t const cbScratchReadMax = cbScratch / cbPage * cbPage;
+ uint32_t cbScratchRead = 0;
+ uint32_t offScratchRead = 0;
+
+ for (;;)
+ {
+ /*
+ * Process the pages in this section.
+ */
+ uint32_t cPagesInSection = (cbRawData + cbPage - 1) / cbPage;
+ for (uint32_t iPage = 0; iPage < cPagesInSection; iPage++)
+ {
+ uint32_t const offPageInSect = iPage * cbPage;
+ uint32_t const offPageInFile = offRawData + offPageInSect;
+ uint32_t const cbPageInFile = RT_MIN(cbPage, cbRawData - offPageInSect);
+ offLastPage = offPageInFile;
+
+ /* Calculate and output the page offset. */
+ *(uint32_t *)pbDst = offPageInFile;
+ pbDst += sizeof(uint32_t);
+
+ /*
+ * Read/find in the raw page.
+ */
+ /* Did we get a cache hit? */
+ uint8_t *pbCur = pbScratch;
+ if ( offPageInFile + cbPageInFile <= offScratchRead + cbScratchRead
+ && offPageInFile >= offScratchRead)
+ pbCur += offPageInFile - offScratchRead;
+ /* Missed, read more. */
+ else
+ {
+ offScratchRead = offPageInFile;
+ cbScratchRead = SpecialPlaces.cbToHash - offPageInFile;
+ if (cbScratchRead > cbScratchReadMax)
+ cbScratchRead = cbScratchReadMax;
+ rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pbCur, cbScratchRead, offScratchRead);
+ if (RT_FAILURE(rc))
+ return VERR_LDRVI_READ_ERROR_HASH;
+ }
+
+ /*
+ * Hash it.
+ */
+ RTLDRPEHASHCTXUNION HashCtx;
+ rc = rtLdrPE_HashInit(&HashCtx, enmDigest);
+ AssertRCReturn(rc, rc);
+
+ /* Deal with special places. */
+ uint32_t cbLeft = cbPageInFile;
+ if (offPageInFile < SpecialPlaces.offEndSpecial)
+ {
+ uint32_t off = offPageInFile;
+ if (off < SpecialPlaces.offCksum)
+ {
+ /* Hash everything up to the checksum. */
+ uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum - off, cbLeft);
+ rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbChunk);
+ pbCur += cbChunk;
+ cbLeft -= cbChunk;
+ off += cbChunk;
+ }
+
+ if (off < SpecialPlaces.offCksum + SpecialPlaces.cbCksum && off >= SpecialPlaces.offCksum)
+ {
+ /* Skip the checksum */
+ uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum + SpecialPlaces.cbCksum - off, cbLeft);
+ pbCur += cbChunk;
+ cbLeft -= cbChunk;
+ off += cbChunk;
+ }
+
+ if (off < SpecialPlaces.offSecDir && off >= SpecialPlaces.offCksum + SpecialPlaces.cbCksum)
+ {
+ /* Hash everything between the checksum and the data dir entry. */
+ uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir - off, cbLeft);
+ rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbChunk);
+ pbCur += cbChunk;
+ cbLeft -= cbChunk;
+ off += cbChunk;
+ }
+
+ if (off < SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir && off >= SpecialPlaces.offSecDir)
+ {
+ /* Skip the security data directory entry. */
+ uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir - off, cbLeft);
+ pbCur += cbChunk;
+ cbLeft -= cbChunk;
+ off += cbChunk;
+ }
+ }
+
+ rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbLeft);
+ if (cbPageInFile < cbPage)
+ rtLdrPE_HashUpdate(&HashCtx, enmDigest, g_abRTZero4K, cbPage - cbPageInFile);
+
+ /*
+ * Finish the hash calculation storing it in the table.
+ */
+ rtLdrPE_HashFinalize(&HashCtx, enmDigest, (PRTLDRPEHASHRESUNION)pbDst);
+ pbDst += cbHash;
+ }
+
+ /*
+ * Advance to the next section.
+ */
+ iSection++;
+ if (iSection >= cSections)
+ break;
+ offRawData = pModPe->paSections[iSection].PointerToRawData;
+ cbRawData = pModPe->paSections[iSection].SizeOfRawData;
+ }
+
+ /*
+ * Add the terminator entry.
+ */
+ *(uint32_t *)pbDst = offLastPage + cbPage;
+ RT_BZERO(&pbDst[sizeof(uint32_t)], cbHash);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Creates the page hash table for the image.
+ *
+ * Used for handling RTLDRPROP_SHA1_PAGE_HASHES and
+ * RTLDRPROP_SHA256_PAGE_HASHES.
+ *
+ * @returns IPRT status code.
+ * @param pModPe The PE module.
+ * @param enmDigest The digest to use when hashing the pages.
+ * @param pvBuf Where to return the page hash table.
+ * @param cbBuf The size of the buffer @a pvBuf points to.
+ * @param pcbRet Where to return the output/needed size.
+ */
+static int rtLdrPE_QueryPageHashes(PRTLDRMODPE pModPe, RTDIGESTTYPE enmDigest, void *pvBuf, size_t cbBuf, size_t *pcbRet)
+{
+ /*
+ * Check that we've got enough buffer space.
+ */
+ uint32_t const cbPage = _4K;
+ uint32_t const cEntries = rtLdrPE_GetHashablePages(pModPe);
+ uint32_t const cbHash = rtLdrPE_HashGetHashSize(enmDigest);
+ AssertReturn(cbHash > 0, VERR_INTERNAL_ERROR_3);
+
+ size_t const cbNeeded = (size_t)(cbHash + 4) * cEntries;
+ *pcbRet = cbNeeded;
+ if (cbNeeded > cbBuf)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Allocate a scratch buffer and call worker to do the real job.
+ */
+# ifdef IN_RING0
+ uint32_t cbScratch = _256K - _4K;
+# else
+ uint32_t cbScratch = _1M;
+# endif
+ void *pvScratch = RTMemTmpAlloc(cbScratch);
+ if (!pvScratch)
+ {
+ cbScratch = _4K;
+ pvScratch = RTMemTmpAlloc(cbScratch);
+ if (!pvScratch)
+ return VERR_NO_TMP_MEMORY;
+ }
+
+ int rc = rtLdrPE_CalcPageHashes(pModPe, enmDigest, cbHash, (uint8_t *)pvBuf, (uint8_t *)pvScratch, cbScratch, cbPage);
+
+ RTMemTmpFree(pvScratch);
+ return rc;
+}
+
+#endif /* !IPRT_WITHOUT_LDR_PAGE_HASHING */
+#ifndef IPRT_WITHOUT_LDR_VERIFY
+
+/**
+ * Verifies image preconditions not checked by the open validation code.
+ *
+ * @returns IPRT status code.
+ * @param pModPe The PE module.
+ * @param pErrInfo Optional error info buffer.
+ */
+static int rtldrPE_VerifySignatureImagePrecoditions(PRTLDRMODPE pModPe, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate the sections. While doing so, track the amount of section raw
+ * section data in the file so we can use this to validate the signature
+ * table location later.
+ */
+ uint32_t offNext = pModPe->cbHeaders; /* same */
+ for (uint32_t i = 0; i < pModPe->cSections; i++)
+ if (pModPe->paSections[i].SizeOfRawData > 0)
+ {
+ uint64_t offEnd = (uint64_t)pModPe->paSections[i].PointerToRawData + pModPe->paSections[i].SizeOfRawData;
+ if (offEnd > offNext)
+ {
+ if (offEnd >= _2G)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_SECTION_RAW_DATA_VALUES,
+ "Section %#u specifies file data after 2GB: PointerToRawData=%#x SizeOfRawData=%#x",
+ i, pModPe->paSections[i].PointerToRawData, pModPe->paSections[i].SizeOfRawData);
+ offNext = (uint32_t)offEnd;
+ }
+ }
+ uint32_t offEndOfSectionData = offNext;
+
+ /*
+ * Validate the signature.
+ */
+ if (!pModPe->SecurityDir.Size)
+ return RTErrInfoSet(pErrInfo, VERR_LDRVI_NOT_SIGNED, "Not signed.");
+
+ uint32_t const offSignature = pModPe->SecurityDir.VirtualAddress;
+ uint32_t const cbSignature = pModPe->SecurityDir.Size;
+ if ( cbSignature <= sizeof(WIN_CERTIFICATE)
+ || cbSignature >= RTLDRMODPE_MAX_SECURITY_DIR_SIZE
+ || offSignature >= _2G)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY,
+ "Invalid security data dir entry: cb=%#x off=%#x", cbSignature, offSignature);
+
+ if (offSignature < offEndOfSectionData)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY,
+ "Invalid security data dir entry offset: %#x offEndOfSectionData=%#x",
+ offSignature, offEndOfSectionData);
+
+ if (RT_ALIGN_32(offSignature, WIN_CERTIFICATE_ALIGNMENT) != offSignature)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY,
+ "Misaligned security dir entry offset: %#x (alignment=%#x)",
+ offSignature, WIN_CERTIFICATE_ALIGNMENT);
+
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Reads and checks the raw signature data.
+ *
+ * @returns IPRT status code.
+ * @param pModPe The PE module.
+ * @param ppSignature Where to return the pointer to the parsed
+ * signature data. Pass to
+ * rtldrPE_VerifySignatureDestroy when done.
+ * @param pErrInfo Optional error info buffer.
+ */
+static int rtldrPE_VerifySignatureRead(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE *ppSignature, PRTERRINFO pErrInfo)
+{
+ *ppSignature = NULL;
+ AssertReturn(pModPe->SecurityDir.Size > 0, VERR_INTERNAL_ERROR_2);
+
+ /*
+ * Allocate memory for reading and parsing it.
+ */
+ if (pModPe->SecurityDir.Size >= RTLDRMODPE_MAX_SECURITY_DIR_SIZE)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY,
+ "Signature directory is to large: %#x", pModPe->SecurityDir.Size);
+
+ PRTLDRPESIGNATURE pSignature = (PRTLDRPESIGNATURE)RTMemTmpAllocZ(sizeof(*pSignature) + 64 + pModPe->SecurityDir.Size);
+ if (!pSignature)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_NO_MEMORY_SIGNATURE, "Failed to allocate %zu bytes",
+ sizeof(*pSignature) + 64 + pModPe->SecurityDir.Size);
+ pSignature->pRawData = RT_ALIGN_PT(pSignature + 1, 64, WIN_CERTIFICATE const *);
+
+
+ /*
+ * Read it.
+ */
+ int rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, (void *)pSignature->pRawData,
+ pModPe->SecurityDir.Size, pModPe->SecurityDir.VirtualAddress);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check the table we've read in.
+ */
+ uint32_t cbLeft = pModPe->SecurityDir.Size;
+ WIN_CERTIFICATE const *pEntry = pSignature->pRawData;
+ for (;;)
+ {
+ if ( cbLeft < sizeof(*pEntry)
+ || pEntry->dwLength > cbLeft
+ || pEntry->dwLength < sizeof(*pEntry))
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_HDR_LENGTH,
+ "Bad WIN_CERTIFICATE length: %#x (max %#x, signature=%u)",
+ pEntry->dwLength, cbLeft, 0);
+ else if (pEntry->wRevision != WIN_CERT_REVISION_2_0)
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_HDR_REVISION,
+ "Unsupported WIN_CERTIFICATE revision value: %#x (signature=%u)",
+ pEntry->wRevision, 0);
+ else if (pEntry->wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA)
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_HDR_TYPE,
+ "Unsupported WIN_CERTIFICATE certificate type: %#x (signature=%u)",
+ pEntry->wCertificateType, 0);
+ else
+ {
+ /* advance */
+ uint32_t cbEntry = RT_ALIGN(pEntry->dwLength, WIN_CERTIFICATE_ALIGNMENT);
+ if (cbEntry >= cbLeft)
+ break;
+ cbLeft -= cbEntry;
+ pEntry = (WIN_CERTIFICATE *)((uintptr_t)pEntry + cbEntry);
+
+ /* For now, only one entry is supported. */
+ rc = RTErrInfoSet(pErrInfo, VERR_LDRVI_BAD_CERT_MULTIPLE, "Multiple WIN_CERTIFICATE entries are not supported.");
+ }
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ *ppSignature = pSignature;
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_READ_ERROR_SIGNATURE, "Signature read error: %Rrc", rc);
+ RTMemTmpFree(pSignature);
+ return rc;
+}
+
+
+/**
+ * Destroys the parsed signature.
+ *
+ * @param pModPe The PE module.
+ * @param pSignature The signature data to destroy.
+ */
+static void rtldrPE_VerifySignatureDestroy(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature)
+{
+ RT_NOREF_PV(pModPe);
+ RTCrPkcs7ContentInfo_Delete(&pSignature->PrimaryContentInfo);
+ if (pSignature->paNested)
+ {
+ RTMemTmpFree(pSignature->paNested);
+ pSignature->paNested = NULL;
+ }
+ RTMemTmpFree(pSignature);
+}
+
+
+/**
+ * Handles nested signatures.
+ *
+ * @returns IPRT status code.
+ * @param pSignature The signature status structure. Returns with
+ * cNested = 0 and paNested = NULL if no nested
+ * signatures.
+ * @param pErrInfo Where to return extended error info (optional).
+ */
+static int rtldrPE_VerifySignatureDecodeNested(PRTLDRPESIGNATURE pSignature, PRTERRINFO pErrInfo)
+{
+ Assert(pSignature->cNested == 0);
+ Assert(pSignature->paNested == NULL);
+
+ /*
+ * Count nested signatures.
+ */
+ uint32_t cNested = 0;
+ for (uint32_t iSignerInfo = 0; iSignerInfo < pSignature->Primary.pSignedData->SignerInfos.cItems; iSignerInfo++)
+ {
+ PCRTCRPKCS7SIGNERINFO pSignerInfo = pSignature->Primary.pSignedData->SignerInfos.papItems[iSignerInfo];
+ for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->UnauthenticatedAttributes.cItems; iAttrib++)
+ {
+ PCRTCRPKCS7ATTRIBUTE pAttrib = pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib];
+ if (pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
+ {
+ Assert(pAttrib->uValues.pContentInfos);
+ cNested += pAttrib->uValues.pContentInfos->cItems;
+ }
+ }
+ }
+ if (!cNested)
+ return VINF_SUCCESS;
+
+ /*
+ * Allocate and populate the info structures.
+ */
+ pSignature->paNested = (PRTLDRPESIGNATUREONE)RTMemTmpAllocZ(sizeof(pSignature->paNested[0]) * cNested);
+ if (!pSignature->paNested)
+ return RTErrInfoSetF(pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate space for %u nested signatures", cNested);
+ pSignature->cNested = cNested;
+
+ cNested = 0;
+ for (uint32_t iSignerInfo = 0; iSignerInfo < pSignature->Primary.pSignedData->SignerInfos.cItems; iSignerInfo++)
+ {
+ PCRTCRPKCS7SIGNERINFO pSignerInfo = pSignature->Primary.pSignedData->SignerInfos.papItems[iSignerInfo];
+ for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->UnauthenticatedAttributes.cItems; iAttrib++)
+ {
+ PCRTCRPKCS7ATTRIBUTE pAttrib = pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib];
+ if (pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
+ {
+ for (uint32_t iItem = 0; iItem < pAttrib->uValues.pContentInfos->cItems; iItem++, cNested++)
+ {
+ PRTLDRPESIGNATUREONE pInfo = &pSignature->paNested[cNested];
+ PRTCRPKCS7CONTENTINFO pContentInfo = pAttrib->uValues.pContentInfos->papItems[iItem];
+ pInfo->pContentInfo = pContentInfo;
+ pInfo->iSignature = cNested;
+
+ if (RTCrPkcs7ContentInfo_IsSignedData(pInfo->pContentInfo))
+ { /* likely */ }
+ else
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID, /** @todo error code*/
+ "Nested#%u: PKCS#7 is not 'signedData': %s", cNested, pInfo->pContentInfo->ContentType.szObjId);
+ PRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData;
+ pInfo->pSignedData = pSignedData;
+
+ /*
+ * Check the authenticode bits.
+ */
+ if (!strcmp(pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID))
+ { /* likely */ }
+ else
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID,
+ "Nested#%u: Unknown pSignedData.ContentInfo.ContentType.szObjId value: %s (expected %s)",
+ cNested, pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID);
+ pInfo->pIndData = pSignedData->ContentInfo.u.pIndirectDataContent;
+ Assert(pInfo->pIndData);
+
+ /*
+ * Check that things add up.
+ */
+ int rc = RTCrPkcs7SignedData_CheckSanity(pSignedData,
+ RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE
+ | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
+ | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
+ pErrInfo, "SD");
+ if (RT_SUCCESS(rc))
+ rc = RTCrSpcIndirectDataContent_CheckSanityEx(pInfo->pIndData,
+ pSignedData,
+ RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH,
+ pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ PCRTCRX509ALGORITHMIDENTIFIER pDigestAlgorithm = &pInfo->pIndData->DigestInfo.DigestAlgorithm;
+ pInfo->enmDigest = RTCrX509AlgorithmIdentifier_GetDigestType(pDigestAlgorithm,
+ true /*fPureDigestsOnly*/);
+ AssertReturn(pInfo->enmDigest != RTDIGESTTYPE_INVALID, VERR_INTERNAL_ERROR_4); /* Checked above! */
+ }
+ else
+ return rc;
+ }
+ }
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Decodes the raw signature.
+ *
+ * @returns IPRT status code.
+ * @param pModPe The PE module.
+ * @param pSignature The signature data.
+ * @param pErrInfo Optional error info buffer.
+ */
+static int rtldrPE_VerifySignatureDecode(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature, PRTERRINFO pErrInfo)
+{
+ WIN_CERTIFICATE const *pEntry = pSignature->pRawData;
+ AssertReturn(pEntry->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA, VERR_INTERNAL_ERROR_2);
+ AssertReturn(pEntry->wRevision == WIN_CERT_REVISION_2_0, VERR_INTERNAL_ERROR_2);
+ RT_NOREF_PV(pModPe);
+
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor,
+ &pEntry->bCertificate[0],
+ pEntry->dwLength - RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate),
+ pErrInfo,
+ &g_RTAsn1DefaultAllocator,
+ 0,
+ "WinCert");
+
+ PRTLDRPESIGNATUREONE pInfo = &pSignature->Primary;
+ pInfo->pContentInfo = &pSignature->PrimaryContentInfo;
+ int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, pInfo->pContentInfo, "CI");
+ if (RT_SUCCESS(rc))
+ {
+ if (RTCrPkcs7ContentInfo_IsSignedData(pInfo->pContentInfo))
+ {
+ pInfo->pSignedData = pInfo->pContentInfo->u.pSignedData;
+
+ /*
+ * Decode the authenticode bits.
+ */
+ if (!strcmp(pInfo->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID))
+ {
+ pInfo->pIndData = pInfo->pSignedData->ContentInfo.u.pIndirectDataContent;
+ Assert(pInfo->pIndData);
+
+ /*
+ * Check that things add up.
+ */
+ rc = RTCrPkcs7SignedData_CheckSanity(pInfo->pSignedData,
+ RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE
+ | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
+ | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
+ pErrInfo, "SD");
+ if (RT_SUCCESS(rc))
+ rc = RTCrSpcIndirectDataContent_CheckSanityEx(pInfo->pIndData,
+ pInfo->pSignedData,
+ RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH,
+ pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ PCRTCRX509ALGORITHMIDENTIFIER pDigestAlgorithm = &pInfo->pIndData->DigestInfo.DigestAlgorithm;
+ pInfo->enmDigest = RTCrX509AlgorithmIdentifier_GetDigestType(pDigestAlgorithm, true /*fPureDigestsOnly*/);
+ AssertReturn(pInfo->enmDigest != RTDIGESTTYPE_INVALID, VERR_INTERNAL_ERROR_4); /* Checked above! */
+
+ /*
+ * Deal with nested signatures.
+ */
+ rc = rtldrPE_VerifySignatureDecodeNested(pSignature, pErrInfo);
+ }
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID,
+ "Unknown pSignedData.ContentInfo.ContentType.szObjId value: %s (expected %s)",
+ pInfo->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID, /** @todo error code*/
+ "PKCS#7 is not 'signedData': %s", pInfo->pContentInfo->ContentType.szObjId);
+ }
+ return rc;
+}
+
+
+
+static int rtldrPE_VerifyAllPageHashes(PRTLDRMODPE pModPe, PCRTCRSPCSERIALIZEDOBJECTATTRIBUTE pAttrib, RTDIGESTTYPE enmDigest,
+ void *pvScratch, size_t cbScratch, uint32_t iSignature, PRTERRINFO pErrInfo)
+{
+ AssertReturn(cbScratch >= _4K, VERR_INTERNAL_ERROR_3);
+
+ /*
+ * Calculate the special places.
+ */
+ RTLDRPEHASHSPECIALS SpecialPlaces = { 0, 0, 0, 0, 0, 0 }; /* shut up gcc */
+ int rc = rtldrPe_CalcSpecialHashPlaces(pModPe, &SpecialPlaces, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint32_t const cbHash = rtLdrPE_HashGetHashSize(enmDigest);
+ uint32_t const cPages = pAttrib->u.pPageHashes->RawData.Asn1Core.cb / (cbHash + 4);
+ if (cPages * (cbHash + 4) != pAttrib->u.pPageHashes->RawData.Asn1Core.cb)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_SIZE_OVERFLOW,
+ "Signature #%u - Page hashes size issue in: cb=%#x cbHash=%#x",
+ iSignature, pAttrib->u.pPageHashes->RawData.Asn1Core.cb, cbHash);
+
+ /*
+ * Walk the table.
+ */
+ uint32_t const cbScratchReadMax = cbScratch & ~(uint32_t)(_4K - 1);
+ uint32_t cbScratchRead = 0;
+ uint32_t offScratchRead = 0;
+
+ uint32_t offPrev = 0;
+#ifdef COMPLICATED_AND_WRONG
+ uint32_t offSectEnd = pModPe->cbHeaders;
+ uint32_t iSh = UINT32_MAX;
+#endif
+ uint8_t const *pbHashTab = pAttrib->u.pPageHashes->RawData.Asn1Core.uData.pu8;
+ for (uint32_t iPage = 0; iPage < cPages - 1; iPage++)
+ {
+ /* Decode the page offset. */
+ uint32_t const offPageInFile = RT_MAKE_U32_FROM_U8(pbHashTab[0], pbHashTab[1], pbHashTab[2], pbHashTab[3]);
+ if (RT_UNLIKELY(offPageInFile >= SpecialPlaces.cbToHash))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_TOO_LONG,
+ "Signature #%u - Page hash entry #%u is beyond the signature table start: %#x, %#x",
+ iSignature, iPage, offPageInFile, SpecialPlaces.cbToHash);
+ if (RT_UNLIKELY(offPageInFile < offPrev))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_NOT_STRICTLY_SORTED,
+ "Signature #%u - Page hash table is not strictly sorted: entry #%u @%#x, previous @%#x\n",
+ iSignature, iPage, offPageInFile, offPrev);
+
+#ifdef COMPLICATED_AND_WRONG
+ /* Figure out how much to read and how much to zero. Need keep track
+ of the on-disk section boundraries. */
+ if (offPageInFile >= offSectEnd)
+ {
+ iSh++;
+ if ( iSh < pModPe->cSections
+ && offPageInFile - pModPe->paSections[iSh].PointerToRawData < pModPe->paSections[iSh].SizeOfRawData)
+ offSectEnd = pModPe->paSections[iSh].PointerToRawData + pModPe->paSections[iSh].SizeOfRawData;
+ else
+ {
+ iSh = 0;
+ while ( iSh < pModPe->cSections
+ && offPageInFile - pModPe->paSections[iSh].PointerToRawData >= pModPe->paSections[iSh].SizeOfRawData)
+ iSh++;
+ if (iSh < pModPe->cSections)
+ offSectEnd = pModPe->paSections[iSh].PointerToRawData + pModPe->paSections[iSh].SizeOfRawData;
+ else
+ return RTErrInfoSetF(pErrInfo, VERR_PAGE_HASH_TAB_HASHES_NON_SECTION_DATA,
+ "Signature #%u - Page hash entry #%u isn't in any section: %#x",
+ iSignature, iPage, offPageInFile);
+ }
+ }
+
+#else
+ /* Figure out how much to read and how much take as zero. Use the next
+ page offset and the signature as upper boundraries. */
+#endif
+ uint32_t cbPageInFile = _4K;
+#ifdef COMPLICATED_AND_WRONG
+ if (offPageInFile + cbPageInFile > offSectEnd)
+ cbPageInFile = offSectEnd - offPageInFile;
+#else
+ if (iPage + 1 < cPages)
+ {
+ uint32_t offNextPage = RT_MAKE_U32_FROM_U8(pbHashTab[0 + 4 + cbHash], pbHashTab[1 + 4 + cbHash],
+ pbHashTab[2 + 4 + cbHash], pbHashTab[3 + 4 + cbHash]);
+ if (offNextPage - offPageInFile < cbPageInFile)
+ cbPageInFile = offNextPage - offPageInFile;
+ }
+#endif
+
+ if (offPageInFile + cbPageInFile > SpecialPlaces.cbToHash)
+ cbPageInFile = SpecialPlaces.cbToHash - offPageInFile;
+
+ /* Did we get a cache hit? */
+ uint8_t *pbCur = (uint8_t *)pvScratch;
+ if ( offPageInFile + cbPageInFile <= offScratchRead + cbScratchRead
+ && offPageInFile >= offScratchRead)
+ pbCur += offPageInFile - offScratchRead;
+ /* Missed, read more. */
+ else
+ {
+ offScratchRead = offPageInFile;
+#ifdef COMPLICATED_AND_WRONG
+ cbScratchRead = offSectEnd - offPageInFile;
+#else
+ cbScratchRead = SpecialPlaces.cbToHash - offPageInFile;
+#endif
+ if (cbScratchRead > cbScratchReadMax)
+ cbScratchRead = cbScratchReadMax;
+ rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pbCur, cbScratchRead, offScratchRead);
+ if (RT_FAILURE(rc))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_READ_ERROR_HASH,
+ "Signature #%u - Page hash read error at %#x: %Rrc (cbScratchRead=%#zx)",
+ iSignature, offScratchRead, rc, cbScratchRead);
+ }
+
+ /*
+ * Hash it.
+ */
+ RTLDRPEHASHCTXUNION HashCtx;
+ rc = rtLdrPE_HashInit(&HashCtx, enmDigest);
+ AssertRCReturn(rc, rc);
+
+ /* Deal with special places. */
+ uint32_t cbLeft = cbPageInFile;
+ if (offPageInFile < SpecialPlaces.offEndSpecial)
+ {
+ uint32_t off = offPageInFile;
+ if (off < SpecialPlaces.offCksum)
+ {
+ /* Hash everything up to the checksum. */
+ uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum - off, cbLeft);
+ rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbChunk);
+ pbCur += cbChunk;
+ cbLeft -= cbChunk;
+ off += cbChunk;
+ }
+
+ if (off < SpecialPlaces.offCksum + SpecialPlaces.cbCksum && off >= SpecialPlaces.offCksum)
+ {
+ /* Skip the checksum */
+ uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum + SpecialPlaces.cbCksum - off, cbLeft);
+ pbCur += cbChunk;
+ cbLeft -= cbChunk;
+ off += cbChunk;
+ }
+
+ if (off < SpecialPlaces.offSecDir && off >= SpecialPlaces.offCksum + SpecialPlaces.cbCksum)
+ {
+ /* Hash everything between the checksum and the data dir entry. */
+ uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir - off, cbLeft);
+ rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbChunk);
+ pbCur += cbChunk;
+ cbLeft -= cbChunk;
+ off += cbChunk;
+ }
+
+ if (off < SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir && off >= SpecialPlaces.offSecDir)
+ {
+ /* Skip the security data directory entry. */
+ uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir - off, cbLeft);
+ pbCur += cbChunk;
+ cbLeft -= cbChunk;
+ off += cbChunk;
+ }
+ }
+
+ rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbLeft);
+ if (cbPageInFile < _4K)
+ rtLdrPE_HashUpdate(&HashCtx, enmDigest, g_abRTZero4K, _4K - cbPageInFile);
+
+ /*
+ * Finish the hash calculation and compare the result.
+ */
+ RTLDRPEHASHRESUNION HashRes;
+ rtLdrPE_HashFinalize(&HashCtx, enmDigest, &HashRes);
+
+ pbHashTab += 4;
+ if (memcmp(pbHashTab, &HashRes, cbHash) != 0)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_MISMATCH,
+ "Signature #%u - Page hash failed for page #%u, @%#x, %#x bytes: %.*Rhxs != %.*Rhxs",
+ iSignature, iPage, offPageInFile, cbPageInFile, (size_t)cbHash, pbHashTab,
+ (size_t)cbHash, &HashRes);
+ pbHashTab += cbHash;
+ offPrev = offPageInFile;
+ }
+
+ /*
+ * Check that the last table entry has a hash value of zero.
+ */
+ if (!ASMMemIsZero(pbHashTab + 4, cbHash))
+ return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_TOO_LONG,
+ "Signature #%u - Malformed final page hash table entry: #%u %#010x %.*Rhxs",
+ iSignature, cPages - 1, RT_MAKE_U32_FROM_U8(pbHashTab[0], pbHashTab[1], pbHashTab[2], pbHashTab[3]),
+ (size_t)cbHash, pbHashTab + 4);
+ return VINF_SUCCESS;
+}
+
+
+static int rtldrPE_VerifySignatureValidateOnePageHashes(PRTLDRMODPE pModPe, PRTLDRPESIGNATUREONE pInfo,
+ void *pvScratch, uint32_t cbScratch, PRTERRINFO pErrInfo)
+{
+ /*
+ * Compare the page hashes if present.
+ *
+ * Seems the difference between V1 and V2 page hash attributes is
+ * that v1 uses SHA-1 while v2 uses SHA-256. The data structures
+ * seems to be identical otherwise. Initially we assumed the digest
+ * algorithm was supposed to be RTCRSPCINDIRECTDATACONTENT::DigestInfo,
+ * i.e. the same as for the whole image hash. The initial approach
+ * worked just fine, but this makes more sense.
+ *
+ * (See also comments in osslsigncode.c (google it).)
+ */
+ PCRTCRSPCSERIALIZEDOBJECTATTRIBUTE pAttrib;
+ /* V2 - SHA-256: */
+ pAttrib = RTCrSpcIndirectDataContent_GetPeImageObjAttrib(pInfo->pIndData,
+ RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V2);
+ if (pAttrib)
+ return rtldrPE_VerifyAllPageHashes(pModPe, pAttrib, RTDIGESTTYPE_SHA256, pvScratch, cbScratch,
+ pInfo->iSignature + 1, pErrInfo);
+
+ /* V1 - SHA-1: */
+ pAttrib = RTCrSpcIndirectDataContent_GetPeImageObjAttrib(pInfo->pIndData,
+ RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1);
+ if (pAttrib)
+ return rtldrPE_VerifyAllPageHashes(pModPe, pAttrib, RTDIGESTTYPE_SHA1, pvScratch, cbScratch,
+ pInfo->iSignature + 1, pErrInfo);
+
+ /* No page hashes: */
+ return VINF_SUCCESS;
+}
+
+
+static int rtldrPE_VerifySignatureValidateOneImageHash(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature,
+ PRTLDRPESIGNATUREONE pInfo, void *pvScratch, uint32_t cbScratch,
+ PRTERRINFO pErrInfo)
+{
+ /*
+ * Assert sanity.
+ */
+ AssertReturn(pInfo->enmDigest > RTDIGESTTYPE_INVALID && pInfo->enmDigest < RTDIGESTTYPE_END, VERR_INTERNAL_ERROR_4);
+ AssertPtrReturn(pInfo->pIndData, VERR_INTERNAL_ERROR_5);
+ AssertReturn(RTASN1CORE_IS_PRESENT(&pInfo->pIndData->DigestInfo.Digest.Asn1Core), VERR_INTERNAL_ERROR_5);
+ AssertPtrReturn(pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv, VERR_INTERNAL_ERROR_5);
+
+ /* Check that the hash is supported by the code here before continuing. */
+ AssertReturn(rtLdrPE_HashIsSupported(pInfo->enmDigest),
+ RTErrInfoSetF(pErrInfo, VERR_CR_DIGEST_NOT_SUPPORTED, "Unsupported digest type: %d", pInfo->enmDigest));
+
+ /*
+ * Skip it if we've already verified it.
+ */
+ if (pInfo->fValidatedImageHash)
+ return VINF_SUCCESS;
+
+ /*
+ * Calculate it.
+ */
+ uint32_t const cbHash = rtLdrPE_HashGetHashSize(pInfo->enmDigest);
+ AssertReturn(pInfo->pIndData->DigestInfo.Digest.Asn1Core.cb == cbHash, VERR_INTERNAL_ERROR_5);
+
+ int rc = rtldrPE_HashImageCommon(pModPe, pvScratch, cbScratch, pInfo->enmDigest,
+ &pSignature->HashCtx, &pInfo->HashRes, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ pInfo->fValidatedImageHash = true;
+ if (memcmp(&pInfo->HashRes, pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv, cbHash) == 0)
+ {
+ /*
+ * Verify other signatures with the same digest type.
+ */
+ RTLDRPEHASHRESUNION const * const pHashRes = &pInfo->HashRes;
+ RTDIGESTTYPE const enmDigestType = pInfo->enmDigest;
+ for (uint32_t i = 0; i < pSignature->cNested; i++)
+ {
+ pInfo = &pSignature->paNested[i]; /* Note! pInfo changes! */
+ if ( !pInfo->fValidatedImageHash
+ && pInfo->enmDigest == enmDigestType
+ /* paranoia from the top of this function: */
+ && pInfo->pIndData
+ && RTASN1CORE_IS_PRESENT(&pInfo->pIndData->DigestInfo.Digest.Asn1Core)
+ && pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv
+ && pInfo->pIndData->DigestInfo.Digest.Asn1Core.cb == cbHash)
+ {
+ pInfo->fValidatedImageHash = true;
+ if (memcmp(pHashRes, pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv, cbHash) != 0)
+ {
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_IMAGE_HASH_MISMATCH,
+ "Full image signature #%u mismatch: %.*Rhxs, expected %.*Rhxs", pInfo->iSignature + 1,
+ cbHash, pHashRes,
+ cbHash, pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv);
+ break;
+ }
+ }
+ }
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_IMAGE_HASH_MISMATCH,
+ "Full image signature #%u mismatch: %.*Rhxs, expected %.*Rhxs", pInfo->iSignature + 1,
+ cbHash, &pInfo->HashRes,
+ cbHash, pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv);
+ }
+ return rc;
+}
+
+
+/**
+ * Validates the image hash, including page hashes if present.
+ *
+ * @returns IPRT status code.
+ * @param pModPe The PE module.
+ * @param pSignature The decoded signature data.
+ * @param pErrInfo Optional error info buffer.
+ */
+static int rtldrPE_VerifySignatureValidateHash(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature, PRTERRINFO pErrInfo)
+{
+ /*
+ * Allocate a temporary memory buffer.
+ * Note! The _4K that gets subtracted is to avoid that the 16-byte heap
+ * block header in ring-0 (iprt) caused any unnecessary internal
+ * heap fragmentation.
+ */
+# ifdef IN_RING0
+ uint32_t cbScratch = _256K - _4K;
+# else
+ uint32_t cbScratch = _1M;
+# endif
+ void *pvScratch = RTMemTmpAlloc(cbScratch);
+ if (!pvScratch)
+ {
+ cbScratch = _4K;
+ pvScratch = RTMemTmpAlloc(cbScratch);
+ if (!pvScratch)
+ return RTErrInfoSet(pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate 4KB of scratch space for hashing image.");
+ }
+
+ /*
+ * Verify signatures.
+ */
+ /* Image hashes: */
+ int rc = rtldrPE_VerifySignatureValidateOneImageHash(pModPe, pSignature, &pSignature->Primary,
+ pvScratch, cbScratch, pErrInfo);
+ for (unsigned i = 0; i < pSignature->cNested && RT_SUCCESS(rc); i++)
+ rc = rtldrPE_VerifySignatureValidateOneImageHash(pModPe, pSignature, &pSignature->paNested[i],
+ pvScratch, cbScratch, pErrInfo);
+
+ /* Page hashes: */
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtldrPE_VerifySignatureValidateOnePageHashes(pModPe, &pSignature->Primary, pvScratch, cbScratch, pErrInfo);
+ for (unsigned i = 0; i < pSignature->cNested && RT_SUCCESS(rc); i++)
+ rc = rtldrPE_VerifySignatureValidateOnePageHashes(pModPe, &pSignature->paNested[i], pvScratch, cbScratch, pErrInfo);
+ }
+
+ /*
+ * Ditch the scratch buffer.
+ */
+ RTMemTmpFree(pvScratch);
+ return rc;
+}
+
+#endif /* !IPRT_WITHOUT_LDR_VERIFY */
+
+
+/** @interface_method_impl{RTLDROPS,pfnVerifySignature} */
+static DECLCALLBACK(int) rtldrPE_VerifySignature(PRTLDRMODINTERNAL pMod, PFNRTLDRVALIDATESIGNEDDATA pfnCallback, void *pvUser,
+ PRTERRINFO pErrInfo)
+{
+#ifndef IPRT_WITHOUT_LDR_VERIFY
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+
+ int rc = rtldrPE_VerifySignatureImagePrecoditions(pModPe, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ PRTLDRPESIGNATURE pSignature = NULL;
+ rc = rtldrPE_VerifySignatureRead(pModPe, &pSignature, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtldrPE_VerifySignatureDecode(pModPe, pSignature, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = rtldrPE_VerifySignatureValidateHash(pModPe, pSignature, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Work the callback.
+ */
+ /* The primary signature: */
+ RTLDRSIGNATUREINFO Info;
+ Info.iSignature = 0;
+ Info.cSignatures = (uint16_t)(1 + pSignature->cNested);
+ Info.enmType = RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA;
+ Info.pvSignature = pSignature->Primary.pContentInfo;
+ Info.cbSignature = sizeof(*pSignature->Primary.pContentInfo);
+ Info.pvExternalData = NULL;
+ Info.cbExternalData = 0;
+ rc = pfnCallback(&pModPe->Core, &Info, pErrInfo, pvUser);
+
+ /* The nested signatures: */
+ for (uint32_t iNested = 0; iNested < pSignature->cNested && rc == VINF_SUCCESS; iNested++)
+ {
+ Info.iSignature = (uint16_t)(1 + iNested);
+ Info.cSignatures = (uint16_t)(1 + pSignature->cNested);
+ Info.enmType = RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA;
+ Info.pvSignature = pSignature->paNested[iNested].pContentInfo;
+ Info.cbSignature = sizeof(*pSignature->paNested[iNested].pContentInfo);
+ Info.pvExternalData = NULL;
+ Info.cbExternalData = 0;
+ rc = pfnCallback(&pModPe->Core, &Info, pErrInfo, pvUser);
+ }
+ }
+ rtldrPE_VerifySignatureDestroy(pModPe, pSignature);
+ }
+ }
+ return rc;
+#else
+ RT_NOREF_PV(pMod); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(pvUser); RT_NOREF_PV(pErrInfo);
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnHashImage}
+ */
+static DECLCALLBACK(int) rtldrPE_HashImage(PRTLDRMODINTERNAL pMod, RTDIGESTTYPE enmDigest, uint8_t *pabHash, size_t cbHash)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+
+ /*
+ * Allocate a temporary memory buffer.
+ */
+ uint32_t cbScratch = _16K;
+ void *pvScratch = RTMemTmpAlloc(cbScratch);
+ if (!pvScratch)
+ {
+ cbScratch = _4K;
+ pvScratch = RTMemTmpAlloc(cbScratch);
+ if (!pvScratch)
+ return VERR_NO_TMP_MEMORY;
+ }
+
+ /*
+ * Do the hashing.
+ */
+ RTLDRPEHASHCTXUNION HashCtx;
+ RTLDRPEHASHRESUNION HashRes;
+ int rc = rtldrPE_HashImageCommon(pModPe, pvScratch, cbScratch, enmDigest, &HashCtx, &HashRes, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Copy out the result.
+ */
+ RT_NOREF(cbHash); /* verified by caller */
+ switch (enmDigest)
+ {
+ case RTDIGESTTYPE_SHA512: memcpy(pabHash, HashRes.abSha512, sizeof(HashRes.abSha512)); break;
+ case RTDIGESTTYPE_SHA256: memcpy(pabHash, HashRes.abSha256, sizeof(HashRes.abSha256)); break;
+ case RTDIGESTTYPE_SHA1: memcpy(pabHash, HashRes.abSha1, sizeof(HashRes.abSha1)); break;
+ case RTDIGESTTYPE_MD5: memcpy(pabHash, HashRes.abMd5, sizeof(HashRes.abMd5)); break;
+ default: AssertFailedReturn(VERR_INTERNAL_ERROR_3);
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Binary searches the lookup table.
+ *
+ * @returns RVA of unwind info on success, UINT32_MAX on failure.
+ * @param paFunctions The table to lookup @a uRva in.
+ * @param iEnd Size of the table.
+ * @param uRva The RVA of the function we want.
+ */
+DECLINLINE(PCIMAGE_RUNTIME_FUNCTION_ENTRY)
+rtldrPE_LookupRuntimeFunctionEntry(PCIMAGE_RUNTIME_FUNCTION_ENTRY paFunctions, size_t iEnd, uint32_t uRva)
+{
+ size_t iBegin = 0;
+ while (iBegin < iEnd)
+ {
+ size_t const i = iBegin + (iEnd - iBegin) / 2;
+ PCIMAGE_RUNTIME_FUNCTION_ENTRY pEntry = &paFunctions[i];
+ if (uRva < pEntry->BeginAddress)
+ iEnd = i;
+ else if (uRva > pEntry->EndAddress)
+ iBegin = i + 1;
+ else
+ return pEntry;
+ }
+ return NULL;
+}
+
+
+/**
+ * Processes an IRET frame.
+ *
+ * @returns IPRT status code.
+ * @param pState The unwind state being worked.
+ * @param fErrCd Non-zero if there is an error code on the stack.
+ */
+static int rtldrPE_UnwindFrame_Amd64_IRet(PRTDBGUNWINDSTATE pState, uint8_t fErrCd)
+{
+ /* POP ErrCd (optional): */
+ Assert(fErrCd <= 1);
+ int rcRet;
+ if (fErrCd)
+ {
+ pState->u.x86.uErrCd = 0;
+ pState->u.x86.Loaded.s.fErrCd = 1;
+ rcRet = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.uErrCd);
+ pState->u.x86.auRegs[X86_GREG_xSP] += 8;
+ }
+ else
+ {
+ pState->u.x86.Loaded.s.fErrCd = 0;
+ rcRet = VINF_SUCCESS;
+ }
+
+ /* Set return type and frame pointer. */
+ pState->enmRetType = RTDBGRETURNTYPE_IRET64;
+ pState->u.x86.FrameAddr.off = pState->u.x86.auRegs[X86_GREG_xSP] - /* pretend rbp is pushed on the stack */ 8;
+ pState->u.x86.FrameAddr.sel = pState->u.x86.auSegs[X86_SREG_SS];
+
+ /* POP RIP: */
+ int rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->uPc);
+ if (RT_FAILURE(rc))
+ rcRet = rc;
+ pState->u.x86.auRegs[X86_GREG_xSP] += 8;
+
+ /* POP CS: */
+ rc = RTDbgUnwindLoadStackU16(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.auSegs[X86_SREG_CS]);
+ if (RT_FAILURE(rc))
+ rcRet = rc;
+ pState->u.x86.auRegs[X86_GREG_xSP] += 8;
+
+ /* POP RFLAGS: */
+ rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.uRFlags);
+ if (RT_FAILURE(rc))
+ rcRet = rc;
+ pState->u.x86.auRegs[X86_GREG_xSP] += 8;
+
+ /* POP RSP, part 1: */
+ uint64_t uNewRsp = (pState->u.x86.auRegs[X86_GREG_xSP] - 8) & ~(uint64_t)15;
+ rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &uNewRsp);
+ if (RT_FAILURE(rc))
+ rcRet = rc;
+ pState->u.x86.auRegs[X86_GREG_xSP] += 8;
+
+ /* POP SS: */
+ rc = RTDbgUnwindLoadStackU16(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.auSegs[X86_SREG_SS]);
+ if (RT_FAILURE(rc))
+ rcRet = rc;
+ pState->u.x86.auRegs[X86_GREG_xSP] += 8;
+
+ /* POP RSP, part 2: */
+ pState->u.x86.auRegs[X86_GREG_xSP] = uNewRsp;
+
+ /* Set loaded indicators: */
+ pState->u.x86.Loaded.s.fRegs |= RT_BIT(X86_GREG_xSP);
+ pState->u.x86.Loaded.s.fSegs |= RT_BIT(X86_SREG_CS) | RT_BIT(X86_SREG_SS);
+ pState->u.x86.Loaded.s.fPc = 1;
+ pState->u.x86.Loaded.s.fFrameAddr = 1;
+ pState->u.x86.Loaded.s.fRFlags = 1;
+ return VINF_SUCCESS;
+}
+
+
+static int rtldrPE_UnwindFrame_Amd64(PRTLDRMODPE pThis, void const *pvBits, PRTDBGUNWINDSTATE pState, uint32_t uRvaPc,
+ PCIMAGE_RUNTIME_FUNCTION_ENTRY pEntry)
+{
+ /* Did we find any unwind information? */
+ if (!pEntry)
+ return VERR_DBG_UNWIND_INFO_NOT_FOUND;
+
+ /*
+ * Do the unwinding.
+ */
+ IMAGE_RUNTIME_FUNCTION_ENTRY ChainedEntry;
+ unsigned iFrameReg = ~0U;
+ unsigned offFrameReg = 0;
+
+ int fInEpilog = -1; /* -1: not-determined-assume-false; 0: false; 1: true. */
+ uint8_t cbEpilog = 0;
+ uint8_t offEpilog = UINT8_MAX;
+ int rcRet = VINF_SUCCESS;
+ int rc;
+ for (unsigned cChainLoops = 0; ; cChainLoops++)
+ {
+ /*
+ * Get the info.
+ */
+ union
+ {
+ uint32_t uRva;
+ uint8_t ab[ RT_OFFSETOF(IMAGE_UNWIND_INFO, aOpcodes)
+ + sizeof(IMAGE_UNWIND_CODE) * 256
+ + sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)];
+ } uBuf;
+ rc = rtldrPEReadPartByRvaInfoBuf(pThis, pvBits, pEntry->UnwindInfoAddress, sizeof(uBuf), &uBuf);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Check the info.
+ */
+ ASMCompilerBarrier(); /* we're aliasing */
+ PCIMAGE_UNWIND_INFO pInfo = (PCIMAGE_UNWIND_INFO)&uBuf;
+
+ if (pInfo->Version != 1 && pInfo->Version != 2)
+ return VERR_DBG_MALFORMED_UNWIND_INFO;
+
+ /*
+ * Execute the opcodes.
+ */
+ unsigned const cOpcodes = pInfo->CountOfCodes;
+ unsigned iOpcode = 0;
+
+ /*
+ * Check for epilog opcodes at the start and see if we're in an epilog.
+ */
+ if ( pInfo->Version >= 2
+ && iOpcode < cOpcodes
+ && pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_EPILOG)
+ {
+ if (fInEpilog == -1)
+ {
+ cbEpilog = pInfo->aOpcodes[iOpcode].u.CodeOffset;
+ Assert(cbEpilog > 0);
+
+ uint32_t uRvaEpilog = pEntry->EndAddress - cbEpilog;
+ iOpcode++;
+ if ( (pInfo->aOpcodes[iOpcode - 1].u.OpInfo & 1)
+ && uRvaPc >= uRvaEpilog)
+ {
+ offEpilog = uRvaPc - uRvaEpilog;
+ fInEpilog = 1;
+ }
+ else
+ {
+ fInEpilog = 0;
+ while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_EPILOG)
+ {
+ uRvaEpilog = pEntry->EndAddress
+ - (pInfo->aOpcodes[iOpcode].u.CodeOffset + (pInfo->aOpcodes[iOpcode].u.OpInfo << 8));
+ iOpcode++;
+ if (uRvaPc - uRvaEpilog < cbEpilog)
+ {
+ offEpilog = uRvaPc - uRvaEpilog;
+ fInEpilog = 1;
+ break;
+ }
+ }
+ }
+ }
+ while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_EPILOG)
+ iOpcode++;
+ }
+ if (fInEpilog != 1)
+ {
+ /*
+ * Skip opcodes that doesn't apply to us if we're in the prolog.
+ */
+ uint32_t offPc = uRvaPc - pEntry->BeginAddress;
+ if (offPc < pInfo->SizeOfProlog)
+ while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.CodeOffset > offPc)
+ iOpcode++;
+
+ /*
+ * Execute the opcodes.
+ */
+ if (pInfo->FrameRegister != 0)
+ {
+ iFrameReg = pInfo->FrameRegister;
+ offFrameReg = pInfo->FrameOffset * 16;
+ }
+ while (iOpcode < cOpcodes)
+ {
+ Assert(pInfo->aOpcodes[iOpcode].u.CodeOffset <= offPc);
+ uint8_t const uOpInfo = pInfo->aOpcodes[iOpcode].u.OpInfo;
+ uint8_t const uUnwindOp = pInfo->aOpcodes[iOpcode].u.UnwindOp;
+ switch (uUnwindOp)
+ {
+ case IMAGE_AMD64_UWOP_PUSH_NONVOL:
+ rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.auRegs[uOpInfo]);
+ if (RT_FAILURE(rc))
+ rcRet = rc;
+ pState->u.x86.Loaded.s.fRegs |= RT_BIT(uOpInfo);
+ pState->u.x86.auRegs[X86_GREG_xSP] += 8;
+ iOpcode++;
+ break;
+
+ case IMAGE_AMD64_UWOP_ALLOC_LARGE:
+ if (uOpInfo == 0)
+ {
+ iOpcode += 2;
+ AssertBreak(iOpcode <= cOpcodes);
+ pState->u.x86.auRegs[X86_GREG_xSP] += pInfo->aOpcodes[iOpcode - 1].FrameOffset * 8;
+ }
+ else
+ {
+ iOpcode += 3;
+ AssertBreak(iOpcode <= cOpcodes);
+ pState->u.x86.auRegs[X86_GREG_xSP] += RT_MAKE_U32(pInfo->aOpcodes[iOpcode - 2].FrameOffset,
+ pInfo->aOpcodes[iOpcode - 1].FrameOffset);
+ }
+ break;
+
+ case IMAGE_AMD64_UWOP_ALLOC_SMALL:
+ AssertBreak(iOpcode <= cOpcodes);
+ pState->u.x86.auRegs[X86_GREG_xSP] += uOpInfo * 8 + 8;
+ iOpcode++;
+ break;
+
+ case IMAGE_AMD64_UWOP_SET_FPREG:
+ iFrameReg = uOpInfo;
+ offFrameReg = pInfo->FrameOffset * 16;
+ pState->u.x86.auRegs[X86_GREG_xSP] = pState->u.x86.auRegs[iFrameReg] - offFrameReg;
+ iOpcode++;
+ break;
+
+ case IMAGE_AMD64_UWOP_SAVE_NONVOL:
+ case IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR:
+ {
+ uint32_t off = 0;
+ iOpcode++;
+ if (iOpcode < cOpcodes)
+ {
+ off = pInfo->aOpcodes[iOpcode].FrameOffset;
+ iOpcode++;
+ if (uUnwindOp == IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR && iOpcode < cOpcodes)
+ {
+ off |= (uint32_t)pInfo->aOpcodes[iOpcode].FrameOffset << 16;
+ iOpcode++;
+ }
+ }
+ off *= 8;
+ rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP] + off,
+ &pState->u.x86.auRegs[uOpInfo]);
+ if (RT_FAILURE(rc))
+ rcRet = rc;
+ pState->u.x86.Loaded.s.fRegs |= RT_BIT(uOpInfo);
+ break;
+ }
+
+ case IMAGE_AMD64_UWOP_SAVE_XMM128:
+ iOpcode += 2;
+ break;
+
+ case IMAGE_AMD64_UWOP_SAVE_XMM128_FAR:
+ iOpcode += 3;
+ break;
+
+ case IMAGE_AMD64_UWOP_PUSH_MACHFRAME:
+ return rtldrPE_UnwindFrame_Amd64_IRet(pState, uOpInfo);
+
+ case IMAGE_AMD64_UWOP_EPILOG:
+ iOpcode += 1;
+ break;
+
+ case IMAGE_AMD64_UWOP_RESERVED_7:
+ AssertFailedReturn(VERR_DBG_MALFORMED_UNWIND_INFO);
+
+ default:
+ AssertMsgFailedReturn(("%u\n", uUnwindOp), VERR_DBG_MALFORMED_UNWIND_INFO);
+ }
+ }
+ }
+ else
+ {
+ /*
+ * We're in the POP sequence of an epilog. The POP sequence should
+ * mirror the PUSH sequence exactly.
+ *
+ * Note! We should only end up here for the initial frame (just consider
+ * RSP, stack allocations, non-volatile register restores, ++).
+ */
+ while (iOpcode < cOpcodes)
+ {
+ uint8_t const uOpInfo = pInfo->aOpcodes[iOpcode].u.OpInfo;
+ uint8_t const uUnwindOp = pInfo->aOpcodes[iOpcode].u.UnwindOp;
+ switch (uUnwindOp)
+ {
+ case IMAGE_AMD64_UWOP_PUSH_NONVOL:
+ pState->u.x86.auRegs[X86_GREG_xSP] += 8;
+ if (offEpilog == 0)
+ {
+ rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP],
+ &pState->u.x86.auRegs[uOpInfo]);
+ if (RT_FAILURE(rc))
+ rcRet = rc;
+ pState->u.x86.Loaded.s.fRegs |= RT_BIT(uOpInfo);
+ }
+ else
+ {
+ /* Decrement offEpilog by estimated POP instruction length. */
+ offEpilog -= 1;
+ if (offEpilog > 0 && uOpInfo >= 8)
+ offEpilog -= 1;
+ }
+ iOpcode++;
+ break;
+
+ case IMAGE_AMD64_UWOP_PUSH_MACHFRAME: /* Must terminate an epilog, so always execute this. */
+ return rtldrPE_UnwindFrame_Amd64_IRet(pState, uOpInfo);
+
+ case IMAGE_AMD64_UWOP_ALLOC_SMALL:
+ case IMAGE_AMD64_UWOP_SET_FPREG:
+ case IMAGE_AMD64_UWOP_EPILOG:
+ iOpcode++;
+ break;
+ case IMAGE_AMD64_UWOP_SAVE_NONVOL:
+ case IMAGE_AMD64_UWOP_SAVE_XMM128:
+ iOpcode += 2;
+ break;
+ case IMAGE_AMD64_UWOP_ALLOC_LARGE:
+ case IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR:
+ case IMAGE_AMD64_UWOP_SAVE_XMM128_FAR:
+ iOpcode += 3;
+ break;
+
+ default:
+ AssertMsgFailedReturn(("%u\n", uUnwindOp), VERR_DBG_MALFORMED_UNWIND_INFO);
+ }
+ }
+ }
+
+ /*
+ * Chained stuff?
+ */
+ if (!(pInfo->Flags & IMAGE_UNW_FLAGS_CHAININFO))
+ break;
+ ChainedEntry = *(PCIMAGE_RUNTIME_FUNCTION_ENTRY)&pInfo->aOpcodes[(cOpcodes + 1) & ~1];
+ pEntry = &ChainedEntry;
+ AssertReturn(cChainLoops < 32, VERR_DBG_MALFORMED_UNWIND_INFO);
+ }
+
+ /*
+ * RSP should now give us the return address, so perform a RET.
+ */
+ pState->enmRetType = RTDBGRETURNTYPE_NEAR64;
+
+ pState->u.x86.FrameAddr.off = pState->u.x86.auRegs[X86_GREG_xSP] - /* pretend rbp is pushed on the stack */ 8;
+ pState->u.x86.FrameAddr.sel = pState->u.x86.auSegs[X86_SREG_SS];
+ pState->u.x86.Loaded.s.fFrameAddr = 1;
+
+ rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->uPc);
+ if (RT_FAILURE(rc))
+ rcRet = rc;
+ pState->u.x86.auRegs[X86_GREG_xSP] += 8;
+ pState->u.x86.Loaded.s.fPc = 1;
+ return rcRet;
+}
+
+
+/**
+ * @interface_method_impl{RTLDROPS,pfnUnwindFrame}
+ */
+static DECLCALLBACK(int) rtldrPE_UnwindFrame(PRTLDRMODINTERNAL pMod, void const *pvBits,
+ uint32_t iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
+{
+ PRTLDRMODPE pThis = (PRTLDRMODPE)pMod;
+
+ /*
+ * Translate the segment + offset into an RVA.
+ */
+ RTLDRADDR uRvaPc = off;
+ if (iSeg != UINT32_MAX)
+ {
+ int rc = rtldrPE_SegOffsetToRva(pMod, iSeg, off, &uRvaPc);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Check for unwind info and match the architecture.
+ */
+ if ( pThis->ExceptionDir.Size == 0
+ || pThis->ExceptionDir.VirtualAddress < pThis->cbHeaders)
+ return VERR_DBG_NO_UNWIND_INFO;
+ if (pThis->Core.enmArch != pState->enmArch)
+ return VERR_DBG_UNWIND_INFO_NOT_FOUND;
+
+ /* Currently only AMD64 unwinding is implemented, so head it off right away. */
+ if (pThis->Core.enmArch != RTLDRARCH_AMD64)
+ return VERR_DBG_UNWIND_INFO_NOT_FOUND;
+
+ /*
+ * Make the lookup table available to us.
+ */
+ void const *pvTable = NULL;
+ uint32_t const cbTable = pThis->ExceptionDir.Size;
+ AssertReturn( cbTable < pThis->cbImage
+ && pThis->ExceptionDir.VirtualAddress < pThis->cbImage
+ && pThis->ExceptionDir.VirtualAddress + cbTable <= pThis->cbImage, VERR_INTERNAL_ERROR_3);
+ int rc = rtldrPEReadPartByRva(pThis, pvBits, pThis->ExceptionDir.VirtualAddress, pThis->ExceptionDir.Size, &pvTable);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * The rest is architecture dependent.
+ *
+ * Note! On windows we try catch access violations so we can safely use
+ * this code on mapped images during assertions.
+ */
+#if defined(_MSC_VER) && defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
+ __try
+ {
+#endif
+ switch (pThis->Core.enmArch)
+ {
+ case RTLDRARCH_AMD64:
+ rc = rtldrPE_UnwindFrame_Amd64(pThis, pvBits, pState, uRvaPc,
+ rtldrPE_LookupRuntimeFunctionEntry((PCIMAGE_RUNTIME_FUNCTION_ENTRY)pvTable,
+ cbTable / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY),
+ (uint32_t)uRvaPc));
+ break;
+
+ default:
+ rc = VERR_DBG_UNWIND_INFO_NOT_FOUND;
+ break;
+ }
+#if defined(_MSC_VER) && defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
+ }
+ __except (1 /*EXCEPTION_EXECUTE_HANDLER*/)
+ {
+ rc = VERR_DBG_UNWIND_INFO_NOT_FOUND;
+ }
+#endif
+ rtldrPEFreePart(pThis, pvBits, pvTable);
+ return rc;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnDone} */
+static DECLCALLBACK(int) rtldrPEDone(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+ if (pModPe->pvBits)
+ {
+ RTMemFree(pModPe->pvBits);
+ pModPe->pvBits = NULL;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/** @interface_method_impl{RTLDROPS,pfnClose} */
+static DECLCALLBACK(int) rtldrPEClose(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
+ if (pModPe->paSections)
+ {
+ RTMemFree(pModPe->paSections);
+ pModPe->paSections = NULL;
+ }
+ if (pModPe->pvBits)
+ {
+ RTMemFree(pModPe->pvBits);
+ pModPe->pvBits = NULL;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Operations for a 32-bit PE module.
+ */
+static const RTLDROPSPE s_rtldrPE32Ops =
+{
+ {
+ "pe32",
+ rtldrPEClose,
+ NULL,
+ rtldrPEDone,
+ rtldrPEEnumSymbols,
+ /* ext */
+ rtldrPEGetImageSize,
+ rtldrPEGetBits,
+ rtldrPERelocate,
+ rtldrPEGetSymbolEx,
+ rtldrPE_QueryForwarderInfo,
+ rtldrPE_EnumDbgInfo,
+ rtldrPE_EnumSegments,
+ rtldrPE_LinkAddressToSegOffset,
+ rtldrPE_LinkAddressToRva,
+ rtldrPE_SegOffsetToRva,
+ rtldrPE_RvaToSegOffset,
+ NULL,
+ rtldrPE_QueryProp,
+ rtldrPE_VerifySignature,
+ rtldrPE_HashImage,
+ NULL /*pfnUnwindFrame*/,
+ 42
+ },
+ rtldrPEResolveImports32,
+ 42
+};
+
+
+/**
+ * Operations for a 64-bit PE module.
+ */
+static const RTLDROPSPE s_rtldrPE64Ops =
+{
+ {
+ "pe64",
+ rtldrPEClose,
+ NULL,
+ rtldrPEDone,
+ rtldrPEEnumSymbols,
+ /* ext */
+ rtldrPEGetImageSize,
+ rtldrPEGetBits,
+ rtldrPERelocate,
+ rtldrPEGetSymbolEx,
+ rtldrPE_QueryForwarderInfo,
+ rtldrPE_EnumDbgInfo,
+ rtldrPE_EnumSegments,
+ rtldrPE_LinkAddressToSegOffset,
+ rtldrPE_LinkAddressToRva,
+ rtldrPE_SegOffsetToRva,
+ rtldrPE_RvaToSegOffset,
+ NULL,
+ rtldrPE_QueryProp,
+ rtldrPE_VerifySignature,
+ rtldrPE_HashImage,
+ rtldrPE_UnwindFrame,
+ 42
+ },
+ rtldrPEResolveImports64,
+ 42
+};
+
+
+/**
+ * Converts the optional header from 32 bit to 64 bit.
+ * This is a rather simple task, if you start from the right end.
+ *
+ * @param pOptHdr On input this is a PIMAGE_OPTIONAL_HEADER32.
+ * On output this will be a PIMAGE_OPTIONAL_HEADER64.
+ */
+static void rtldrPEConvert32BitOptionalHeaderTo64Bit(PIMAGE_OPTIONAL_HEADER64 pOptHdr)
+{
+ /*
+ * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
+ */
+ IMAGE_OPTIONAL_HEADER32 volatile *pOptHdr32 = (IMAGE_OPTIONAL_HEADER32 volatile *)pOptHdr;
+ IMAGE_OPTIONAL_HEADER64 volatile *pOptHdr64 = pOptHdr;
+
+ /* from LoaderFlags and out the difference is 4 * 32-bits. */
+ Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, LoaderFlags) + 16 == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, LoaderFlags));
+ Assert( RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]) + 16
+ == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]));
+ uint32_t volatile *pu32Dst = (uint32_t *)&pOptHdr64->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
+ const uint32_t volatile *pu32Src = (uint32_t *)&pOptHdr32->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
+ const uint32_t volatile *pu32SrcLast = (uint32_t *)&pOptHdr32->LoaderFlags;
+ while (pu32Src >= pu32SrcLast)
+ *pu32Dst-- = *pu32Src--;
+
+ /* the previous 4 fields are 32/64 and needs special attention. */
+ pOptHdr64->SizeOfHeapCommit = pOptHdr32->SizeOfHeapCommit;
+ pOptHdr64->SizeOfHeapReserve = pOptHdr32->SizeOfHeapReserve;
+ pOptHdr64->SizeOfStackCommit = pOptHdr32->SizeOfStackCommit;
+ uint32_t u32SizeOfStackReserve = pOptHdr32->SizeOfStackReserve;
+ pOptHdr64->SizeOfStackReserve = u32SizeOfStackReserve;
+
+ /* The rest matches except for BaseOfData which has been merged into ImageBase in the 64-bit version..
+ * Thus, ImageBase needs some special treatment. It will probably work fine assigning one to the
+ * other since this is all declared volatile, but taking now chances, we'll use a temp variable.
+ */
+ Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, SizeOfStackReserve) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, SizeOfStackReserve));
+ Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, BaseOfData) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, ImageBase));
+ Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, SectionAlignment) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, SectionAlignment));
+ uint32_t u32ImageBase = pOptHdr32->ImageBase;
+ pOptHdr64->ImageBase = u32ImageBase;
+}
+
+
+/**
+ * Converts the load config directory from 32 bit to 64 bit.
+ * This is a rather simple task, if you start from the right end.
+ *
+ * @param pLoadCfg On input this is a PIMAGE_LOAD_CONFIG_DIRECTORY32.
+ * On output this will be a PIMAGE_LOAD_CONFIG_DIRECTORY64.
+ */
+static void rtldrPEConvert32BitLoadConfigTo64Bit(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg)
+{
+ /*
+ * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
+ */
+ IMAGE_LOAD_CONFIG_DIRECTORY32_V13 volatile *pLoadCfg32 = (IMAGE_LOAD_CONFIG_DIRECTORY32_V13 volatile *)pLoadCfg;
+ IMAGE_LOAD_CONFIG_DIRECTORY64_V13 volatile *pLoadCfg64 = pLoadCfg;
+
+ pLoadCfg64->CastGuardOsDeterminedFailureMode = pLoadCfg32->CastGuardOsDeterminedFailureMode;
+ pLoadCfg64->GuardXFGTableDispatchFunctionPointer = pLoadCfg32->GuardXFGTableDispatchFunctionPointer;
+ pLoadCfg64->GuardXFGDispatchFunctionPointer = pLoadCfg32->GuardXFGDispatchFunctionPointer;
+ pLoadCfg64->GuardXFGCheckFunctionPointer = pLoadCfg32->GuardXFGCheckFunctionPointer;
+ pLoadCfg64->GuardEHContinuationCount = pLoadCfg32->GuardEHContinuationCount;
+ pLoadCfg64->GuardEHContinuationTable = pLoadCfg32->GuardEHContinuationTable;
+ pLoadCfg64->VolatileMetadataPointer = pLoadCfg32->VolatileMetadataPointer;
+ pLoadCfg64->EnclaveConfigurationPointer = pLoadCfg32->EnclaveConfigurationPointer;
+ pLoadCfg64->Reserved3 = pLoadCfg32->Reserved3;
+ pLoadCfg64->HotPatchTableOffset = pLoadCfg32->HotPatchTableOffset;
+ pLoadCfg64->GuardRFVerifyStackPointerFunctionPointer = pLoadCfg32->GuardRFVerifyStackPointerFunctionPointer;
+ pLoadCfg64->Reserved2 = pLoadCfg32->Reserved2;
+ pLoadCfg64->DynamicValueRelocTableSection = pLoadCfg32->DynamicValueRelocTableSection;
+ pLoadCfg64->DynamicValueRelocTableOffset = pLoadCfg32->DynamicValueRelocTableOffset;
+ pLoadCfg64->GuardRFFailureRoutineFunctionPointer = pLoadCfg32->GuardRFFailureRoutineFunctionPointer;
+ pLoadCfg64->GuardRFFailureRoutine = pLoadCfg32->GuardRFFailureRoutine;
+ pLoadCfg64->CHPEMetadataPointer = pLoadCfg32->CHPEMetadataPointer;
+ pLoadCfg64->DynamicValueRelocTable = pLoadCfg32->DynamicValueRelocTable;
+ pLoadCfg64->GuardLongJumpTargetCount = pLoadCfg32->GuardLongJumpTargetCount;
+ pLoadCfg64->GuardLongJumpTargetTable = pLoadCfg32->GuardLongJumpTargetTable;
+ pLoadCfg64->GuardAddressTakenIatEntryCount = pLoadCfg32->GuardAddressTakenIatEntryCount;
+ pLoadCfg64->GuardAddressTakenIatEntryTable = pLoadCfg32->GuardAddressTakenIatEntryTable;
+ pLoadCfg64->CodeIntegrity.Reserved = pLoadCfg32->CodeIntegrity.Reserved;
+ pLoadCfg64->CodeIntegrity.CatalogOffset = pLoadCfg32->CodeIntegrity.CatalogOffset;
+ pLoadCfg64->CodeIntegrity.Catalog = pLoadCfg32->CodeIntegrity.Catalog;
+ pLoadCfg64->CodeIntegrity.Flags = pLoadCfg32->CodeIntegrity.Flags;
+ pLoadCfg64->GuardFlags = pLoadCfg32->GuardFlags;
+ pLoadCfg64->GuardCFFunctionCount = pLoadCfg32->GuardCFFunctionCount;
+ pLoadCfg64->GuardCFFunctionTable = pLoadCfg32->GuardCFFunctionTable;
+ pLoadCfg64->GuardCFDispatchFunctionPointer = pLoadCfg32->GuardCFDispatchFunctionPointer;
+ pLoadCfg64->GuardCFCCheckFunctionPointer = pLoadCfg32->GuardCFCCheckFunctionPointer;
+ pLoadCfg64->SEHandlerCount = pLoadCfg32->SEHandlerCount;
+ pLoadCfg64->SEHandlerTable = pLoadCfg32->SEHandlerTable;
+ pLoadCfg64->SecurityCookie = pLoadCfg32->SecurityCookie;
+ pLoadCfg64->EditList = pLoadCfg32->EditList;
+ pLoadCfg64->DependentLoadFlags = pLoadCfg32->DependentLoadFlags;
+ pLoadCfg64->CSDVersion = pLoadCfg32->CSDVersion;
+ pLoadCfg64->ProcessHeapFlags = pLoadCfg32->ProcessHeapFlags; /* switched place with ProcessAffinityMask, but we're more than 16 byte off by now so it doesn't matter. */
+ pLoadCfg64->ProcessAffinityMask = pLoadCfg32->ProcessAffinityMask;
+ pLoadCfg64->VirtualMemoryThreshold = pLoadCfg32->VirtualMemoryThreshold;
+ pLoadCfg64->MaximumAllocationSize = pLoadCfg32->MaximumAllocationSize;
+ pLoadCfg64->LockPrefixTable = pLoadCfg32->LockPrefixTable;
+ pLoadCfg64->DeCommitTotalFreeThreshold = pLoadCfg32->DeCommitTotalFreeThreshold;
+ uint32_t u32DeCommitFreeBlockThreshold = pLoadCfg32->DeCommitFreeBlockThreshold;
+ pLoadCfg64->DeCommitFreeBlockThreshold = u32DeCommitFreeBlockThreshold;
+ /* the rest is equal. */
+ Assert( RT_UOFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY32, DeCommitFreeBlockThreshold)
+ == RT_UOFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY64, DeCommitFreeBlockThreshold));
+}
+
+
+/**
+ * Translate the PE/COFF machine name to a string.
+ *
+ * @returns Name string (read-only).
+ * @param uMachine The PE/COFF machine.
+ */
+static const char *rtldrPEGetArchName(uint16_t uMachine)
+{
+ switch (uMachine)
+ {
+ case IMAGE_FILE_MACHINE_I386: return "X86_32";
+ case IMAGE_FILE_MACHINE_AMD64: return "AMD64";
+
+ case IMAGE_FILE_MACHINE_UNKNOWN: return "UNKNOWN";
+ case IMAGE_FILE_MACHINE_AM33: return "AM33";
+ case IMAGE_FILE_MACHINE_ARM: return "ARM";
+ case IMAGE_FILE_MACHINE_THUMB: return "THUMB";
+ case IMAGE_FILE_MACHINE_ARMNT: return "ARMNT";
+ case IMAGE_FILE_MACHINE_ARM64: return "ARM64";
+ case IMAGE_FILE_MACHINE_EBC: return "EBC";
+ case IMAGE_FILE_MACHINE_IA64: return "IA64";
+ case IMAGE_FILE_MACHINE_M32R: return "M32R";
+ case IMAGE_FILE_MACHINE_MIPS16: return "MIPS16";
+ case IMAGE_FILE_MACHINE_MIPSFPU: return "MIPSFPU";
+ case IMAGE_FILE_MACHINE_MIPSFPU16: return "MIPSFPU16";
+ case IMAGE_FILE_MACHINE_WCEMIPSV2: return "WCEMIPSV2";
+ case IMAGE_FILE_MACHINE_POWERPC: return "POWERPC";
+ case IMAGE_FILE_MACHINE_POWERPCFP: return "POWERPCFP";
+ case IMAGE_FILE_MACHINE_R4000: return "R4000";
+ case IMAGE_FILE_MACHINE_SH3: return "SH3";
+ case IMAGE_FILE_MACHINE_SH3DSP: return "SH3DSP";
+ case IMAGE_FILE_MACHINE_SH4: return "SH4";
+ case IMAGE_FILE_MACHINE_SH5: return "SH5";
+ default: return "UnknownMachine";
+ }
+}
+
+
+/**
+ * Validates the file header.
+ *
+ * @returns iprt status code.
+ * @param pFileHdr Pointer to the file header that needs validating.
+ * @param fFlags Valid RTLDR_O_XXX combination.
+ * @param pszLogName The log name to prefix the errors with.
+ * @param penmArch Where to store the CPU architecture.
+ * @param pErrInfo Where to return additional error information.
+ */
+static int rtldrPEValidateFileHeader(PIMAGE_FILE_HEADER pFileHdr, uint32_t fFlags, const char *pszLogName,
+ PRTLDRARCH penmArch, PRTERRINFO pErrInfo)
+{
+ RT_NOREF_PV(pszLogName);
+
+ size_t cbOptionalHeader;
+ switch (pFileHdr->Machine)
+ {
+ case IMAGE_FILE_MACHINE_I386:
+ cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32);
+ *penmArch = RTLDRARCH_X86_32;
+ break;
+ case IMAGE_FILE_MACHINE_AMD64:
+ cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64);
+ *penmArch = RTLDRARCH_AMD64;
+ break;
+
+ default:
+ Log(("rtldrPEOpen: %s: Unsupported Machine=%#x\n", pszLogName, pFileHdr->Machine));
+ *penmArch = RTLDRARCH_INVALID;
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Unsupported Machine=%#x", pFileHdr->Machine);
+ }
+ if (pFileHdr->SizeOfOptionalHeader != cbOptionalHeader)
+ {
+ Log(("rtldrPEOpen: %s: SizeOfOptionalHeader=%#x expected %#x\n", pszLogName, pFileHdr->SizeOfOptionalHeader, cbOptionalHeader));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfOptionalHeader=%#x expected %#x",
+ pFileHdr->SizeOfOptionalHeader, cbOptionalHeader);
+ }
+ /* This restriction needs to be implemented elsewhere. */
+ if ( (pFileHdr->Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
+ && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
+ {
+ Log(("rtldrPEOpen: %s: IMAGE_FILE_RELOCS_STRIPPED\n", pszLogName));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "IMAGE_FILE_RELOCS_STRIPPED");
+ }
+ if (pFileHdr->NumberOfSections > 42)
+ {
+ Log(("rtldrPEOpen: %s: NumberOfSections=%d - our limit is 42, please raise it if the binary makes sense.(!!!)\n",
+ pszLogName, pFileHdr->NumberOfSections));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "NumberOfSections=%d, implementation max is 42", pFileHdr->NumberOfSections);
+ }
+ if (pFileHdr->NumberOfSections < 1)
+ {
+ Log(("rtldrPEOpen: %s: NumberOfSections=%d - we can't have an image without sections (!!!)\n",
+ pszLogName, pFileHdr->NumberOfSections));
+ return RTERRINFO_LOG_SET(pErrInfo, VERR_BAD_EXE_FORMAT, "Image has no sections");
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Validates the optional header (64/32-bit)
+ *
+ * @returns iprt status code.
+ * @param pOptHdr Pointer to the optional header which needs validation.
+ * @param pszLogName The log name to prefix the errors with.
+ * @param offNtHdrs The offset of the NT headers from the start of the file.
+ * @param pFileHdr Pointer to the file header (valid).
+ * @param cbRawImage The raw image size.
+ * @param fFlags Loader flags, RTLDR_O_XXX.
+ * @param pErrInfo Where to return additional error information.
+ */
+static int rtldrPEValidateOptionalHeader(const IMAGE_OPTIONAL_HEADER64 *pOptHdr, const char *pszLogName, RTFOFF offNtHdrs,
+ const IMAGE_FILE_HEADER *pFileHdr, uint64_t cbRawImage, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ RT_NOREF_PV(pszLogName);
+
+ const uint16_t CorrectMagic = pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
+ ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC;
+ if (pOptHdr->Magic != CorrectMagic)
+ {
+ Log(("rtldrPEOpen: %s: Magic=%#x - expected %#x!!!\n", pszLogName, pOptHdr->Magic, CorrectMagic));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Magic=%#x, expected %#x", pOptHdr->Magic, CorrectMagic);
+ }
+ const uint32_t cbImage = pOptHdr->SizeOfImage;
+ if (cbImage > _1G)
+ {
+ Log(("rtldrPEOpen: %s: SizeOfImage=%#x - Our limit is 1GB (%#x)!!!\n", pszLogName, cbImage, _1G));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfImage=%#x - Our limit is 1GB (%#x)", cbImage, _1G);
+ }
+ const uint32_t cbMinImageSize = pFileHdr->SizeOfOptionalHeader + sizeof(*pFileHdr) + 4 + (uint32_t)offNtHdrs;
+ if (cbImage < cbMinImageSize)
+ {
+ Log(("rtldrPEOpen: %s: SizeOfImage=%#x to small, minimum %#x!!!\n", pszLogName, cbImage, cbMinImageSize));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfImage=%#x to small, minimum %#x", cbImage, cbMinImageSize);
+ }
+ if (pOptHdr->AddressOfEntryPoint >= cbImage)
+ {
+ Log(("rtldrPEOpen: %s: AddressOfEntryPoint=%#x - beyond image size (%#x)!!!\n",
+ pszLogName, pOptHdr->AddressOfEntryPoint, cbImage));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "AddressOfEntryPoint=%#x - beyond image size (%#x)", pOptHdr->AddressOfEntryPoint, cbImage);
+ }
+ if (pOptHdr->BaseOfCode >= cbImage)
+ {
+ Log(("rtldrPEOpen: %s: BaseOfCode=%#x - beyond image size (%#x)!!!\n",
+ pszLogName, pOptHdr->BaseOfCode, cbImage));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "BaseOfCode=%#x - beyond image size (%#x)", pOptHdr->BaseOfCode, cbImage);
+ }
+#if 0/* only in 32-bit header */
+ if (pOptHdr->BaseOfData >= cbImage)
+ {
+ Log(("rtldrPEOpen: %s: BaseOfData=%#x - beyond image size (%#x)!!!\n",
+ pszLogName, pOptHdr->BaseOfData, cbImage));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "BaseOfData=%#x - beyond image size (%#x)", pOptHdr->BaseOfData, cbImage);
+ }
+#endif
+ if (!RT_IS_POWER_OF_TWO(pOptHdr->SectionAlignment))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "SectionAlignment=%#x - not a power of two", pOptHdr->SectionAlignment);
+ if (pOptHdr->SectionAlignment < 16 || pOptHdr->SectionAlignment > _128K)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "SectionAlignment=%#x - unsupported value, not between 16 and 128KB", pOptHdr->SectionAlignment);
+ if (pOptHdr->SizeOfHeaders >= cbImage)
+ {
+ Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - beyond image size (%#x)!!!\n",
+ pszLogName, pOptHdr->SizeOfHeaders, cbImage));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
+ "SizeOfHeaders=%#x - beyond image size (%#x)", pOptHdr->SizeOfHeaders, cbImage);
+ }
+ /* don't know how to do the checksum, so ignore it. */
+ if (pOptHdr->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
+ {
+ Log(("rtldrPEOpen: %s: Subsystem=%#x (unknown)!!!\n", pszLogName, pOptHdr->Subsystem));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Subsystem=%#x (unknown)", pOptHdr->Subsystem);
+ }
+ if (pOptHdr->SizeOfHeaders < cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER))
+ {
+ Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - cbMinImageSize %#x + sections %#x = %#llx!!!\n",
+ pszLogName, pOptHdr->SizeOfHeaders,
+ cbMinImageSize, pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER),
+ cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfHeaders=%#x - cbMinImageSize %#x + sections %#x = %#llx",
+ pOptHdr->SizeOfHeaders, cbMinImageSize,
+ pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER),
+ cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) );
+ }
+ if (pOptHdr->SizeOfStackReserve < pOptHdr->SizeOfStackCommit)
+ {
+ Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
+ pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfStackReserve %#x < SizeOfStackCommit %#x",
+ pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit);
+ }
+ if (pOptHdr->SizeOfHeapReserve < pOptHdr->SizeOfHeapCommit)
+ {
+ Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
+ pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfStackReserve %#x < SizeOfStackCommit %#x\n",
+ pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit);
+ }
+
+ /* DataDirectory */
+ if (pOptHdr->NumberOfRvaAndSizes != RT_ELEMENTS(pOptHdr->DataDirectory))
+ {
+ Log(("rtldrPEOpen: %s: NumberOfRvaAndSizes=%d!!!\n", pszLogName, pOptHdr->NumberOfRvaAndSizes));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "NumberOfRvaAndSizes=%d, expected %d",
+ pOptHdr->NumberOfRvaAndSizes, RT_ELEMENTS(pOptHdr->DataDirectory));
+ }
+ for (unsigned i = 0; i < RT_ELEMENTS(pOptHdr->DataDirectory); i++)
+ {
+ IMAGE_DATA_DIRECTORY const *pDir = &pOptHdr->DataDirectory[i];
+ if (!pDir->Size)
+ continue;
+ size_t cb = cbImage;
+ switch (i)
+ {
+ case IMAGE_DIRECTORY_ENTRY_EXPORT: // 0
+ case IMAGE_DIRECTORY_ENTRY_IMPORT: // 1
+ case IMAGE_DIRECTORY_ENTRY_RESOURCE: // 2
+ case IMAGE_DIRECTORY_ENTRY_EXCEPTION: // 3
+ case IMAGE_DIRECTORY_ENTRY_BASERELOC: // 5
+ case IMAGE_DIRECTORY_ENTRY_DEBUG: // 6
+ case IMAGE_DIRECTORY_ENTRY_COPYRIGHT: // 7
+ case IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: // 11
+ case IMAGE_DIRECTORY_ENTRY_IAT: // 12 /* Import Address Table */
+ break;
+ case IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: // 10 - need to check for lock prefixes.
+ /* Delay inspection after section table is validated. */
+ break;
+
+ case IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: // 13
+ if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
+ break;
+ Log(("rtldrPEOpen: %s: dir no. %d (DELAY_IMPORT) VirtualAddress=%#x Size=%#x is not supported!!!\n",
+ pszLogName, i, pDir->VirtualAddress, pDir->Size));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_DELAY_IMPORT,
+ "DELAY_IMPORT VirtualAddress=%#x Size=%#x: not supported", pDir->VirtualAddress, pDir->Size);
+
+ case IMAGE_DIRECTORY_ENTRY_SECURITY: // 4
+ /* The VirtualAddress is a PointerToRawData. */
+ cb = (size_t)cbRawImage; Assert((uint64_t)cb == cbRawImage);
+ Log(("rtldrPEOpen: %s: dir no. %d (SECURITY) VirtualAddress=%#x Size=%#x\n", pszLogName, i, pDir->VirtualAddress, pDir->Size));
+ if (pDir->Size < sizeof(WIN_CERTIFICATE))
+ {
+ Log(("rtldrPEOpen: %s: Security directory #%u is too small: %#x bytes\n", pszLogName, i, pDir->Size));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
+ "Security directory is too small: %#x bytes", pDir->Size);
+ }
+ if (pDir->Size >= RTLDRMODPE_MAX_SECURITY_DIR_SIZE)
+ {
+ Log(("rtldrPEOpen: %s: Security directory #%u is too large: %#x bytes\n", pszLogName, i, pDir->Size));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
+ "Security directory is too large: %#x bytes", pDir->Size);
+ }
+ if (pDir->VirtualAddress & 7)
+ {
+ Log(("rtldrPEOpen: %s: Security directory #%u is misaligned: %#x\n", pszLogName, i, pDir->VirtualAddress));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
+ "Security directory is misaligned: %#x", pDir->VirtualAddress);
+ }
+ /* When using the in-memory reader with a debugger, we may get
+ into trouble here since we might not have access to the whole
+ physical file. So skip the tests below. Makes VBoxGuest.sys
+ load and check out just fine, for instance. */
+ if (fFlags & RTLDR_O_FOR_DEBUG)
+ continue;
+ break;
+
+ case IMAGE_DIRECTORY_ENTRY_GLOBALPTR: // 8 /* (MIPS GP) */
+ Log(("rtldrPEOpen: %s: dir no. %d (GLOBALPTR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
+ pszLogName, i, pDir->VirtualAddress, pDir->Size));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_GLOBALPTR, "GLOBALPTR VirtualAddress=%#x Size=%#x: not supported",
+ pDir->VirtualAddress, pDir->Size);
+
+ case IMAGE_DIRECTORY_ENTRY_TLS: // 9
+ if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
+ break;
+ Log(("rtldrPEOpen: %s: dir no. %d (TLS) VirtualAddress=%#x Size=%#x is not supported!!!\n",
+ pszLogName, i, pDir->VirtualAddress, pDir->Size));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_TLS, "TLS VirtualAddress=%#x Size=%#x: not supported",
+ pDir->VirtualAddress, pDir->Size);
+
+ case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:// 14
+ if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
+ break;
+ Log(("rtldrPEOpen: %s: dir no. %d (COM_DESCRIPTOR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
+ pszLogName, i, pDir->VirtualAddress, pDir->Size));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_COM_DESCRIPTOR,
+ "COM_DESCRIPTOR VirtualAddress=%#x Size=%#x: not supported",
+ pDir->VirtualAddress, pDir->Size);
+
+ default:
+ Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x Size=%#x is not supported!!!\n",
+ pszLogName, i, pDir->VirtualAddress, pDir->Size));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d VirtualAddress=%#x Size=%#x is not supported",
+ i, pDir->VirtualAddress, pDir->Size);
+ }
+ if (pDir->VirtualAddress >= cb)
+ {
+ Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x is invalid (limit %#x)!!!\n",
+ pszLogName, i, pDir->VirtualAddress, cb));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d VirtualAddress=%#x is invalid (limit %#x)",
+ i, pDir->VirtualAddress, cb);
+ }
+ if (pDir->Size > cb - pDir->VirtualAddress)
+ {
+ Log(("rtldrPEOpen: %s: dir no. %d Size=%#x is invalid (rva=%#x, limit=%#x)!!!\n",
+ pszLogName, i, pDir->Size, pDir->VirtualAddress, cb));
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d Size=%#x is invalid (rva=%#x, limit=%#x)",
+ i, pDir->Size, pDir->VirtualAddress, cb);
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Validates and touch up the section headers.
+ *
+ * The touching up is restricted to setting the VirtualSize field for old-style
+ * linkers that sets it to zero.
+ *
+ * @returns iprt status code.
+ * @param paSections Pointer to the array of sections that is to be validated.
+ * @param cSections Number of sections in that array.
+ * @param pszLogName The log name to prefix the errors with.
+ * @param pOptHdr Pointer to the optional header (valid).
+ * @param cbRawImage The raw image size.
+ * @param fFlags Loader flags, RTLDR_O_XXX.
+ * @param fNoCode Verify that the image contains no code.
+ */
+static int rtldrPEValidateAndTouchUpSectionHeaders(IMAGE_SECTION_HEADER *paSections, unsigned cSections, const char *pszLogName,
+ const IMAGE_OPTIONAL_HEADER64 *pOptHdr, uint64_t cbRawImage, uint32_t fFlags,
+ bool fNoCode)
+{
+ RT_NOREF_PV(pszLogName);
+
+ /*
+ * Do a quick pass to detect linker setting VirtualSize to zero.
+ */
+ bool fFixupVirtualSize = true;
+ IMAGE_SECTION_HEADER *pSH = &paSections[0];
+ for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
+ if ( pSH->Misc.VirtualSize != 0
+ && !(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD))
+ {
+ fFixupVirtualSize = false;
+ break;
+ }
+
+ /*
+ * Actual pass.
+ */
+ const uint32_t cbImage = pOptHdr->SizeOfImage;
+ uint32_t uRvaPrev = pOptHdr->SizeOfHeaders;
+ pSH = &paSections[0];
+ Log3(("RTLdrPE: Section Headers:\n"));
+ for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
+ {
+ const unsigned iSH = (unsigned)(pSH - &paSections[0]); NOREF(iSH);
+ Log3(("RTLdrPE: #%d '%-8.8s' Characteristics: %08RX32\n"
+ "RTLdrPE: VirtAddr: %08RX32 VirtSize: %08RX32\n"
+ "RTLdrPE: FileOff: %08RX32 FileSize: %08RX32\n"
+ "RTLdrPE: RelocOff: %08RX32 #Relocs: %08RX32\n"
+ "RTLdrPE: LineOff: %08RX32 #Lines: %08RX32\n",
+ iSH, pSH->Name, pSH->Characteristics,
+ pSH->VirtualAddress, pSH->Misc.VirtualSize,
+ pSH->PointerToRawData, pSH->SizeOfRawData,
+ pSH->PointerToRelocations, pSH->NumberOfRelocations,
+ pSH->PointerToLinenumbers, pSH->NumberOfLinenumbers));
+
+ AssertCompile(IMAGE_SCN_MEM_16BIT == IMAGE_SCN_MEM_PURGEABLE);
+ if ( ( pSH->Characteristics & (IMAGE_SCN_MEM_PURGEABLE | IMAGE_SCN_MEM_PRELOAD | IMAGE_SCN_MEM_FARDATA) )
+ && !(fFlags & RTLDR_O_FOR_DEBUG)) /* purgable/16-bit seen on w2ksp0 hal.dll, ignore the bunch. */
+ {
+ Log(("rtldrPEOpen: %s: Unsupported section flag(s) %#x section #%d '%.*s'!!!\n",
+ pszLogName, pSH->Characteristics, iSH, sizeof(pSH->Name), pSH->Name));
+ return VERR_BAD_EXE_FORMAT;
+ }
+
+ if ( pSH->PointerToRawData > cbRawImage /// @todo pSH->PointerToRawData >= cbRawImage ?
+ || pSH->SizeOfRawData > cbRawImage
+ || pSH->PointerToRawData + pSH->SizeOfRawData > cbRawImage)
+ {
+ Log(("rtldrPEOpen: %s: PointerToRawData=%#x SizeOfRawData=%#x - beyond end of file (%#llx) - section #%d '%.*s'!!!\n",
+ pszLogName, pSH->PointerToRawData, pSH->SizeOfRawData, cbRawImage,
+ iSH, sizeof(pSH->Name), pSH->Name));
+ return VERR_BAD_EXE_FORMAT;
+ }
+
+ if (pSH->PointerToRawData & (pOptHdr->FileAlignment - 1)) //ASSUMES power of 2 alignment.
+ {
+ Log(("rtldrPEOpen: %s: PointerToRawData=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
+ pszLogName, pSH->PointerToRawData, pOptHdr->FileAlignment, iSH, sizeof(pSH->Name), pSH->Name));
+ return VERR_BAD_EXE_FORMAT;
+ }
+
+ if (!(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD)) /* binutils uses this for '.stab' even if it's reserved/obsoleted by MS. */
+ {
+ /* Calc VirtualSize if necessary. This is for internal reasons. */
+ if ( pSH->Misc.VirtualSize == 0
+ && fFixupVirtualSize)
+ {
+ pSH->Misc.VirtualSize = cbImage - RT_MIN(pSH->VirtualAddress, cbImage);
+ for (uint32_t i = 1; i < cSHdrsLeft; i++)
+ if ( !(pSH[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
+ && pSH[i].VirtualAddress >= pSH->VirtualAddress)
+ {
+ pSH->Misc.VirtualSize = RT_MIN(pSH[i].VirtualAddress - pSH->VirtualAddress, pSH->Misc.VirtualSize);
+ break;
+ }
+ }
+
+ if (pSH->Misc.VirtualSize > 0)
+ {
+ if (pSH->VirtualAddress < uRvaPrev)
+ {
+ Log(("rtldrPEOpen: %s: Overlaps previous section or sections aren't in ascending order, VirtualAddress=%#x uRvaPrev=%#x - section #%d '%.*s'!!!\n",
+ pszLogName, pSH->VirtualAddress, uRvaPrev, iSH, sizeof(pSH->Name), pSH->Name));
+ return VERR_BAD_EXE_FORMAT;
+ }
+ if (pSH->VirtualAddress > cbImage)
+ {
+ Log(("rtldrPEOpen: %s: VirtualAddress=%#x - beyond image size (%#x) - section #%d '%.*s'!!!\n",
+ pszLogName, pSH->VirtualAddress, cbImage, iSH, sizeof(pSH->Name), pSH->Name));
+ return VERR_BAD_EXE_FORMAT;
+ }
+
+ if (pSH->VirtualAddress & (pOptHdr->SectionAlignment - 1)) //ASSUMES power of 2 alignment.
+ {
+ Log(("rtldrPEOpen: %s: VirtualAddress=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
+ pszLogName, pSH->VirtualAddress, pOptHdr->SectionAlignment, iSH, sizeof(pSH->Name), pSH->Name));
+ return VERR_BAD_EXE_FORMAT;
+ }
+
+#ifdef PE_FILE_OFFSET_EQUALS_RVA
+ /* Our loader code assume rva matches the file offset. */
+ if ( pSH->SizeOfRawData
+ && pSH->PointerToRawData != pSH->VirtualAddress)
+ {
+ Log(("rtldrPEOpen: %s: ASSUMPTION FAILED: file offset %#x != RVA %#x - section #%d '%.*s'!!!\n",
+ pszLogName, pSH->PointerToRawData, pSH->VirtualAddress, iSH, sizeof(pSH->Name), pSH->Name));
+ return VERR_BAD_EXE_FORMAT;
+ }
+#endif
+
+ uRvaPrev = pSH->VirtualAddress + pSH->Misc.VirtualSize;
+ }
+ }
+
+ /* ignore the relocations and linenumbers. */
+ }
+
+ /*
+ * Do a separate run if we need to validate the no-code claim from the
+ * optional header.
+ */
+ if (fNoCode)
+ {
+ pSH = &paSections[0];
+ for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
+ if (pSH->Characteristics & (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE))
+ return VERR_LDR_ARCH_MISMATCH;
+ }
+
+
+ /** @todo r=bird: more sanity checks! */
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Reads image data by RVA using the section headers.
+ *
+ * @returns iprt status code.
+ * @param pModPe The PE module instance.
+ * @param pvBuf Where to store the bits.
+ * @param cb Number of bytes to tread.
+ * @param RVA Where to read from.
+ */
+static int rtldrPEReadRVA(PRTLDRMODPE pModPe, void *pvBuf, uint32_t cb, uint32_t RVA)
+{
+ const IMAGE_SECTION_HEADER *pSH = pModPe->paSections;
+ PRTLDRREADER pReader = pModPe->Core.pReader;
+ uint32_t cbRead;
+ int rc;
+
+ /*
+ * Is it the headers, i.e. prior to the first section.
+ */
+ if (RVA < pModPe->cbHeaders)
+ {
+ cbRead = RT_MIN(pModPe->cbHeaders - RVA, cb);
+ rc = pReader->pfnRead(pReader, pvBuf, cbRead, RVA);
+ if ( cbRead == cb
+ || RT_FAILURE(rc))
+ return rc;
+ cb -= cbRead;
+ RVA += cbRead;
+ pvBuf = (uint8_t *)pvBuf + cbRead;
+ }
+
+ /* In the zero space between headers and the first section? */
+ if (RVA < pSH->VirtualAddress)
+ {
+ cbRead = RT_MIN(pSH->VirtualAddress - RVA, cb);
+ memset(pvBuf, 0, cbRead);
+ if (cbRead == cb)
+ return VINF_SUCCESS;
+ cb -= cbRead;
+ RVA += cbRead;
+ pvBuf = (uint8_t *)pvBuf + cbRead;
+ }
+
+ /*
+ * Iterate the sections.
+ */
+ for (unsigned cLeft = pModPe->cSections;
+ cLeft > 0;
+ cLeft--, pSH++)
+ {
+ uint32_t off = RVA - pSH->VirtualAddress;
+ if (off < pSH->Misc.VirtualSize)
+ {
+ cbRead = RT_MIN(pSH->Misc.VirtualSize - off, cb);
+ rc = pReader->pfnRead(pReader, pvBuf, cbRead, pSH->PointerToRawData + off);
+ if ( cbRead == cb
+ || RT_FAILURE(rc))
+ return rc;
+ cb -= cbRead;
+ RVA += cbRead;
+ pvBuf = (uint8_t *)pvBuf + cbRead;
+ }
+ uint32_t RVANext = cLeft ? pSH[1].VirtualAddress : pModPe->cbImage;
+ if (RVA < RVANext)
+ {
+ cbRead = RT_MIN(RVANext - RVA, cb);
+ memset(pvBuf, 0, cbRead);
+ if (cbRead == cb)
+ return VINF_SUCCESS;
+ cb -= cbRead;
+ RVA += cbRead;
+ pvBuf = (uint8_t *)pvBuf + cbRead;
+ }
+ }
+
+ AssertFailed();
+ return VERR_INTERNAL_ERROR;
+}
+
+
+/**
+ * Validates the data of some selected data directories entries and remember
+ * important bits for later.
+ *
+ * This requires a valid section table and thus has to wait till after we've
+ * read and validated it.
+ *
+ * @returns iprt status code.
+ * @param pModPe The PE module instance.
+ * @param pOptHdr Pointer to the optional header (valid).
+ * @param fFlags Loader flags, RTLDR_O_XXX.
+ * @param pErrInfo Where to return extended error information. Optional.
+ */
+static int rtldrPEValidateDirectoriesAndRememberStuff(PRTLDRMODPE pModPe, const IMAGE_OPTIONAL_HEADER64 *pOptHdr, uint32_t fFlags,
+ PRTERRINFO pErrInfo)
+{
+ const char *pszLogName = pModPe->Core.pReader->pfnLogName(pModPe->Core.pReader); NOREF(pszLogName);
+ union /* combine stuff we're reading to help reduce stack usage. */
+ {
+ IMAGE_LOAD_CONFIG_DIRECTORY64 Cfg64;
+ uint8_t abZeros[sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64) * 4];
+ } u;
+
+ /*
+ * The load config entry may include lock prefix tables and whatnot which we don't implement.
+ * It does also include a lot of stuff which we can ignore, so we'll have to inspect the
+ * actual data before we can make up our mind about it all.
+ */
+ IMAGE_DATA_DIRECTORY Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG];
+ if (Dir.Size)
+ {
+ const size_t cbExpectV13 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V13)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V13);
+ const size_t cbExpectV12 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V12)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V12);
+ const size_t cbExpectV11 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V11)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V11);
+ const size_t cbExpectV10 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V10)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V10);
+ const size_t cbExpectV9 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V9)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V9);
+ const size_t cbExpectV8 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V8)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V8);
+ const size_t cbExpectV7 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V7)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V7);
+ const size_t cbExpectV6 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V6)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V6);
+ const size_t cbExpectV5 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V5)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V5);
+ const size_t cbExpectV4 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V4)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V4);
+ const size_t cbExpectV3 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V3)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V3);
+ const size_t cbExpectV2 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V2)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V2);
+ const size_t cbExpectV1 = !pModPe->f64Bit
+ ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V1)
+ : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V2) /*No V1*/;
+ const size_t cbNewHack = cbExpectV5; /* Playing safe here since there might've been revisions between V5 and V6 we don't know about . */
+ const size_t cbMaxKnown = cbExpectV12;
+
+ bool fNewerStructureHack = false;
+ if ( Dir.Size != cbExpectV13
+ && Dir.Size != cbExpectV12
+ && Dir.Size != cbExpectV11
+ && Dir.Size != cbExpectV10
+ && Dir.Size != cbExpectV9
+ && Dir.Size != cbExpectV8
+ && Dir.Size != cbExpectV7
+ && Dir.Size != cbExpectV6
+ && Dir.Size != cbExpectV5
+ && Dir.Size != cbExpectV4
+ && Dir.Size != cbExpectV3
+ && Dir.Size != cbExpectV2
+ && Dir.Size != cbExpectV1)
+ {
+ fNewerStructureHack = Dir.Size > cbNewHack /* These structure changes are slowly getting to us! More futher down. */
+ && Dir.Size <= sizeof(u);
+ Log(("rtldrPEOpen: %s: load cfg dir: unexpected dir size of %u bytes, expected %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, or %zu.%s\n",
+ pszLogName, Dir.Size, cbExpectV13, cbExpectV12, cbExpectV11, cbExpectV10, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1,
+ fNewerStructureHack ? " Will try ignore extra bytes if all zero." : ""));
+ if (!fNewerStructureHack)
+ return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
+ "Unexpected load config dir size of %u bytes; supported sized: %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, or %zu",
+ Dir.Size, cbExpectV13, cbExpectV12, cbExpectV11, cbExpectV10, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1);
+ }
+
+ /*
+ * Read, check new stuff and convert to 64-bit.
+ *
+ * If we accepted a newer structures when loading for debug or validation,
+ * otherwise we require the new bits to be all zero and hope that they are
+ * insignificant where image loading is concerned (that's mostly been the
+ * case even for non-zero bits, only hard exception is LockPrefixTable).
+ */
+ RT_ZERO(u.Cfg64);
+ int rc = rtldrPEReadRVA(pModPe, &u.Cfg64, Dir.Size, Dir.VirtualAddress);
+ if (RT_FAILURE(rc))
+ return rc;
+ if ( fNewerStructureHack
+ && Dir.Size > cbMaxKnown
+ && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
+ && !ASMMemIsZero(&u.abZeros[cbMaxKnown], Dir.Size - cbMaxKnown))
+ {
+ Log(("rtldrPEOpen: %s: load cfg dir: Unexpected bytes are non-zero (%u bytes of which %u expected to be zero): %.*Rhxs\n",
+ pszLogName, Dir.Size, Dir.Size - cbMaxKnown, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]));
+ return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
+ "Grown load config (%u to %u bytes) includes non-zero bytes: %.*Rhxs",
+ cbMaxKnown, Dir.Size, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]);
+ }
+ rtldrPEConvert32BitLoadConfigTo64Bit(&u.Cfg64);
+
+ if (u.Cfg64.Size != Dir.Size)
+ {
+ /* Kludge #1: ntdll.dll from XP seen with Dir.Size=0x40 and Cfg64.Size=0x00. */
+ if (Dir.Size == 0x40 && u.Cfg64.Size == 0x00 && !pModPe->f64Bit)
+ {
+ Log(("rtldrPEOpen: %s: load cfg dir: Header (%d) and directory (%d) size mismatch, applying the XP kludge.\n",
+ pszLogName, u.Cfg64.Size, Dir.Size));
+ u.Cfg64.Size = Dir.Size;
+ }
+ /* Kludge #2: This happens a lot. Structure changes, but the linker doesn't get
+ updated and stores some old size in the directory. Use the header size. */
+ else if ( u.Cfg64.Size == cbExpectV13
+ || u.Cfg64.Size == cbExpectV12
+ || u.Cfg64.Size == cbExpectV11
+ || u.Cfg64.Size == cbExpectV10
+ || u.Cfg64.Size == cbExpectV9
+ || u.Cfg64.Size == cbExpectV8
+ || u.Cfg64.Size == cbExpectV7
+ || u.Cfg64.Size == cbExpectV6
+ || u.Cfg64.Size == cbExpectV5
+ || u.Cfg64.Size == cbExpectV4
+ || u.Cfg64.Size == cbExpectV3
+ || u.Cfg64.Size == cbExpectV2
+ || u.Cfg64.Size == cbExpectV1
+ || (fNewerStructureHack = (u.Cfg64.Size > cbNewHack && u.Cfg64.Size <= sizeof(u))) )
+ {
+ Log(("rtldrPEOpen: %s: load cfg dir: Header (%d) and directory (%d) size mismatch, applying the old linker kludge.\n",
+ pszLogName, u.Cfg64.Size, Dir.Size));
+
+ uint32_t const uOrgDir = Dir.Size;
+ Dir.Size = u.Cfg64.Size;
+ RT_ZERO(u.Cfg64);
+ rc = rtldrPEReadRVA(pModPe, &u.Cfg64, Dir.Size, Dir.VirtualAddress);
+ if (RT_FAILURE(rc))
+ return rc;
+ if ( fNewerStructureHack
+ && Dir.Size > cbMaxKnown
+ && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
+ && !ASMMemIsZero(&u.abZeros[cbMaxKnown], Dir.Size - cbMaxKnown))
+ {
+ Log(("rtldrPEOpen: %s: load cfg dir: Unknown bytes are non-zero (%u bytes of which %u expected to be zero): %.*Rhxs\n",
+ pszLogName, Dir.Size, Dir.Size - cbMaxKnown, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]));
+ return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
+ "Grown load config (%u to %u bytes, dir %u) includes non-zero bytes: %.*Rhxs",
+ cbMaxKnown, Dir.Size, uOrgDir, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]);
+ }
+ rtldrPEConvert32BitLoadConfigTo64Bit(&u.Cfg64);
+ AssertReturn(u.Cfg64.Size == Dir.Size,
+ RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE, "Data changed while reading! (%d vs %d)\n",
+ u.Cfg64.Size, Dir.Size));
+ }
+ else
+ {
+ Log(("rtldrPEOpen: %s: load cfg hdr: unexpected hdr size of %u bytes (dir %u), expected %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, or %zu.\n",
+ pszLogName, u.Cfg64.Size, Dir.Size, cbExpectV12, cbExpectV11, cbExpectV10, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1));
+ return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
+ "Unexpected load config header size of %u bytes (dir %u); supported sized: %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, or %zu",
+ u.Cfg64.Size, Dir.Size, cbExpectV12, cbExpectV11, cbExpectV10, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1);
+ }
+ }
+ if (u.Cfg64.LockPrefixTable && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
+ {
+ Log(("rtldrPEOpen: %s: load cfg dir: lock prefix table at %RX64. We don't support lock prefix tables!\n",
+ pszLogName, u.Cfg64.LockPrefixTable));
+ return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOCK_PREFIX_TABLE,
+ "Lock prefix table not supported: %RX64", u.Cfg64.LockPrefixTable);
+ }
+#if 0/* this seems to be safe to ignore. */
+ if ( u.Cfg64.SEHandlerTable
+ || u.Cfg64.SEHandlerCount)
+ {
+ Log(("rtldrPEOpen: %s: load cfg dir: SEHandlerTable=%RX64 and SEHandlerCount=%RX64 are unsupported!\n",
+ pszLogName, u.Cfg64.SEHandlerTable, u.Cfg64.SEHandlerCount));
+ return VERR_BAD_EXE_FORMAT;
+ }
+#endif
+ if (u.Cfg64.EditList && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
+ {
+ Log(("rtldrPEOpen: %s: load cfg dir: EditList=%RX64 is unsupported!\n",
+ pszLogName, u.Cfg64.EditList));
+ return RTErrInfoSetF(pErrInfo, VERR_BAD_EXE_FORMAT, "Load config EditList=%RX64 is not supported", u.Cfg64.EditList);
+ }
+ /** @todo GuardCFC? Possibly related to:
+ * http://research.microsoft.com/pubs/69217/ccs05-cfi.pdf
+ * Not trusting something designed by bakas who don't know how to modify a
+ * structure without messing up its natural alignment. */
+ if ( ( u.Cfg64.GuardCFCCheckFunctionPointer
+ || u.Cfg64.GuardCFDispatchFunctionPointer
+ || u.Cfg64.GuardCFFunctionTable
+ || u.Cfg64.GuardCFFunctionCount
+ || u.Cfg64.GuardFlags
+ || u.Cfg64.GuardAddressTakenIatEntryTable
+ || u.Cfg64.GuardAddressTakenIatEntryCount
+ || u.Cfg64.GuardLongJumpTargetTable
+ || u.Cfg64.GuardLongJumpTargetCount)
+ && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)) )
+ {
+ Log(("rtldrPEOpen: %s: load cfg dir: Guard stuff: %RX64,%RX64,%RX64,%RX64,%RX32,%RX64,%RX64,%RX64,%RX64!\n",
+ pszLogName, u.Cfg64.GuardCFCCheckFunctionPointer, u.Cfg64.GuardCFDispatchFunctionPointer,
+ u.Cfg64.GuardCFFunctionTable, u.Cfg64.GuardCFFunctionCount, u.Cfg64.GuardFlags,
+ u.Cfg64.GuardAddressTakenIatEntryTable, u.Cfg64.GuardAddressTakenIatEntryCount,
+ u.Cfg64.GuardLongJumpTargetTable, u.Cfg64.GuardLongJumpTargetCount ));
+#if 0 /* ntdll 15002 uses this. */
+ return RTErrInfoSetF(pErrInfo, VERR_LDRPE_GUARD_CF_STUFF,
+ "Guard bits in load config: %RX64,%RX64,%RX64,%RX64,%RX32,%RX64,%RX64,%RX64,%RX64!",
+ u.Cfg64.GuardCFCCheckFunctionPointer, u.Cfg64.GuardCFDispatchFunctionPointer,
+ u.Cfg64.GuardCFFunctionTable, u.Cfg64.GuardCFFunctionCount, u.Cfg64.GuardFlags,
+ u.Cfg64.GuardAddressTakenIatEntryTable, u.Cfg64.GuardAddressTakenIatEntryCount,
+ u.Cfg64.GuardLongJumpTargetTable, u.Cfg64.GuardLongJumpTargetCount);
+#endif
+ }
+ }
+
+ /*
+ * If the image is signed and we're not doing this for debug purposes,
+ * take a look at the signature.
+ */
+ Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
+ if (Dir.Size)
+ {
+ PWIN_CERTIFICATE pFirst = (PWIN_CERTIFICATE)RTMemTmpAlloc(Dir.Size);
+ if (!pFirst)
+ return VERR_NO_TMP_MEMORY;
+ int rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pFirst, Dir.Size, Dir.VirtualAddress);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t off = 0;
+ do
+ {
+ PWIN_CERTIFICATE pCur = (PWIN_CERTIFICATE)((uint8_t *)pFirst + off);
+
+ /* validate the members. */
+ if ( pCur->dwLength < sizeof(WIN_CERTIFICATE)
+ || pCur->dwLength + off > Dir.Size)
+ {
+ Log(("rtldrPEOpen: %s: cert at %#x/%#x: dwLength=%#x\n", pszLogName, off, Dir.Size, pCur->dwLength));
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
+ "Cert at %#x LB %#x: Bad header length value: %#x", off, Dir.Size, pCur->dwLength);
+ break;
+ }
+ if ( pCur->wRevision != WIN_CERT_REVISION_2_0
+ && pCur->wRevision != WIN_CERT_REVISION_1_0)
+ {
+ Log(("rtldrPEOpen: %s: cert at %#x/%#x: wRevision=%#x\n", pszLogName, off, Dir.Size, pCur->wRevision));
+ if (pCur->wRevision >= WIN_CERT_REVISION_1_0)
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_UNSUPPORTED,
+ "Cert at %#x LB %#x: Unsupported revision: %#x", off, Dir.Size, pCur->wRevision);
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
+ "Cert at %#x LB %#x: Malformed revision: %#x", off, Dir.Size, pCur->wRevision);
+ break;
+ }
+ if ( pCur->wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA
+ && pCur->wCertificateType != WIN_CERT_TYPE_X509
+ /*&& pCur->wCertificateType != WIN_CERT_TYPE_RESERVED_1*/
+ /*&& pCur->wCertificateType != WIN_CERT_TYPE_TS_STACK_SIGNED*/
+ && pCur->wCertificateType != WIN_CERT_TYPE_EFI_PKCS115
+ && pCur->wCertificateType != WIN_CERT_TYPE_EFI_GUID
+ )
+ {
+ Log(("rtldrPEOpen: %s: cert at %#x/%#x: wCertificateType=%#x\n", pszLogName, off, Dir.Size, pCur->wCertificateType));
+ if (pCur->wCertificateType)
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_UNSUPPORTED,
+ "Cert at %#x LB %#x: Unsupported certificate type: %#x",
+ off, Dir.Size, pCur->wCertificateType);
+ else
+ rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
+ "Cert at %#x LB %#x: Malformed certificate type: %#x",
+ off, Dir.Size, pCur->wCertificateType);
+ break;
+ }
+
+ /* Remember the first signed data certificate. */
+ if ( pCur->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA
+ && pModPe->offPkcs7SignedData == 0)
+ {
+ pModPe->offPkcs7SignedData = Dir.VirtualAddress
+ + (uint32_t)((uintptr_t)&pCur->bCertificate[0] - (uintptr_t)pFirst);
+ pModPe->cbPkcs7SignedData = pCur->dwLength - RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate);
+ }
+
+ /* next */
+ off += RT_ALIGN(pCur->dwLength, WIN_CERTIFICATE_ALIGNMENT);
+ } while (off < Dir.Size);
+ }
+ RTMemTmpFree(pFirst);
+ if (RT_FAILURE(rc) && !(fFlags & RTLDR_O_FOR_DEBUG))
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Open a PE image.
+ *
+ * @returns iprt status code.
+ * @param pReader The loader reader instance which will provide the raw image bits.
+ * @param fFlags Loader flags, RTLDR_O_XXX.
+ * @param enmArch Architecture specifier.
+ * @param offNtHdrs The offset of the NT headers (where you find "PE\0\0").
+ * @param phLdrMod Where to store the handle.
+ * @param pErrInfo Where to return extended error information. Optional.
+ */
+DECLHIDDEN(int) rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offNtHdrs,
+ PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
+{
+ /*
+ * Read and validate the file header.
+ */
+ IMAGE_FILE_HEADER FileHdr;
+ int rc = pReader->pfnRead(pReader, &FileHdr, sizeof(FileHdr), offNtHdrs + 4);
+ if (RT_FAILURE(rc))
+ return rc;
+ RTLDRARCH enmArchImage;
+ const char *pszLogName = pReader->pfnLogName(pReader);
+ rc = rtldrPEValidateFileHeader(&FileHdr, fFlags, pszLogName, &enmArchImage, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Match the CPU architecture.
+ */
+ bool fArchNoCodeCheckPending = false;
+ if ( enmArch != enmArchImage
+ && ( enmArch != RTLDRARCH_WHATEVER
+ && !(fFlags & RTLDR_O_WHATEVER_ARCH)) )
+ {
+ if (!(fFlags & RTLDR_O_IGNORE_ARCH_IF_NO_CODE))
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Image is for '%s', only accepting images for '%s'.",
+ rtldrPEGetArchName(FileHdr.Machine), RTLdrArchName(enmArch));
+ fArchNoCodeCheckPending = true;
+ }
+
+ /*
+ * Read and validate the "optional" header. Convert 32->64 if necessary.
+ */
+ IMAGE_OPTIONAL_HEADER64 OptHdr;
+ rc = pReader->pfnRead(pReader, &OptHdr, FileHdr.SizeOfOptionalHeader, offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER));
+ if (RT_FAILURE(rc))
+ return rc;
+ if (FileHdr.SizeOfOptionalHeader != sizeof(OptHdr))
+ rtldrPEConvert32BitOptionalHeaderTo64Bit(&OptHdr);
+ rc = rtldrPEValidateOptionalHeader(&OptHdr, pszLogName, offNtHdrs, &FileHdr, pReader->pfnSize(pReader), fFlags, pErrInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (fArchNoCodeCheckPending && OptHdr.SizeOfCode != 0)
+ return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH,
+ "Image is for '%s' and contains code (%#x), only accepting images for '%s' with code.",
+ rtldrPEGetArchName(FileHdr.Machine), OptHdr.SizeOfCode, RTLdrArchName(enmArch));
+
+ /*
+ * Read and validate section headers.
+ */
+ const size_t cbSections = sizeof(IMAGE_SECTION_HEADER) * FileHdr.NumberOfSections;
+ PIMAGE_SECTION_HEADER paSections = (PIMAGE_SECTION_HEADER)RTMemAlloc(cbSections);
+ if (!paSections)
+ return VERR_NO_MEMORY;
+ rc = pReader->pfnRead(pReader, paSections, cbSections,
+ offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtldrPEValidateAndTouchUpSectionHeaders(paSections, FileHdr.NumberOfSections, pszLogName,
+ &OptHdr, pReader->pfnSize(pReader), fFlags, fArchNoCodeCheckPending);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate and initialize the PE module structure.
+ */
+ PRTLDRMODPE pModPe = (PRTLDRMODPE)RTMemAllocZ(sizeof(*pModPe));
+ if (pModPe)
+ {
+ pModPe->Core.u32Magic = RTLDRMOD_MAGIC;
+ pModPe->Core.eState = LDR_STATE_OPENED;
+ if (FileHdr.SizeOfOptionalHeader == sizeof(OptHdr))
+ pModPe->Core.pOps = &s_rtldrPE64Ops.Core;
+ else
+ pModPe->Core.pOps = &s_rtldrPE32Ops.Core;
+ pModPe->Core.pReader = pReader;
+ pModPe->Core.enmFormat= RTLDRFMT_PE;
+ pModPe->Core.enmType = FileHdr.Characteristics & IMAGE_FILE_DLL
+ ? FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
+ ? RTLDRTYPE_EXECUTABLE_FIXED
+ : RTLDRTYPE_EXECUTABLE_RELOCATABLE
+ : FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
+ ? RTLDRTYPE_SHARED_LIBRARY_FIXED
+ : RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE;
+ pModPe->Core.enmEndian= RTLDRENDIAN_LITTLE;
+ pModPe->Core.enmArch = FileHdr.Machine == IMAGE_FILE_MACHINE_I386
+ ? RTLDRARCH_X86_32
+ : FileHdr.Machine == IMAGE_FILE_MACHINE_AMD64
+ ? RTLDRARCH_AMD64
+ : RTLDRARCH_WHATEVER;
+ pModPe->pvBits = NULL;
+ pModPe->offNtHdrs = offNtHdrs;
+ pModPe->offEndOfHdrs = offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader + cbSections;
+ pModPe->u16Machine = FileHdr.Machine;
+ pModPe->fFile = FileHdr.Characteristics;
+ pModPe->cSections = FileHdr.NumberOfSections;
+ pModPe->paSections = paSections;
+ pModPe->uEntryPointRVA= OptHdr.AddressOfEntryPoint;
+ pModPe->uImageBase = (RTUINTPTR)OptHdr.ImageBase;
+ pModPe->cbImage = OptHdr.SizeOfImage;
+ pModPe->cbHeaders = OptHdr.SizeOfHeaders;
+ pModPe->uSectionAlign = OptHdr.SectionAlignment;
+ pModPe->uTimestamp = FileHdr.TimeDateStamp;
+ pModPe->cImports = UINT32_MAX;
+ pModPe->f64Bit = FileHdr.SizeOfOptionalHeader == sizeof(OptHdr);
+ pModPe->ImportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
+ pModPe->RelocDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
+ pModPe->ExportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
+ pModPe->DebugDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
+ pModPe->SecurityDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
+ pModPe->ExceptionDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
+ pModPe->fDllCharacteristics = OptHdr.DllCharacteristics;
+
+ /*
+ * Perform validation of some selected data directories which requires
+ * inspection of the actual data. This also saves some certificate
+ * information.
+ */
+ rc = rtldrPEValidateDirectoriesAndRememberStuff(pModPe, &OptHdr, fFlags, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ *phLdrMod = &pModPe->Core;
+ return VINF_SUCCESS;
+ }
+ RTMemFree(pModPe);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ RTMemFree(paSections);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/ldr/ldrVfsFile.cpp b/src/VBox/Runtime/common/ldr/ldrVfsFile.cpp
new file mode 100644
index 00000000..3f717583
--- /dev/null
+++ b/src/VBox/Runtime/common/ldr/ldrVfsFile.cpp
@@ -0,0 +1,293 @@
+/* $Id: ldrVfsFile.cpp $ */
+/** @file
+ * IPRT - Binary Image Loader, The File Oriented Parts, VFS variant.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloc.h>
+#include <iprt/file.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/vfs.h>
+#include <iprt/formats/mz.h>
+#include "internal/ldr.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * VFS file Reader instance.
+ * This provides raw image bits from a file.
+ */
+typedef struct RTLDRREADERVFSFILE
+{
+ /** The core. */
+ RTLDRREADER Core;
+ /** The VFS file. */
+ RTVFSFILE hVfsFile;
+ /** Number of users or the mapping. */
+ RTUINT cMappings;
+ /** Pointer to the in memory mapping. */
+ void *pvMapping;
+ /** The filename (variable size). */
+ char szFilename[1];
+} RTLDRREADERVFSFILE;
+typedef RTLDRREADERVFSFILE *PRTLDRREADERVFSFILE;
+
+
+/** @copydoc RTLDRREADER::pfnRead */
+static DECLCALLBACK(int) rtldrVfsFileRead(PRTLDRREADER pReader, void *pvBuf, size_t cb, RTFOFF off)
+{
+ PRTLDRREADERVFSFILE pFileReader = (PRTLDRREADERVFSFILE)pReader;
+ return RTVfsFileReadAt(pFileReader->hVfsFile, off, pvBuf, cb, NULL);
+}
+
+
+/** @copydoc RTLDRREADER::pfnTell */
+static DECLCALLBACK(RTFOFF) rtldrVfsFileTell(PRTLDRREADER pReader)
+{
+ PRTLDRREADERVFSFILE pFileReader = (PRTLDRREADERVFSFILE)pReader;
+ return RTVfsFileTell(pFileReader->hVfsFile);
+}
+
+
+/** @copydoc RTLDRREADER::pfnSize */
+static DECLCALLBACK(uint64_t) rtldrVfsFileSize(PRTLDRREADER pReader)
+{
+ PRTLDRREADERVFSFILE pFileReader = (PRTLDRREADERVFSFILE)pReader;
+ uint64_t cbFile;
+ int rc = RTVfsFileQuerySize(pFileReader->hVfsFile, &cbFile);
+ if (RT_SUCCESS(rc))
+ return cbFile;
+ return 0;
+}
+
+
+/** @copydoc RTLDRREADER::pfnLogName */
+static DECLCALLBACK(const char *) rtldrVfsFileLogName(PRTLDRREADER pReader)
+{
+ PRTLDRREADERVFSFILE pFileReader = (PRTLDRREADERVFSFILE)pReader;
+ return pFileReader->szFilename;
+}
+
+
+/** @copydoc RTLDRREADER::pfnMap */
+static DECLCALLBACK(int) rtldrVfsFileMap(PRTLDRREADER pReader, const void **ppvBits)
+{
+ PRTLDRREADERVFSFILE pFileReader = (PRTLDRREADERVFSFILE)pReader;
+
+ /*
+ * Already mapped?
+ */
+ if (pFileReader->pvMapping)
+ {
+ pFileReader->cMappings++;
+ *ppvBits = pFileReader->pvMapping;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Allocate memory.
+ */
+ uint64_t cbFile = rtldrVfsFileSize(pReader);
+ size_t cb = (size_t)cbFile;
+ if ((uint64_t)cb != cbFile)
+ return VERR_IMAGE_TOO_BIG;
+ pFileReader->pvMapping = RTMemAlloc(cb);
+ if (!pFileReader->pvMapping)
+ return VERR_NO_MEMORY;
+ int rc = rtldrVfsFileRead(pReader, pFileReader->pvMapping, cb, 0);
+ if (RT_SUCCESS(rc))
+ {
+ pFileReader->cMappings = 1;
+ *ppvBits = pFileReader->pvMapping;
+ }
+ else
+ {
+ RTMemFree(pFileReader->pvMapping);
+ pFileReader->pvMapping = NULL;
+ }
+
+ return rc;
+}
+
+
+/** @copydoc RTLDRREADER::pfnUnmap */
+static DECLCALLBACK(int) rtldrVfsFileUnmap(PRTLDRREADER pReader, const void *pvBits)
+{
+ PRTLDRREADERVFSFILE pFileReader = (PRTLDRREADERVFSFILE)pReader;
+ AssertReturn(pFileReader->cMappings > 0, VERR_INVALID_PARAMETER);
+
+ if (!--pFileReader->cMappings)
+ {
+ RTMemFree(pFileReader->pvMapping);
+ pFileReader->pvMapping = NULL;
+ }
+
+ NOREF(pvBits);
+ return VINF_SUCCESS;
+}
+
+
+/** @copydoc RTLDRREADER::pfnDestroy */
+static DECLCALLBACK(int) rtldrVfsFileDestroy(PRTLDRREADER pReader)
+{
+ PRTLDRREADERVFSFILE pFileReader = (PRTLDRREADERVFSFILE)pReader;
+ if (pFileReader->hVfsFile != NIL_RTVFSFILE)
+ {
+ RTVfsFileRelease(pFileReader->hVfsFile);
+ pFileReader->hVfsFile = NIL_RTVFSFILE;
+ }
+ RTMemFree(pFileReader);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Opens a loader file reader.
+ *
+ * @returns iprt status code.
+ * @param pszFilename The file to open, can be VFS chain.
+ * @param ppReader Where to store the reader instance on success.
+ * @param poffError Where to return the offset into @a pszFilename of an VFS
+ * chain element causing trouble. Optional.
+ * @param pErrInfo Where to return extended error information. Optional.
+ */
+static int rtldrVfsFileCreate(const char *pszFilename, PRTLDRREADER *ppReader, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+ size_t cbFilename = strlen(pszFilename) + 1;
+ int rc = VERR_NO_MEMORY;
+ PRTLDRREADERVFSFILE pFileReader = (PRTLDRREADERVFSFILE)RTMemAlloc(RT_UOFFSETOF_DYN(RTLDRREADERVFSFILE, szFilename[cbFilename]));
+ if (pFileReader)
+ {
+ memcpy(pFileReader->szFilename, pszFilename, cbFilename);
+ pFileReader->szFilename[0] = '\0';
+ rc = RTVfsChainOpenFile(pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE, &pFileReader->hVfsFile,
+ poffError, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ pFileReader->Core.uMagic = RTLDRREADER_MAGIC;
+ pFileReader->Core.pfnRead = rtldrVfsFileRead;
+ pFileReader->Core.pfnTell = rtldrVfsFileTell;
+ pFileReader->Core.pfnSize = rtldrVfsFileSize;
+ pFileReader->Core.pfnLogName = rtldrVfsFileLogName;
+ pFileReader->Core.pfnMap = rtldrVfsFileMap;
+ pFileReader->Core.pfnUnmap = rtldrVfsFileUnmap;
+ pFileReader->Core.pfnDestroy = rtldrVfsFileDestroy;
+ pFileReader->cMappings = 0;
+ pFileReader->pvMapping = NULL;
+ *ppReader = &pFileReader->Core;
+ return VINF_SUCCESS;
+ }
+ RTMemFree(pFileReader);
+ }
+ *ppReader = NULL;
+ return rc;
+}
+
+
+/**
+ * Open a binary image file allowing VFS chains in the filename.
+ *
+ * @returns iprt status code.
+ * @param pszFilename Image filename, VFS chain specifiers allowed.
+ * @param fFlags Valid RTLDR_O_XXX combination.
+ * @param enmArch CPU architecture specifier for the image to be loaded.
+ * @param phLdrMod Where to store the handle to the loader module.
+ * @param poffError Where to return the offset into @a pszFilename of an VFS
+ * chain element causing trouble. Optional.
+ * @param pErrInfo Where to return extended error information. Optional.
+ */
+RTDECL(int) RTLdrOpenVfsChain(const char *pszFilename, uint32_t fFlags, RTLDRARCH enmArch,
+ PRTLDRMOD phLdrMod, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+ LogFlow(("RTLdrOpenVfsChain: pszFilename=%p:{%s} fFlags=%#x enmArch=%d phLdrMod=%p\n",
+ pszFilename, pszFilename, fFlags, enmArch, phLdrMod));
+ AssertMsgReturn(!(fFlags & ~RTLDR_O_VALID_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(enmArch > RTLDRARCH_INVALID && enmArch < RTLDRARCH_END, ("%d\n", enmArch), VERR_INVALID_PARAMETER);
+
+ /*
+ * Create file reader & invoke worker which identifies and calls the image interpreter.
+ */
+ PRTLDRREADER pReader;
+ int rc = rtldrVfsFileCreate(pszFilename, &pReader, poffError, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ if (poffError)
+ *poffError = 0;
+ rc = RTLdrOpenWithReader(pReader, fFlags, enmArch, phLdrMod, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlow(("RTLdrOpenEx: return %Rrc *phLdrMod=%p\n", rc, *phLdrMod));
+ return rc;
+ }
+ pReader->pfnDestroy(pReader);
+ }
+ *phLdrMod = NIL_RTLDRMOD;
+ LogFlow(("RTLdrOpenEx: return %Rrc\n", rc));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTLdrOpenVfsChain);
+
+
+/**
+ * Open a binary image file using kLdr allowing VFS chains in the filename.
+ *
+ * @returns iprt status code.
+ * @param pszFilename Image filename.
+ * @param fFlags Reserved, MBZ.
+ * @param enmArch CPU architecture specifier for the image to be loaded.
+ * @param phLdrMod Where to store the handle to the loaded module.
+ * @param poffError Where to return the offset into @a pszFilename of an VFS
+ * chain element causing trouble. Optional.
+ * @param pErrInfo Where to return extended error information. Optional.
+ * @remark Primarily for testing the loader.
+ */
+RTDECL(int) RTLdrOpenVfsChainkLdr(const char *pszFilename, uint32_t fFlags, RTLDRARCH enmArch,
+ PRTLDRMOD phLdrMod, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+ return RTLdrOpenVfsChain(pszFilename, fFlags, enmArch, phLdrMod, poffError, pErrInfo);
+}
+RT_EXPORT_SYMBOL(RTLdrOpenVfsChainkLdr);
+