summaryrefslogtreecommitdiffstats
path: root/src/lib/kStuff/kDbg/kDbgModPE.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/kStuff/kDbg/kDbgModPE.cpp')
-rw-r--r--src/lib/kStuff/kDbg/kDbgModPE.cpp384
1 files changed, 384 insertions, 0 deletions
diff --git a/src/lib/kStuff/kDbg/kDbgModPE.cpp b/src/lib/kStuff/kDbg/kDbgModPE.cpp
new file mode 100644
index 0000000..85de91c
--- /dev/null
+++ b/src/lib/kStuff/kDbg/kDbgModPE.cpp
@@ -0,0 +1,384 @@
+/* $Id: kDbgModPE.cpp 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kDbg - The Debug Info Reader, PE Module (Generic).
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "kDbg.h"
+#include "kDbgInternal.h"
+#include <kLdrModPE.h>
+#include <string.h>
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * A dbghelp based PE debug reader.
+ */
+typedef struct KDBGMODPE
+{
+ /** The common module core. */
+ KDBGMOD Core;
+ /** The image size. */
+ uint32_t cbImage;
+ /** The number of sections. (We've added the implicit header section.) */
+ int32_t cSections;
+ /** The section headers (variable size). The first section is the
+ * implicit header section.*/
+ IMAGE_SECTION_HEADER aSections[1];
+} KDBGMODPE, *PKDBGMODPE;
+
+
+/**
+ * Calcs the RVA for a segment:offset address.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pModPe The PE debug module instance.
+ * @param iSegment The segment number. Special segments are dealt with as well.
+ * @param off The segment offset.
+ * @param puRVA Where to store the RVA on success.
+ */
+static int kDbgModPeSegOffToRVA(PKDBGMODPE pModPe, int32_t iSegment, KDBGADDR off, uint32_t *puRVA)
+{
+ if (iSegment >= 0)
+ {
+ kDbgAssertMsgReturn(iSegment < pModPe->cSections, ("iSegment=%x cSections=%x\n", iSegment, pModPe->cSections),
+ KDBG_ERR_INVALID_ADDRESS);
+ kDbgAssertMsgReturn(off < pModPe->aSections[iSegment].Misc.VirtualSize,
+ ("off=" PRI_KDBGADDR " VirtualSize=%x\n", off, pModPe->aSections[iSegment].Misc.VirtualSize),
+ KDBG_ERR_INVALID_ADDRESS);
+ *puRVA = pModPe->aSections[iSegment].VirtualAddress + (uint32_t)off;
+ return 0;
+ }
+
+ if (iSegment == KDBGSEG_RVA)
+ {
+ kDbgAssertMsgReturn(off < pModPe->cbImage, ("off=" PRI_KDBGADDR ", cbImage=%x\n", off, pModPe->cbImage),
+ KDBG_ERR_INVALID_ADDRESS);
+ *puRVA = (uint32_t)off;
+ return 0;
+ }
+ kDbgAssertMsgFailedReturn(("iSegment=%d\n", iSegment), KDBG_ERR_INVALID_ADDRESS);
+}
+
+
+/**
+ * Calcs the segment:offset address for a RVA.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pModPe The PE debug module instance.
+ * @param uRVA The RVA.
+ * @param piSegment Where to store the segment number.
+ * @param poff Where to store the segment offset.
+ */
+static int kDbgModPeRVAToSegOff(PKDBGMODPE pModPe, uint32_t uRVA, int32_t *piSegment, KDBGADDR *poff)
+{
+ kDbgAssertMsgReturn(uRVA < pModPe->cbImage, ("uRVA=%x, cbImage=%x\n", uRVA, pModPe->cbImage),
+ KDBG_ERR_INVALID_ADDRESS);
+ for (int32_t iSegment = 0; iSegment < pModPe->cSections; iSegment++)
+ {
+ /** @todo should probably be less strict about address in the alignment gaps. */
+ uint32_t off = uRVA - pModPe->aSections[iSegment].VirtualAddress;
+ if (off < pModPe->aSections[iSegment].Misc.VirtualSize)
+ {
+ *poff = off;
+ *piSegment = iSegment;
+ return 0;
+ }
+ }
+ kDbgAssertMsgFailedReturn(("uRVA=%x\n", uRVA), KDBG_ERR_INVALID_ADDRESS);
+}
+
+
+/**
+ * @copydoc KDBGMODOPS::pfnQueryLine
+ */
+static int kDbgModPeQueryLine(PKDBGMOD pMod, int32_t iSegment, KDBGADDR off, PKDBGLINE pLine)
+{
+ PKDBGMODPE pModPe = (PKDBGMODPE)pMod;
+
+ /*
+ * Translate the address to an RVA.
+ */
+ uint32_t uRVA;
+ int rc = kDbgModPeSegOffToRVA(pModPe, iSegment, off, &uRVA);
+ if (!rc)
+ {
+#if 0
+ DWORD64 off;
+ IMAGEHLP_LINE64 Line;
+ Line.SizeOfStruct = sizeof(Line);
+ if (g_pfnSymGetLineFromAddr64(pModPe->hSymInst, pModPe->ImageBase + uRVA, &off, &Line))
+ {
+ pLine->RVA = (KDBGADDR)(Line.Address - pModPe->ImageBase);
+ rc = kDbgModPeRVAToSegOff(pModPe, pLine->RVA, &pLine->iSegment, &pLine->offSegment);
+ pLine->iLine = Line.LineNumber;
+ pLine->cchFile = strlen(Line.FileName);
+ if (pLine->cchFile >= sizeof(pLine->szFile))
+ pLine->cchFile = sizeof(pLine->szFile) - 1;
+ memcpy(pLine->szFile, Line.FileName, pLine->cchFile + 1);
+ }
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = kDbgModPeConvWinError(Err);
+ }
+#endif
+ rc = KERR_NOT_IMPLEMENTED;
+ }
+ return rc;
+}
+
+
+/**
+ * @copydoc KDBGMODOPS::pfnQuerySymbol
+ */
+static int kDbgModPeQuerySymbol(PKDBGMOD pMod, int32_t iSegment, KDBGADDR off, PKDBGSYMBOL pSym)
+{
+ PKDBGMODPE pModPe = (PKDBGMODPE)pMod;
+
+ /*
+ * Translate the address to an RVA.
+ */
+ uint32_t uRVA;
+ int rc = kDbgModPeSegOffToRVA(pModPe, iSegment, off, &uRVA);
+ if (!rc)
+ {
+#if 0
+ DWORD64 off;
+ union
+ {
+ SYMBOL_INFO Sym;
+ char achBuffer[sizeof(SYMBOL_INFO) + KDBG_SYMBOL_MAX];
+ } Buf;
+ Buf.Sym.SizeOfStruct = sizeof(SYMBOL_INFO);
+ Buf.Sym.MaxNameLen = KDBG_SYMBOL_MAX;
+ if (g_pfnSymFromAddr(pModPe->hSymInst, pModPe->ImageBase + uRVA, &off, &Buf.Sym))
+ {
+ pSym->cb = Buf.Sym.Size;
+ pSym->fFlags = 0;
+ if (Buf.Sym.Flags & SYMFLAG_FUNCTION)
+ pSym->fFlags |= KDBGSYM_FLAGS_CODE;
+ else if (Buf.Sym.Flags & SYMFLAG_CONSTANT)
+ pSym->fFlags |= KDBGSYM_FLAGS_ABS; /** @todo SYMFLAG_CONSTANT must be tested - documentation is too brief to say what is really meant here.*/
+ else
+ pSym->fFlags |= KDBGSYM_FLAGS_DATA;
+ if (Buf.Sym.Flags & SYMFLAG_EXPORT)
+ pSym->fFlags |= KDBGSYM_FLAGS_EXPORTED;
+ if ((Buf.Sym.Flags & (SYMFLAG_VALUEPRESENT | SYMFLAG_CONSTANT)) == (SYMFLAG_VALUEPRESENT | SYMFLAG_CONSTANT))
+ {
+ pSym->iSegment = KDBGSEG_ABS;
+ pSym->offSegment = (KDBGADDR)Buf.Sym.Value;
+ pSym->RVA = (KDBGADDR)Buf.Sym.Value;
+ }
+ else
+ {
+ pSym->RVA = (KDBGADDR)(Buf.Sym.Address - pModPe->ImageBase);
+ rc = kDbgModPeRVAToSegOff(pModPe, pSym->RVA, &pSym->iSegment, &pSym->offSegment);
+ }
+ pSym->cchName = (uint16_t)Buf.Sym.NameLen;
+ if (pSym->cchName >= sizeof(pSym->szName))
+ pSym->cchName = sizeof(pSym->szName) - 1;
+ memcpy(pSym->szName, Buf.Sym.Name, Buf.Sym.NameLen);
+ pSym->szName[Buf.Sym.NameLen] = '\0';
+ }
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = kDbgModPeConvWinError(Err);
+ }
+#endif
+ rc = KERR_NOT_IMPLEMENTED;
+ }
+ return rc;
+}
+
+
+/**
+ * @copydoc KDBGMODOPS::pfnClose
+ */
+static int kDbgModPeClose(PKDBGMOD pMod)
+{
+ PKDBGMODPE pModPe = (PKDBGMODPE)pMod;
+
+ //if (g_pfnSymCleanup(pModPe->hSymInst))
+ // return 0;
+ //
+ //DWORD Err = GetLastError();
+ //int rc = kDbgModPeConvWinError(Err);
+ //kDbgAssertMsgFailed(("SymInitialize failed: Err=%d rc=%Rrc\n", Err, rc));
+ //return rc;
+ return KERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * Opens the debug info for a PE image using the windows dbghelp library.
+ *
+ * @returns IPRT status code.
+ *
+ * @param pFile The handle to the module.
+ * @param offHdr The offset of the PE header.
+ * @param pszModulePath The path to the module.
+ * @param ppDbgMod Where to store the module handle.
+ *
+ */
+int kdbgModPEOpen(PKDBGHLPFILE pFile, int64_t offHdr, const char *pszModulePath, PKDBGMOD *ppDbgMod)
+{
+ /*
+ * We need to read the section headers and get the image size.
+ */
+ IMAGE_FILE_HEADER FHdr;
+ int rc = kDbgHlpReadAt(pFile, offHdr + KDBG_OFFSETOF(IMAGE_NT_HEADERS32, FileHeader), &FHdr, sizeof(FHdr));
+ kDbgAssertRCReturn(rc, rc);
+
+ uint32_t cbImage;
+ if (FHdr.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32))
+ rc = kDbgHlpReadAt(pFile, offHdr + KDBG_OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader.SizeOfImage),
+ &cbImage, sizeof(cbImage));
+ else if (FHdr.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64))
+ rc = kDbgHlpReadAt(pFile, offHdr + KDBG_OFFSETOF(IMAGE_NT_HEADERS64, OptionalHeader.SizeOfImage),
+ &cbImage, sizeof(cbImage));
+ else
+ kDbgAssertFailedReturn(KDBG_ERR_BAD_EXE_FORMAT);
+ kDbgAssertRCReturn(rc, rc);
+
+ /*
+ * Allocate the module and read/construct the section headers.
+ */
+ PKDBGMODPE pModPe = (PKDBGMODPE)kDbgHlpAlloc(KDBG_OFFSETOF(KDBGMODPE, aSections[FHdr.NumberOfSections + 2]));
+ kDbgAssertReturn(pModPe, KERR_NO_MEMORY);
+ pModPe->Core.u32Magic = KDBGMOD_MAGIC;
+ pModPe->Core.pOps = &g_kDbgModPeOps;
+ pModPe->Core.pFile = pFile;
+ pModPe->cbImage = cbImage;
+ pModPe->cSections = 1 + FHdr.NumberOfSections;
+ rc = kDbgHlpReadAt(pFile, offHdr + KDBG_OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader) + FHdr.SizeOfOptionalHeader,
+ &pModPe->aSections[1], sizeof(pModPe->aSections[0]) * FHdr.NumberOfSections);
+ if (!rc)
+ {
+ PIMAGE_SECTION_HEADER pSH = &pModPe->aSections[0];
+ memcpy(pSH->Name, "headers", sizeof(pSH->Name));
+ pSH->Misc.VirtualSize = pModPe->aSections[1].VirtualAddress;
+ pSH->VirtualAddress = 0;
+ pSH->SizeOfRawData = pSH->Misc.VirtualSize;
+ pSH->PointerToRawData = 0;
+ pSH->PointerToRelocations = 0;
+ pSH->PointerToLinenumbers = 0;
+ pSH->NumberOfRelocations = 0;
+ pSH->NumberOfLinenumbers = 0;
+ pSH->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ;
+
+ uint32_t uTheEnd = pModPe->aSections[FHdr.NumberOfSections].VirtualAddress
+ + pModPe->aSections[FHdr.NumberOfSections].Misc.VirtualSize;
+ if (uTheEnd < cbImage)
+ {
+ pSH = &pModPe->aSections[pModPe->cSections++];
+ memcpy(pSH->Name, "tail\0\0\0", sizeof(pSH->Name));
+ pSH->Misc.VirtualSize = cbImage - uTheEnd;
+ pSH->VirtualAddress = uTheEnd;
+ pSH->SizeOfRawData = pSH->Misc.VirtualSize;
+ pSH->PointerToRawData = 0;
+ pSH->PointerToRelocations = 0;
+ pSH->PointerToLinenumbers = 0;
+ pSH->NumberOfRelocations = 0;
+ pSH->NumberOfLinenumbers = 0;
+ pSH->Characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ;
+ }
+
+#if 0
+ /*
+ * Find a new dbghelp handle.
+ *
+ * We assume 4GB of handles outlast most debugging sessions, or in anyways that
+ * when we start reusing handles they are no longer in use. :-)
+ */
+ static volatile uint32_t s_u32LastHandle = 1;
+ HANDLE hSymInst = (HANDLE)ASMAtomicIncU32(&s_u32LastHandle);
+ while ( hSymInst == INVALID_HANDLE_VALUE
+ || hSymInst == (HANDLE)0
+ || hSymInst == GetCurrentProcess())
+ hSymInst = (HANDLE)ASMAtomicIncU32(&s_u32LastHandle);
+
+ /*
+ * Initialize dbghelp and try open the specified module.
+ */
+ if (g_pfnSymInitialize(hSymInst, NULL, FALSE))
+ {
+ g_pfnSymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_AUTO_PUBLICS | SYMOPT_ALLOW_ABSOLUTE_SYMBOLS);
+
+ kDbgHlpSeek(pFile, 0); /* don't know if this is required or not... */
+ DWORD64 ImageBase = g_pfnSymLoadModule64(hSymInst, (HANDLE)File, pszModulePath, NULL, 0x00400000, 0);
+ if (ImageBase)
+ {
+ pModPe->hSymInst = hSymInst;
+ pModPe->ImageBase = ImageBase;
+ *ppDbgMod = &pModPe->Core;
+ return rc;
+ }
+
+ DWORD Err = GetLastError();
+ rc = kDbgModPeConvWinError(Err);
+ kDbgAssertMsgFailed(("SymLoadModule64 failed: Err=%d rc=%Rrc\n", Err, rc));
+ g_pfnSymCleanup(hSymInst);
+ }
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = kDbgModPeConvWinError(Err);
+ kDbgAssertMsgFailed(("SymInitialize failed: Err=%d rc=%Rrc\n", Err, rc));
+ }
+#endif
+ rc = KERR_NOT_IMPLEMENTED;
+ }
+ else
+ kDbgAssertRC(rc);
+
+ kDbgHlpFree(pModPe);
+ return rc;
+}
+
+
+/**
+ * Methods for a PE module.
+ */
+const KDBGMODOPS g_kDbgModPeOps =
+{
+ "PE",
+ kDbgModPeClose,
+ kDbgModPeQuerySymbol,
+ kDbgModPeQueryLine
+};
+
+
+