From 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:21:29 +0200 Subject: Adding upstream version 1:0.1.9998svn3589+dfsg. Signed-off-by: Daniel Baumann --- src/lib/kStuff/kDbg/Makefile.kmk | 73 +++ src/lib/kStuff/kDbg/kDbgDump.cpp | 174 +++++++ src/lib/kStuff/kDbg/kDbgHlp.h | 306 +++++++++++++ src/lib/kStuff/kDbg/kDbgHlpCrt.cpp | 239 ++++++++++ src/lib/kStuff/kDbg/kDbgInternal.h | 137 ++++++ src/lib/kStuff/kDbg/kDbgLine.cpp | 78 ++++ src/lib/kStuff/kDbg/kDbgModLdr.cpp | 109 +++++ src/lib/kStuff/kDbg/kDbgModPE.cpp | 384 ++++++++++++++++ src/lib/kStuff/kDbg/kDbgModWinDbgHelp.cpp | 724 ++++++++++++++++++++++++++++++ src/lib/kStuff/kDbg/kDbgModule.cpp | 440 ++++++++++++++++++ src/lib/kStuff/kDbg/kDbgSpace.cpp | 192 ++++++++ src/lib/kStuff/kDbg/kDbgSymbol.cpp | 78 ++++ 12 files changed, 2934 insertions(+) create mode 100644 src/lib/kStuff/kDbg/Makefile.kmk create mode 100644 src/lib/kStuff/kDbg/kDbgDump.cpp create mode 100644 src/lib/kStuff/kDbg/kDbgHlp.h create mode 100644 src/lib/kStuff/kDbg/kDbgHlpCrt.cpp create mode 100644 src/lib/kStuff/kDbg/kDbgInternal.h create mode 100644 src/lib/kStuff/kDbg/kDbgLine.cpp create mode 100644 src/lib/kStuff/kDbg/kDbgModLdr.cpp create mode 100644 src/lib/kStuff/kDbg/kDbgModPE.cpp create mode 100644 src/lib/kStuff/kDbg/kDbgModWinDbgHelp.cpp create mode 100644 src/lib/kStuff/kDbg/kDbgModule.cpp create mode 100644 src/lib/kStuff/kDbg/kDbgSpace.cpp create mode 100644 src/lib/kStuff/kDbg/kDbgSymbol.cpp (limited to 'src/lib/kStuff/kDbg') diff --git a/src/lib/kStuff/kDbg/Makefile.kmk b/src/lib/kStuff/kDbg/Makefile.kmk new file mode 100644 index 0000000..af8bed3 --- /dev/null +++ b/src/lib/kStuff/kDbg/Makefile.kmk @@ -0,0 +1,73 @@ +# $Id: Makefile.kmk 29 2009-07-01 20:30:29Z bird $ +## @file +# kDbg - The Debug Info Reader, sub-makefile. +# + +# +# Copyright (c) 2006-2007 Knut St. Osmundsen +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +DEPTH ?= .. +SUB_DEPTH = .. +include $(PATH_KBUILD)/subheader.kmk + +# +# kDbg - The profiler module. +# +#DLLS += kDbg - disabled for now. +kDbg_TEMPLATE = kStuffDLL +kDbg_DEFS = KDBG_BUILDING KDBG_RESIDES_IN_DLL +kDbg_SOURCES := \ + kDbgModule.cpp \ + kDbgModLdr.cpp \ + kDbgLine.cpp \ + kDbgSymbol.cpp + +kDbg_SOURCES.win += \ + kDbgModWinDbgHelp.cpp + +# +# kDbgStatic - The profiler module. +# +LIBRARIES += kDbgStatic +kDbgStatic_TEMPLATE = kStuffLIB +kDbgStatic_DEFS = KDBG_BUILDING +kDbgStatic_SOURCES = $(kDbg_SOURCES) +kDbgStatic_SOURCES.win = $(kDbg_SOURCES.win) + +# +# kDbgDump - Test program which dumps whatever is thrown at it. +# +PROGRAMS += kDbgDump +kDbgDump_TEMPLATE = kStuffEXE +kDbgDump_SOURCES = kDbgDump.cpp +kDbgDump_LIBS = \ + $(TARGET_kDbgStatic) \ + $(subst kDbg,kLdr,$(TARGET_kDbgStatic)) \ + $(subst kDbg,kRdr,$(TARGET_kDbgStatic)) \ + $(subst kDbg,kHlpCRT,$(TARGET_kDbgStatic)) + +# Generate the rules +include $(PATH_KBUILD)/subfooter.kmk + diff --git a/src/lib/kStuff/kDbg/kDbgDump.cpp b/src/lib/kStuff/kDbg/kDbgDump.cpp new file mode 100644 index 0000000..83cb36b --- /dev/null +++ b/src/lib/kStuff/kDbg/kDbgDump.cpp @@ -0,0 +1,174 @@ +/* $Id: kDbgDump.cpp 29 2009-07-01 20:30:29Z bird $ */ +/** @file + * kDbgDump - Debug Info Dumper. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include +#include +#include + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** @name Options + * @{ */ +static int g_fGlobalSyms = 1; +static int g_fPrivateSyms = 1; +static int g_fLineNumbers = 0; +/** @} */ + + +/** + * Dumps one file. + * + * @returns main exit status. + * @param pszFile The file to dump (path to it). + */ +static int DumpFile(const char *pszFile) +{ + PKDBGMOD pDbgMod; + int rc = kDbgModuleOpen(&pDbgMod, pszFile, NULL); + if (rc) + { + printf("kDbgDump: error: kDbgModuleOpen('%s',) failed with rc=%d.\n", pszFile, rc); + return 1; + } + + + + return 0; +} + + +/** + * Prints the version number + * @return 0 + */ +static int ShowVersion() +{ + printf("kDbgDump v0.0.1\n"); + return 0; +} + + +/** + * Prints the program syntax. + * + * @returns 1 + * @param argv0 The program name. + */ +static int ShowSyntax(const char *argv0) +{ + ShowVersion(); + printf("syntax: %s [options] \n" + "\n", + argv0); + return 1; +} + +int main(int argc, char **argv) +{ + int rcRet = 0; + + /* + * Parse arguments. + */ + int fArgsDone = 0; + for (int i = 1; i < argc; i++) + { + const char *psz = argv[i]; + + if (!fArgsDone && psz[0] == '-' && psz[1]) + { + /* convert long option to short. */ + if (*++psz == '-') + { + psz++; + if (!*psz) /* -- */ + { + fArgsDone = 1; + continue; + } + if (!strcmp(psz, "line-numbers")) + psz = "l"; + else if (!strcmp(psz, "no-line-numbers")) + psz = "L"; + else if (!strcmp(psz, "global-syms") || !strcmp(psz, "public-syms")) + psz = "g"; + else if (!strcmp(psz, "no-global-syms") || !strcmp(psz, "no-public-syms")) + psz = "G"; + else if (!strcmp(psz, "privat-syms") || !strcmp(psz, "local-syms")) + psz = "p"; + else if (!strcmp(psz, "no-privat-syms") || !strcmp(psz, "no-local-syms")) + psz = "P"; + else if (!strcmp(psz, "version")) + psz = "v"; + else if (!strcmp(psz, "help")) + psz = "h"; + else + { + fprintf(stderr, "%s: syntax error: unknown option '--%s'\n", argv[0], psz); + return 1; + } + } + + /* eat short options. */ + while (*psz) + switch (*psz++) + { + case 'l': g_fLineNumbers = 1; break; + case 'L': g_fLineNumbers = 0; break; + case 'p': g_fPrivateSyms = 1; break; + case 'P': g_fPrivateSyms = 0; break; + case 'g': g_fGlobalSyms = 1; break; + case 'G': g_fGlobalSyms = 0; break; + case '?': + case 'H': + case 'h': return ShowSyntax(argv[0]); + case 'v': return ShowVersion(); + default: + fprintf(stderr, "%s: syntax error: unknown option '-%c'.\n", argv[0], psz[-1]); + return 1; + } + } + else + { + /* Dump does it's own bitching if something goes wrong. */ + int rc = DumpFile(psz); + if (rc && !rcRet) + rc = rcRet; + } + } + + return rcRet; +} + diff --git a/src/lib/kStuff/kDbg/kDbgHlp.h b/src/lib/kStuff/kDbg/kDbgHlp.h new file mode 100644 index 0000000..cd5116d --- /dev/null +++ b/src/lib/kStuff/kDbg/kDbgHlp.h @@ -0,0 +1,306 @@ +/* $Id: kDbgHlp.h 78 2016-07-13 15:52:04Z bird $ */ +/** @file + * kDbg - The Debug Info Reader, Internal Header. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef ___kDbgHlp_h___ +#define ___kDbgHlp_h___ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @defgroup grp_kDbgHlpHeap kDbg Internal Heap APIs. + * @internal + * @{ + */ + +/** + * Allocates memory. + * + * @returns Pointer to the allocated memory. + * NULL on failure. + * @param cb The number of bytes to allocate. + */ +void *kDbgHlpAlloc(size_t cb); + +/** + * Allocates memory like kDbgHlpAlloc, except that it's zeroed. + * + * @returns Pointer to the allocated memory. + * NULL on failure. + * @param cb The number of bytes to allocate. + */ +void *kDbgHlpAllocZ(size_t cb); + +/** + * Combination of kDbgHlpAlloc and memcpy. + * + * @returns Pointer to the duplicate. + * NULL on failure. + * + * @param pv The memory to be duplicate. + * @param cb The size of the block. + */ +void *kDbgHlpAllocDup(const void *pv, size_t cb); + +/** + * Reallocates a memory block returned by kDbgHlpAlloc, kDbgHlpAllocZ + * kDbgHlpAllocDup or this function. + * + * The content of new memory added to the memory block is undefined. + * + * @returns Pointer to the allocated memory. + * NULL on failure, the old block remains intact. + * @param pv The memory block to reallocate. + * If NULL this function will work like kDbgHlpAlloc. + * @param cb The number of bytes to allocate. + * If 0 this function will work like kDbgHlpFree. + */ +void *kDbgHlpRealloc(void *pv, size_t cb); + +/** + * Frees memory allocated by kDbgHlpAlloc, kDbgHlpAllocZ + * kDbgHlpAllocDup, or kDbgHlpRealloc. + * + * @param pv + */ +void kDbgHlpFree(void *pv); + +/** @} */ + + +/** @defgroup grp_kDbgHlpFile kDbg Internal File Access APIs. + * @internal + * @{ + */ +/** + * Opens the specified file as read-only, buffered if possible. + * + * @returns 0 on success, or the appropriate KDBG_ERR_* on failure. + * + * @param pszFilename The file to open. + * @param ppFile Where to store the handle to the open file. + */ +int kDbgHlpOpenRO(const char *pszFilename, PKDBGHLPFILE *ppFile); + + +/** + * Closes a file opened by kDbgHlpOpenRO. + * + * @param pFile The file handle. + */ +void kDbgHlpClose(PKDBGHLPFILE pFile); + +/** + * Gets the native file handle. + * + * @return The native file handle. + * -1 on failure. + * @param pFile The file handle. + */ +uintptr_t kDbgHlpNativeFileHandle(PKDBGHLPFILE pFile); + +/** + * Gets the size of an open file. + * + * @returns The file size in bytes on success. + * On failure -1 is returned. + * @param pFile The file handle. + */ +int64_t kDbgHlpFileSize(PKDBGHLPFILE pFile); + +/** + * Reads a number of bytes at a specified file location. + * + * This will change the current file position to off + cb on success, + * while on failure the position will be undefined. + * + * @returns The file size in bytes on success. + * On failure -1 is returned. + * @param pFile The file handle. + * @param off Where to read. + * @param pv Where to store the data. + * @param cb How much to read. + */ +int kDbgHlpReadAt(PKDBGHLPFILE pFile, int64_t off, void *pv, size_t cb); + +/** + * Reads a number of bytes at the current file position. + * + * This will advance the current file position by cb bytes on success + * while on failure the position will be undefined. + * + * @returns The file size in bytes on success. + * On failure -1 is returned. + * @param pFile The file handle. + * @param pv Where to store the data. + * @param cb How much to read. + * @param off Where to read. + */ +int kDbgHlpRead(PKDBGHLPFILE pFile, void *pv, size_t cb); + +/** + * Sets the current file position. + * + * @returns 0 on success, and KDBG_ERR_* on failure. + * @param pFile The file handle. + * @param off The desired file position. + */ +int kDbgHlpSeek(PKDBGHLPFILE pFile, int64_t off); + +/** + * Move the file position relative to the current one. + * + * @returns 0 on success, and KDBG_ERR_* on failure. + * @param pFile The file handle. + * @param off How much to move the file position by. + */ +int kDbgHlpSeekByCur(PKDBGHLPFILE pFile, int64_t off); + +/** + * Move the file position relative to the end of the file. + * + * @returns 0 on success, and KDBG_ERR_* on failure. + * @param pFile The file handle. + * @param off The offset relative to the end, positive number. + */ +int kDbgHlpSeekByEnd(PKDBGHLPFILE pFile, int64_t off); + +/** + * Gets the current file position. + * + * @returns The current file position on success. + * -1 on failure. + * @param pFile The file handle. + */ +int64_t kDbgHlpTell(PKDBGHLPFILE pFile); + +/** @} */ + +/** @defgroup grp_kDbgHlpAssert kDbg Internal Assertion Macros. + * @internal + * @{ + */ + +#ifdef _MSC_VER +# define kDbgAssertBreakpoint() do { __debugbreak(); } while (0) +#else +# define kDbgAssertBreakpoint() do { __asm__ __volatile__ ("int3"); } while (0) +#endif + +/** + * Helper function that displays the first part of the assertion message. + * + * @param pszExpr The expression. + * @param pszFile The file name. + * @param iLine The line number is the file. + * @param pszFunction The function name. + */ +void kDbgAssertMsg1(const char *pszExpr, const char *pszFile, unsigned iLine, const char *pszFunction); + +/** + * Helper function that displays custom assert message. + * + * @param pszFormat Format string that get passed to vprintf. + * @param ... Format arguments. + */ +void kDbgAssertMsg2(const char *pszFormat, ...); + + +#ifdef KDBG_STRICT + +# define kDbgAssert(expr) \ + do { \ + if (!(expr)) \ + { \ + kDbgAssertMsg1(#expr, __FILE__, __LINE__, K_FUNCTION); \ + kDbgAssertBreakpoint(); \ + } \ + } while (0) + +# define kDbgAssertReturn(expr, rcRet) \ + do { \ + if (!(expr)) \ + { \ + kDbgAssertMsg1(#expr, __FILE__, __LINE__, K_FUNCTION); \ + kDbgAssertBreakpoint(); \ + return (rcRet); \ + } \ + } while (0) + +# define kDbgAssertMsg(expr, msg) \ + do { \ + if (!(expr)) \ + { \ + kDbgAssertMsg1(#expr, __FILE__, __LINE__, K_FUNCTION); \ + kDbgAssertMsg2 msg; \ + kDbgAssertBreakpoint(); \ + } \ + } while (0) + +# define kDbgAssertMsgReturn(expr, msg, rcRet) \ + do { \ + if (!(expr)) \ + { \ + kDbgAssertMsg1(#expr, __FILE__, __LINE__, K_FUNCTION); \ + kDbgAssertMsg2 msg; \ + kDbgAssertBreakpoint(); \ + return (rcRet); \ + } \ + } while (0) + +#else /* !KDBG_STRICT */ +# define kDbgAssert(expr) do { } while (0) +# define kDbgAssertReturn(expr, rcRet) do { if (!(expr)) return (rcRet); } while (0) +# define kDbgAssertMsg(expr, msg) do { } while (0) +# define kDbgAssertMsgReturn(expr, msg, rcRet) do { if (!(expr)) return (rcRet); } while (0) +#endif /* !KDBG_STRICT */ + +#define kDbgAssertPtr(ptr) kDbgAssertMsg(KDBG_VALID_PTR(ptr), ("%s = %p\n", #ptr, (ptr))) +#define kDbgAssertPtrReturn(ptr, rcRet) kDbgAssertMsgReturn(KDBG_VALID_PTR(ptr), ("%s = %p -> %d\n", #ptr, (ptr), (rcRet)), (rcRet)) +#define kDbgAssertPtrNull(ptr) kDbgAssertMsg(!(ptr) || KDBG_VALID_PTR(ptr), ("%s = %p\n", #ptr, (ptr))) +#define kDbgAssertPtrNullReturn(ptr, rcRet) kDbgAssertMsgReturn(!(ptr) || KDBG_VALID_PTR(ptr), ("%s = %p -> %d\n", #ptr, (ptr), (rcRet)), (rcRet)) +#define kDbgAssertRC(rc) kDbgAssertMsg((rc) == 0, ("%s = %d\n", #rc, (rc))) +#define kDbgAssertRCReturn(rc, rcRet) kDbgAssertMsgReturn((rc) == 0, ("%s = %d -> %d\n", #rc, (rc), (rcRet)), (rcRet)) +#define kDbgAssertFailed() kDbgAssert(0) +#define kDbgAssertFailedReturn(rcRet) kDbgAssertReturn(0, (rcRet)) +#define kDbgAssertMsgFailed(msg) kDbgAssertMsg(0, msg) +#define kDbgAssertMsgFailedReturn(msg, rcRet) kDbgAssertMsgReturn(0, msg, (rcRet)) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/lib/kStuff/kDbg/kDbgHlpCrt.cpp b/src/lib/kStuff/kDbg/kDbgHlpCrt.cpp new file mode 100644 index 0000000..a218404 --- /dev/null +++ b/src/lib/kStuff/kDbg/kDbgHlpCrt.cpp @@ -0,0 +1,239 @@ +/* $Id: kDbgHlpCrt.cpp 77 2016-06-22 17:03:55Z bird $ */ +/** @file + * kDbg - The Debug Info Reader, Helpers, CRT Based Implementation. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "kDbgHlp.h" +#include "kDbg.h" + +#include +#include +#include +#include +#ifdef _MSC_VER +# include +#endif + + + +/** + * The stdio base implementation of KDBGHLPFILE. + */ +typedef struct KDBGHLPFILE +{ + /** Pointer to the stdio file stream. */ + FILE *pStrm; +} KDBGHLPFILE; + +/** @def HAVE_FSEEKO + * Define HAVE_FSEEKO to indicate that fseeko and ftello should be used. */ +#if !defined(_MSC_VER) +# define HAVE_FSEEKO +#endif + + +void *kDbgHlpAlloc(size_t cb) +{ + return malloc(cb); +} + + +void *kDbgHlpAllocZ(size_t cb) +{ + return calloc(1, cb); +} + + +void *kDbgHlpAllocDup(const void *pv, size_t cb) +{ + void *pvNew = malloc(cb); + if (pvNew) + memcpy(pvNew, pv, cb); + return pvNew; +} + + +void *kDbgHlpReAlloc(void *pv, size_t cb) +{ + return realloc(pv, cb); +} + + +void kDbgHlpFree(void *pv) +{ + free(pv); +} + + +int kDbgHlpCrtConvErrno(int rc) +{ + switch (rc) + { + case 0: return 0; + case EINVAL: return KERR_INVALID_PARAMETER; + case ENOMEM: return KERR_NO_MEMORY; + case EISDIR: + case ENOENT: return KERR_FILE_NOT_FOUND; + default: return KERR_GENERAL_FAILURE; + } +} + + +int kDbgHlpOpenRO(const char *pszFilename, PKDBGHLPFILE *ppFile) +{ + PKDBGHLPFILE pFile = (PKDBGHLPFILE)kDbgHlpAlloc(sizeof(*pFile)); + if (!pFile) + return KERR_NO_MEMORY; + + pFile->pStrm = fopen(pszFilename, "rb"); + if (pFile->pStrm) + { + *ppFile = pFile; + return 0; + } + return kDbgHlpCrtConvErrno(errno); +} + + +void kDbgHlpClose(PKDBGHLPFILE pFile) +{ + if (pFile) + { + fclose(pFile->pStrm); + pFile->pStrm = NULL; + kDbgHlpFree(pFile); + } +} + + +uintptr_t kDbgHlpNativeFileHandle(PKDBGHLPFILE pFile) +{ + int fd = fileno(pFile->pStrm); +#ifdef _MSC_VER + return _get_osfhandle(fd); +#else + return fd; +#endif +} + + +int64_t kDbgHlpFileSize(PKDBGHLPFILE pFile) +{ + int64_t cbFile; + int64_t offCur = kDbgHlpTell(pFile); + if (offCur >= 0) + { + if (kDbgHlpSeekByEnd(pFile, 0) == 0) + cbFile = kDbgHlpTell(pFile); + else + cbFile = -1; + kDbgHlpSeek(pFile, offCur); + } + else + cbFile = -1; + return cbFile; +} + + +int kDbgHlpReadAt(PKDBGHLPFILE pFile, int64_t off, void *pv, size_t cb) +{ + int rc = kDbgHlpSeek(pFile, off); + if (!rc) + rc = kDbgHlpRead(pFile, pv, cb); + return rc; +} + + +int kDbgHlpRead(PKDBGHLPFILE pFile, void *pv, size_t cb) +{ + if (fread(pv, cb, 1, pFile->pStrm) == 1) + return 0; + return -1; +} + + +int kDbgHlpSeek(PKDBGHLPFILE pFile, int64_t off) +{ +#ifdef HAVE_FSEEKO + if (!fseeko(pFile->pStrm, off, SEEK_SET)) + return 0; +#else + long l = (long)off; + if (l != off) + return KERR_OUT_OF_RANGE; + if (!fseek(pFile->pStrm, l, SEEK_SET)) + return 0; +#endif + return kDbgHlpCrtConvErrno(errno); +} + + +int kDbgHlpSeekByCur(PKDBGHLPFILE pFile, int64_t off) +{ +#ifdef HAVE_FSEEKO + if (!fseeko(pFile->pStrm, off, SEEK_CUR)) + return 0; +#else + long l = (long)off; + if (l != off) + return KERR_OUT_OF_RANGE; + if (!fseek(pFile->pStrm, l, SEEK_CUR)) + return 0; +#endif + return kDbgHlpCrtConvErrno(errno); +} + + +int kDbgHlpSeekByEnd(PKDBGHLPFILE pFile, int64_t off) +{ +#ifdef HAVE_FSEEKO + if (!fseeko(pFile->pStrm, -off, SEEK_END)) + return 0; +#else + long l = (long)off; + if (l != off) + return KERR_OUT_OF_RANGE; + if (!fseek(pFile->pStrm, -l, SEEK_END)) + return 0; +#endif + return kDbgHlpCrtConvErrno(errno); +} + + +int64_t kDbgHlpTell(PKDBGHLPFILE pFile) +{ +#ifdef HAVE_FSEEKO + return ftello(pFile->pStrm); +#else + return ftell(pFile->pStrm); +#endif +} + diff --git a/src/lib/kStuff/kDbg/kDbgInternal.h b/src/lib/kStuff/kDbg/kDbgInternal.h new file mode 100644 index 0000000..fdb3fcd --- /dev/null +++ b/src/lib/kStuff/kDbg/kDbgInternal.h @@ -0,0 +1,137 @@ +/* $Id: kDbgInternal.h 29 2009-07-01 20:30:29Z bird $ */ +/** @file + * kDbg - The Debug Info Reader, Internal Header. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef ___kDbgInternal_h___ +#define ___kDbgInternal_h___ + +#include +#include +#include +#include + + +/** @defgroup grp_kDbgInternal Internal + * @internal + * @addtogroup grp_kDbg + * @{ + */ + +/** @def KDBG_STRICT + * If defined the kDbg assertions and other runtime checks will be enabled. */ +#ifdef K_ALL_STRICT +# undef KDBG_STRICT +# define KDBG_STRICT +#endif + +/** @name Our Assert macros + * @{ */ +#ifdef KDBG_STRICT +# define kDbgAssert(expr) kHlpAssert(expr) +# define kDbgAssertReturn(expr, rcRet) kHlpAssertReturn(expr, rcRet) +# define kDbgAssertReturnVoid(expr) kHlpAssertReturnVoid(expr) +# define kDbgAssertMsg(expr, msg) kHlpAssertMsg(expr, msg) +# define kDbgAssertMsgReturn(expr, msg, rcRet) kHlpAssertMsgReturn(expr, msg, rcRet) +# define kDbgAssertMsgReturnVoid(expr, msg) kHlpAssertMsgReturnVoid(expr, msg) +#else /* !KDBG_STRICT */ +# define kDbgAssert(expr) do { } while (0) +# define kDbgAssertReturn(expr, rcRet) do { if (!(expr)) return (rcRet); } while (0) +# define kDbgAssertMsg(expr, msg) do { } while (0) +# define kDbgAssertMsgReturn(expr, msg, rcRet) do { if (!(expr)) return (rcRet); } while (0) +#endif /* !KDBG_STRICT */ + +#define kDbgAssertPtr(ptr) kDbgAssertMsg(K_VALID_PTR(ptr), ("%s = %p\n", #ptr, (ptr))) +#define kDbgAssertPtrReturn(ptr, rcRet) kDbgAssertMsgReturn(K_VALID_PTR(ptr), ("%s = %p -> %d\n", #ptr, (ptr), (rcRet)), (rcRet)) +#define kDbgAssertPtrReturnVoid(ptr) kDbgAssertMsgReturnVoid(K_VALID_PTR(ptr), ("%s = %p -> %d\n", #ptr, (ptr), (rcRet))) +#define kDbgAssertPtrNull(ptr) kDbgAssertMsg(!(ptr) || K_VALID_PTR(ptr), ("%s = %p\n", #ptr, (ptr))) +#define kDbgAssertPtrNullReturn(ptr, rcRet) kDbgAssertMsgReturn(!(ptr) || K_VALID_PTR(ptr), ("%s = %p -> %d\n", #ptr, (ptr), (rcRet)), (rcRet)) +#define kDbgAssertPtrNullReturnVoid(ptr) kDbgAssertMsgReturnVoid(!(ptr) || K_VALID_PTR(ptr), ("%s = %p -> %d\n", #ptr, (ptr), (rcRet))) +#define kDbgAssertRC(rc) kDbgAssertMsg((rc) == 0, ("%s = %d\n", #rc, (rc))) +#define kDbgAssertRCReturn(rc, rcRet) kDbgAssertMsgReturn((rc) == 0, ("%s = %d -> %d\n", #rc, (rc), (rcRet)), (rcRet)) +#define kDbgAssertRCReturnVoid(rc) kDbgAssertMsgReturnVoid((rc) == 0, ("%s = %d -> %d\n", #rc, (rc), (rcRet))) +#define kDbgAssertFailed() kDbgAssert(0) +#define kDbgAssertFailedReturn(rcRet) kDbgAssertReturn(0, (rcRet)) +#define kDbgAssertFailedReturnVoid() kDbgAssertReturnVoid(0) +#define kDbgAssertMsgFailed(msg) kDbgAssertMsg(0, msg) +#define kDbgAssertMsgFailedReturn(msg, rcRet) kDbgAssertMsgReturn(0, msg, (rcRet)) +#define kDbgAssertMsgFailedReturnVoid(msg) kDbgAssertMsgReturnVoid(0, msg) +/** @} */ + +/** Return / crash validation of a reader argument. */ +#define KDBGMOD_VALIDATE_EX(pDbgMod, rc) \ + do { \ + kDbgAssertPtrReturn((pDbgMod), (rc)); \ + kDbgAssertReturn((pDbgMod)->u32Magic == KDBGMOD_MAGIC, (rc)); \ + kDbgAssertReturn((pDbgMod)->pOps != NULL, (rc)); \ + } while (0) + +/** Return / crash validation of a reader argument. */ +#define KDBGMOD_VALIDATE(pDbgMod) \ + do { \ + kDbgAssertPtrReturn((pDbgMod), KERR_INVALID_POINTER); \ + kDbgAssertReturn((pDbgMod)->u32Magic == KDBGMOD_MAGIC, KERR_INVALID_HANDLE); \ + kDbgAssertReturn((pDbgMod)->pOps != NULL, KERR_INVALID_HANDLE); \ + } while (0) + +/** Return / crash validation of a reader argument. */ +#define KDBGMOD_VALIDATE_VOID(pDbgMod) \ + do { \ + kDbgAssertPtrReturnVoid((pDbgMod)); \ + kDbgAssertReturnVoid((pDbgMod)->u32Magic == KDBGMOD_MAGIC); \ + kDbgAssertReturnVoid((pDbgMod)->pOps != NULL); \ + } while (0) + + +#ifdef __cplusplus +extern "C" { +#endif + +/** @name Built-in Debug Module Readers + * @{ */ +extern KDBGMODOPS const g_kDbgModWinDbgHelpOpen; +extern KDBGMODOPS const g_kDbgModLdr; +extern KDBGMODOPS const g_kDbgModCv8; +extern KDBGMODOPS const g_kDbgModDwarf; +extern KDBGMODOPS const g_kDbgModHll; +extern KDBGMODOPS const g_kDbgModStabs; +extern KDBGMODOPS const g_kDbgModSym; +extern KDBGMODOPS const g_kDbgModMapILink; +extern KDBGMODOPS const g_kDbgModMapMSLink; +extern KDBGMODOPS const g_kDbgModMapNm; +extern KDBGMODOPS const g_kDbgModMapWLink; +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif + diff --git a/src/lib/kStuff/kDbg/kDbgLine.cpp b/src/lib/kStuff/kDbg/kDbgLine.cpp new file mode 100644 index 0000000..52e573f --- /dev/null +++ b/src/lib/kStuff/kDbg/kDbgLine.cpp @@ -0,0 +1,78 @@ +/* $Id: kDbgLine.cpp 29 2009-07-01 20:30:29Z bird $ */ +/** @file + * kDbg - The Debug Info Read, Line Numbers. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "kDbgInternal.h" +#include + + +/** + * Duplicates a line number. + * + * To save heap space, the returned line number will not own more heap space + * than it strictly need to. So, it's not possible to append stuff to the symbol + * or anything of that kind. + * + * @returns Pointer to the duplicate. + * This must be freed using kDbgSymbolFree(). + * @param pLine The line number to be duplicated. + */ +KDBG_DECL(PKDBGLINE) kDbgLineDup(PCKDBGLINE pLine) +{ + kDbgAssertPtrReturn(pLine, NULL); + KSIZE cb = K_OFFSETOF(KDBGLINE, szFile[pLine->cchFile + 1]); + PKDBGLINE pNewLine = (PKDBGLINE)kHlpDup(pLine, cb); + if (pNewLine) + pNewLine->cbSelf = cb; + return pNewLine; +} + + +/** + * Frees a line number obtained from the kDbg API. + * + * @returns 0 on success. + * @returns KERR_INVALID_POINTER if pLine isn't a valid pointer. + * + * @param pLine The line number to be freed. The null pointer is ignored. + */ +KDBG_DECL(int) kDbgLineFree(PKDBGLINE pLine) +{ + if (pLine) + { + kDbgAssertPtrReturn(pLine, KERR_INVALID_POINTER); + pLine->cbSelf = 0; + kHlpFree(pLine); + } + return 0; +} + diff --git a/src/lib/kStuff/kDbg/kDbgModLdr.cpp b/src/lib/kStuff/kDbg/kDbgModLdr.cpp new file mode 100644 index 0000000..5e77095 --- /dev/null +++ b/src/lib/kStuff/kDbg/kDbgModLdr.cpp @@ -0,0 +1,109 @@ +/* $Id: kDbgModLdr.cpp 29 2009-07-01 20:30:29Z bird $ */ +/** @file + * kDbg - The Debug Info Reader, kLdr Based. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include +#include +#include "kDbgInternal.h" + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * A kLdr based debug reader. + */ +typedef struct KDBGMODLDR +{ + /** The common module core. */ + KDBGMOD Core; + /** Pointer to the loader module. */ + PKLDRMOD pLdrMod; +} KDBGMODLDR, *PKDBGMODLDR; + + +/** + * @copydoc KDBGMODOPS::pfnQueryLine + */ +static int kDbgModLdrQueryLine(PKDBGMOD pMod, KI32 iSegment, KDBGADDR uOffset, PKDBGLINE pLine) +{ + //PKDBGMODLDR pThis = (PKDBGMODLDR)pMod; + return KERR_NOT_IMPLEMENTED; +} + + +/** + * @copydoc KDBGMODOPS::pfnQuerySymbol + */ +static int kDbgModLdrQuerySymbol(PKDBGMOD pMod, KI32 iSegment, KDBGADDR off, PKDBGSYMBOL pSym) +{ + //PKDBGMODLDR pThis = (PKDBGMODLDR)pMod; + return KERR_NOT_IMPLEMENTED; +} + + +/** + * @copydoc KDBGMODOPS::pfnClose + */ +static int kDbgModLdrClose(PKDBGMOD pMod) +{ + //PKDBGMODLDr pThis = (PKDBGMODLDR)pMod; + return KERR_NOT_IMPLEMENTED; +} + + +/** + * @copydocs KDBGMODOPS::pfnOpen. + */ +static int kDbgModLdrOpen(PKDBGMOD *ppMod, PKRDR pRdr, KBOOL fCloseRdr, KFOFF off, KFOFF cb, struct KLDRMOD *pLdrMod) +{ + return KERR_NOT_IMPLEMENTED; +} + + +/** + * Methods for a PE module. + */ +const KDBGMODOPS g_kDbgModLdr = +{ + "kLdr", + NULL, + kDbgModLdrOpen, + kDbgModLdrClose, + kDbgModLdrQuerySymbol, + kDbgModLdrQueryLine, + "kLdr" +}; + + + + 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 + * + * 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 +#include + + +/******************************************************************************* +* 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 +}; + + + diff --git a/src/lib/kStuff/kDbg/kDbgModWinDbgHelp.cpp b/src/lib/kStuff/kDbg/kDbgModWinDbgHelp.cpp new file mode 100644 index 0000000..3c30773 --- /dev/null +++ b/src/lib/kStuff/kDbg/kDbgModWinDbgHelp.cpp @@ -0,0 +1,724 @@ +/* $Id: kDbgModWinDbgHelp.cpp 29 2009-07-01 20:30:29Z bird $ */ +/** @file + * kDbg - The Debug Info Reader, DbgHelp Based Reader. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include +#define _IMAGEHLP64 +#include + +#include "kDbgInternal.h" +#include +#include + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** The dbghelp.dll module handle. */ +static HMODULE g_hDbgHelp = NULL; +/** Pointer to the dbhelp.dll SymInitialize function. */ +static BOOL (WINAPI *g_pfnSymInitialize)(IN HANDLE,IN LPSTR,IN BOOL); +/** Pointer to the dbhelp.dll SymCleanup function. */ +static BOOL (WINAPI *g_pfnSymCleanup)(IN HANDLE); +/** Pointer to the dbhelp.dll SymSetOptions function. */ +static DWORD (WINAPI *g_pfnSymSetOptions)(IN DWORD); +/** Pointer to the dbhelp.dll SymLoadModule64 function. */ +static DWORD64 (WINAPI *g_pfnSymLoadModule64)(IN HANDLE, IN HANDLE, IN PCSTR, IN PCSTR ModuleName, IN DWORD64, IN DWORD); +/** Pointer to the dbhelp.dll SymFromAddr function. */ +static DWORD (WINAPI *g_pfnSymFromAddr)(IN HANDLE, IN DWORD64, OUT PDWORD64, OUT PSYMBOL_INFO); +/** Pointer to the dbhelp.dll SymGetLineFromAddr64 function. */ +static DWORD (WINAPI *g_pfnSymGetLineFromAddr64)(IN HANDLE, IN DWORD64, OUT PDWORD64, OUT PIMAGEHLP_LINE64); + + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * A dbghelp based PE debug reader. + */ +typedef struct KDBGMODDBGHELP +{ + /** The common module core. */ + KDBGMOD Core; + /** The image base. */ + DWORD64 ImageBase; + /** The "process" handle we present dbghelp. */ + HANDLE hSymInst; + /** The image size. */ + KU32 cbImage; + /** The number of sections. (We've added the implicit header section.) */ + KI32 cSections; + /** The section headers (variable size). The first section is the + * implicit header section.*/ + IMAGE_SECTION_HEADER aSections[1]; +} KDBGMODDBGHELP, *PKDBGMODDBGHELP; + + +/** + * Convers a Windows error to kDbg error code. + * + * @returns kDbg status code. + * @param rc The Windows error. + */ +static int kdbgModDHConvWinError(DWORD rc) +{ + switch (rc) + { + case 0: return 0; + default: return KERR_GENERAL_FAILURE; + } +} + + +/** + * Calcs the RVA for a segment:offset address. + * + * @returns IPRT status code. + * + * @param pModDH 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 kdbgModDHSegOffToRVA(PKDBGMODDBGHELP pModDH, KI32 iSegment, KDBGADDR off, KU32 *puRVA) +{ + if (iSegment >= 0) + { + kDbgAssertMsgReturn(iSegment < pModDH->cSections, ("iSegment=%x cSections=%x\n", iSegment, pModDH->cSections), + KDBG_ERR_INVALID_ADDRESS); + kDbgAssertMsgReturn(off < pModDH->aSections[iSegment].Misc.VirtualSize, + ("off=" PRI_KDBGADDR " VirtualSize=%x\n", off, pModDH->aSections[iSegment].Misc.VirtualSize), + KDBG_ERR_INVALID_ADDRESS); + *puRVA = pModDH->aSections[iSegment].VirtualAddress + (KU32)off; + return 0; + } + + if (iSegment == KDBGSEG_RVA) + { + kDbgAssertMsgReturn(off < pModDH->cbImage, ("off=" PRI_KDBGADDR ", cbImage=%x\n", off, pModDH->cbImage), + KDBG_ERR_INVALID_ADDRESS); + *puRVA = (KU32)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 pModDH 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 kdbgModDHRVAToSegOff(PKDBGMODDBGHELP pModDH, KU32 uRVA, KI32 *piSegment, KDBGADDR *poff) +{ + kDbgAssertMsgReturn(uRVA < pModDH->cbImage, ("uRVA=%x, cbImage=%x\n", uRVA, pModDH->cbImage), + KDBG_ERR_INVALID_ADDRESS); + for (KI32 iSegment = 0; iSegment < pModDH->cSections; iSegment++) + { + /** @todo should probably be less strict about address in the alignment gaps. */ + KU32 off = uRVA - pModDH->aSections[iSegment].VirtualAddress; + if (off < pModDH->aSections[iSegment].Misc.VirtualSize) + { + *poff = off; + *piSegment = iSegment; + return 0; + } + } + kDbgAssertMsgFailedReturn(("uRVA=%x\n", uRVA), KDBG_ERR_INVALID_ADDRESS); +} + + +/** + * @copydoc KDBGMODOPS::pfnQueryLine + */ +static int kdbgModDHQueryLine(PKDBGMOD pMod, KI32 iSegment, KDBGADDR off, PKDBGLINE pLine) +{ + PKDBGMODDBGHELP pModDH = (PKDBGMODDBGHELP)pMod; + + /* + * Translate the address to an RVA. + */ + KU32 uRVA; + int rc = kdbgModDHSegOffToRVA(pModDH, iSegment, off, &uRVA); + if (!rc) + { + DWORD64 off; + IMAGEHLP_LINE64 Line; + Line.SizeOfStruct = sizeof(Line); + if (g_pfnSymGetLineFromAddr64(pModDH->hSymInst, pModDH->ImageBase + uRVA, &off, &Line)) + { + pLine->RVA = (KDBGADDR)(Line.Address - pModDH->ImageBase); + rc = kdbgModDHRVAToSegOff(pModDH, (KU32)pLine->RVA, &pLine->iSegment, &pLine->offSegment); + pLine->iLine = Line.LineNumber; + KSIZE cchFile = kHlpStrLen(Line.FileName); + pLine->cchFile = cchFile < sizeof(pLine->szFile) + ? (KU16)cchFile + : (KU16)sizeof(pLine->szFile) - 1; + kHlpMemCopy(pLine->szFile, Line.FileName, pLine->cchFile); + pLine->szFile[pLine->cchFile] = '\0'; + } + else + { + DWORD Err = GetLastError(); + rc = kdbgModDHConvWinError(Err); + } + } + return rc; +} + + +/** + * @copydoc KDBGMODOPS::pfnQuerySymbol + */ +static int kdbgModDHQuerySymbol(PKDBGMOD pMod, KI32 iSegment, KDBGADDR off, PKDBGSYMBOL pSym) +{ + PKDBGMODDBGHELP pModDH = (PKDBGMODDBGHELP)pMod; + + /* + * Translate the address to an RVA. + */ + KU32 uRVA; + int rc = kdbgModDHSegOffToRVA(pModDH, iSegment, off, &uRVA); + if (!rc) + { + 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(pModDH->hSymInst, pModDH->ImageBase + uRVA, &off, &Buf.Sym)) + { + pSym->cb = Buf.Sym.Size; + pSym->Address = NIL_KDBGADDR; + 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 - pModDH->ImageBase); + rc = kdbgModDHRVAToSegOff(pModDH, (KU32)pSym->RVA, &pSym->iSegment, &pSym->offSegment); + } + pSym->cchName = (KU16)Buf.Sym.NameLen; + if (pSym->cchName >= sizeof(pSym->szName)) + pSym->cchName = sizeof(pSym->szName) - 1; + kHlpMemCopy(pSym->szName, Buf.Sym.Name, Buf.Sym.NameLen); + pSym->szName[Buf.Sym.NameLen] = '\0'; + } + else + { + DWORD Err = GetLastError(); + rc = kdbgModDHConvWinError(Err); + } + } + return rc; +} + + +/** + * @copydoc KDBGMODOPS::pfnClose + */ +static int kdbgModDHClose(PKDBGMOD pMod) +{ + PKDBGMODDBGHELP pModDH = (PKDBGMODDBGHELP)pMod; + + if (g_pfnSymCleanup(pModDH->hSymInst)) + return 0; + + DWORD Err = GetLastError(); + int rc = kdbgModDHConvWinError(Err); + kDbgAssertMsgFailed(("SymInitialize failed: Err=%d rc=%d\n", Err, rc)); + return rc; +} + + +/** + * Checks if the specified dbghelp.dll is usable. + * + * @returns IPRT status code. + * + * @param pszPath the path to the dbghelp.dll. + */ +static int kdbgModDHTryDbgHelp(const char *pszPath, KU32 *pu32FileVersionMS, KU32 *pu32FileVersionLS) +{ + int rc; + DWORD dwHandle = 0; + DWORD cb = GetFileVersionInfoSize(pszPath, &dwHandle); + if (cb > 0) + { + void *pvBuf = alloca(cb); + if (GetFileVersionInfo(pszPath, dwHandle, cb, pvBuf)) + { + UINT cbValue = 0; + VS_FIXEDFILEINFO *pFileInfo; + if (VerQueryValue(pvBuf, "\\", (void **)&pFileInfo, &cbValue)) + { + /** @todo somehow reject 64-bit .dlls when in 32-bit mode... dwFileOS is completely useless. */ + if ( *pu32FileVersionMS < pFileInfo->dwFileVersionMS + || ( *pu32FileVersionMS == pFileInfo->dwFileVersionMS + && *pu32FileVersionLS > pFileInfo->dwFileVersionLS)) + { + *pu32FileVersionMS = pFileInfo->dwFileVersionMS; + *pu32FileVersionLS = pFileInfo->dwFileVersionLS; + } + if (pFileInfo->dwFileVersionMS >= 0x60004) + rc = 0; + else + rc = KDBG_ERR_DBGHLP_VERSION_MISMATCH; + } + else + rc = KERR_GENERAL_FAILURE; + } + else + rc = kdbgModDHConvWinError(GetLastError()); + } + else + rc = kdbgModDHConvWinError(GetLastError()); + return rc; +} + + +/** + * Find the dbghelp.dll + */ +static int kdbgModDHFindDbgHelp(char *pszPath, KSIZE cchPath) +{ + /* + * Try the current directory. + */ + KU32 FileVersionMS = 0; + KU32 FileVersionLS = 0; + int rc = KERR_GENERAL_FAILURE; + static char s_szDbgHelp[] = "\\dbghelp.dll"; + if (GetCurrentDirectory((DWORD)(cchPath - sizeof(s_szDbgHelp) + 1), pszPath)) + { + strcat(pszPath, s_szDbgHelp); + int rc2 = kdbgModDHTryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS); + if (!rc2) + return rc2; + if (rc != KDBG_ERR_DBGHLP_VERSION_MISMATCH) + rc = rc2; + } + + /* + * Try the application directory. + */ + if (GetModuleFileName(NULL, pszPath, (DWORD)(cchPath - sizeof(s_szDbgHelp) + 1))) + { + kHlpStrCat(kHlpStrRChr(pszPath, '\\'), s_szDbgHelp); + int rc2 = kdbgModDHTryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS); + if (!rc) + return rc2; + if (rc != KDBG_ERR_DBGHLP_VERSION_MISMATCH) + rc = rc2; + } + + /* + * Try the windows directory. + */ + if (GetSystemDirectory(pszPath, (DWORD)(cchPath - sizeof(s_szDbgHelp) + 1))) + { + kHlpStrCat(pszPath, s_szDbgHelp); + int rc2 = kdbgModDHTryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS); + if (!rc2) + return rc2; + if (rc != KDBG_ERR_DBGHLP_VERSION_MISMATCH) + rc = rc2; + } + + /* + * Try the windows directory. + */ + if (GetWindowsDirectory(pszPath, (DWORD)(cchPath - sizeof(s_szDbgHelp) + 1))) + { + kHlpStrCat(pszPath, s_szDbgHelp); + int rc2 = kdbgModDHTryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS); + if (!rc2) + return rc2; + if (rc != KDBG_ERR_DBGHLP_VERSION_MISMATCH) + rc = rc2; + } + + /* + * Try the path. + */ + /** @todo find the actual path specs, I'm probably not doing this 100% correctly here. */ + DWORD cb = GetEnvironmentVariable("PATH", NULL, 0) + 64; + char *pszSearchPath = (char *) alloca(cb); + if (GetEnvironmentVariable("PATH", pszSearchPath, cb) < cb) + { + char *psz = pszSearchPath; + while (*psz) + { + /* find the end of the path. */ + char *pszEnd = kHlpStrChr(psz, ';'); + if (!pszEnd) + pszEnd = kHlpStrChr(psz, '\0'); + if (pszEnd != psz) + { + /* construct filename and try it out */ + kHlpMemCopy(pszPath, psz, pszEnd - psz); + kHlpMemCopy(&pszPath[pszEnd - psz], s_szDbgHelp, sizeof(s_szDbgHelp)); + int rc2 = kdbgModDHTryDbgHelp(pszPath, &FileVersionMS, &FileVersionLS); + if (!rc2) + return rc2; + if (rc != KDBG_ERR_DBGHLP_VERSION_MISMATCH) + rc = rc2; + } + + /* next path */ + if (!*pszEnd) + break; + psz = pszEnd + 1; + } + } + + if (rc == KDBG_ERR_DBGHLP_VERSION_MISMATCH) + kDbgAssertMsgFailed(("dbghelp.dll found, but it was ancient! The highest file version found was 0x%08x'%08x.\n" + "This program require a file version of at least 0x00060004'00000000. Please download\n" + "the latest windbg and use the dbghelp.dll from that package. Just put it somewhere in\n" + "the PATH and we'll find it.\n", FileVersionMS, FileVersionLS)); + else + kDbgAssertMsgFailed(("dbghelp.dll was not found! Download the latest windbg and use the dbghelp.dll\n" + "from that package - just put it somewhere in the PATH and we'll find it.\n")); + return rc; +} + + +/** + * Loads the dbghelp.dll, check that it's the right version, and + * resolves all the symbols we need. + * + * @returns IPRT status code. + */ +static int kdbgModDHLoadDbgHelp(void) +{ + if (g_hDbgHelp) + return 0; + + /* primitive locking - make some useful API for this kind of spinning! */ + static volatile long s_lLock = 0; + while (InterlockedCompareExchange(&s_lLock, 1, 0)) + while (s_lLock) + Sleep(1); + if (g_hDbgHelp) + { + InterlockedExchange(&s_lLock, 0); + return 0; + } + + /* + * Load it - try current dir first. + */ + char szPath[260]; + int rc = kdbgModDHFindDbgHelp(szPath, sizeof(szPath)); + if (rc) + { + InterlockedExchange(&s_lLock, 0); + return rc; + } + + HMODULE hmod = LoadLibraryEx(szPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + if (!hmod) + { + DWORD Err = GetLastError(); + int rc = kdbgModDHConvWinError(Err); + InterlockedExchange(&s_lLock, 0); + kDbgAssertMsgFailedReturn(("Failed to load '%s', Err=%d rc=%d\n", szPath, Err, rc), rc); + } + + /* + * Check the API version (too). + */ + LPAPI_VERSION (WINAPI *pfnImagehlpApiVersion)(VOID); + FARPROC *ppfn = (FARPROC *)&pfnImagehlpApiVersion; + *ppfn = GetProcAddress(hmod, "ImagehlpApiVersion"); + if (*ppfn) + { + LPAPI_VERSION pVersion = pfnImagehlpApiVersion(); + if ( pVersion + && ( pVersion->MajorVersion > 4 + || (pVersion->MajorVersion == 4 && pVersion->MinorVersion > 0) + || (pVersion->MajorVersion == 4 && pVersion->MinorVersion == 0 && pVersion->Revision >= 5) + ) + ) + { + /* + * Resolve the entrypoints we need. + */ + static const struct + { + const char *pszName; + FARPROC *ppfn; + } s_aFunctions[] = + { + { "SymInitialize", (FARPROC *)&g_pfnSymInitialize }, + { "SymCleanup", (FARPROC *)&g_pfnSymCleanup }, + { "SymSetOptions", (FARPROC *)&g_pfnSymSetOptions }, + { "SymLoadModule64", (FARPROC *)&g_pfnSymLoadModule64 }, + { "SymFromAddr", (FARPROC *)&g_pfnSymFromAddr }, + { "SymFromAddr", (FARPROC *)&g_pfnSymFromAddr }, + { "SymGetLineFromAddr64", (FARPROC *)&g_pfnSymGetLineFromAddr64 }, + }; + for (unsigned i = 0; i < K_ELEMENTS(s_aFunctions); i++) + { + FARPROC pfn = GetProcAddress(hmod, s_aFunctions[i].pszName); + if (!pfn) + { + DWORD Err = GetLastError(); + rc = kdbgModDHConvWinError(Err); + kDbgAssertMsgFailed(("Failed to resolve %s in dbghelp, Err=%d rc=%d\n", + s_aFunctions[i].pszName, Err, rc)); + break; + } + *s_aFunctions[i].ppfn = pfn; + } + if (!rc) + { + g_hDbgHelp = hmod; + Sleep(1); + InterlockedExchange(&s_lLock, 0); + return 0; + } + } + else + { + rc = KDBG_ERR_DBGHLP_VERSION_MISMATCH; + kDbgAssertMsgFailed(("ImagehlpApiVersion -> %p and MajorVersion=%d.\n", pVersion, pVersion ? pVersion->MajorVersion : 0)); + } + } + else + { + DWORD Err = GetLastError(); + rc = kdbgModDHConvWinError(Err); + kDbgAssertMsgFailed(("Failed to resolve ImagehlpApiVersionEx in dbghelp, Err=%d rc=%d\n", Err, rc)); + } + FreeLibrary(hmod); + InterlockedExchange(&s_lLock, 0); + return rc; +} + + +/** + * @copydoc KDBGMODOPS::pfnOpen + */ +static int kdbgModDHOpen(PKDBGMOD *ppMod, PKRDR pRdr, KBOOL fCloseRdr, KFOFF off, KFOFF cb, struct KLDRMOD *pLdrMod) +{ + /* + * This reader doesn't support partial files. + * Also weed out small files early on as they cannot be + * PE images and will only cause read errors + */ + if ( off != 0 + || cb != KFOFF_MAX) + return KDBG_ERR_UNKOWN_FORMAT; + if (kRdrSize(pRdr) < sizeof(IMAGE_NT_HEADERS32) + sizeof(IMAGE_SECTION_HEADER)) + return KDBG_ERR_UNKOWN_FORMAT; + + /* + * We need to read the section headers and get the image size. + */ + /* Find the PE header magic. */ + KU32 offHdr = 0; + KU32 u32Magic; + int rc = kRdrRead(pRdr, &u32Magic, sizeof(u32Magic), 0); + kDbgAssertRCReturn(rc, rc); + if ((KU16)u32Magic == IMAGE_DOS_SIGNATURE) + { + rc = kRdrRead(pRdr, &offHdr, sizeof(offHdr), K_OFFSETOF(IMAGE_DOS_HEADER, e_lfanew)); + kDbgAssertRCReturn(rc, rc); + if (!offHdr) + return KDBG_ERR_FORMAT_NOT_SUPPORTED; + if ( offHdr < sizeof(IMAGE_DOS_SIGNATURE) + || offHdr >= kRdrSize(pRdr) - 4) + return KDBG_ERR_BAD_EXE_FORMAT; + + rc = kRdrRead(pRdr, &u32Magic, sizeof(u32Magic), offHdr); + kDbgAssertRCReturn(rc, rc); + } + if (u32Magic != IMAGE_NT_SIGNATURE) + return KDBG_ERR_FORMAT_NOT_SUPPORTED; + + /* read the file header and the image size in the optional header.. */ + IMAGE_FILE_HEADER FHdr; + rc = kRdrRead(pRdr, &FHdr, sizeof(FHdr), offHdr + K_OFFSETOF(IMAGE_NT_HEADERS32, FileHeader)); + kDbgAssertRCReturn(rc, rc); + + KU32 cbImage; + if (FHdr.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)) + rc = kRdrRead(pRdr, &cbImage, sizeof(cbImage), + offHdr + K_OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader.SizeOfImage)); + else if (FHdr.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64)) + rc = kRdrRead(pRdr, &cbImage, sizeof(cbImage), + offHdr + K_OFFSETOF(IMAGE_NT_HEADERS64, OptionalHeader.SizeOfImage)); + else + kDbgAssertFailedReturn(KDBG_ERR_BAD_EXE_FORMAT); + kDbgAssertRCReturn(rc, rc); + + /* + * Load dbghelp.dll. + */ + rc = kdbgModDHLoadDbgHelp(); + if (rc) + return rc; + + /* + * Allocate the module and read/construct the section headers. + */ + PKDBGMODDBGHELP pModDH = (PKDBGMODDBGHELP)kHlpAlloc(K_OFFSETOF(KDBGMODDBGHELP, aSections[FHdr.NumberOfSections + 2])); + kDbgAssertReturn(pModDH, KERR_NO_MEMORY); + pModDH->Core.u32Magic = KDBGMOD_MAGIC; + pModDH->Core.pOps = &g_kDbgModWinDbgHelpOpen; + pModDH->Core.pRdr = pRdr; + pModDH->Core.fCloseRdr = fCloseRdr; + pModDH->Core.pLdrMod = pLdrMod; + pModDH->cbImage = cbImage; + pModDH->cSections = 1 + FHdr.NumberOfSections; + + rc = kRdrRead(pRdr, &pModDH->aSections[1], sizeof(pModDH->aSections[0]) * FHdr.NumberOfSections, + offHdr + K_OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader) + FHdr.SizeOfOptionalHeader); + if (!rc) + { + PIMAGE_SECTION_HEADER pSH = &pModDH->aSections[0]; + kHlpMemCopy(pSH->Name, "headers", sizeof(pSH->Name)); + pSH->Misc.VirtualSize = pModDH->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; + + KU32 uTheEnd = pModDH->aSections[FHdr.NumberOfSections].VirtualAddress + + pModDH->aSections[FHdr.NumberOfSections].Misc.VirtualSize; + if (uTheEnd < cbImage) + { + pSH = &pModDH->aSections[pModDH->cSections++]; + kHlpMemCopy(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; + } + + /* + * 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 long s_u32LastHandle = 1; + HANDLE hSymInst = (HANDLE)InterlockedIncrement(&s_u32LastHandle); + while ( hSymInst == INVALID_HANDLE_VALUE + || hSymInst == (HANDLE)0 + || hSymInst == GetCurrentProcess()) + hSymInst = (HANDLE)InterlockedIncrement(&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); + + KIPTR NativeFH = kRdrNativeFH(pRdr); + DWORD64 ImageBase = g_pfnSymLoadModule64(hSymInst, NativeFH == -1 ? NULL : (HANDLE)NativeFH, + kRdrName(pRdr), NULL, 0x00400000, 0); + if (ImageBase) + { + pModDH->hSymInst = hSymInst; + pModDH->ImageBase = ImageBase; + *ppMod = &pModDH->Core; + return rc; + } + + DWORD Err = GetLastError(); + rc = kdbgModDHConvWinError(Err); + kDbgAssertMsgFailed(("SymLoadModule64 failed: Err=%d rc=%d\n", Err, rc)); + g_pfnSymCleanup(hSymInst); + } + else + { + DWORD Err = GetLastError(); + rc = kdbgModDHConvWinError(Err); + kDbgAssertMsgFailed(("SymInitialize failed: Err=%d rc=%d\n", Err, rc)); + } + } + else + kDbgAssertRC(rc); + + kHlpFree(pModDH); + return rc; +} + + +/** + * Methods for a PE module. + */ +KDBGMODOPS const g_kDbgModWinDbgHelpOpen = +{ + "Windows DbgHelp", + NULL, + kdbgModDHOpen, + kdbgModDHClose, + kdbgModDHQuerySymbol, + kdbgModDHQueryLine, + "Windows DbgHelp" +}; + diff --git a/src/lib/kStuff/kDbg/kDbgModule.cpp b/src/lib/kStuff/kDbg/kDbgModule.cpp new file mode 100644 index 0000000..c43fb16 --- /dev/null +++ b/src/lib/kStuff/kDbg/kDbgModule.cpp @@ -0,0 +1,440 @@ +/* $Id: kDbgModule.cpp 29 2009-07-01 20:30:29Z bird $ */ +/** @file + * kDbg - The Debug Info Reader, Module API. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "kDbgInternal.h" +#include +#include + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** + * The built-in debug module readers. + */ +static PCKDBGMODOPS const g_aBuiltIns[] = +{ +#if K_OS == K_OS_WINDOWS + &g_kDbgModWinDbgHelpOpen, +#endif + &g_kDbgModLdr, +// &g_kDbgModCv8, +// &g_kDbgModDwarf, +// &g_kDbgModHll, +// &g_kDbgModStabs, +// &g_kDbgModSym, +// &g_kDbgModMapILink, +// &g_kDbgModMapMSLink, +// &g_kDbgModMapNm, +// &g_kDbgModMapWLink +}; + +/** + * The debug module readers registered at runtime. + */ +static PKDBGMODOPS g_pHead = NULL; + + +/** + * Register a debug module reader with the kDbgModule component. + * + * Dynamically registered readers are kept in FIFO order, and external + * readers will be tried after the builtin ones. + * + * Like all other kDbg APIs serializing is left to the caller. + * + * @returns 0 on success. + * @returns KERR_INVALID_POINTER if pOps is missing bits. + * @returns KERR_INVALID_PARAMETER if pOps is already in the list. + * @param pOps The reader method table, kDbg takes owner ship of + * this. This must be writeable as the pNext pointer + * will be update. It must also stick around for as + * long as kDbg is in use. + */ +KDBG_DECL(int) kDbgModuleRegisterReader(PKDBGMODOPS pOps) +{ + /* + * Validate input. + */ + kDbgAssertPtrReturn(pOps, KERR_INVALID_POINTER); + kDbgAssertPtrReturn(pOps->pszName, KERR_INVALID_POINTER); + kDbgAssertPtrReturn(pOps->pfnOpen, KERR_INVALID_POINTER); + kDbgAssertPtrReturn(pOps->pfnClose, KERR_INVALID_POINTER); + kDbgAssertPtrReturn(pOps->pfnQuerySymbol, KERR_INVALID_POINTER); + kDbgAssertPtrReturn(pOps->pfnQueryLine, KERR_INVALID_POINTER); + kDbgAssertPtrReturn(pOps->pszName2, KERR_INVALID_POINTER); + if (kHlpStrComp(pOps->pszName, pOps->pszName2)) + return KERR_INVALID_PARAMETER; + kDbgAssertReturn(pOps->pNext == NULL, KERR_INVALID_PARAMETER); + + /* + * Link it into the list. + */ + if (!g_pHead) + g_pHead = pOps; + else + { + PKDBGMODOPS pPrev = g_pHead; + while (pPrev->pNext) + pPrev = pPrev->pNext; + kDbgAssertReturn(pPrev != pOps, KERR_INVALID_PARAMETER); + pPrev->pNext = pOps; + } + return 0; +} + + +/** + * Deregister a debug module reader previously registered using + * the kDbgModuleRegisterReader API. + * + * Deregistering a reader does not mean that non of its functions + * will be called after successful return, it only means that it + * will no longer be subjected to new module. + * + * @returns 0 on success. + * @returns KERR_INVALID_POINTER if pOps isn't a valid pointer. + * @returns KERR_INVALID_PARAMETER if pOps wasn't registered. + * @param pOps The debug module method table to deregister. + */ +KDBG_DECL(int) kDbgModuleDeregisterReader(PKDBGMODOPS pOps) +{ + /* + * Validate the pointer. + */ + kDbgAssertPtrReturn(pOps, KERR_INVALID_POINTER); + + /* + * Find it in the list and unlink it. + */ + if (g_pHead == pOps) + g_pHead = pOps->pNext; + else + { + PKDBGMODOPS pPrev = g_pHead; + while (pPrev && pPrev->pNext != pOps) + pPrev = pPrev->pNext; + if (!pPrev) + return KERR_INVALID_PARAMETER; + pPrev->pNext = pOps->pNext; + } + pOps->pNext = NULL; + return 0; +} + + + +/** + * Worker for the kDbgModuleOpen* APIs. + * + * This will make sure the reader is buffered. I will also take care of + * closing the reader opened by kDbgModuleOpen on failure. + * + * @returns 0 on success. An appropriate kErrors status code on failure. + * @param ppDbgMod Where to store the new debug module reader instance. + * @param pRdr The file provider. + * @param fCloseRdr Whether pRdr should be close or not. This applies both + * to the failure path and to the success path, where it'll + * be close when the module is closed by kDbgModuleClose(). + * @param off The offset into the file where the debug info is supposed + * to be found. + * This is 0 if the entire file is the subject. + * @param cb The size of the debug info part of the file. + * This is KFOFF_MAX if the entire file is the subject. + * @param pLdrMod An optional kLdrMod association. + */ +static int kdbgModuleOpenWorker(PPKDBGMOD ppDbgMod, PKRDR pRdr, KBOOL fCloseRdr, KFOFF off, KFOFF cb, struct KLDRMOD *pLdrMod) +{ + /* + * If the reader isn't buffered create a buffered wrapper for it. + */ + int rc; + PKRDR pRdrWrapped = NULL; + if (!kRdrBufIsBuffered(pRdr)) + { + rc = kRdrBufWrap(&pRdrWrapped, pRdr, fCloseRdr); + if (rc) + { + if (fCloseRdr) + kRdrClose(pRdr); + return rc; + } + pRdr = pRdrWrapped; + } + + /* + * Walk the built-in table and the list of registered readers + * and let each of them have a go at the file. Stop and return + * on the first one returning successfully. + */ + rc = KDBG_ERR_UNKOWN_FORMAT; + for (KSIZE i = 0; i < K_ELEMENTS(g_aBuiltIns); i++) + if (g_aBuiltIns[i]->pfnOpen) + { + int rc2 = g_aBuiltIns[i]->pfnOpen(ppDbgMod, pRdr, fCloseRdr, off, cb, pLdrMod); + if (!rc2) + return 0; + if (rc2 != KDBG_ERR_UNKOWN_FORMAT && rc == KDBG_ERR_UNKOWN_FORMAT) + rc = rc2; + } + + for (PKDBGMODOPS pCur = g_pHead; pCur; pCur = pCur->pNext) + if (pCur->pfnOpen) + { + int rc2 = pCur->pfnOpen(ppDbgMod, pRdr, fCloseRdr, off, cb, pLdrMod); + if (!rc2) + return 0; + if (rc2 != KDBG_ERR_UNKOWN_FORMAT && rc == KDBG_ERR_UNKOWN_FORMAT) + rc = rc2; + } + + if (pRdrWrapped) + kRdrClose(pRdrWrapped); + else if (fCloseRdr) + kRdrClose(pRdr); + return rc; +} + + +/** + * Opens a debug module reader for the specified file or file section + * + * @returns kStuff status code. + * @param ppDbgMod Where to store the debug module reader handle. + * @param pRdr The file reader. + * @param off The offset of the file section. If the entire file, pass 0. + * @param cb The size of the file section. If the entire file, pass KFOFF_MAX. + * @param pLdrMod Associated kLdr module that the kDbg component can use to + * verify and suplement the debug info found in the file specified + * by pszFilename. The module will be used by kDbg for as long as + * the returned kDbg module remains open. + * This is an optional parameter, pass NULL if no kLdr module at hand. + */ +KDBG_DECL(int) kDbgModuleOpenFilePart(PPKDBGMOD ppDbgMod, PKRDR pRdr, KFOFF off, KFOFF cb, struct KLDRMOD *pLdrMod) +{ + /* + * Validate input. + */ + kDbgAssertPtrReturn(ppDbgMod, KERR_INVALID_POINTER); + kDbgAssertPtrReturn(pRdr, KERR_INVALID_POINTER); + kDbgAssertPtrNullReturn(pLdrMod, KERR_INVALID_POINTER); + kDbgAssertMsgReturn(off >= 0 && off < KFOFF_MAX, (KFOFF_PRI "\n", off), KERR_INVALID_OFFSET); + kDbgAssertMsgReturn(cb >= 0 && cb <= KFOFF_MAX, (KFOFF_PRI "\n", cb), KERR_INVALID_SIZE); + kDbgAssertMsgReturn(off + cb > off, ("off=" KFOFF_PRI " cb=" KFOFF_PRI "\n", off, cb), KERR_INVALID_RANGE); + *ppDbgMod = NULL; + + /* + * Hand it over to the internal worker. + */ + return kdbgModuleOpenWorker(ppDbgMod, pRdr, K_FALSE /* fCloseRdr */, off, cb, pLdrMod); +} + + +/** + * Opens a debug module reader for the specified file. + * + * @returns kStuff status code. + * @param ppDbgMod Where to store the debug module reader handle. + * @param pRdr The file reader. + * @param pLdrMod Associated kLdr module that the kDbg component can use to + * verify and suplement the debug info found in the file specified + * by pszFilename. The module will be used by kDbg for as long as + * the returned kDbg module remains open. + * This is an optional parameter, pass NULL if no kLdr module at hand. + */ +KDBG_DECL(int) kDbgModuleOpenFile(PPKDBGMOD ppDbgMod, PKRDR pRdr, struct KLDRMOD *pLdrMod) +{ + return kDbgModuleOpenFilePart(ppDbgMod, pRdr, 0, KFOFF_MAX, pLdrMod); +} + + +/** + * Opens the debug info for a specified executable module. + * + * @returns kStuff status code. + * @param ppDbgMod Where to store the debug module handle. + * @param pszFilename The name of the file containing debug info and/or which + * debug info is wanted. + * @param pLdrMod Associated kLdr module that the kDbg component can use to + * verify and suplement the debug info found in the file specified + * by pszFilename. The module will be used by kDbg for as long as + * the returned kDbg module remains open. + * This is an optional parameter, pass NULL if no kLdr module at hand. + */ +KDBG_DECL(int) kDbgModuleOpen(PPKDBGMOD ppDbgMod, const char *pszFilename, struct KLDRMOD *pLdrMod) +{ + /* + * Validate input. + */ + kDbgAssertPtrReturn(ppDbgMod, KERR_INVALID_POINTER); + kDbgAssertPtrReturn(pszFilename, KERR_INVALID_POINTER); + kDbgAssertMsgReturn(*pszFilename, ("%p\n", pszFilename), KERR_INVALID_PARAMETER); + kDbgAssertPtrNullReturn(pLdrMod, KERR_INVALID_POINTER); + *ppDbgMod = NULL; + + /* + * Open the file and see if we can read it. + */ + PKRDR pRdr; + int rc = kRdrBufOpen(&pRdr, pszFilename); + if (rc) + return rc; + rc = kdbgModuleOpenWorker(ppDbgMod, pRdr, K_TRUE /* fCloseRdr */, 0, KFOFF_MAX, pLdrMod); + return rc; +} + + +/** + * Closes the module. + * + * @returns IPRT status code. + * @param pMod The module handle. + */ +KDBG_DECL(int) kDbgModuleClose(PKDBGMOD pMod) +{ + KDBGMOD_VALIDATE(pMod); + int rc = pMod->pOps->pfnClose(pMod); + if (!rc) + { + pMod->u32Magic++; + kHlpFree(pMod); + } + return rc; +} + + +/** + * Gets a symbol by segment:offset. + * This will be approximated to the nearest symbol if there is no exact match. + * + * @returns IPRT status code. + * @param pMod The module. + * @param iSegment The segment this offset is relative to. + * The -1 segment is special, it means that the addres is relative to + * the image base. The image base is where the first bit of the image + * is mapped during load. + * @param off The offset into the segment. + * @param pSym Where to store the symbol details. + */ +KDBG_DECL(int) kDbgModuleQuerySymbol(PKDBGMOD pMod, KI32 iSegment, KDBGADDR off, PKDBGSYMBOL pSym) +{ + KDBGMOD_VALIDATE(pMod); + kDbgAssertPtrReturn(pSym, KERR_INVALID_POINTER); + return pMod->pOps->pfnQuerySymbol(pMod, iSegment, off, pSym); +} + + +/** + * Gets & allocates a symbol by segment:offset. + * This will be approximated to the nearest symbol if there is no exact match. + * + * @returns IPRT status code. + * @param pMod The module. + * @param iSegment The segment this offset is relative to. + * The -1 segment is special, it means that the addres is relative to + * the image base. The image base is where the first bit of the image + * is mapped during load. + * @param off The offset into the segment. + * @param ppSym Where to store the pointer to the symbol info. + * Free the returned symbol using kDbgSymbolFree(). + */ +KDBG_DECL(int) kDbgModuleQuerySymbolA(PKDBGMOD pMod, KI32 iSegment, KDBGADDR off, PPKDBGSYMBOL ppSym) +{ + kDbgAssertPtrReturn(ppSym, KERR_INVALID_POINTER); + + KDBGSYMBOL Sym; + int rc = kDbgModuleQuerySymbol(pMod, iSegment, off, &Sym); + if (!rc) + { + *ppSym = kDbgSymbolDup(&Sym); + if (!*ppSym) + rc = KERR_NO_MEMORY; + } + else + *ppSym = NULL; + return rc; +} + + +/** + * Gets a line number entry by segment:offset. + * This will be approximated to the nearest line number there is no exact match. + * + * @returns IPRT status code. + * @param pMod The module. + * @param iSegment The segment this offset is relative to. + * The -1 segment is special, it means that the addres is relative to + * the image base. The image base is where the first bit of the image + * is mapped during load. + * @param off The offset into the segment. + * @param pLine Where to store the line number details. + */ +KDBG_DECL(int) kDbgModuleQueryLine(PKDBGMOD pMod, KI32 iSegment, KDBGADDR off, PKDBGLINE pLine) +{ + KDBGMOD_VALIDATE(pMod); + kDbgAssertPtrReturn(pLine, KERR_INVALID_POINTER); + return pMod->pOps->pfnQueryLine(pMod, iSegment, off, pLine); +} + + +/** + * Gets & allocates a line number entry by segment:offset. + * This will be approximated to the nearest line number there is no exact match. + * + * @returns IPRT status code. + * @param pMod The module. + * @param iSegment The segment this offset is relative to. + * The -1 segment is special, it means that the addres is relative to + * the image base. The image base is where the first bit of the image + * is mapped during load. + * @param off The offset into the segment. + * @param ppLine Where to store the pointer to the line number info. + * Free the returned line number using kDbgLineFree(). + */ +KDBG_DECL(int) kDbgModuleQueryLineA(PKDBGMOD pMod, KI32 iSegment, KDBGADDR off, PPKDBGLINE ppLine) +{ + kDbgAssertPtrReturn(ppLine, KERR_INVALID_POINTER); + + KDBGLINE Line; + int rc = kDbgModuleQueryLine(pMod, iSegment, off, &Line); + if (!rc) + { + *ppLine = kDbgLineDup(&Line); + if (!*ppLine) + rc = KERR_NO_MEMORY; + } + else + *ppLine = NULL; + return rc; +} + diff --git a/src/lib/kStuff/kDbg/kDbgSpace.cpp b/src/lib/kStuff/kDbg/kDbgSpace.cpp new file mode 100644 index 0000000..2d6f1c0 --- /dev/null +++ b/src/lib/kStuff/kDbg/kDbgSpace.cpp @@ -0,0 +1,192 @@ +/* $Id: kDbgSpace.cpp 29 2009-07-01 20:30:29Z bird $ */ +/** @file + * kDbg - The Debug Info Reader, Address Space Manager. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "kDbgInternal.h" +#include +#include +#include + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** Pointer to a name space module. */ +typedef struct KDBGSPACEMOD *PKDBGSPACEMOD; + +/** + * Tracks a module segment in the address space. + * + * These segments are organized in two trees, by address in the + * KDBGSPACE::pSegRoot tree and by selector value in the + * KDBGSPACE::pSegSelRoot tree. + * + * While the debug module reader could easily provide us with + * segment names and it could perhaps be interesting to lookup + * a segment by its name in some situations, this has been + * considered too much bother for now. :-) + */ +typedef struct KDBGSPACESEG +{ + /** The module segment index. */ + KI32 iSegment; + /** The address space module structure this segment belongs to. */ + PKDBGSPACEMOD pSpaceMod; +} KDBGSPACESEG; +typedef KDBGSPACESEG *PKDBGSPACESEG; + + +/** + * Track a module in the name space. + * + * Each module in the address space can be addressed efficiently + * by module name. The module name has to be unique. + */ +typedef struct KDBGSPACEMOD +{ + /** The module name hash. */ + KU32 u32Hash; + /** The length of the module name. */ + KU32 cchName; + /** The module name. */ + char *pszName; + /** The next module in the same bucket. */ + PKDBGSPACEMOD pNext; + /** Pointer to the debug module reader. */ + PKDBGMOD pMod; + /** The number of segments. */ + KU32 cSegs; + /** The segment array. (variable length) */ + KDBGSPACESEG aSegs[1]; +} KDBGSPACEMOD; + + +typedef struct KDBGCACHEDSYM *PKDBGCACHEDSYM; +/** + * A cached symbol. + */ +typedef struct KDBGCACHEDSYM +{ + /** The symbol name hash. */ + KU32 u32Hash; + /** The next symbol in the same bucket. */ + PKDBGCACHEDSYM pNext; + /** The next symbol belonging to the same module as this. */ + PKDBGCACHEDSYM pNextMod; + /** The cached symbol information. */ + KDBGSYMBOL Sym; +} KDBGCACHEDSYM; + + +/** + * A symbol cache. + */ +typedef struct KDBGSYMCACHE +{ + /** The symbol cache magic. (KDBGSYMCACHE_MAGIC) */ + KU32 u32Magic; + /** The maximum number of symbols.*/ + KU32 cMax; + /** The current number of symbols.*/ + KU32 cCur; + /** The number of hash buckets. */ + KU32 cBuckets; + /** The address lookup tree. */ + PKDBGADDRAVL pAddrTree; + /** Array of hash buckets. + * The size is selected according to the cache size. */ + PKDBGCACHEDSYM *paBuckets[1]; +} KDBGSYMCACHE; +typedef KDBGSYMCACHE *PKDBGSYMCACHE; + + +/** + * A user symbol record. + * + * The user symbols are organized in the KDBGSPACE::pUserRoot tree + * and form an overlay that overrides the debug info retrieved from + * the KDBGSPACE::pSegRoot tree. + * + * In the current implementation the user symbols are unique and + * one would have to first delete a symbol in order to add another + * at the same address. This may be changed later, perhaps. + */ +typedef struct KDBGSPACEUSERSYM +{ + +} KDBGSPACEUSERSYM; +typedef KDBGSPACEUSERSYM *PKDBGSPACEUSERSYM; + + + +/** + * Address space. + */ +typedef struct KDBGSPACE +{ + /** The addresspace magic. (KDBGSPACE_MAGIC) */ + KU32 u32Magic; + /** User defined address space identifier or data pointer. */ + KUPTR uUser; + /** The name of the address space. (Optional) */ + const char *pszName; + + +} KDBGSPACE; +/** Pointer to an address space. */ +typedef struct KDBGSPACE *PKDBGSPACE; +/** Pointer to an address space pointer. */ +typedef PKDBGSPACE *PPKDBGSPACE; + + +KDBG_DECL(int) kDbgSpaceCreate(PPDBGSPACE ppSpace, KDBGADDR LowAddr, DBGADDR HighAddr, + KUPTR uUser, const char *pszName) +{ + /* + * Validate input. + */ + kDbgAssertPtrReturn(ppSpace); + *ppSpace = NULL; + kDbgAssertPtrNullReturn(pszName); + kDbgAssertReturn(LowAddr < HighAddr); + + /* + * Create and initialize the address space. + */ + PKDBGSPACE pSpace = (PKDBGSPACE)kHlpAlloc(sizeof(*pSpace)); + if (!pSpace) + return KERR_NO_MEMORY; + pSpace->u32Magic = KDBGSPACE_MAGIC; + pSpace->uUser = uUser; + pSpace->pszName = pszName; + +} diff --git a/src/lib/kStuff/kDbg/kDbgSymbol.cpp b/src/lib/kStuff/kDbg/kDbgSymbol.cpp new file mode 100644 index 0000000..d542807 --- /dev/null +++ b/src/lib/kStuff/kDbg/kDbgSymbol.cpp @@ -0,0 +1,78 @@ +/* $Id: kDbgSymbol.cpp 29 2009-07-01 20:30:29Z bird $ */ +/** @file + * kDbg - The Debug Info Reader, Symbols. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "kDbgInternal.h" +#include + + +/** + * Duplicates a symbol. + * + * To save heap space, the returned symbol will not own more heap space than + * it strictly need to. So, it's not possible to append stuff to the symbol + * or anything of that kind. + * + * @returns Pointer to the duplicate. + * This must be freed using kDbgSymbolFree(). + * @param pSymbol The symbol to be duplicated. + */ +KDBG_DECL(PKDBGSYMBOL) kDbgSymbolDup(PCKDBGSYMBOL pSymbol) +{ + kDbgAssertPtrReturn(pSymbol, NULL); + KSIZE cb = K_OFFSETOF(KDBGSYMBOL, szName[pSymbol->cchName + 1]); + PKDBGSYMBOL pNewSymbol = (PKDBGSYMBOL)kHlpDup(pSymbol, cb); + if (pNewSymbol) + pNewSymbol->cbSelf = cb; + return pNewSymbol; +} + + +/** + * Frees a symbol obtained from the kDbg API. + * + * @returns 0 on success. + * @returns KERR_INVALID_POINTER if pSymbol isn't a valid pointer. + * + * @param pSymbol The symbol to be freed. The null pointer is ignored. + */ +KDBG_DECL(int) kDbgSymbolFree(PKDBGSYMBOL pSymbol) +{ + if (!pSymbol) + { + kDbgAssertPtrReturn(pSymbol, KERR_INVALID_POINTER); + pSymbol->cbSelf = 0; + kHlpFree(pSymbol); + } + return 0; +} + -- cgit v1.2.3