/*++

Copyright (c) 1995  Microsoft Corporation

Module Name:

    symbolsp.c

Abstract:

    This function implements a generic simple symbol handler.

Author:

    Wesley Witt (wesw) 1-Sep-1994

Environment:

    User Mode

--*/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntldr.h>
#include "private.h"
#include "symbols.h"
#include "globals.h"
#include "tlhelp32.h"

#include "fecache.hpp"

typedef BOOL   (WINAPI *PMODULE32)(HANDLE, LPMODULEENTRY32);
typedef HANDLE (WINAPI *PCREATE32SNAPSHOT)(DWORD, DWORD);

typedef ULONG (NTAPI *PRTLQUERYPROCESSDEBUGINFORMATION)(HANDLE,ULONG,PRTL_DEBUG_INFORMATION);
typedef PRTL_DEBUG_INFORMATION (NTAPI *PRTLCREATEQUERYDEBUGBUFFER)(ULONG,BOOLEAN);
typedef NTSTATUS (NTAPI *PRTLDESTROYQUERYDEBUGBUFFER)(PRTL_DEBUG_INFORMATION);
typedef NTSTATUS (NTAPI *PNTQUERYSYSTEMINFORMATION)(SYSTEM_INFORMATION_CLASS,PVOID,ULONG,PULONG);
typedef ULONG (NTAPI *PRTLNTSTATUSTODOSERROR)(NTSTATUS);
//typedef NTSTATUS (NTAPI *PNTQUERYINFORMATIONPROCESS)(UINT_PTR,PROCESSINFOCLASS,UINT_PTR,ULONG,UINT_PTR);
typedef NTSTATUS (NTAPI *PNTQUERYINFORMATIONPROCESS)(HANDLE,PROCESSINFOCLASS,PVOID,ULONG,PULONG);

DWORD_PTR Win95GetProcessModules(HANDLE, PINTERNAL_GET_MODULE ,PVOID);
DWORD_PTR NTGetProcessModules(HANDLE, PINTERNAL_GET_MODULE ,PVOID);
DWORD64 miGetModuleBase(HANDLE hProcess, DWORD64 Address);

// private version of qsort used to avoid compat problems on NT4 and win2k.
// code is published from base\crts
extern
void __cdecl dbg_qsort(void *, size_t, size_t,
                       int (__cdecl *) (const void *, const void *));


typedef struct _SYMBOL_INFO_LOOKUP {
    ULONG       Segment;
    ULONG64     Offset;
    PCHAR       NamePtr;
    SYMBOL_INFO SymInfo;
} SYMBOL_INFO_LOOKUP;


BOOL
LoadSymbols(
    HANDLE        hp,
    PMODULE_ENTRY mi,
    DWORD         flags
    )
{
    if (flags & LS_JUST_TEST) {
        if ((mi->Flags & MIF_DEFERRED_LOAD) && !(mi->Flags & MIF_NO_SYMBOLS))
            return FALSE;
        else
            return TRUE;
    }

    if (flags & LS_QUALIFIED) {
        if (g.SymOptions & SYMOPT_NO_UNQUALIFIED_LOADS) {
            if ((mi->Flags & MIF_DEFERRED_LOAD) && !(mi->Flags & MIF_NO_SYMBOLS))
                return FALSE;
        }
    }

    if ((mi->Flags & MIF_DEFERRED_LOAD) && !(mi->Flags & MIF_NO_SYMBOLS))
        return load(hp, mi);
    else if (flags & LS_FAIL_IF_LOADED)
        return FALSE;

    return TRUE;
}


//
// Get the address form section no and offset in a PE file
//
ULONG
GetAddressFromOffset(
    PMODULE_ENTRY mi,
    ULONG         section,
    ULONG64       Offset,
    PULONG64      pAddress
    )
{
    ULONG Bias;
    if (section > mi->NumSections
        || !pAddress
        || !section
        || !mi
        )
    {
        // Invalid !!
        return FALSE;
    }

    *pAddress = mi->BaseOfDll + mi->OriginalSectionHdrs[section-1].VirtualAddress + Offset;
    *pAddress = ConvertOmapFromSrc( mi, *pAddress, &Bias );
    if (*pAddress) {
        *pAddress += Bias;
    }
    return TRUE;
}

/*
 * GetSymbolInfo
 *         This extracts useful information from a CV SYMBOl record into a generic
 *         SYMBOL_ENTRY structure.
 *
 *
 */
ULONG
GetSymbolInfo(
    PMODULE_ENTRY me,
    PCHAR         pRawSym,
    SYMBOL_INFO_LOOKUP  *pSymEntry
    )
{
    PCHAR SymbolInfo = pRawSym;
    ULONG symIndex, typeIndex=0, segmentNum=0;
    ULONG64 Offset=0, Address=0, Value=0;
//  ULONG Register=0, bpRel=0, BaseReg=0;
    BOOL HasAddr=FALSE, HasValue=FALSE;
    PSYMBOL_INFO pSymInfo = &pSymEntry->SymInfo;

    if ((pRawSym != NULL) && (pSymEntry != NULL)) {

        SymbolInfo = (PCHAR) pRawSym;
        typeIndex = 0;
        symIndex = ((SYMTYPE *) (pRawSym))->rectyp;
        ZeroMemory(pSymEntry, sizeof(SYMBOL_INFO));

        pSymInfo->ModBase = me->BaseOfDll;


#define ExtractSymName(from) (pSymEntry->NamePtr = ((PCHAR) from) + 1); pSymInfo->NameLen = (UCHAR) *((PUCHAR) from);
        switch (symIndex) {
        case S_COMPILE : // 0x0001   Compile flags symbol
        case S_REGISTER_16t : { // 0x0002   Register variable
            break;
        }
        case S_CONSTANT_16t : { // 0x0003   constant symbol
            DWORD len=4;
            CONSTSYM_16t *constSym;

            constSym = (CONSTSYM_16t *) SymbolInfo;
            typeIndex = constSym->typind;
//            GetNumericValue((PCHAR)&constSym->value, &Value, &len);

            pSymInfo->Flags |= IMAGEHLP_SYMBOL_INFO_VALUEPRESENT;
            pSymInfo->Value = Value;
            ExtractSymName((constSym->name + len));
            break;
        }
        case S_UDT_16t : { // 0x0004   User defined type
            UDTSYM_16t *udtSym;

            udtSym = (UDTSYM_16t *) SymbolInfo;
            typeIndex = udtSym->typind;
            ExtractSymName(udtSym->name); // strncpy(name, (PCHAR)symReturned + 7, (UCHAR) symReturned[6]);
            break;
        }
        case S_SSEARCH : // 0x0005   Start Search
        case S_END : // 0x0006   Block, procedure, "with" or thunk end
        case S_SKIP : // 0x0007   Reserve symbol space in $$Symbols table
        case S_CVRESERVE : // 0x0008   Reserved symbol for CV internal use
        case S_OBJNAME : // 0x0009   path to object file name
        case S_ENDARG : // 0x000a   end of argument/return list
        case S_COBOLUDT_16t : // 0x000b   special UDT for cobol that does not symbol pack
        case S_MANYREG_16t : // 0x000c   multiple register variable
        case S_RETURN : // 0x000d   return description symbol
        case S_ENTRYTHIS : // 0x000e   description of this pointer on entry
            break;

        case S_BPREL16 : // 0x0100   BP-relative
        case S_LDATA16 : // 0x0101   Module-local symbol
        case S_GDATA16 : // 0x0102   Global data symbol
        case S_PUB16 : // 0x0103   a public symbol
        case S_LPROC16 : // 0x0104   Local procedure start
        case S_GPROC16 : // 0x0105   Global procedure start
        case S_THUNK16 : // 0x0106   Thunk Start
        case S_BLOCK16 : // 0x0107   block start
        case S_WITH16 : // 0x0108   with start
        case S_LABEL16 : // 0x0109   code label
        case S_CEXMODEL16 : // 0x010a   change execution model
        case S_VFTABLE16 : // 0x010b   address of virtual function table
        case S_REGREL16 : // 0x010c   register relative address
        case S_BPREL32_16t : { // 0x0200   BP-relative
            break;
        }
        case S_LDATA32_16t :// 0x0201   Module-local symbol
        case S_GDATA32_16t :// 0x0202   Global data symbol
        case S_PUB32_16t : { // 0x0203   a public symbol (CV internal reserved)
            DATASYM32_16t *pData;

            pData = (DATASYM32_16t *) SymbolInfo;
            typeIndex = pData->typind;
            Offset = pData->off; segmentNum = pData->seg;
            HasAddr = TRUE;
            ExtractSymName(pData->name);
            // strncpy(name, (PCHAR)&pData->name[1], (UCHAR) pData->name[0]);
            break;
        }
        case S_LPROC32_16t : // 0x0204   Local procedure start
        case S_GPROC32_16t : { // 0x0205   Global procedure start
            PROCSYM32_16t *procSym;

            procSym = (PROCSYM32_16t *)SymbolInfo;
            // CONTEXT-SENSITIVE
            // Offset = procSym->off; segmentNum = procSym->seg;
            typeIndex = procSym->typind;
            ExtractSymName(procSym->name);
            // strncpy(name, (PCHAR)symReturned + 36, (UCHAR) symReturned[35]);
            break;
        }
        case S_THUNK32 : // 0x0206   Thunk Start
        case S_BLOCK32 : // 0x0207   block start
        case S_WITH32 : // 0x0208   with start
        case S_LABEL32 : // 0x0209   code label
        case S_CEXMODEL32 : // 0x020a   change execution model
        case S_VFTABLE32_16t : // 0x020b   address of virtual function table
        case S_REGREL32_16t : // 0x020c   register relative address
        case S_LTHREAD32_16t : // 0x020d   local thread storage
        case S_GTHREAD32_16t : // 0x020e   global thread storage
        case S_SLINK32 : // 0x020f   static link for MIPS EH implementation
        case S_LPROCMIPS_16t : // 0x0300   Local procedure start
        case S_GPROCMIPS_16t : { // 0x0301   Global procedure start
            break;
        }

        case S_PROCREF : { // 0x0400   Reference to a procedure
            // typeIndex = ((PDWORD) symReturned) + 3;
            // strncpy(name, symReturned + 13, (char) *(symReturned+12));
            break;
        }
        case S_DATAREF : // 0x0401   Reference to data
        case S_ALIGN : // 0x0402   Used for page alignment of symbols
        case S_LPROCREF : // 0x0403   Local Reference to a procedure

            // sym records with 32-bit types embedded instead of 16-bit
            // all have  0x1000 bit set for easy identification
            // only do the 32-bit target versions since we don't really
            // care about 16-bit ones anymore.
        case S_TI16_MAX : // 0x1000,
            break;

        case S_REGISTER : { // 0x1001   Register variable
            REGSYM *regSym;

            regSym             = (REGSYM *)SymbolInfo;
            typeIndex          = regSym->typind;
            pSymInfo->Flags    = IMAGEHLP_SYMBOL_INFO_REGISTER;
            LookupRegID((DWORD)regSym->reg, me->MachineType, &pSymInfo->Register);
            ExtractSymName(regSym->name);
            break;
        }

        case S_CONSTANT : { // 0x1002   constant symbol
            CONSTSYM *constSym;
            DWORD len=4, val;

            constSym = (CONSTSYM *) SymbolInfo;
//            GetNumericValue((PCHAR)&constSym->value, &Value, &len);

            pSymInfo->Flags |= IMAGEHLP_SYMBOL_INFO_VALUEPRESENT;
            pSymInfo->Value = Value;
            typeIndex = constSym->typind;
            ExtractSymName((constSym->name+len));
            break;
        }
        case S_UDT : { // 0x1003   User defined type
            UDTSYM *udtSym;

            udtSym = (UDTSYM *) SymbolInfo;
            typeIndex = udtSym->typind;
            ExtractSymName(udtSym->name);
            break;
        }

        case S_COBOLUDT : // 0x1004   special UDT for cobol that does not symbol pack
            break;

        case S_MANYREG : // 0x1005   multiple register variable
#if 0
typedef struct MANYREGSYM {
    unsigned short  reclen;     // Record length
    unsigned short  rectyp;     // S_MANYREG
    CV_typ_t        typind;     // Type index
    unsigned char   count;      // count of number of registers
    unsigned char   reg[1];     // count register enumerates followed by
                                // length-prefixed name.  Registers are
                                // most significant first.
} MANYREGSYM;

typedef struct MANYREGSYM2 {
    unsigned short  reclen;     // Record length
    unsigned short  rectyp;     // S_MANYREG2
    CV_typ_t        typind;     // Type index
    unsigned short  count;      // count of number of registers
    unsigned short  reg[1];     // count register enumerates followed by
                                // length-prefixed name.  Registers are
                                // most significant first.
} MANYREGSYM2;
#endif
            break;

        case S_BPREL32 : { // 0x1006   BP-relative
            BPRELSYM32 *bprelSym;

            bprelSym = (BPRELSYM32 *)SymbolInfo;
            typeIndex = bprelSym->typind;
            pSymInfo->Flags   = IMAGEHLP_SYMBOL_INFO_FRAMERELATIVE;
            pSymInfo->Address = bprelSym->off;
            ExtractSymName(bprelSym->name);
            break;
        }

        case S_LDATA32 : // 0x1007   Module-local symbol
        case S_GDATA32 : // 0x1008   Global data symbol
        case S_PUB32 : { // 0x1009   a public symbol (CV internal reserved)
            DATASYM32 *dataSym;

            dataSym = (DATASYM32 *)SymbolInfo;
            HasAddr = TRUE;
            Offset = dataSym->off; segmentNum = dataSym->seg;
            typeIndex = dataSym->typind; //(PDWORD) symReturned;
            ExtractSymName(dataSym->name);  // strncpy(name, (PCHAR)symReturned+11, (UCHAR) symReturned[10]);

            break;
        }
        case S_LPROC32 :  // 0x100a   Local procedure start
        case S_GPROC32 : { // 0x100b   Global procedure start
            PROCSYM32 *procSym;

            procSym = (PROCSYM32 *) SymbolInfo;
            // CONTEXT-SENSITIVE
            HasAddr = TRUE;
            Offset = procSym->off; segmentNum = procSym->seg;
            typeIndex = procSym->typind;
            ExtractSymName(procSym->name);
            break;
        }

        case S_VFTABLE32 : // 0x100c   address of virtual function table
            break;

        case S_REGREL32 : { // 0x100d   register relative address
            REGREL32 *regrelSym;

            regrelSym = (REGREL32 *)SymbolInfo;
            typeIndex = regrelSym->typind;
            pSymInfo->Flags   = IMAGEHLP_SYMBOL_INFO_REGRELATIVE;
            pSymInfo->Address = regrelSym->off;
            LookupRegID((DWORD)regrelSym->reg, me->MachineType, &pSymInfo->Register);
            ExtractSymName(regrelSym->name);
            break;
        }

        case S_LTHREAD32 : // 0x100e   local thread storage
        case S_GTHREAD32 : // 0x100f   global thread storage
        case S_LPROCMIPS : // 0x1010   Local procedure start
        case S_GPROCMIPS : // 0x1011   Global procedure start
        case S_FRAMEPROC : // 0x1012   extra frame and proc information
        case S_COMPILE2 : // 0x1013   extended compile flags and info
        case S_MANYREG2 : // 0x1014   multiple register variable
        case S_LPROCIA64 : // 0x1015   Local procedure start (IA64)
        case S_GPROCIA64 : // 0x1016   Global procedure start (IA64)
        case S_RECTYPE_MAX :
        default:
            return FALSE;
        } /* switch */


        if (HasAddr && GetAddressFromOffset(me, segmentNum, Offset, &Address)) {
            pSymInfo->Address   = Address;
        }

        pSymInfo->TypeIndex = typeIndex;
        pSymEntry->Offset   = Offset;
        pSymEntry->Segment  = segmentNum;

    } else {
        return FALSE;
    }

    return TRUE;
}

/*
 * cvExtractSymbolInfo
 *         This extracts useful information from a CV SYMBOl record into a generic
 *         SYMBOL_ENTRY structure.
 *
 *
 */
ULONG
cvExtractSymbolInfo(
    PMODULE_ENTRY me,
    PCHAR         pRawSym,
    PSYMBOL_ENTRY pSymEntry,
    BOOL          fCopyName
    )
{
    SYMBOL_INFO_LOOKUP SymInfoLookup={0};
    ULONG reg;

    pSymEntry->Size       = 0;
    pSymEntry->Flags      = 0;
    pSymEntry->Address    = 0;
    if (fCopyName)
        *pSymEntry->Name  = 0;
    else
        pSymEntry->Name   = 0;
    pSymEntry->NameLength = 0;
    pSymEntry->Segment    = 0;
    pSymEntry->Offset     = 0;
    pSymEntry->TypeIndex  = 0;
    pSymEntry->ModBase    = 0;

    if (GetSymbolInfo(me, pRawSym, &SymInfoLookup)) {
        LARGE_INTEGER li;
        pSymEntry->NameLength = SymInfoLookup.SymInfo.NameLen;
        pSymEntry->TypeIndex  = SymInfoLookup.SymInfo.TypeIndex;
        pSymEntry->Offset  = SymInfoLookup.Offset;
        pSymEntry->Segment = SymInfoLookup.Segment;
        pSymEntry->ModBase = me->BaseOfDll;
        // NOTE: this was implented as a mask - but used differently
        switch (SymInfoLookup.SymInfo.Flags)
        {
        case IMAGEHLP_SYMBOL_INFO_REGISTER:
            pSymEntry->Flags = SYMF_REGISTER;
            pSymEntry->Address = SymInfoLookup.SymInfo.Register;
            break;

        case IMAGEHLP_SYMBOL_INFO_REGRELATIVE:
            // DBGHELP_HACK - HiPart of Addr = RegId , LowPart = Pffset
            pSymEntry->Flags = SYMF_REGREL;
            //LookupRegID((DWORD)SymInfoLookup.SymInfo.Register, me->MachineType, &pSymEntry->Segment);
            li.LowPart         = (ULONG) SymInfoLookup.SymInfo.Address;
            li.HighPart        = SymInfoLookup.SymInfo.Register;
            pSymEntry->Segment = SymInfoLookup.SymInfo.Register;
            pSymEntry->Address = li.QuadPart;
            break;

        case IMAGEHLP_SYMBOL_INFO_FRAMERELATIVE:
            pSymEntry->Flags = SYMF_FRAMEREL;
            pSymEntry->Address = SymInfoLookup.SymInfo.Address;
            break;

        case IMAGEHLP_SYMBOL_INFO_VALUEPRESENT:
        default:
            pSymEntry->Address = SymInfoLookup.SymInfo.Address;
            break;
        }
        if (fCopyName) {
            if (!pSymEntry->Name)
                return FALSE;
            *pSymEntry->Name = 0;
            strncpy(pSymEntry->Name, SymInfoLookup.NamePtr ? SymInfoLookup.NamePtr : "", SymInfoLookup.SymInfo.NameLen);
        } else {
            pSymEntry->Name = SymInfoLookup.NamePtr;
        }
        return TRUE;
    }

    return FALSE;
}

DWORD_PTR
NTGetPID(
    HANDLE hProcess
    )
{
    HMODULE hModule;
    PNTQUERYINFORMATIONPROCESS NtQueryInformationProcess;
    PROCESS_BASIC_INFORMATION pi;
    NTSTATUS status;

    hModule = GetModuleHandle( "ntdll.dll" );
    if (!hModule) {
        return ERROR_MOD_NOT_FOUND;
    }

    NtQueryInformationProcess = (PNTQUERYINFORMATIONPROCESS)GetProcAddress(
        hModule,
        "NtQueryInformationProcess"
        );

    if (!NtQueryInformationProcess) {
        return ERROR_INVALID_FUNCTION;
    }


    status = NtQueryInformationProcess(hProcess,
                                       ProcessBasicInformation,
                                       &pi,
                                       sizeof(pi),
                                       NULL);

    if (!NT_SUCCESS(status))
        return 0;

    return pi.UniqueProcessId;
}


//
// the block bounded by the #ifdef _X86_ statement
// contains the code for getting the PID from an
// HPROCESS when running under Win9X
//

#ifdef _X86_

#define HANDLE_INVALID                 ((HANDLE)0xFFFFFFFF)
#define HANDLE_CURRENT_PROCESS   ((HANDLE)0x7FFFFFFF)
#define HANDLE_CURRENT_THREAD    ((HANDLE)0xFFFFFFFE)
#define MAX_HANDLE_VALUE         ((HANDLE)0x00FFFFFF)


// Thread Information Block.

typedef struct _TIB {

    DWORD     unknown[12];
    DWORD_PTR ppdb;

} TIB, *PTIB;

// Task Data Block

typedef struct _TDB {

    DWORD unknown[2];
    TIB         tib;

} TDB, *PTDB;

typedef struct _OBJ {

    BYTE    typObj;             // object type
    BYTE    objFlags;           // object flags
    WORD    cntUses;            // count of this objects usage

} OBJ, *POBJ;

typedef struct _HTE {

    DWORD   flFlags;
    POBJ    pobj;

} HTE, *PHTE;

typedef struct _HTB {

    DWORD   chteMax;
    HTE     rghte[1];

} HTB, *PHTB;

typedef struct _W9XPDB {

    DWORD  unknown[17];
    PHTB   phtbHandles;

} W9XPDB, *PW9XPDB;

#pragma warning(disable:4035)

_inline struct _TIB * GetCurrentTib(void) { _asm mov eax, fs:[0x18] }

// stuff needed to convert local handle

#define IHTETOHANDLESHIFT  2
#define GLOBALHANDLEMASK  (0x453a4d3cLU)

#define IHTEFROMHANDLE(hnd) ((hnd) == HANDLE_INVALID ? (DWORD)(hnd) : (((DWORD)(hnd)) >> IHTETOHANDLESHIFT))

#define IHTEISGLOBAL(ihte) \
        (((ihte) >> (32 - 8 - IHTETOHANDLESHIFT)) == (((DWORD)GLOBALHANDLEMASK) >> 24))

#define IS_WIN32_PREDEFINED_HANDLE(hnd) \
        ((hnd == HANDLE_CURRENT_PROCESS)||(hnd == HANDLE_CURRENT_THREAD)||(hnd == HANDLE_INVALID))

DWORD
GetWin9xObsfucator(
  VOID
  )
/*++

Routine Description:

  GetWin9xObsfucator()


Arguments:

  none


Return Value:

  Obsfucator key used by Windows9x to hide Process and Thread Id's


Notes:

  The code has only been tested on Windows98SE and Millennium.


--*/
{
    DWORD ppdb       = 0;      // W9XPDB = Process Data Block
    DWORD processId  = (DWORD) GetCurrentProcessId();

    // get PDB pointer

    ppdb = GetCurrentTib()->ppdb;

    return ppdb ^ processId;
}


DWORD_PTR
GetPtrFromHandle(
  IN HANDLE Handle
  )
/*++

Routine Description:

  GetPtrFromHandle()


Arguments:

  Handle - handle from Process handle table


Return Value:

  Real Pointer to object


Notes:

  The code has only been tested on Windows98SE and Millennium.


--*/
{
    DWORD_PTR ptr  = 0;
    DWORD     ihte = 0;
    PW9XPDB   ppdb = 0;

    ppdb = (PW9XPDB) GetCurrentTib()->ppdb;

    // check for pre-defined handle values.

    if (Handle == HANDLE_CURRENT_PROCESS) {
        ptr = (DWORD_PTR) ppdb;
    } else if (Handle == HANDLE_CURRENT_THREAD) {
        ptr = (DWORD_PTR) CONTAINING_RECORD(GetCurrentTib(), TDB, tib);
    } else if (Handle == HANDLE_INVALID) {
        ptr = 0;
    } else {
        // not a special handle, we can perform our magic.

        ihte = IHTEFROMHANDLE(Handle);

        // if we have a global handle, it is only meaningful in the context
        // of the kernel process's handle table...we don't currently deal with
        // this type of handle

        if (!(IHTEISGLOBAL(ihte))) {
            ptr = (DWORD_PTR) ppdb->phtbHandles->rghte[ihte].pobj;
        }
    }

    return ptr;
}


DWORD_PTR
Win9xGetPID(
  IN HANDLE hProcess
  )
/*++

Routine Description:

  Win9xGetPid()


Arguments:

  hProcess - Process handle


Return Value:

  Process Id


Notes:

  The code has only been tested on Windows98SE and Millennium.


--*/
{
    static DWORD dwObsfucator = 0;

    // check to see that we have a predefined handle or an index into
    // our local handle table.

    if (IS_WIN32_PREDEFINED_HANDLE(hProcess) || (hProcess < MAX_HANDLE_VALUE)) {
        if (!dwObsfucator) {
            dwObsfucator = GetWin9xObsfucator();
            assert(dwObsfucator != 0);
        }
        return dwObsfucator ^ GetPtrFromHandle(hProcess);
    }

    // don't know what we have here

    return 0;
}

#endif // _X86_


DWORD_PTR
GetPID(
    HANDLE hProcess
    )
{
    OSVERSIONINFO VerInfo;

    if (hProcess == GetCurrentProcess())
        return GetCurrentProcessId();

    VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx(&VerInfo);
    if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
        return NTGetPID(hProcess);
    } else {
#ifdef _X86_
        return Win9xGetPID(hProcess);
#else
        return 0;
#endif
    }
}


PMODULE_ENTRY
GetModFromAddr(
    PPROCESS_ENTRY    pe,
    IN  DWORD64       addr
    )
{
    PMODULE_ENTRY mi = NULL;

    __try {
        mi = GetModuleForPC(pe, addr, FALSE);
        if (!mi) {
            SetLastError(ERROR_MOD_NOT_FOUND);
            return NULL;
        }

        if (!LoadSymbols(pe->hProcess, mi, 0)) {
            SetLastError(ERROR_MOD_NOT_FOUND);
            return NULL;
        }

    } __except (EXCEPTION_EXECUTE_HANDLER) {

        ImagepSetLastErrorFromStatus(GetExceptionCode());
        return NULL;
    }

    return mi;
}


DWORD
GetProcessModules(
    HANDLE                  hProcess,
    PINTERNAL_GET_MODULE    InternalGetModule,
    PVOID                   Context
    )
{
#ifdef _X86_
    OSVERSIONINFO VerInfo;

    VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx(&VerInfo);
    if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
        return NTGetProcessModules(hProcess, InternalGetModule, Context);
    } else {
        return Win95GetProcessModules(hProcess, InternalGetModule, Context);
    }
}


DWORD
Win95GetProcessModules(
    HANDLE                  hProcess,
    PINTERNAL_GET_MODULE    InternalGetModule,
    PVOID                   Context
    )
{
    MODULEENTRY32 mi;
    PMODULE32     pModule32Next, pModule32First;
    PCREATE32SNAPSHOT pCreateToolhelp32Snapshot;
    HANDLE hSnapshot;
    HMODULE hToolHelp;
    DWORD pid;

    // get the PID:
    // this hack supports old bug workaround, in which callers were passing
    // a pid, because an hprocess didn't work on W9X.

    pid = GetPID(hProcess);
    if (!pid)
        pid = (DWORD)hProcess;

    // get the module list from toolhelp apis

    hToolHelp = GetModuleHandle("kernel32.dll");
    if (!hToolHelp)
        return ERROR_MOD_NOT_FOUND;

    pModule32Next = (PMODULE32)GetProcAddress(hToolHelp, "Module32Next");
    pModule32First = (PMODULE32)GetProcAddress(hToolHelp, "Module32First");
    pCreateToolhelp32Snapshot = (PCREATE32SNAPSHOT)GetProcAddress(hToolHelp, "CreateToolhelp32Snapshot");
    if (!pModule32Next || !pModule32First || !pCreateToolhelp32Snapshot)
        return ERROR_MOD_NOT_FOUND;

    hSnapshot = pCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
    if (hSnapshot == (HANDLE)-1) {
        return ERROR_MOD_NOT_FOUND;
    }

    mi.dwSize = sizeof(MODULEENTRY32);

    if (pModule32First(hSnapshot, &mi)) {
        do
        {
            if (!InternalGetModule(
                    hProcess,
                    mi.szModule,
                    (DWORD) mi.modBaseAddr,
                    mi.modBaseSize,
                    Context))
            {
                break;
            }

        } while ( pModule32Next(hSnapshot, &mi) );
    }

    CloseHandle(hSnapshot);

    return(ERROR_SUCCESS);
}


DWORD
NTGetProcessModules(
    HANDLE                  hProcess,
    PINTERNAL_GET_MODULE    InternalGetModule,
    PVOID                   Context
    )
{

#endif      // _X86_

    PRTLQUERYPROCESSDEBUGINFORMATION    RtlQueryProcessDebugInformation;
    PRTLCREATEQUERYDEBUGBUFFER          RtlCreateQueryDebugBuffer;
    PRTLDESTROYQUERYDEBUGBUFFER         RtlDestroyQueryDebugBuffer;
    HMODULE                             hModule;
    NTSTATUS                            Status;
    PRTL_DEBUG_INFORMATION              Buffer;
    ULONG                               i;
    DWORD_PTR                           ProcessId;

    hModule = GetModuleHandle( "ntdll.dll" );
    if (!hModule) {
        return ERROR_MOD_NOT_FOUND;
    }

    RtlQueryProcessDebugInformation = (PRTLQUERYPROCESSDEBUGINFORMATION)GetProcAddress(
        hModule,
        "RtlQueryProcessDebugInformation"
        );

    if (!RtlQueryProcessDebugInformation) {
        return ERROR_INVALID_FUNCTION;
    }

    RtlCreateQueryDebugBuffer = (PRTLCREATEQUERYDEBUGBUFFER)GetProcAddress(
        hModule,
        "RtlCreateQueryDebugBuffer"
        );

    if (!RtlCreateQueryDebugBuffer) {
        return ERROR_INVALID_FUNCTION;
    }

    RtlDestroyQueryDebugBuffer = (PRTLDESTROYQUERYDEBUGBUFFER)GetProcAddress(
        hModule,
        "RtlDestroyQueryDebugBuffer"
        );

    if (!RtlDestroyQueryDebugBuffer) {
        return ERROR_INVALID_FUNCTION;
    }

    Buffer = RtlCreateQueryDebugBuffer( 0, FALSE );
    if (!Buffer) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    ProcessId = GetPID(hProcess);

    // for backwards compatibility with an old bug
    if (!ProcessId)
        ProcessId = (DWORD_PTR)hProcess;

    ULONG QueryFlags = RTL_QUERY_PROCESS_MODULES |
                       RTL_QUERY_PROCESS_NONINVASIVE;
                       
    if (g.SymOptions & SYMOPT_INCLUDE_32BIT_MODULES) {
        QueryFlags |= RTL_QUERY_PROCESS_MODULES32;
    }

    Status = RtlQueryProcessDebugInformation(
        (HANDLE)ProcessId,
        QueryFlags,
        Buffer
        );

    if (Status != STATUS_SUCCESS) {
        RtlDestroyQueryDebugBuffer( Buffer );
        return(ImagepSetLastErrorFromStatus(Status));
    }

    for (i=0; i<Buffer->Modules->NumberOfModules; i++) {
        PRTL_PROCESS_MODULE_INFORMATION Module = &Buffer->Modules->Modules[i];
        if (!InternalGetModule(
                hProcess,
                (LPSTR) &Module->FullPathName[Module->OffsetToFileName],
                (DWORD64)Module->ImageBase,
                (DWORD)Module->ImageSize,
                Context
                ))
        {
            break;
        }
    }

    RtlDestroyQueryDebugBuffer( Buffer );
    return ERROR_SUCCESS;
}


VOID
FreeModuleEntry(
    PPROCESS_ENTRY pe,
    PMODULE_ENTRY  mi
    )
{
    FunctionEntryCache* Cache;

    if (pe && (Cache = GetFeCache(mi->MachineType, FALSE))) {
        Cache->InvalidateProcessOrModule(pe->hProcess, mi->BaseOfDll);
    }
    if (pe && pe->ipmi == mi) {
        pe->ipmi = NULL;
    }
    if (mi->symbolTable) {
        MemFree( mi->symbolTable  );
    }
    if (mi->SectionHdrs) {
        MemFree( mi->SectionHdrs );
    }
    if (mi->OriginalSectionHdrs) {
        MemFree( mi->OriginalSectionHdrs );
    }
    if (mi->pFpoData) {
        VirtualFree( mi->pFpoData, 0, MEM_RELEASE );
    }
    if (mi->pFpoDataOmap) {
        VirtualFree( mi->pFpoDataOmap, 0, MEM_RELEASE );
    }
    if (mi->pExceptionData) {
        VirtualFree( mi->pExceptionData, 0, MEM_RELEASE );
    }
    if (mi->pPData) {
        MemFree( mi->pPData );
    }
    if (mi->pXData) {
        MemFree( mi->pXData );
    }
    if (mi->TmpSym.Name) {
        MemFree( mi->TmpSym.Name );
    }
    if (mi->ImageName) {
        MemFree( mi->ImageName );
    }
    if (mi->LoadedImageName) {
        MemFree( mi->LoadedImageName );
    }
    if (mi->LoadedPdbName) {
        MemFree( mi->LoadedPdbName );
    }
    if (mi->pOmapTo) {
        MemFree( mi->pOmapTo );
    }
    if (mi->pOmapFrom) {
        MemFree( mi->pOmapFrom );
    }
    if (mi->CallerData) {
        MemFree( mi->CallerData );
    }
    if (mi->SourceFiles) {
        PSOURCE_ENTRY Src, SrcNext;

        for (Src = mi->SourceFiles; Src != NULL; Src = SrcNext) {
            SrcNext = Src->Next;
            MemFree(Src);
        }
    }
    if (mi->dia) {
        diaRelease(mi->dia);
    }

    MemFree( mi );
}




BOOL
MatchSymbolName(
    PSYMBOL_ENTRY       sym,
    LPSTR               SymName
    )
{
    if (g.SymOptions & SYMOPT_CASE_INSENSITIVE) {
        if (_stricmp( sym->Name, SymName ) == 0) {
            return TRUE;
        }
    } else {
        if (strcmp( sym->Name, SymName ) == 0) {
            return TRUE;
        }
    }

    return FALSE;
}


PSYMBOL_ENTRY
HandleDuplicateSymbols(
    PPROCESS_ENTRY  pe,
    PMODULE_ENTRY   mi,
    PSYMBOL_ENTRY   sym
    )
{
    DWORD                       i;
    DWORD                       Dups;
    DWORD                       NameSize;
    PIMAGEHLP_SYMBOL64          Syms64 = NULL;
    PIMAGEHLP_SYMBOL          Syms32 = NULL;
    PIMAGEHLP_DUPLICATE_SYMBOL64 DupSym64 = NULL;
    PIMAGEHLP_DUPLICATE_SYMBOL DupSym32 = NULL;
    PULONG                      SymSave;


    if (!pe->pCallbackFunction32 && !pe->pCallbackFunction64) {
        return sym;
    }

    if (!(sym->Flags & SYMF_DUPLICATE)) {
        return sym;
    }

    Dups = 0;
    NameSize = 0;
    for (i = 0; i < mi->numsyms; i++) {
        if ((mi->symbolTable[i].NameLength == sym->NameLength) &&
            (strcmp( mi->symbolTable[i].Name, sym->Name ) == 0)) {
                Dups += 1;
                NameSize += (mi->symbolTable[i].NameLength + 1);
        }
    }

    if (pe->pCallbackFunction32) {
        DupSym32 = (PIMAGEHLP_DUPLICATE_SYMBOL) MemAlloc( sizeof(IMAGEHLP_DUPLICATE_SYMBOL) );
        if (!DupSym32) {
            return sym;
        }

        Syms32 = (PIMAGEHLP_SYMBOL) MemAlloc( (sizeof(IMAGEHLP_SYMBOL) * Dups) + NameSize );
        if (!Syms32) {
            MemFree( DupSym32 );
            return sym;
        }

        SymSave = (PULONG) MemAlloc( sizeof(ULONG) * Dups );
        if (!SymSave) {
            MemFree( Syms32 );
            MemFree( DupSym32 );
            return sym;
        }

        DupSym32->SizeOfStruct    = sizeof(IMAGEHLP_DUPLICATE_SYMBOL);
        DupSym32->NumberOfDups    = Dups;
        DupSym32->Symbol          = Syms32;
        DupSym32->SelectedSymbol  = (ULONG) -1;

        Dups = 0;
        for (i = 0; i < mi->numsyms; i++) {
            if ((mi->symbolTable[i].NameLength == sym->NameLength) &&
                (strcmp( mi->symbolTable[i].Name, sym->Name ) == 0)) {
                    symcpy32( Syms32, &mi->symbolTable[i] );
                    Syms32 += (sizeof(IMAGEHLP_SYMBOL) + mi->symbolTable[i].NameLength + 1);
                    SymSave[Dups] = i;
                    Dups += 1;
            }
        }

    } else {
        DupSym64 = (PIMAGEHLP_DUPLICATE_SYMBOL64) MemAlloc( sizeof(IMAGEHLP_DUPLICATE_SYMBOL64) );
        if (!DupSym64) {
            return sym;
        }

        Syms64 = (PIMAGEHLP_SYMBOL64) MemAlloc( (sizeof(IMAGEHLP_SYMBOL64) * Dups) + NameSize );
        if (!Syms64) {
            MemFree( DupSym64 );
            return sym;
        }

        SymSave = (PULONG) MemAlloc( sizeof(ULONG) * Dups );
        if (!SymSave) {
            MemFree( Syms64 );
            MemFree( DupSym64 );
            return sym;
        }

        DupSym64->SizeOfStruct    = sizeof(IMAGEHLP_DUPLICATE_SYMBOL64);
        DupSym64->NumberOfDups    = Dups;
        DupSym64->Symbol          = Syms64;
        DupSym64->SelectedSymbol  = (ULONG) -1;

        Dups = 0;
        for (i = 0; i < mi->numsyms; i++) {
            if ((mi->symbolTable[i].NameLength == sym->NameLength) &&
                (strcmp( mi->symbolTable[i].Name, sym->Name ) == 0)) {
                    symcpy64( Syms64, &mi->symbolTable[i] );
                    Syms64 += (sizeof(IMAGEHLP_SYMBOL64) + mi->symbolTable[i].NameLength + 1);
                    SymSave[Dups] = i;
                    Dups += 1;
            }
        }

    }

    sym = NULL;

    __try {

        if (pe->pCallbackFunction32) {
            pe->pCallbackFunction32(
                pe->hProcess,
                CBA_DUPLICATE_SYMBOL,
                (PVOID) DupSym32,
                (PVOID) pe->CallbackUserContext
                );

            if (DupSym32->SelectedSymbol != (ULONG) -1) {
                if (DupSym32->SelectedSymbol < DupSym32->NumberOfDups) {
                    sym = &mi->symbolTable[SymSave[DupSym32->SelectedSymbol]];
                }
            }
        } else {
            pe->pCallbackFunction64(
                pe->hProcess,
                CBA_DUPLICATE_SYMBOL,
                (ULONG64) &DupSym64,
                pe->CallbackUserContext
                );

            if (DupSym64->SelectedSymbol != (ULONG) -1) {
                if (DupSym64->SelectedSymbol < DupSym64->NumberOfDups) {
                    sym = &mi->symbolTable[SymSave[DupSym64->SelectedSymbol]];
                }
            }
        }

    } __except (EXCEPTION_EXECUTE_HANDLER) {
        ;
    }

    if (DupSym32) {
        MemFree( DupSym32 );
    }
    if (DupSym64) {
        MemFree( DupSym64 );
    }
    if (Syms32) {
        MemFree( Syms32 );
    }
    if (Syms64) {
        MemFree( Syms64 );
    }
    MemFree( SymSave );

    return sym;
}


PSYMBOL_ENTRY
FindSymbolByName(
    PPROCESS_ENTRY  pe,
    PMODULE_ENTRY   mi,
    LPSTR           SymName
    )
{
    DWORD               hash;
    PSYMBOL_ENTRY       sym;
    DWORD               i;

    if (!mi || mi->dia)
        return diaFindSymbolByName(pe, mi, SymName);

    hash = ComputeHash( SymName, strlen(SymName) );
    sym = mi->NameHashTable[hash];

    if (sym) {
        //
        // there are collision(s) so lets walk the
        // collision list and match the names
        //
        while( sym ) {
            if (MatchSymbolName( sym, SymName )) {
                sym = HandleDuplicateSymbols( pe, mi, sym );
                return sym;
            }
            sym = sym->Next;
        }
    }

    //
    // the symbol did not hash to anything valid
    // this is possible if the caller passed an undecorated name
    // now we must look linearly thru the list
    //
    for (i=0; i<mi->numsyms; i++) {
        sym = &mi->symbolTable[i];
        if (MatchSymbolName( sym, SymName )) {
            sym = HandleDuplicateSymbols( pe, mi, sym );
            return sym;
        }
    }

    return NULL;
}


IMGHLP_RVA_FUNCTION_DATA *
SearchRvaFunctionTable(
    IMGHLP_RVA_FUNCTION_DATA *FunctionTable,
    LONG High,
    LONG Low,
    DWORD dwPC
    )
{
    LONG    Middle;
    IMGHLP_RVA_FUNCTION_DATA *FunctionEntry;

    // Perform binary search on the function table for a function table
    // entry that subsumes the specified PC.

    while (High >= Low) {

        // Compute next probe index and test entry. If the specified PC
        // is greater than of equal to the beginning address and less
        // than the ending address of the function table entry, then
        // return the address of the function table entry. Otherwise,
        // continue the search.

        Middle = (Low + High) >> 1;
        FunctionEntry = &FunctionTable[Middle];
        if (dwPC < FunctionEntry->rvaBeginAddress) {
            High = Middle - 1;

        } else if (dwPC >= FunctionEntry->rvaEndAddress) {
            Low = Middle + 1;

        } else {
            return FunctionEntry;
        }
    }
    return NULL;
}

PIMGHLP_RVA_FUNCTION_DATA
GetFunctionEntryFromDebugInfo (
    PPROCESS_ENTRY  pe,
    DWORD64         ControlPc
    )
{
    PMODULE_ENTRY mi;
    IMGHLP_RVA_FUNCTION_DATA   *FunctionTable;

    mi = GetModuleForPC( pe, ControlPc, FALSE );
    if (mi == NULL) {
        return NULL;
    }

    if (!GetPData(pe->hProcess, mi)) {
        return NULL;
    }

    FunctionTable = (IMGHLP_RVA_FUNCTION_DATA *)mi->pExceptionData;
    return SearchRvaFunctionTable(FunctionTable, mi->dwEntries - 1, 0,
                                  (ULONG)(ControlPc - mi->BaseOfDll));
}

PIMAGE_FUNCTION_ENTRY
LookupFunctionEntryAxp32 (
    HANDLE        hProcess,
    DWORD         ControlPc
    )
{
    FunctionEntryCache* Cache;
    FeCacheEntry* FunctionEntry;

    if ((Cache = GetFeCache(IMAGE_FILE_MACHINE_ALPHA, TRUE)) == NULL) {
        return NULL;
    }

    // Don't specify the function table access callback or it will
    // cause recursion.

    FunctionEntry = Cache->
        Find(hProcess, (ULONG64)(LONG)ControlPc, ReadInProcMemory,
             miGetModuleBase, NULL);
    if ( FunctionEntry == NULL ) {
        return NULL;
    }

    // Alpha function entries are always stored as 64-bit
    // so downconvert.
    tlsvar(FunctionEntry32).StartingAddress =
        (ULONG)FunctionEntry->Data.Axp64.BeginAddress;
    tlsvar(FunctionEntry32).EndingAddress =
        (ULONG)FunctionEntry->Data.Axp64.EndAddress;
    tlsvar(FunctionEntry32).EndOfPrologue =
        (ULONG)FunctionEntry->Data.Axp64.PrologEndAddress;
    return &tlsvar(FunctionEntry32);
}

PIMAGE_FUNCTION_ENTRY64
LookupFunctionEntryAxp64 (
    HANDLE        hProcess,
    DWORD64       ControlPc
    )
{
    FunctionEntryCache* Cache;
    FeCacheEntry* FunctionEntry;

    if ((Cache = GetFeCache(IMAGE_FILE_MACHINE_ALPHA64, TRUE)) == NULL) {
        return NULL;
    }

    // Don't specify the function table access callback or it will
    // cause recursion.

    FunctionEntry = Cache->
        Find(hProcess, ControlPc, ReadInProcMemory,
             miGetModuleBase, NULL);
    if ( FunctionEntry == NULL ) {
        return NULL;
    }

    tlsvar(FunctionEntry64).StartingAddress =
        FunctionEntry->Data.Axp64.BeginAddress;
    tlsvar(FunctionEntry64).EndingAddress =
        FunctionEntry->Data.Axp64.EndAddress;
    tlsvar(FunctionEntry64).EndOfPrologue =
        FunctionEntry->Data.Axp64.PrologEndAddress;
    return &tlsvar(FunctionEntry64);
}

// NTRAID#96939-2000/03/27-patst
//
// All the platform dependent "LookupFunctionEntryXxx" should be retyped as returning
// a PIMAGE_FUNCTION_ENTRY64. This would require a modification of the callers, especially
// the IA64 specific locations that assume that the returned function entries contains RVAs
// and not absolute addresses. I implemented a platform-independant
// "per address space /  per module" cache of function entries - capable of supporting the
// dynamic function entries scheme but I fell short of time in delivering it.

PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY
LookupFunctionEntryIa64 (
    HANDLE        hProcess,
    DWORD64       ControlPc
    )
{
    FunctionEntryCache* Cache;
    FeCacheEntry* FunctionEntry;

    if ((Cache = GetFeCache(IMAGE_FILE_MACHINE_IA64, TRUE)) == NULL) {
        return NULL;
    }

//
// IA64-NOTE 08/99: IA64 Function entries contain file offsets, not absolute relocated addresses.
//                  IA64 Callers assume this.
//


    // Don't specify the function table access callback or it will
    // cause recursion.

    FunctionEntry = Cache->
        Find(hProcess, ControlPc, ReadInProcMemory,
             miGetModuleBase, NULL);
    if ( FunctionEntry == NULL ) {
        return NULL;
    }

    tlsvar(Ia64FunctionEntry) = FunctionEntry->Data.Ia64;
    return &tlsvar(Ia64FunctionEntry);
}

_PIMAGE_RUNTIME_FUNCTION_ENTRY
LookupFunctionEntryAmd64 (
    HANDLE        hProcess,
    DWORD64       ControlPc
    )
{
    FunctionEntryCache* Cache;
    FeCacheEntry* FunctionEntry;

    if ((Cache = GetFeCache(IMAGE_FILE_MACHINE_AMD64, TRUE)) == NULL) {
        return NULL;
    }

    // Don't specify the function table access callback or it will
    // cause recursion.

    FunctionEntry = Cache->
        Find(hProcess, ControlPc, ReadInProcMemory,
             miGetModuleBase, NULL);
    if ( FunctionEntry == NULL ) {
        return NULL;
    }

    tlsvar(Amd64FunctionEntry) = FunctionEntry->Data.Amd64;
    return &tlsvar(Amd64FunctionEntry);
}

PFPO_DATA
SwSearchFpoData(
    DWORD     key,
    PFPO_DATA base,
    DWORD     num
    )
{
    PFPO_DATA  lo = base;
    PFPO_DATA  hi = base + (num - 1);
    PFPO_DATA  mid;
    DWORD      half;

    while (lo <= hi) {
        if (half = num / 2) {
            mid = lo + ((num & 1) ? half : (half - 1));
            if ((key >= mid->ulOffStart)&&(key < (mid->ulOffStart+mid->cbProcSize))) {
                return mid;
            }
            if (key < mid->ulOffStart) {
                hi = mid - 1;
                num = (num & 1) ? half : half-1;
            } else {
                lo = mid + 1;
                num = half;
            }
        } else
        if (num) {
            if ((key >= lo->ulOffStart)&&(key < (lo->ulOffStart+lo->cbProcSize))) {
                return lo;
            } else {
                break;
            }
        } else {
            break;
        }
    }
    return(NULL);
}

BOOL
DoSymbolCallback (
    PPROCESS_ENTRY                  pe,
    ULONG                           CallbackType,
    IN  PMODULE_ENTRY               mi,
    PIMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl64,
    LPSTR                           FileName
    )
{
    BOOL Status;
    IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl32;

    Status = FALSE;
    if (pe->pCallbackFunction32) {
        idsl32.SizeOfStruct  = sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD);
        idsl32.BaseOfImage   = (ULONG)mi->BaseOfDll;
        idsl32.CheckSum      = mi->CheckSum;
        idsl32.TimeDateStamp = mi->TimeDateStamp;
        idsl32.Reparse       = FALSE;
        idsl32.FileName[0] = 0;
        if (FileName) {
            strncat( idsl32.FileName, FileName, MAX_PATH - 1 );
        }

        __try {

            Status = pe->pCallbackFunction32(
                        pe->hProcess,
                        CallbackType,
                        (PVOID)&idsl32,
                        (PVOID)pe->CallbackUserContext
                        );
            idsl64->SizeOfStruct = sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64);
            idsl64->BaseOfImage = idsl32.BaseOfImage;
            idsl64->CheckSum = idsl32.CheckSum;
            idsl64->TimeDateStamp = idsl32.TimeDateStamp;
            idsl64->Reparse = idsl32.Reparse;
            if (idsl32.FileName) {
                strncpy( idsl64->FileName, idsl32.FileName, MAX_PATH );
            }

        } __except (EXCEPTION_EXECUTE_HANDLER) {
        }
    } else
    if (pe->pCallbackFunction64) {
        idsl64->SizeOfStruct  = sizeof(IMAGEHLP_DEFERRED_SYMBOL_LOAD64);
        idsl64->BaseOfImage   = mi->BaseOfDll;
        idsl64->CheckSum      = mi->CheckSum;
        idsl64->TimeDateStamp = mi->TimeDateStamp;
        idsl64->Reparse       = FALSE;
        idsl64->FileName[0] = 0;
        if (FileName) {
            strncat( idsl64->FileName, FileName, MAX_PATH );
        }

        __try {

            Status = pe->pCallbackFunction64(
                        pe->hProcess,
                        CallbackType,
                        (ULONG64)(ULONG_PTR)idsl64,
                        pe->CallbackUserContext
                        );

        } __except (EXCEPTION_EXECUTE_HANDLER) {
        }
    }

    return Status;
}


BOOL
DoCallback(
    PPROCESS_ENTRY pe,
    ULONG          type,
    PVOID          data
    )
{
    BOOL rc = TRUE;

    __try {

        // if we weren't passed a process entry, then call all processes

        if (!pe) {
            BOOL        ret;
            PLIST_ENTRY next;

            next = g.ProcessList.Flink;
            if (!next)
                return FALSE;

            while ((PVOID)next != (PVOID)&g.ProcessList) {
                pe = CONTAINING_RECORD( next, PROCESS_ENTRY, ListEntry );
                next = pe->ListEntry.Flink;
                if (!pe)
                    return rc;
                ret = DoCallback(pe, type, data);
                if (!ret)
                    rc = ret;
            }

            return rc;
        }

        // otherwise call this process

        if (pe->pCallbackFunction32) {
            rc = pe->pCallbackFunction32(pe->hProcess,
                                         type,
                                         data,
                                         (PVOID)pe->CallbackUserContext);
        } else if (pe->pCallbackFunction64) {
            rc = pe->pCallbackFunction64(pe->hProcess,
                                         type,
                                         (ULONG64)data,
                                         pe->CallbackUserContext);
        }

    } __except (EXCEPTION_EXECUTE_HANDLER) {
        rc = FALSE;
    }

    return rc;
}


VOID
SympSendDebugString(
    PPROCESS_ENTRY pe,
    LPSTR          String
    )
{
    __try {
        if (!pe)
            pe = FindFirstProcessEntry();

        if (!pe) {
            printf(String);
        } else if (pe->pCallbackFunction32) {
            pe->pCallbackFunction32(pe->hProcess,
                                              CBA_DEBUG_INFO,
                                              (PVOID)String,
                                              (PVOID)pe->CallbackUserContext
                                              );
        } else if (pe->pCallbackFunction64) {
            pe->pCallbackFunction64(pe->hProcess,
                                              CBA_DEBUG_INFO,
                                              (ULONG64)String,
                                              pe->CallbackUserContext
                                              );
        }
    } __except (EXCEPTION_EXECUTE_HANDLER) {
    }
}

int
WINAPIV
_pprint(
    PPROCESS_ENTRY pe,
    LPSTR Format,
    ...
    )
{
    static char buf[1000] = "DBGHELP: ";
    va_list args;

    va_start(args, Format);
    _vsnprintf(buf+9, sizeof(buf)-9, Format, args);
    va_end(args);
    SympSendDebugString(pe, buf);
    return 1;
}

int
WINAPIV
_peprint(
    PPROCESS_ENTRY pe,
    LPSTR Format,
    ...
    )
{
    static char buf[1000] = "";
    va_list args;

    va_start(args, Format);
    _vsnprintf(buf, sizeof(buf), Format, args);
    va_end(args);
    SympSendDebugString(pe, buf);
    return 1;
}


int
WINAPIV
_dprint(
    LPSTR format,
    ...
    )
{
    static char buf[1000] = "DBGHELP: ";
    va_list args;

    va_start(args, format);
    _vsnprintf(buf+9, sizeof(buf)-9, format, args);
    va_end(args);
    SympSendDebugString(NULL, buf);
    return 1;
}

int
WINAPIV
_eprint(
    LPSTR format,
    ...
    )
{
    static char buf[1000] = "";
    va_list args;

    va_start(args, format);
    _vsnprintf(buf, sizeof(buf), format, args);
    va_end(args);
    SympSendDebugString(NULL, buf);
    return 1;
}


BOOL
WINAPIV
evtprint(
    PPROCESS_ENTRY pe,
    DWORD          severity,
    DWORD          code,
    PVOID          object,
    LPSTR          format,
    ...
    )
{
    static char buf[1000] = "";
    IMAGEHLP_CBA_EVENT evt;
    va_list args;

    va_start(args, format);
    _vsnprintf(buf, sizeof(buf), format, args);
    va_end(args);
    evt.severity = severity;
    evt.code = code;
    evt.desc = buf;
    evt.object = object;

    return DoCallback(pe, CBA_EVENT, &evt);
}

BOOL
traceAddr(
    DWORD64 addr
    )
{
    DWORD64 taddr = 0;

    if (!*g.DebugToken)
        return FALSE;
    sscanf(g.DebugToken, "0x%I64x", &taddr);
    taddr = EXTEND64(taddr);
    addr = EXTEND64(addr);
    return (addr == taddr);
}


BOOL
traceName(
    PCHAR name
    )
{
    if (!*g.DebugToken)
        return FALSE;
    return !_strnicmp(name, g.DebugToken, strlen(g.DebugToken));
}


BOOL
traceSubName(
    PCHAR name
    )
{
    char *lname;
    BOOL  rc;

    if (!*g.DebugToken)
        return FALSE;
    lname = (char *)MemAlloc(sizeof(char) * (strlen(name) + 1));
    if (!lname) {
        return FALSE;
    }
    strcpy(lname, name);
    if (!lname)
        return FALSE;
    _strlwr(lname);
    rc = strstr(lname, g.DebugToken) ? TRUE : FALSE;
    MemFree(lname);

    return rc;
}


BOOL
load(
    IN  HANDLE          hProcess,
    IN  PMODULE_ENTRY   mi
    )
{
    IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
    PPROCESS_ENTRY              pe;
    ULONG                       i;
    PIMGHLP_DEBUG_DATA          pIDD;
    ULONG                       bias;
    PIMAGE_SYMBOL               lpSymbolEntry;
    PUCHAR                      lpStringTable;
    PUCHAR                      p;
    BOOL                        SymbolsLoaded = FALSE;
    PCHAR                       CallbackFileName, ImageName;
    ULONG                       Size;

    g.LastSymLoadError = SYMLOAD_DEFERRED;
    pe = FindProcessEntry( hProcess );
    if (!pe) {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

//  if (traceName(mi->ModuleName)) // for setting debug breakpoints from DBGHELP_TOKEN
//      pprint(pe, "debug(%s)\n", mi->ModuleName);

    CallbackFileName   = mi->LoadedImageName ? mi->LoadedImageName :
                                mi->ImageName ? mi->ImageName : mi->ModuleName;

    DoSymbolCallback(
        pe,
        CBA_DEFERRED_SYMBOL_LOAD_START,
        mi,
        &idsl,
        CallbackFileName
        );

    ImageName = mi->ImageName;
    for (; ;) {
        pIDD = GetDebugData(
            hProcess,
            mi->hFile,
            ImageName,
            pe->SymbolSearchPath,
            mi->BaseOfDll,
            &mi->mld,
            0
            );
        mi->SymLoadError = g.LastSymLoadError;

        if (pIDD) {
            break;
        }

        pprint(pe, "GetDebugData(%p, %s, %s, %I64x, 0) failed\n",
            mi->hFile,
            ImageName,
            pe->SymbolSearchPath,
            mi->BaseOfDll
            );

        if (!DoSymbolCallback(
                 pe,
                 CBA_DEFERRED_SYMBOL_LOAD_FAILURE,
                 mi,
                 &idsl,
                 CallbackFileName
                 ) || !idsl.Reparse)
        {
            mi->SymType = SymNone;
            mi->Flags |= MIF_NO_SYMBOLS;
            return FALSE;
        }

        ImageName = idsl.FileName;
        CallbackFileName = idsl.FileName;
    }

    pIDD->flags = mi->Flags;

    // The following code ONLY works if the dll wasn't rebased
    // during install.  Is it really useful?

    if (!mi->BaseOfDll) {
        //
        // This case occurs when modules are loaded multiple times by
        // name with no explicit base address.
        //
        if (GetModuleForPC( pe, pIDD->ImageBaseFromImage, TRUE )) {
            if (pIDD->ImageBaseFromImage) {
                pprint(pe, "GetModuleForPC(%p, %I64x, TRUE) failed\n",
                    pe,
                    pIDD->ImageBaseFromImage,
                    TRUE
                    );
            } else {
                pprint(pe, "No base address for %s:  Please specify\n", ImageName);
            }
            return FALSE;
        }
        mi->BaseOfDll    = pIDD->ImageBaseFromImage;
    }

    if (!mi->DllSize) {
        mi->DllSize      = pIDD->SizeOfImage;
    }

    mi->hProcess         = pIDD->hProcess;
    mi->InProcImageBase  = pIDD->InProcImageBase;

    mi->CheckSum         = pIDD->CheckSum;
    mi->TimeDateStamp    = pIDD->TimeDateStamp;
    mi->MachineType      = pIDD->Machine;

    mi->ImageType        = pIDD->ImageType;
    mi->PdbSrc           = pIDD->PdbSrc;
    mi->ImageSrc         = pIDD->ImageSrc;

    if (!mi->MachineType && g.MachineType) {
        mi->MachineType = (USHORT) g.MachineType;
    }
    if (pIDD->dia) {
        mi->LoadedPdbName = StringDup(pIDD->PdbFileName);
    }
    if (pIDD->DbgFileMap) {
        mi->LoadedImageName = StringDup(pIDD->DbgFilePath);
    } else if (*pIDD->ImageFilePath) {
        mi->LoadedImageName = StringDup(pIDD->ImageFilePath);
    } else if (pIDD->dia) {
        mi->LoadedImageName = StringDup(pIDD->PdbFileName);
    } else {
        mi->LoadedImageName = StringDup("");
    }

    if (pIDD->fROM) {
        mi->Flags |= MIF_ROM_IMAGE;
    }

    if (!mi->ImageName) {
        mi->ImageName = StringDup(pIDD->OriginalImageFileName);
        _splitpath( mi->ImageName, NULL, NULL, mi->ModuleName, NULL );
        mi->AliasName[0] = 0;
    }

    mi->dsExceptions = pIDD->dsExceptions;

    if (pIDD->cFpo) {
        //
        // use virtualalloc() because the rtf search function
        // return a pointer into this memory.  we want to make
        // all of this memory read only so that callers cannot
        // stomp on imagehlp's data
        //
        mi->pFpoData = (PFPO_DATA)VirtualAlloc(
            NULL,
            sizeof(FPO_DATA) * pIDD->cFpo,
            MEM_COMMIT,
            PAGE_READWRITE
            );
        if (mi->pFpoData) {
            mi->dwEntries = pIDD->cFpo;
            CopyMemory(
                mi->pFpoData,
                pIDD->pFpo,
                sizeof(FPO_DATA) * mi->dwEntries
                );
            VirtualProtect(
                mi->pFpoData,
                sizeof(FPO_DATA) * mi->dwEntries,
                PAGE_READONLY,
                &i
                );
        }
    }

    // copy the pdata block from the pdb

    if (pIDD->pPData) {
        mi->pPData = MemAlloc(pIDD->cbPData);
        if (mi->pPData) {
            mi->cPData = pIDD->cPData;
            mi->cbPData = pIDD->cbPData;
            CopyMemory(mi->pPData, pIDD->pPData, pIDD->cbPData);
        }
    }

    if (pIDD->pXData) {
        mi->pXData = MemAlloc(pIDD->cbXData);
        if (mi->pXData) {
            mi->cXData = pIDD->cXData;
            mi->cbXData = pIDD->cbXData;
            CopyMemory(mi->pXData, pIDD->pXData, pIDD->cbXData);
        }
    }

    // now the sections

    mi->NumSections = pIDD->cCurrentSections;
    if (pIDD->fCurrentSectionsMapped) {
        mi->SectionHdrs = (PIMAGE_SECTION_HEADER) MemAlloc(
            sizeof(IMAGE_SECTION_HEADER) * mi->NumSections
            );
        if (mi->SectionHdrs) {
            CopyMemory(
                mi->SectionHdrs,
                pIDD->pCurrentSections,
                sizeof(IMAGE_SECTION_HEADER) * mi->NumSections
                );
        }
    } else {
        mi->SectionHdrs = pIDD->pCurrentSections;
    }

    if (pIDD->pOriginalSections) {
        mi->OriginalNumSections = pIDD->cOriginalSections;
        mi->OriginalSectionHdrs = pIDD->pOriginalSections;
    } else {
        mi->OriginalNumSections = mi->NumSections;
        mi->OriginalSectionHdrs = (PIMAGE_SECTION_HEADER) MemAlloc(
            sizeof(IMAGE_SECTION_HEADER) * mi->NumSections
            );
        if (mi->OriginalSectionHdrs) {
            CopyMemory(
                mi->OriginalSectionHdrs,
                pIDD->pCurrentSections,
                sizeof(IMAGE_SECTION_HEADER) * mi->NumSections
                );
        }
    }

    // symbols

    mi->TmpSym.Name = (LPSTR) MemAlloc( TMP_SYM_LEN );

    if (pIDD->dia) {
        mi->SymType = SymDia;
        SymbolsLoaded = TRUE;
    } else {
        if (pIDD->pMappedCv) {
            SymbolsLoaded = LoadCodeViewSymbols(
                hProcess,
                mi,
                pIDD
                );
            pprint(pe, "codeview symbols %sloaded\n", SymbolsLoaded?"":"not ");
        }
        if (!SymbolsLoaded && pIDD->pMappedCoff) {
            SymbolsLoaded = LoadCoffSymbols(hProcess, mi, pIDD);
            pprint(pe, "coff symbols %sloaded\n", SymbolsLoaded?"":"not ");
        }

        if (!SymbolsLoaded && pIDD->cExports) {
            SymbolsLoaded = LoadExportSymbols( mi, pIDD );
            if (SymbolsLoaded) {
                mi->PdbSrc = srcNone;
            }
            pprint(pe, "export symbols %sloaded\n", SymbolsLoaded?"":"not ");
        }

        if (!SymbolsLoaded) {
            mi->SymType = SymNone;
            pprint(pe, "no symbols loaded\n");
        }
    }

    mi->dia = pIDD->dia;

    ProcessOmapForModule( mi, pIDD );

    ReleaseDebugData(pIDD,
                     IMGHLP_FREE_FPO | IMGHLP_FREE_SYMPATH | IMGHLP_FREE_PDATA | IMGHLP_FREE_XDATA);
    mi->Flags &= ~MIF_DEFERRED_LOAD;

    DoSymbolCallback(pe,
                       CBA_DEFERRED_SYMBOL_LOAD_COMPLETE,
                       mi,
                       &idsl,
                       CallbackFileName);

    return TRUE;
}


DWORD64
InternalLoadModule(
    IN  HANDLE          hProcess,
    IN  PSTR            ImageName,
    IN  PSTR            ModuleName,
    IN  DWORD64         BaseOfDll,
    IN  DWORD           DllSize,
    IN  HANDLE          hFile,
    IN  PMODLOAD_DATA   data,
    IN  DWORD           flags
    )
{
    IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
    PPROCESS_ENTRY                  pe;
    PMODULE_ENTRY                   mi;
    LPSTR                           p;
    DWORD64                         ip;

//  if (traceSubName(ImageName)) // for setting debug breakpoints from DBGHELP_TOKEN
//      dprint("debug(%s)\n", ImageName);

    if (BaseOfDll == (DWORD64)-1)
        return 0;

    __try {
        CHAR c;
        if (ImageName)
            c = *ImageName;
        if (ModuleName)
            c = *ModuleName;
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return 0;
    }

    pe = FindProcessEntry( hProcess );
    if (!pe) {
        return 0;
    }

    if (BaseOfDll) {
        mi = GetModuleForPC( pe, BaseOfDll, TRUE );
    } else {
        mi = NULL;
    }

    if (mi) {
        //
        // in this case the symbols are already loaded
        // so the caller really wants the deferred
        // symbols to be loaded
        //
        if ( (mi->Flags & MIF_DEFERRED_LOAD) &&
                load( hProcess, mi )) {

            return mi->BaseOfDll;
        } else {
            return 0;
        }
    }

    //
    // look to see if there is an overlapping module entry
    //
    if (BaseOfDll) {
        do {
            mi = GetModuleForPC( pe, BaseOfDll, FALSE );
            if (mi) {
                RemoveEntryList( &mi->ListEntry );

                DoSymbolCallback(
                    pe,
                    CBA_SYMBOLS_UNLOADED,
                    mi,
                    &idsl,
                    mi->LoadedImageName ? mi->LoadedImageName : mi->ImageName ? mi->ImageName : mi->ModuleName
                    );

                FreeModuleEntry(pe, mi);
            }
        } while(mi);
    }

    mi = (PMODULE_ENTRY) MemAlloc( sizeof(MODULE_ENTRY) );
    if (!mi) {
        return 0;
    }
    InitModuleEntry(mi);

    mi->BaseOfDll = BaseOfDll;
    mi->DllSize = DllSize;
    mi->hFile = hFile;
    if (ImageName) {
        char SplitMod[_MAX_FNAME];

        mi->ImageName = StringDup(ImageName);
        _splitpath( ImageName, NULL, NULL, SplitMod, NULL );
        mi->ModuleName[0] = 0;
        strncat(mi->ModuleName, SplitMod, sizeof(mi->ModuleName) - 1);
        if (ModuleName && _stricmp( ModuleName, mi->ModuleName ) != 0) {
            mi->AliasName[0] = 0;
            strncat( mi->AliasName, ModuleName, sizeof(mi->AliasName) - 1 );
        } else {
            mi->AliasName[0] = 0;
        }
    } else {

        if (ModuleName) {
            mi->AliasName[0] = 0;
            strncat( mi->AliasName, ModuleName, sizeof(mi->AliasName) - 1 );
        }

    }
    mi->mod = NULL;
    mi->cbPdbSymbols = 0;
    mi->pPdbSymbols = NULL;

    if (data) {
        if (data->ssize != sizeof(MODLOAD_DATA)) {
            SetLastError(ERROR_INVALID_PARAMETER);
            return 0;
        }
        memcpy(&mi->mld, data, data->ssize);
        mi->CallerData = MemAlloc(mi->mld.size);
        if (!mi->CallerData) {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return 0;
        }
        mi->mld.data = mi->CallerData;
        memcpy(mi->mld.data, data->data, mi->mld.size);
    }

    if ((g.SymOptions & SYMOPT_DEFERRED_LOADS) && BaseOfDll) {
        mi->Flags |= MIF_DEFERRED_LOAD;
        mi->SymType = SymDeferred;
    } else if (!load( hProcess, mi )) {
        FreeModuleEntry(pe, mi);
        return 0;
    }

    pe->Count += 1;

    InsertTailList( &pe->ModuleList, &mi->ListEntry);

    ip = GetIP(pe);
    if ((mi->BaseOfDll <= ip) && (mi->BaseOfDll + DllSize >= ip))
        diaSetModFromIP(pe);

    return mi->BaseOfDll;
}

PPROCESS_ENTRY
FindProcessEntry(
    HANDLE  hProcess
    )
{
    PLIST_ENTRY                 next;
    PPROCESS_ENTRY              pe;
    DWORD                       count;

    next = g.ProcessList.Flink;
    if (!next) {
        return NULL;
    }

    for (count = 0; (PVOID)next != (PVOID)&g.ProcessList; count++) {
        assert(count < g.cProcessList);
        if (count >= g.cProcessList)
            return NULL;
        pe = CONTAINING_RECORD( next, PROCESS_ENTRY, ListEntry );
        next = pe->ListEntry.Flink;
        if (pe->hProcess == hProcess) {
            return pe;
        }
    }

    return NULL;
}

PPROCESS_ENTRY
FindFirstProcessEntry(
    )
{
    return CONTAINING_RECORD(g.ProcessList.Flink, PROCESS_ENTRY, ListEntry);
}


PMODULE_ENTRY
FindModule(
    HANDLE hProcess,
    PPROCESS_ENTRY pe,
    LPSTR ModuleName,
    BOOL fLoad
    )
{
    PLIST_ENTRY next;
    PMODULE_ENTRY mi;

    if (!ModuleName || !*ModuleName)
        return NULL;

    next = pe->ModuleList.Flink;
    if (next) {
        while ((PVOID)next != (PVOID)&pe->ModuleList) {
            mi = CONTAINING_RECORD( next, MODULE_ENTRY, ListEntry );
            next = mi->ListEntry.Flink;

            if ((_stricmp( mi->ModuleName, ModuleName ) == 0) ||
                (mi->AliasName[0] &&
                 _stricmp( mi->AliasName, ModuleName ) == 0))
            {
                if (fLoad && !LoadSymbols(hProcess, mi, 0)) {
                    return NULL;
                }

                return mi;
            }
        }
    }

    return NULL;
}


#ifndef _DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED

BOOL
SymCheckUserGenerated(
   ULONG64 dwAddr,
   PSYMBOL_ENTRY sym,
   PMODULE_ENTRY mi
   )
{
   PSYMBOL_ENTRY nextSym;

   /*
   // We do not know the size of a user generated symbol...
   // This work because of the execution of CompleteSymbolTable() after AllocSym().
   // Particularly, the size has been adjusted.
   */

   if ( !(sym->Flags & SYMF_USER_GENERATED) )   {
      dprint("SymCheckUserGenerated: We should not call this function. This is not a user generated symbol...\n" );
      return FALSE;
   }

   if ( (dwAddr == sym->Address) || (sym == &mi->symbolTable[mi->numsyms - 1]) )  {
       return TRUE;
   }

   nextSym = sym + 1;
   if ( dwAddr < nextSym->Address )   {
       return TRUE;
   }

   return FALSE;

}

#endif // !_DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED

PSYMBOL_ENTRY
GetSymFromAddr(
    DWORD64         dwAddr,
    PDWORD64        pqwDisplacement,
    PMODULE_ENTRY   mi
    )
{
    PSYMBOL_ENTRY           sym = NULL;
    LONG                    High;
    LONG                    Low;
    LONG                    Middle;

    if (mi == NULL) {
        return NULL;
    }

    if (mi->dia)
        return diaGetSymFromAddr(dwAddr, mi, pqwDisplacement);

    //
    // do a binary search to locate the symbol
    //

    Low = 0;
    High = mi->numsyms - 1;

    while (High >= Low) {
        Middle = (Low + High) >> 1;
        sym = &mi->symbolTable[Middle];
        if (dwAddr < sym->Address) {

            High = Middle - 1;

        } else if (dwAddr >= sym->Address + sym->Size) {

#ifndef _DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED

            if ( (sym->Flags & SYMF_USER_GENERATED) && SymCheckUserGenerated( dwAddr, sym, mi ) ) {
                if (pqwDisplacement) {
                   *pqwDisplacement = dwAddr - sym->Address;
                }
                return sym;
            }

#endif // !_DBGHELP_USER_GENERATED_SYMBOLS_NOTSUPPORTED

            Low = Middle + 1;

        } else {

            if (pqwDisplacement) {
                *pqwDisplacement = dwAddr - sym->Address;
            }
            return sym;

        }
    }

    return NULL;
}


PMODULE_ENTRY
GetModuleForPC(
    PPROCESS_ENTRY  pe,
    DWORD64         dwPcAddr,
    BOOL            ExactMatch
    )
{
    static PLIST_ENTRY          next = NULL;
    PMODULE_ENTRY               mi;

    if (dwPcAddr == (DWORD64)-1) {
        if (!next)
            return NULL;
        if ((PVOID)next == (PVOID)&pe->ModuleList) {
            // Reset to NULL so the list can be re-walked
            next = NULL;
            return NULL;
        }
        mi = CONTAINING_RECORD( next, MODULE_ENTRY, ListEntry );
        next = mi->ListEntry.Flink;
        return mi;
    }

    next = pe->ModuleList.Flink;
    if (!next) {
        return NULL;
    }

    while ((PVOID)next != (PVOID)&pe->ModuleList) {
        mi = CONTAINING_RECORD( next, MODULE_ENTRY, ListEntry );
        next = mi->ListEntry.Flink;
        if (dwPcAddr == 0) {
            return mi;
        }
        if (ExactMatch) {
            if (dwPcAddr == mi->BaseOfDll) {
               return mi;
            }
        } else
        if ((dwPcAddr == mi->BaseOfDll && mi->DllSize == 0) ||
            ((dwPcAddr >= mi->BaseOfDll) &&
                (dwPcAddr  < mi->BaseOfDll + mi->DllSize))) {
               return mi;
        }
    }

    return NULL;
}

PSYMBOL_ENTRY
GetSymFromAddrAllContexts(
    DWORD64         dwAddr,
    PDWORD64        pqwDisplacement,
    PPROCESS_ENTRY  pe
    )
{
    PMODULE_ENTRY mi = GetModuleForPC( pe, dwAddr, FALSE );
    if (mi == NULL) {
        return NULL;
    }
    return GetSymFromAddr( dwAddr, pqwDisplacement, mi );
}

DWORD
ComputeHash(
    LPSTR   lpbName,
    ULONG   cb
    )
{
    ULONG UNALIGNED *   lpulName;
    ULONG               ulEnd = 0;
    int                 cul;
    int                 iul;
    ULONG               ulSum = 0;

    while (cb & 3) {
        ulEnd |= (lpbName[cb - 1] & 0xdf);
        ulEnd <<= 8;
        cb -= 1;
    }

    cul = cb / 4;
    lpulName = (ULONG UNALIGNED *) lpbName;
    for (iul =0; iul < cul; iul++) {
        ulSum ^= (lpulName[iul] & 0xdfdfdfdf);
        ulSum = _lrotl( ulSum, 4);
    }
    ulSum ^= ulEnd;
    return ulSum % HASH_MODULO;
}

PSYMBOL_ENTRY
AllocSym(
    PMODULE_ENTRY   mi,
    DWORD64         addr,
    LPSTR           name
    )
{
    PSYMBOL_ENTRY       sym;
    ULONG               Length;


    if (mi->numsyms == mi->MaxSyms) {
//       dprint("AllocSym: ERROR - symbols Table overflow!\n");
        return NULL;
    }

    if (!mi->StringSize) {
//      dprint("AllocSym: ERROR - symbols strings not allocated for module!\n");
        return NULL;
    }

    Length = strlen(name);

    if ((Length + 1) > mi->StringSize) {
//      dprint("AllocSym: ERROR - symbols strings buffer overflow!\n");
        return NULL;
    }

    sym = &mi->symbolTable[mi->numsyms];

    mi->numsyms += 1;
    sym->Name = mi->SymStrings;
    mi->SymStrings += (Length + 2);
    mi->StringSize -= (Length + 2);

    strcpy( sym->Name, name );
    sym->Address = addr;
    sym->Size = 0;
    sym->Flags = 0;
    sym->Next = NULL;
    sym->NameLength = Length;

    return sym;
}

int __cdecl
SymbolTableAddressCompare(
    const void *e1,
    const void *e2
    )
{
    PSYMBOL_ENTRY    sym1 = (PSYMBOL_ENTRY) e1;
    PSYMBOL_ENTRY    sym2 = (PSYMBOL_ENTRY) e2;
    LONG64 diff;

    if ( sym1 && sym2 ) {
        diff = (sym1->Address - sym2->Address);
        return (diff < 0) ? -1 : (diff == 0) ? 0 : 1;
    } else {
        return 1;
    }
}

int __cdecl
SymbolTableNameCompare(
    const void *e1,
    const void *e2
    )
{
    PSYMBOL_ENTRY    sym1 = (PSYMBOL_ENTRY) e1;
    PSYMBOL_ENTRY    sym2 = (PSYMBOL_ENTRY) e2;

    return strcmp( sym1->Name, sym2->Name );
}

VOID
CompleteSymbolTable(
    PMODULE_ENTRY   mi
    )
{
    PSYMBOL_ENTRY       sym;
    PSYMBOL_ENTRY       symH;
    ULONG               Hash;
    ULONG               i;
    ULONG               dups;
    ULONG               seq;


    //
    // sort the symbols by name
    //
    dbg_qsort(
        mi->symbolTable,
        mi->numsyms,
        sizeof(SYMBOL_ENTRY),
        SymbolTableNameCompare
        );

    //
    // mark duplicate names
    //
    seq = 0;
    for (i=0; i<mi->numsyms; i++) {
        dups = 0;
        while ((mi->symbolTable[i+dups].NameLength == mi->symbolTable[i+dups+1].NameLength) &&
               (strcmp( mi->symbolTable[i+dups].Name, mi->symbolTable[i+dups+1].Name ) == 0)) {
                   mi->symbolTable[i+dups].Flags |= SYMF_DUPLICATE;
                   mi->symbolTable[i+dups+1].Flags |= SYMF_DUPLICATE;
                   dups += 1;
        }
        i += dups;
    }

    //
    // sort the symbols by address
    //
    dbg_qsort(
        mi->symbolTable,
        mi->numsyms,
        sizeof(SYMBOL_ENTRY),
        SymbolTableAddressCompare
        );

    //
    // calculate the size of each symbol
    //
    for (i=0; i<mi->numsyms; i++) {
        mi->symbolTable[i].Next = NULL;
        if (i+1 < mi->numsyms) {
            mi->symbolTable[i].Size = (ULONG)(mi->symbolTable[i+1].Address - mi->symbolTable[i].Address);
        }
    }

    //
    // compute the hash for each symbol
    //
    ZeroMemory( mi->NameHashTable, sizeof(mi->NameHashTable) );
    for (i=0; i<mi->numsyms; i++) {
        sym = &mi->symbolTable[i];

        Hash = ComputeHash( sym->Name, sym->NameLength );

        if (mi->NameHashTable[Hash]) {

            //
            // we have a collision
            //
            symH = mi->NameHashTable[Hash];
            while( symH->Next ) {
                symH = symH->Next;
            }
            symH->Next = sym;

        } else {

            mi->NameHashTable[Hash] = sym;

        }
    }
}

BOOL
CreateSymbolTable(
    PMODULE_ENTRY   mi,
    DWORD           SymbolCount,
    SYM_TYPE        SymType,
    DWORD           NameSize
    )
{
    //
    // allocate the symbol table
    //
    NameSize += OMAP_SYM_STRINGS;
    mi->symbolTable = (PSYMBOL_ENTRY) MemAlloc(
        (sizeof(SYMBOL_ENTRY) * (SymbolCount + OMAP_SYM_EXTRA)) + NameSize + (SymbolCount * CPP_EXTRA)
        );
    if (!mi->symbolTable) {
        return FALSE;
    }

    //
    // initialize the relevant fields
    //
    mi->numsyms    = 0;
    mi->MaxSyms    = SymbolCount + OMAP_SYM_EXTRA;
    mi->SymType    = SymType;
    mi->StringSize = NameSize + (SymbolCount * CPP_EXTRA);
    mi->SymStrings = (LPSTR)(mi->symbolTable + SymbolCount + OMAP_SYM_EXTRA);

    return TRUE;
}

PIMAGE_SECTION_HEADER
FindSection(
    PIMAGE_SECTION_HEADER   sh,
    ULONG                   NumSections,
    ULONG                   Address
    )
{
    ULONG i;
    for (i=0; i<NumSections; i++) {
        if (Address >= sh[i].VirtualAddress &&
            Address <  (sh[i].VirtualAddress + sh[i].Misc.VirtualSize)) {
                    return &sh[i];
        }
    }
    return NULL;
}

PVOID
GetSectionPhysical(
    HANDLE             hp,
    ULONG64            base,
    PIMGHLP_DEBUG_DATA pIDD,
    ULONG              Address
    )
{
    PIMAGE_SECTION_HEADER   sh;

    sh = FindSection( pIDD->pCurrentSections, pIDD->cCurrentSections, Address );
    if (!sh) {
        return 0;
    }

    return (PCHAR)pIDD->ImageMap + sh->PointerToRawData + (Address - sh->VirtualAddress);
}

BOOL
ReadSectionInfo(
    HANDLE             hp,
    ULONG64            base,
    PIMGHLP_DEBUG_DATA pIDD,
    ULONG              address,
    PVOID              buf,
    DWORD              size
    )
{
    PIMAGE_SECTION_HEADER   sh;
    DWORD_PTR status = TRUE;

    sh = FindSection( pIDD->pCurrentSections, pIDD->cCurrentSections, address );
    if (!sh)
        return FALSE;

    if (!hp) {
        status = (DWORD_PTR)memcpy((PCHAR)buf,
                               (PCHAR)base + sh->PointerToRawData + (address - sh->VirtualAddress),
                               size);
    } else {
        status = ReadImageData(hp, base, address, buf, size);
    }
    if (!status)
        return FALSE;

    return TRUE;
}


PCHAR
expptr(
    HANDLE             hp,
    ULONG64            base,
    PIMGHLP_DEBUG_DATA pIDD,
    ULONG              address
    )
{
    PIMAGE_SECTION_HEADER   sh;
    DWORD_PTR status = TRUE;

    if (hp)
        return (PCHAR)base + address;

    sh = FindSection( pIDD->pCurrentSections, pIDD->cCurrentSections, address );
    if (!sh)
        return FALSE;

    return (PCHAR)base + sh->PointerToRawData + (address - sh->VirtualAddress);
}


ULONG
LoadExportSymbols(
    PMODULE_ENTRY      mi,
    PIMGHLP_DEBUG_DATA pIDD
    )
{
    PULONG                  names;
    PULONG                  addrs;
    PUSHORT                 ordinals;
    PUSHORT                 ordidx = NULL;
    ULONG                   cnt;
    ULONG                   idx;
    PIMAGE_EXPORT_DIRECTORY expdir;
    PCHAR                   expbuf = NULL;
    ULONG                   i;
    PSYMBOL_ENTRY           sym;
    ULONG                   NameSize;
    HANDLE                  hp;
    ULONG64                 base;
    CHAR                    name[2048];
    BOOL                    rc;
    DWORD64                 endExports;
    PCHAR                   p;

    if (g.SymOptions & SYMOPT_EXACT_SYMBOLS)
        return 0;

    // setup pointers for grabing data

    switch (pIDD->dsExports) {
    case dsInProc:
        hp = pIDD->hProcess;
        expbuf = (PCHAR)MemAlloc(pIDD->cExports);
        if (!expbuf)
            goto cleanup;
        if (!ReadImageData(hp, pIDD->InProcImageBase, pIDD->oExports, expbuf, pIDD->cExports))
            goto cleanup;
        base = (ULONG64)expbuf - pIDD->oExports;
        expdir = (PIMAGE_EXPORT_DIRECTORY)expbuf;
        break;
    case dsImage:
        hp = NULL;
        expbuf = NULL;
        if (!pIDD->ImageMap)
            pIDD->ImageMap = MapItRO(pIDD->ImageFileHandle);
        base = (ULONG64)pIDD->ImageMap;
        expdir = &pIDD->expdir;
        break;
    default:
        return 0;
    }

    cnt = 0;

    names = (PULONG)expptr(hp, base, pIDD, expdir->AddressOfNames);
    if (!names)
        goto cleanup;

    addrs = (PULONG)expptr(hp, base, pIDD, expdir->AddressOfFunctions);
    if (!addrs)
        goto cleanup;

    ordinals = (PUSHORT)expptr(hp, base, pIDD, expdir->AddressOfNameOrdinals);
    if (!ordinals)
        goto cleanup;

    ordidx = (PUSHORT) MemAlloc( max(expdir->NumberOfFunctions, expdir->NumberOfNames) * sizeof(USHORT) );

    if (!ordidx)
        goto cleanup;

    cnt = 0;
    NameSize = 0;

    // count the symbols

    for (i=0; i<expdir->NumberOfNames; i++) {
        *name = 0;
        p = expptr(hp, base, pIDD, names[i]);
        if (!p)
            continue;
        strcpy(name, p);
        if (!*name)
            continue;
        if (g.SymOptions & SYMOPT_UNDNAME) {
            SymUnDNameInternal( mi->TmpSym.Name, TMP_SYM_LEN, name, strlen(name), mi->MachineType, TRUE );
            NameSize += strlen(mi->TmpSym.Name);
            cnt += 1;
        } else {
            NameSize += (strlen(name) + 2);
            cnt += 1;
        }
    }

    for (i=0,idx=expdir->NumberOfNames; i<expdir->NumberOfFunctions; i++) {
        if (!ordidx[i]) {
            NameSize += 16;
            cnt += 1;
        }
    }

    // allocate the symbol table

    if (!CreateSymbolTable( mi, cnt, SymExport, NameSize )) {
        cnt = 0;
        goto cleanup;
    }

    // allocate the symbols

    cnt = 0;
    endExports = pIDD->oExports + pIDD->cExports;

    for (i=0; i<expdir->NumberOfNames; i++) {
        idx = ordinals[i];
        ordidx[idx] = TRUE;
        *name = 0;
        p = expptr(hp, base, pIDD, names[i]);
        if (!p)
            continue;
        strcpy(name, p);
        if (!*name)
            continue;
        if (g.SymOptions & SYMOPT_UNDNAME) {
            SymUnDNameInternal( mi->TmpSym.Name, TMP_SYM_LEN, (LPSTR)name, strlen(name), mi->MachineType, TRUE );
            sym = AllocSym( mi, addrs[idx] + mi->BaseOfDll, mi->TmpSym.Name);
        } else {
            sym = AllocSym( mi, addrs[idx] + mi->BaseOfDll, name);
        }
        if (sym) {
            cnt += 1;
        }
        if (pIDD->oExports <= addrs[idx]
            && addrs[idx] <= endExports)
        {
            sym->Flags |= SYMF_FORWARDER;
        } else {
            sym->Flags |= SYMF_EXPORT;
        }
    }

    for (i=0,idx=expdir->NumberOfNames; i<expdir->NumberOfFunctions; i++) {
        if (!ordidx[i]) {
            CHAR NameBuf[sizeof("Ordinal99999") + 1];       // Ordinals are only 64k max.
            strcpy( NameBuf, "Ordinal" );
            _itoa( i+expdir->Base, &NameBuf[7], 10 );
            sym = AllocSym( mi, addrs[i] + mi->BaseOfDll, NameBuf);
            if (sym) {
                cnt += 1;
            }
            idx += 1;
        }
    }

    CompleteSymbolTable( mi );

cleanup:
    if (expbuf) {
        MemFree(expbuf);
    }
    if (ordidx) {
        MemFree(ordidx);
    }

    return cnt;
}


BOOL
LoadCoffSymbols(
    HANDLE             hProcess,
    PMODULE_ENTRY      mi,
    PIMGHLP_DEBUG_DATA pIDD
    )
{
    PIMAGE_COFF_SYMBOLS_HEADER pCoffHeader = (PIMAGE_COFF_SYMBOLS_HEADER)(pIDD->pMappedCoff);
    PUCHAR              stringTable;
    PIMAGE_SYMBOL       allSymbols;
    DWORD               numberOfSymbols;
    PIMAGE_LINENUMBER   LineNumbers;
    PIMAGE_SYMBOL       NextSymbol;
    PIMAGE_SYMBOL       Symbol;
    PSYMBOL_ENTRY       sym;
    CHAR                szSymName[256];
    DWORD               i;
    DWORD64             addr;
    DWORD               CoffSymbols = 0;
    DWORD               NameSize = 0;
    DWORD64             Bias;

    allSymbols = (PIMAGE_SYMBOL)((PCHAR)pCoffHeader +
                 pCoffHeader->LvaToFirstSymbol);

    stringTable = (PUCHAR)pCoffHeader +
                  pCoffHeader->LvaToFirstSymbol +
                  (pCoffHeader->NumberOfSymbols * IMAGE_SIZEOF_SYMBOL);

    numberOfSymbols = pCoffHeader->NumberOfSymbols;
    LineNumbers = (PIMAGE_LINENUMBER)(PCHAR)pCoffHeader +
                        pCoffHeader->LvaToFirstLinenumber;

    //
    // count the number of actual symbols
    //
    NextSymbol = allSymbols;
    for (i= 0; i < numberOfSymbols; i++) {
        Symbol = NextSymbol++;
        if (Symbol->StorageClass == IMAGE_SYM_CLASS_EXTERNAL &&
            Symbol->SectionNumber > 0) {
            GetSymName( Symbol, stringTable, szSymName, sizeof(szSymName) );
            if (szSymName[0] == '?' && szSymName[1] == '?' &&
                szSymName[2] == '_' && szSymName[3] == 'C'    ) {
                //
                // ignore strings
                //
            } else if (g.SymOptions & SYMOPT_UNDNAME) {
                SymUnDNameInternal(mi->TmpSym.Name,
                                   TMP_SYM_LEN,
                                   szSymName,
                                   strlen(szSymName),
                                   mi->MachineType,
                                   TRUE);
                NameSize += strlen(mi->TmpSym.Name);
                CoffSymbols += 1;
            } else {
                CoffSymbols += 1;
                NameSize += (strlen(szSymName) + 1);
            }
        }

        NextSymbol += Symbol->NumberOfAuxSymbols;
        i += Symbol->NumberOfAuxSymbols;
    }

    //
    // allocate the symbol table
    //
    if (!CreateSymbolTable( mi, CoffSymbols, SymCoff, NameSize )) {
        return FALSE;
    }

    //
    // populate the symbol table
    //

    if (mi->Flags & MIF_ROM_IMAGE) {
        Bias = mi->BaseOfDll & 0xffffffff00000000;
    } else {
        Bias = mi->BaseOfDll;
    }

    NextSymbol = allSymbols;
    for (i= 0; i < numberOfSymbols; i++) {
        Symbol = NextSymbol++;
        if (Symbol->StorageClass == IMAGE_SYM_CLASS_EXTERNAL &&
            Symbol->SectionNumber > 0) {
            GetSymName( Symbol, stringTable, szSymName, sizeof(szSymName) );
            addr = Symbol->Value + Bias;
            if (szSymName[0] == '?' && szSymName[1] == '?' &&
                szSymName[2] == '_' && szSymName[3] == 'C'    ) {
                //
                // ignore strings
                //
            } else if (g.SymOptions & SYMOPT_UNDNAME) {
                SymUnDNameInternal(mi->TmpSym.Name,
                                   TMP_SYM_LEN,
                                   szSymName,
                                   strlen(szSymName),
                                   mi->MachineType,
                                   TRUE);
                AllocSym( mi, addr, mi->TmpSym.Name);
            } else {
                AllocSym( mi, addr, szSymName );
            }
        }

        NextSymbol += Symbol->NumberOfAuxSymbols;
        i += Symbol->NumberOfAuxSymbols;
    }

    CompleteSymbolTable( mi );

    if (g.SymOptions & SYMOPT_LOAD_LINES) {
        AddLinesForCoff(mi, allSymbols, numberOfSymbols, LineNumbers);
    }

    return TRUE;
}

BOOL
LoadCodeViewSymbols(
    HANDLE             hProcess,
    PMODULE_ENTRY      mi,
    PIMGHLP_DEBUG_DATA pIDD
    )
{
    DWORD                   i, j;
    PPROCESS_ENTRY          pe;
    OMFSignature           *omfSig;
    OMFDirHeader           *omfDirHdr;
    OMFDirEntry            *omfDirEntry;
    OMFSymHash             *omfSymHash;
    DATASYM32              *dataSym;
    DWORD64                 addr;
    DWORD                   CvSymbols;
    DWORD                   NameSize;
    SYMBOL_ENTRY            SymEntry;

    pe = FindProcessEntry( hProcess );
    if (!pe) {
        return FALSE;
    }

    pprint(pe, "LoadCodeViewSymbols:\n"
            " hProcess   %p\n"
            " mi         %p\n"
            " pCvData    %p\n"
            " dwSize     %x\n",
            hProcess,
            mi,
            pIDD->pMappedCv,
            pIDD->cMappedCv
            );

    omfSig = (OMFSignature*) pIDD->pMappedCv;
    if ((*(DWORD *)(omfSig->Signature) != '80BN') &&
        (*(DWORD *)(omfSig->Signature) != '90BN') &&
        (*(DWORD *)(omfSig->Signature) != '11BN'))
    {
        if ((*(DWORD *)(omfSig->Signature) != '01BN') &&
            (*(DWORD *)(omfSig->Signature) != 'SDSR'))
        {
            pprint(pe, "unrecognized OMF sig: %x\n", *(DWORD *)(omfSig->Signature));
        }
        return FALSE;
    }

    //
    // count the number of actual symbols
    //
    omfDirHdr = (OMFDirHeader*) ((ULONG_PTR)omfSig + (DWORD)omfSig->filepos);
    omfDirEntry = (OMFDirEntry*) ((ULONG_PTR)omfDirHdr + sizeof(OMFDirHeader));

    NameSize = 0;
    CvSymbols = 0;

    for (i=0; i<omfDirHdr->cDir; i++,omfDirEntry++) {
        LPSTR SymbolName;
        UCHAR SymbolLen;
        SYMBOL_ENTRY SymEntry;

        if (omfDirEntry->SubSection == sstGlobalPub) {
            omfSymHash = (OMFSymHash*) ((ULONG_PTR)omfSig + omfDirEntry->lfo);
            dataSym = (DATASYM32*) ((ULONG_PTR)omfSig + omfDirEntry->lfo + sizeof(OMFSymHash));
            for (j=sizeof(OMFSymHash); j<=omfSymHash->cbSymbol; ) {
                addr = 0;
                cvExtractSymbolInfo(mi, (PCHAR) dataSym, &SymEntry, FALSE);
                if (SymEntry.Segment && (SymEntry.Segment <= mi->OriginalNumSections))
                {
                    addr = mi->OriginalSectionHdrs[SymEntry.Segment-1].VirtualAddress + SymEntry.Offset + mi->BaseOfDll;
                    SymbolName = SymEntry.Name;
                    SymbolLen =  (UCHAR) SymEntry.NameLength;
                    if (SymbolName[0] == '?' &&
                        SymbolName[1] == '?' &&
                        SymbolName[2] == '_' &&
                        SymbolName[3] == 'C' )
                    {
                        //
                        // ignore strings
                        //
                    } else if (g.SymOptions & SYMOPT_UNDNAME) {
                        SymUnDNameInternal(mi->TmpSym.Name,
                                           TMP_SYM_LEN,
                                           SymbolName,
                                           SymbolLen,
                                           mi->MachineType,
                                           TRUE);
                        NameSize += strlen(mi->TmpSym.Name);
                        CvSymbols += 1;
                    } else {
                        CvSymbols += 1;
                        NameSize += SymbolLen + 1;
                    }
                }
                j += dataSym->reclen + 2;
                dataSym = (DATASYM32*) ((ULONG_PTR)dataSym + dataSym->reclen + 2);
            }
            break;
        }
    }

    //
    // allocate the symbol table
    //
    if (!CreateSymbolTable( mi, CvSymbols, SymCv, NameSize )) {
        pprint(pe, "CreateSymbolTable failed\n");
        return FALSE;
    }

    //
    // populate the symbol table
    //
    omfDirHdr = (OMFDirHeader*) ((ULONG_PTR)omfSig + (DWORD)omfSig->filepos);
    omfDirEntry = (OMFDirEntry*) ((ULONG_PTR)omfDirHdr + sizeof(OMFDirHeader));
    for (i=0; i<omfDirHdr->cDir; i++,omfDirEntry++) {
        LPSTR SymbolName;
        if (omfDirEntry->SubSection == sstGlobalPub) {
            omfSymHash = (OMFSymHash*) ((ULONG_PTR)omfSig + omfDirEntry->lfo);
            dataSym = (DATASYM32*) ((ULONG_PTR)omfSig + omfDirEntry->lfo + sizeof(OMFSymHash));
            for (j=sizeof(OMFSymHash); j<=omfSymHash->cbSymbol; ) {
                addr = 0;
                cvExtractSymbolInfo(mi, (PCHAR) dataSym, &SymEntry, FALSE);


                if (SymEntry.Segment && (SymEntry.Segment <= mi->OriginalNumSections))
                {
                    addr = mi->OriginalSectionHdrs[SymEntry.Segment-1].VirtualAddress + SymEntry.Offset + mi->BaseOfDll;
                    SymbolName = SymEntry.Name;
                    if (SymbolName[0] == '?' &&
                        SymbolName[1] == '?' &&
                        SymbolName[2] == '_' &&
                        SymbolName[3] == 'C' )
                    {
                        //
                        // ignore strings
                        //
                    } else if (g.SymOptions & SYMOPT_UNDNAME) {
                        SymUnDNameInternal(mi->TmpSym.Name,
                                           TMP_SYM_LEN,
                                           SymbolName,
                                           SymEntry.NameLength,
                                           mi->MachineType,
                                           TRUE);
                        AllocSym( mi, addr, (LPSTR) mi->TmpSym.Name);
                    } else {
                        mi->TmpSym.NameLength = SymEntry.NameLength;
                        memcpy( mi->TmpSym.Name, SymbolName, mi->TmpSym.NameLength );
                        mi->TmpSym.Name[mi->TmpSym.NameLength] = 0;
                        AllocSym( mi, addr, mi->TmpSym.Name);
                    }
                }
                j += dataSym->reclen + 2;
                dataSym = (DATASYM32*) ((ULONG_PTR)dataSym + dataSym->reclen + 2);
            }
            break;
        }
        else if (omfDirEntry->SubSection == sstSrcModule &&
                 (g.SymOptions & SYMOPT_LOAD_LINES)) {
            AddLinesForOmfSourceModule(mi,
                                       (PUCHAR)(pIDD->pMappedCv)+omfDirEntry->lfo,
                                       (OMFSourceModule *)
                                       ((PCHAR)(pIDD->pMappedCv)+omfDirEntry->lfo),
                                       NULL);
        }
    }

    CompleteSymbolTable( mi );

    return TRUE;
}

VOID
GetSymName(
    PIMAGE_SYMBOL Symbol,
    PUCHAR        StringTable,
    LPSTR         s,
    DWORD         size
    )
{
    DWORD i;

    if (Symbol->n_zeroes) {
        for (i=0; i<8; i++) {
            if ((Symbol->n_name[i]>0x1f) && (Symbol->n_name[i]<0x7f)) {
                *s++ = Symbol->n_name[i];
            }
        }
        *s = 0;
    }
    else {
        strncpy( s, (char *) &StringTable[Symbol->n_offset], size );
    }
}


VOID
ProcessOmapForModule(
    PMODULE_ENTRY      mi,
    PIMGHLP_DEBUG_DATA pIDD
    )
{
    PSYMBOL_ENTRY       sym;
    PSYMBOL_ENTRY       symN;
    DWORD               i;
    ULONG64             addr;
    DWORD               bias;
    PFPO_DATA           fpo;

    if (pIDD->cOmapTo && pIDD->pOmapTo) {
        if (pIDD->fOmapToMapped || pIDD->dia) {
            mi->pOmapTo = (POMAP)MemAlloc(pIDD->cOmapTo * sizeof(OMAP));
            if (mi->pOmapTo) {
                CopyMemory(
                    mi->pOmapTo,
                    pIDD->pOmapTo,
                    pIDD->cOmapTo * sizeof(OMAP)
                    );
            }
        } else {
            mi->pOmapTo = pIDD->pOmapTo;
        }
        mi->cOmapTo = pIDD->cOmapTo;
    }

    if (pIDD->cOmapFrom && pIDD->pOmapFrom) {
        if (pIDD->fOmapFromMapped) {
            mi->pOmapFrom = (POMAP)MemAlloc(pIDD->cOmapFrom * sizeof(OMAP));
            if (mi->pOmapFrom) {
                CopyMemory(
                    mi->pOmapFrom,
                    pIDD->pOmapFrom,
                    pIDD->cOmapFrom * sizeof(OMAP)
                    );
            }
        } else {
            mi->pOmapFrom = pIDD->pOmapFrom;
        }
        mi->cOmapFrom = pIDD->cOmapFrom;
    }

    if (mi->pFpoData) {
        //
        // if this module is BBT-optimized, then build
        // another fpo table with omap transalation
        //
        mi->pFpoDataOmap = (PFPO_DATA)VirtualAlloc(
            NULL,
            sizeof(FPO_DATA) * mi->dwEntries,
            MEM_COMMIT,
            PAGE_READWRITE
            );
        if (mi->pFpoDataOmap) {
            CopyMemory(
                mi->pFpoDataOmap,
                pIDD->pFpo,
                sizeof(FPO_DATA) * mi->dwEntries
                );
            for (i = 0, fpo = mi->pFpoDataOmap;
                 i < mi->dwEntries;
                 i++, fpo++) {
                addr =  ConvertOmapFromSrc(mi,
                                           mi->BaseOfDll + fpo->ulOffStart,
                                           &bias);
                if (addr)
                    fpo->ulOffStart = (ULONG)(addr - mi->BaseOfDll) + bias;
            }
            VirtualProtect(
                mi->pFpoData,
                sizeof(FPO_DATA) * mi->dwEntries,
                PAGE_READONLY,
                &i
                );
        }
    }

    if (!mi->pOmapFrom ||
        !mi->symbolTable ||
        ((mi->SymType != SymCoff) && (mi->SymType != SymCv))
       )
    {
        return;
    }

    for (i=0; i<mi->numsyms; i++) {
        ProcessOmapSymbol( mi, &mi->symbolTable[i] );
    }

    CompleteSymbolTable( mi );
}


BOOL
ProcessOmapSymbol(
    PMODULE_ENTRY       mi,
    PSYMBOL_ENTRY       sym
    )
{
    DWORD           bias;
    DWORD64         OptimizedSymAddr;
    DWORD           rvaSym;
    POMAPLIST       pomaplistHead;
    DWORD64         SymbolValue;
    DWORD64         OrgSymAddr;
    POMAPLIST       pomaplistNew;
    POMAPLIST       pomaplistPrev;
    POMAPLIST       pomaplistCur;
    POMAPLIST       pomaplistNext;
    DWORD           rva;
    DWORD           rvaTo;
    DWORD           cb;
    DWORD           end;
    DWORD           rvaToNext;
    LPSTR           NewSymName;
    CHAR            Suffix[32];
    DWORD64         addrNew;
    POMAP           pomap;
    PSYMBOL_ENTRY   symOmap;

    if ((sym->Flags & SYMF_OMAP_GENERATED) || (sym->Flags & SYMF_OMAP_MODIFIED)) {
        return FALSE;
    }

    OrgSymAddr = SymbolValue = sym->Address;

    OptimizedSymAddr = ConvertOmapFromSrc( mi, SymbolValue, &bias );

    if (OptimizedSymAddr == 0) {
        //
        // No equivalent address
        //
        sym->Address = 0;
        return FALSE;

    }

    //
    // We have successfully converted
    //
    sym->Address = OptimizedSymAddr + bias;

    rvaSym = (ULONG)(SymbolValue - mi->BaseOfDll);
    SymbolValue = sym->Address;

    pomap = GetOmapFromSrcEntry( mi, OrgSymAddr );
    if (!pomap) {
        goto exit;
    }

    pomaplistHead = NULL;

    //
    // Look for all OMAP entries belonging to SymbolEntry
    //

    end = (ULONG)(OrgSymAddr - mi->BaseOfDll + sym->Size);

    while (pomap && (pomap->rva < end)) {

        if (pomap->rvaTo == 0) {
            pomap++;
            continue;
        }

        //
        // Allocate and initialize a new entry
        //
        pomaplistNew = (POMAPLIST) MemAlloc( sizeof(OMAPLIST) );
        if (!pomaplistNew) {
            return FALSE;
        }

        pomaplistNew->omap = *pomap;
        pomaplistNew->cb = pomap[1].rva - pomap->rva;

        pomaplistPrev = NULL;
        pomaplistCur = pomaplistHead;

        while (pomaplistCur != NULL) {
            if (pomap->rvaTo < pomaplistCur->omap.rvaTo) {
                //
                // Insert between Prev and Cur
                //
                break;
            }
            pomaplistPrev = pomaplistCur;
            pomaplistCur = pomaplistCur->next;
        }

        if (pomaplistPrev == NULL) {
            //
            // Insert in head position
            //
            pomaplistHead = pomaplistNew;
        } else {
            pomaplistPrev->next = pomaplistNew;
        }

        pomaplistNew->next = pomaplistCur;

        pomap++;
    }

    if (pomaplistHead == NULL) {
        goto exit;
    }

    pomaplistCur = pomaplistHead;
    pomaplistNext = pomaplistHead->next;

    //
    // we do have a list
    //
    while (pomaplistNext != NULL) {
        rva = pomaplistCur->omap.rva;
        rvaTo  = pomaplistCur->omap.rvaTo;
        cb = pomaplistCur->cb;
        rvaToNext = pomaplistNext->omap.rvaTo;

        if (rvaToNext == sym->Address - mi->BaseOfDll) {
            //
            // Already inserted above
            //
        } else if (rvaToNext < (rvaTo + cb + 8)) {
            //
            // Adjacent to previous range
            //
        } else {
            addrNew = mi->BaseOfDll + rvaToNext;
            Suffix[0] = '_';
            _ltoa( pomaplistNext->omap.rva - rvaSym, &Suffix[1], 10 );
            memcpy( mi->TmpSym.Name, sym->Name, sym->NameLength );
            strncpy( &mi->TmpSym.Name[sym->NameLength], Suffix, strlen(Suffix) + 1 );
            symOmap = AllocSym( mi, addrNew, mi->TmpSym.Name);
            if (symOmap) {
                symOmap->Flags |= SYMF_OMAP_GENERATED;
            }
        }

        MemFree(pomaplistCur);

        pomaplistCur = pomaplistNext;
        pomaplistNext = pomaplistNext->next;
    }

    MemFree(pomaplistCur);

exit:
    if (sym->Address != OrgSymAddr) {
        sym->Flags |= SYMF_OMAP_MODIFIED;
    }

    return TRUE;
}


DWORD64
ConvertOmapFromSrc(
    PMODULE_ENTRY  mi,
    DWORD64        addr,
    LPDWORD        bias
    )
{
    DWORD   rva;
    DWORD   comap;
    POMAP   pomapLow;
    POMAP   pomapHigh;
    DWORD   comapHalf;
    POMAP   pomapMid;


    *bias = 0;

    if (!mi->pOmapFrom) {
        return addr;
    }

    rva = (DWORD)(addr - mi->BaseOfDll);

    comap = mi->cOmapFrom;
    pomapLow = mi->pOmapFrom;
    pomapHigh = pomapLow + comap;

    while (pomapLow < pomapHigh) {

        comapHalf = comap / 2;

        pomapMid = pomapLow + ((comap & 1) ? comapHalf : (comapHalf - 1));

        if (rva == pomapMid->rva) {
            if (pomapMid->rvaTo) {
                return mi->BaseOfDll + pomapMid->rvaTo;
            } else {
                return(0);      // No need adding the base.  This address was discarded...
            }
        }

        if (rva < pomapMid->rva) {
            pomapHigh = pomapMid;
            comap = (comap & 1) ? comapHalf : (comapHalf - 1);
        } else {
            pomapLow = pomapMid + 1;
            comap = comapHalf;
        }
    }

    //
    // If no exact match, pomapLow points to the next higher address
    //
    if (pomapLow == mi->pOmapFrom) {
        //
        // This address was not found
        //
        return 0;
    }

    if (pomapLow[-1].rvaTo == 0) {
        //
        // This address is in a discarded block
        //
        return 0;
    }

    //
    // Return the closest address plus the bias
    //
    *bias = rva - pomapLow[-1].rva;

    return mi->BaseOfDll + pomapLow[-1].rvaTo;
}


DWORD64
ConvertOmapToSrc(
    PMODULE_ENTRY  mi,
    DWORD64        addr,
    LPDWORD        bias,
    BOOL           fBackup
    )
{
    DWORD   rva;
    DWORD   comap;
    POMAP   pomapLow;
    POMAP   pomapHigh;
    DWORD   comapHalf;
    POMAP   pomapMid;

    *bias = 0;

    if (!mi->pOmapTo) {
        return addr;
    }

    rva = (DWORD)(addr - mi->BaseOfDll);

    comap = mi->cOmapTo;
    pomapLow = mi->pOmapTo;
    pomapHigh = pomapLow + comap;

    while (pomapLow < pomapHigh) {

        comapHalf = comap / 2;

        pomapMid = pomapLow + ((comap & 1) ? comapHalf : (comapHalf - 1));

        if (rva == pomapMid->rva) {
            if (pomapMid->rvaTo == 0) {
                //
                // We may be at the start of an inserted branch instruction
                //

                if (fBackup) {
                    //
                    // Return information about the next lower address
                    //

                    rva--;
                    pomapLow = pomapMid;
                    break;
                }

                return 0;
            }

            return mi->BaseOfDll + pomapMid->rvaTo;
        }

        if (rva < pomapMid->rva) {
            pomapHigh = pomapMid;
            comap = (comap & 1) ? comapHalf : (comapHalf - 1);
        } else {
            pomapLow = pomapMid + 1;
            comap = comapHalf;
        }
    }

    //
    // If no exact match, pomapLow points to the next higher address
    //

    if (pomapLow == mi->pOmapTo) {
        //
        // This address was not found
        //
        return 0;
    }

    // find the previous valid item in the omap

    do {
        pomapLow--;
        if (pomapLow->rvaTo)
            break;
    } while (pomapLow > mi->pOmapTo);

    // should never occur

//  assert(pomapLow->rvaTo);
    if (pomapLow->rvaTo == 0) {
        return 0;
    }

    //
    // Return the new address plus the bias
    //
    *bias = rva - pomapLow->rva;

    return mi->BaseOfDll + pomapLow->rvaTo;
}

POMAP
GetOmapFromSrcEntry(
    PMODULE_ENTRY  mi,
    DWORD64        addr
    )
{
    DWORD   rva;
    DWORD   comap;
    POMAP   pomapLow;
    POMAP   pomapHigh;
    DWORD   comapHalf;
    POMAP   pomapMid;


    if (mi->pOmapFrom == NULL) {
        return NULL;
    }

    rva = (DWORD)(addr - mi->BaseOfDll);

    comap = mi->cOmapFrom;
    pomapLow = mi->pOmapFrom;
    pomapHigh = pomapLow + comap;

    while (pomapLow < pomapHigh) {

        comapHalf = comap / 2;

        pomapMid = pomapLow + ((comap & 1) ? comapHalf : (comapHalf - 1));

        if (rva == pomapMid->rva) {
            return pomapMid;
        }

        if (rva < pomapMid->rva) {
            pomapHigh = pomapMid;
            comap = (comap & 1) ? comapHalf : (comapHalf - 1);
        } else {
            pomapLow = pomapMid + 1;
            comap = comapHalf;
        }
    }

    return NULL;
}


VOID
DumpOmapForModule(
    PMODULE_ENTRY      mi
    )
{
    POMAP pomap;
    DWORD i;

    i = sizeof(ULONG_PTR);
    i = sizeof(DWORD);

    if (!mi->pOmapFrom)
        return;

    dprint("\nOMAP FROM:\n");
    for(i = 0, pomap = mi->pOmapFrom;
        i < 100; // mi->cOmapFrom;
        i++, pomap++)
    {
        dprint("%8x %8x\n", pomap->rva, pomap->rvaTo);
    }

    if (!mi->pOmapTo)
        return;

    dprint("\nOMAP TO:\n");
    for(i = 0, pomap = mi->pOmapTo;
        i < 100; // mi->cOmapTo;
        i++, pomap++)
    {
        dprint("%8x %8x\n", pomap->rva, pomap->rvaTo);
    }
}


LPSTR
StringDup(
    LPSTR str
    )
{
    LPSTR ds = (LPSTR) MemAlloc( strlen(str) + 1 );
    if (ds) {
        strcpy( ds, str );
    }
    return ds;
}


BOOL
InternalGetModule(
    HANDLE  hProcess,
    LPSTR   ModuleName,
    DWORD64 ImageBase,
    DWORD   ImageSize,
    PVOID   Context
    )
{
    InternalLoadModule(
            hProcess,
            ModuleName,
            NULL,
            ImageBase,
            ImageSize,
            NULL,
            0,
            NULL
            );

    return TRUE;
}


BOOL
LoadedModuleEnumerator(
    HANDLE         hProcess,
    LPSTR          ModuleName,
    DWORD64        ImageBase,
    DWORD          ImageSize,
    PLOADED_MODULE lm
    )
{
    if (lm->EnumLoadedModulesCallback64) {
        return lm->EnumLoadedModulesCallback64( ModuleName, ImageBase, ImageSize, lm->Context );
    } else {
        return lm->EnumLoadedModulesCallback32( ModuleName, (DWORD)ImageBase, ImageSize, lm->Context );
    }
}


BOOL
ToggleFailCriticalErrors(
    BOOL reset
    )
{
    static UINT oldmode = 0;

    if (!(g.SymOptions & SYMOPT_FAIL_CRITICAL_ERRORS))
        return FALSE;

    if (reset)
        SetErrorMode(oldmode);
    else
        oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);

    return TRUE;
}


DWORD
fnGetFileAttributes(
    LPCTSTR lpFileName
    )
{
    DWORD rc;

    SetCriticalErrorMode();
    rc = GetFileAttributes(lpFileName);
    ResetCriticalErrorMode();

    return rc;
}


LPSTR
SymUnDNameInternal(
    LPSTR UnDecName,
    DWORD UnDecNameLength,
    LPSTR DecName,
    DWORD DecNameLength,
    DWORD MachineType,
    BOOL  IsPublic
    )
{
    LPSTR p;
    ULONG Suffix;
    ULONG i;
    LPSTR TmpDecName;

    UnDecName[0] = 0;

    if (DecName[0] == '?' || !strncmp(DecName, ".?", 2) || !strncmp(DecName, "..?", 3)) {

        __try {

            if (DecName[0] == '.') {
                if (DecName[1] == '.') {
                    Suffix = 2;
                    UnDecName[0] = '.';
                    UnDecName[1] = '.';
                }  else { // DecName[1] = '?'
                    Suffix = 1;
                    UnDecName[0] = '.';
                }
            } else {  // DecName[0] = '?'
                Suffix = 0;
            }

            TmpDecName = (LPSTR)MemAlloc( 4096 );
            if (!TmpDecName) {
                strncat( UnDecName, DecName, min(DecNameLength,UnDecNameLength) );
                return UnDecName;
            }
            TmpDecName[0] = 0;
            strncat( TmpDecName, DecName+Suffix, DecNameLength );

            if (UnDecorateSymbolName( TmpDecName,
                                     UnDecName+Suffix,
                                     UnDecNameLength-Suffix,
                                     UNDNAME_NAME_ONLY ) == 0 ) {
                strncat( UnDecName, DecName, min(DecNameLength,UnDecNameLength) );
            }

            MemFree( TmpDecName );

        } __except (EXCEPTION_EXECUTE_HANDLER) {

            strncat( UnDecName, DecName, min(DecNameLength,UnDecNameLength) );

        }

    } else {

        __try {

            if ((IsPublic && (DecName[0] == '_' || DecName[0] == '.'))
                || DecName[0] == '@') {
                DecName += 1;
                DecNameLength -= 1;
            }

            p = 0;
            for (i = 0; i < DecNameLength; i++) {
                if (DecName [i] == '@') {
                    p = &DecName [i];
                    break;
                }
            }
            if (p) {
                i = (int)(p - DecName);
            } else {
                i = min(DecNameLength,UnDecNameLength);
            }

            strncat( UnDecName, DecName, i );

        } __except (EXCEPTION_EXECUTE_HANDLER) {

            strncat( UnDecName, DecName, min(DecNameLength,UnDecNameLength) );

        }
    }

    if (g.SymOptions & SYMOPT_NO_CPP) {
        while (p = strstr( UnDecName, "::" )) {
            p[0] = '_';
            p[1] = '_';
        }
    }

    return UnDecName;
}


BOOL
MatchSymName(
    LPSTR matchName,
    LPSTR symName
    )
{
    assert(matchName && symName);
    if (!*matchName || !*symName)
        return FALSE;

    if (g.SymOptions & SYMOPT_CASE_INSENSITIVE) {
        if (!_strnicmp(matchName, symName, MAX_SYM_NAME))
            return TRUE;
    } else {
        if (!strncmp(matchName, symName, MAX_SYM_NAME))
            return TRUE;
    }

    return FALSE;
}


PIMAGEHLP_SYMBOL
symcpy32(
    PIMAGEHLP_SYMBOL  External,
    PSYMBOL_ENTRY       Internal
    )
{
    External->Address      = (ULONG)Internal->Address;
    External->Size         = Internal->Size;
    External->Flags        = Internal->Flags;

    External->Name[0] = 0;
    strncat( External->Name, Internal->Name, External->MaxNameLength );

    return External;
}

PIMAGEHLP_SYMBOL64
symcpy64(
    PIMAGEHLP_SYMBOL64  External,
    PSYMBOL_ENTRY       Internal
    )
{
    External->Address      = Internal->Address;
    External->Size         = Internal->Size;
    External->Flags        = Internal->Flags;

    External->Name[0] = 0;
    strncat( External->Name, Internal->Name, External->MaxNameLength );

    return External;
}

BOOL
SympConvertSymbol64To32(
    PIMAGEHLP_SYMBOL64 Symbol64,
    PIMAGEHLP_SYMBOL Symbol32
    )
{
    Symbol32->Address = (DWORD)Symbol64->Address;
    Symbol32->Size = Symbol64->Size;
    Symbol32->Flags = Symbol64->Flags;
    Symbol32->MaxNameLength = Symbol64->MaxNameLength;
    Symbol32->Name[0] = 0;
    strncat( Symbol32->Name, Symbol64->Name, Symbol32->MaxNameLength );

    return (Symbol64->Address >> 32) == 0;
}

BOOL
SympConvertSymbol32To64(
    PIMAGEHLP_SYMBOL Symbol32,
    PIMAGEHLP_SYMBOL64 Symbol64
    )
{
    Symbol64->Address = Symbol32->Address;
    Symbol64->Size = Symbol32->Size;
    Symbol64->Flags = Symbol32->Flags;
    Symbol64->MaxNameLength = Symbol32->MaxNameLength;
    Symbol64->Name[0] = 0;
    strncat( Symbol64->Name, Symbol32->Name, Symbol64->MaxNameLength );

    return TRUE;
}

BOOL
SympConvertLine64To32(
    PIMAGEHLP_LINE64 Line64,
    PIMAGEHLP_LINE Line32
    )
{
    Line32->Key = Line64->Key;
    Line32->LineNumber = Line64->LineNumber;
    Line32->FileName = Line64->FileName;
    Line32->Address = (DWORD)Line64->Address;

    return (Line64->Address >> 32) == 0;
}

BOOL
SympConvertLine32To64(
    PIMAGEHLP_LINE Line32,
    PIMAGEHLP_LINE64 Line64
    )
{
    Line64->Key = Line32->Key;
    Line64->LineNumber = Line32->LineNumber;
    Line64->FileName = Line32->FileName;
    Line64->Address = Line32->Address;

    return TRUE;
}

BOOL
__stdcall
ReadInProcMemory(
    HANDLE    hProcess,
    DWORD64   addr,
    PVOID     buf,
    DWORD     bytes,
    DWORD    *bytesread
    )
{
    DWORD                    rc;
    PPROCESS_ENTRY           pe;
    IMAGEHLP_CBA_READ_MEMORY rm;

    rm.addr      = addr;
    rm.buf       = buf;
    rm.bytes     = bytes;
    rm.bytesread = bytesread;

    rc = FALSE;

    __try {
        pe = FindProcessEntry(hProcess);
        if (!pe) {
            SetLastError( ERROR_INVALID_HANDLE );
            return FALSE;
        }

        if (pe->pCallbackFunction32) {
            rc = pe->pCallbackFunction32(pe->hProcess,
                                         CBA_READ_MEMORY,
                                         (PVOID)&rm,
                                         (PVOID)pe->CallbackUserContext);

        } else if (pe->pCallbackFunction64) {
            rc = pe->pCallbackFunction64(pe->hProcess,
                                         CBA_READ_MEMORY,
                                         (ULONG64)&rm,
                                         pe->CallbackUserContext);
        } else {
            SIZE_T RealBytesRead=0;
            rc = ReadProcessMemory(hProcess,
                                   (LPVOID)(ULONG_PTR)addr,
                                   buf,
                                   bytes,
                                   &RealBytesRead);
            *bytesread = (DWORD)RealBytesRead;
        }
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        rc = FALSE;
    }

    return (rc != FALSE);
}

DWORD64
miGetModuleBase(
    HANDLE  hProcess,
    DWORD64 Address
    )
{
    IMAGEHLP_MODULE64 ModuleInfo = {0};
    ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);

    if (SymGetModuleInfo64(hProcess, Address, &ModuleInfo)) {
        return ModuleInfo.BaseOfImage;
    } else {
        return 0;
    }
}

BOOL
GetPData(
    HANDLE        hp,
    PMODULE_ENTRY mi
    )
{
    BOOL status;
    ULONG cb;
    PCHAR pc;
    BOOL  fROM = FALSE;
    IMAGE_DOS_HEADER DosHeader;
    IMAGE_NT_HEADERS ImageNtHeaders;
    PIMAGE_FILE_HEADER ImageFileHdr;
    PIMAGE_OPTIONAL_HEADER ImageOptionalHdr;
    PIMAGE_OPTIONAL_HEADER32 OptionalHeader32 = NULL;
    PIMAGE_OPTIONAL_HEADER64 OptionalHeader64 = NULL;
    ULONG feCount = 0;
    ULONG i;

    HANDLE fh = 0;
    PCHAR  base = NULL;
    USHORT                       filetype;
    PIMAGE_SEPARATE_DEBUG_HEADER  sdh;
    PIMAGE_DOS_HEADER dh;
    PIMAGE_NT_HEADERS inth;
    PIMAGE_OPTIONAL_HEADER32 ioh32;
    PIMAGE_OPTIONAL_HEADER64 ioh64;
    ULONG cdd;
    PCHAR p;
    PIMAGE_DEBUG_DIRECTORY dd;
    ULONG cexp = 0;
    ULONG tsize;
    ULONG csize = 0;

    // if the pdata is already loaded, return

    if (mi->pExceptionData)
        return TRUE;

    if (!LoadSymbols(hp, mi, 0))
        return FALSE;

    // try to get pdata from dia

    if (mi->dia) {
        if ((mi->pPData) && (mi->dsExceptions == dsDia))
            goto dia;

        if (diaGetPData(mi)) {
            p = (PCHAR)mi->pPData;
            csize = mi->cbPData;
            goto dia;
        }
    }

    if (!mi->dsExceptions)
        return FALSE;

    // open the file and get the file type

    SetCriticalErrorMode();

    fh = CreateFile(mi->LoadedImageName,
                    GENERIC_READ,
                    g.OSVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ? (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE) : (FILE_SHARE_READ | FILE_SHARE_WRITE),
                    NULL,
                    OPEN_EXISTING,
                    0,
                    NULL
                    );

    ResetCriticalErrorMode();

    if (fh == INVALID_HANDLE_VALUE)
        return FALSE;

    base = (PCHAR)MapItRO(fh);
    if (!base)
        goto cleanup;
    p = base;

    filetype = *(USHORT *)p;
    if (filetype == IMAGE_DOS_SIGNATURE)
        goto image;
    if (filetype == IMAGE_SEPARATE_DEBUG_SIGNATURE)
        goto dbg;
    goto cleanup;

image:

    // process disk-based image

    dh = (PIMAGE_DOS_HEADER)p;
    p  += dh->e_lfanew;
    inth = (PIMAGE_NT_HEADERS)p;

    if (inth->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
        ioh32 = (PIMAGE_OPTIONAL_HEADER32)&inth->OptionalHeader;
        p = base + ioh32->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
        csize = ioh32->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
    }
    else if (inth->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
        ioh64 = (PIMAGE_OPTIONAL_HEADER64)&inth->OptionalHeader;
        p = base + ioh64->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
        csize = ioh64->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
    }

dia:

    if (!csize)
        goto cleanup;

    switch (mi->MachineType)
    {
    case IMAGE_FILE_MACHINE_ALPHA:
        cexp = csize / sizeof(IMAGE_ALPHA_RUNTIME_FUNCTION_ENTRY);
        break;
    case IMAGE_FILE_MACHINE_ALPHA64:
        cexp = csize / sizeof(IMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY);
        break;
    case IMAGE_FILE_MACHINE_IA64:
        cexp = csize / sizeof(IMAGE_IA64_RUNTIME_FUNCTION_ENTRY);
        break;
    case IMAGE_FILE_MACHINE_AMD64:
        cexp = csize / sizeof(_IMAGE_RUNTIME_FUNCTION_ENTRY);
        break;
    default:
        goto cleanup;
    }

    goto table;

dbg:

    // process dbg file

    sdh = (PIMAGE_SEPARATE_DEBUG_HEADER)p;
    cdd = sdh->DebugDirectorySize / sizeof(IMAGE_DEBUG_DIRECTORY);
    p +=  sizeof(IMAGE_SEPARATE_DEBUG_HEADER) +
          (sdh->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)) +
          sdh->ExportedNamesSize;
    dd = (PIMAGE_DEBUG_DIRECTORY)p;

    for (i = 0; i < cdd; i++, dd++) {
        if (dd->Type == IMAGE_DEBUG_TYPE_EXCEPTION) {
            p = base + dd->PointerToRawData;
            cexp = dd->SizeOfData / sizeof(IMAGE_FUNCTION_ENTRY);
            break;
        }
    }

table:

    // parse the pdata into a table

    if (!cexp)
        goto cleanup;

    tsize = cexp * sizeof(IMGHLP_RVA_FUNCTION_DATA);

    mi->pExceptionData = (PIMGHLP_RVA_FUNCTION_DATA)VirtualAlloc( NULL, tsize, MEM_COMMIT, PAGE_READWRITE );

    if (mi->pExceptionData) {
        PIMGHLP_RVA_FUNCTION_DATA pIRFD = mi->pExceptionData;
        switch (mi->MachineType) {

        case IMAGE_FILE_MACHINE_ALPHA:
            if (filetype == IMAGE_SEPARATE_DEBUG_SIGNATURE) {
                // easy case.  The addresses are already in rva format.
                PIMAGE_FUNCTION_ENTRY pFE = (PIMAGE_FUNCTION_ENTRY)p;
                for (i = 0; i < cexp; i++) {
                    pIRFD[i].rvaBeginAddress     = pFE[i].StartingAddress;
                    pIRFD[i].rvaEndAddress       = pFE[i].EndingAddress;
                    pIRFD[i].rvaPrologEndAddress = pFE[i].EndOfPrologue;
                    pIRFD[i].rvaExceptionHandler = 0;
                    pIRFD[i].rvaHandlerData      = 0;
                }
            } else {
                PIMAGE_ALPHA_RUNTIME_FUNCTION_ENTRY pRFE = (PIMAGE_ALPHA_RUNTIME_FUNCTION_ENTRY)p;
                for (i = 0; i < cexp; i++) {
                    pIRFD[i].rvaBeginAddress     = pRFE[i].BeginAddress - (ULONG)mi->BaseOfDll;
                    pIRFD[i].rvaEndAddress       = pRFE[i].EndAddress - (ULONG)mi->BaseOfDll;
                    pIRFD[i].rvaPrologEndAddress = pRFE[i].PrologEndAddress - (ULONG)mi->BaseOfDll;
                    pIRFD[i].rvaExceptionHandler = pRFE[i].ExceptionHandler - (ULONG)mi->BaseOfDll;
                    pIRFD[i].rvaHandlerData      = pRFE[i].HandlerData - (ULONG)mi->BaseOfDll;
                }
            }
            break;

        case IMAGE_FILE_MACHINE_ALPHA64:
            {
                PIMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY pRFE = (PIMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY)p;
                for (i = 0; i < cexp; i++) {
                    pIRFD[i].rvaBeginAddress     = (DWORD)(pRFE[i].BeginAddress - mi->BaseOfDll);
                    pIRFD[i].rvaEndAddress       = (DWORD)(pRFE[i].EndAddress - mi->BaseOfDll);
                    pIRFD[i].rvaPrologEndAddress = (DWORD)(pRFE[i].PrologEndAddress - mi->BaseOfDll);
                    pIRFD[i].rvaExceptionHandler = (DWORD)(pRFE[i].ExceptionHandler - mi->BaseOfDll);
                    pIRFD[i].rvaHandlerData      = (DWORD)(pRFE[i].HandlerData - mi->BaseOfDll);
                }
            }
            break;

        case IMAGE_FILE_MACHINE_IA64:
            {
                PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY pRFE = (PIMAGE_IA64_RUNTIME_FUNCTION_ENTRY)p;
                for (i = 0; i < cexp; i++) {
                    pIRFD[i].rvaBeginAddress     = pRFE[i].BeginAddress;
                    pIRFD[i].rvaEndAddress       = pRFE[i].EndAddress;
                    pIRFD[i].rvaPrologEndAddress = pRFE[i].UnwindInfoAddress;
                    pIRFD[i].rvaExceptionHandler = 0;
                    pIRFD[i].rvaHandlerData      = 0;
                }
            }
            break;

        case IMAGE_FILE_MACHINE_AMD64:
            {
                _PIMAGE_RUNTIME_FUNCTION_ENTRY pRFE = (_PIMAGE_RUNTIME_FUNCTION_ENTRY)p;
                for (i = 0; i < cexp; i++) {
                    pIRFD[i].rvaBeginAddress     = pRFE[i].BeginAddress;
                    pIRFD[i].rvaEndAddress       = pRFE[i].EndAddress;
                    pIRFD[i].rvaPrologEndAddress = pRFE[i].UnwindInfoAddress;
                    pIRFD[i].rvaExceptionHandler = 0;
                    pIRFD[i].rvaHandlerData      = 0;
                }
            }
            break;

        default:
            break;
        }

        VirtualProtect( mi->pExceptionData, tsize, PAGE_READONLY, &i );

        mi->dwEntries = cexp;
    }

cleanup:

    if (mi->pPData) {
        MemFree(mi->pPData);
        mi->pPData = NULL;
    }

    if (base)
        UnmapViewOfFile(base);

    if (fh)
        CloseHandle(fh);

    return (cexp) ? TRUE : FALSE;
}

BOOL
GetXData(
    HANDLE        hp,
    PMODULE_ENTRY mi
    )
{
    if (mi->pXData)
        return TRUE;

    if (LoadSymbols(hp, mi, 0) && !mi->pXData && mi->dia && !diaGetXData(mi))
        return FALSE;

    return (mi->pXData != NULL);
}

PVOID
GetXDataFromBase(
    HANDLE     hp,
    DWORD64    base,
    ULONG_PTR* size
    )
{
    PPROCESS_ENTRY pe;
    PMODULE_ENTRY  mi;

    pe = FindProcessEntry(hp);
    if (!pe) {
        SetLastError(ERROR_INVALID_HANDLE);
        return NULL;
    }

    mi = GetModuleForPC(pe, base, FALSE);
    if (!mi) {
        SetLastError(ERROR_MOD_NOT_FOUND);
        return NULL;
    }

    if (!GetXData(hp, mi))
        return NULL;

    if (size) *size = mi->cbXData;
    return mi->pXData;
}

PVOID
GetUnwindInfoFromSymbols(
    HANDLE     hProcess,
    DWORD64    ModuleBase,
    ULONG      UnwindInfoAddress,
    ULONG_PTR* Size
    )
{
    ULONG_PTR XDataSize;

    PBYTE pXData = (PBYTE)GetXDataFromBase(hProcess, ModuleBase, &XDataSize);
    if (!pXData)
        return NULL;

    DWORD DataBase = *(DWORD*)pXData;
    pXData += sizeof(DWORD);

    if (DataBase > UnwindInfoAddress)
        return NULL;

    ULONG_PTR Offset = UnwindInfoAddress - DataBase;

    if (Offset >= XDataSize)
        return NULL;

    if (Size) *Size = XDataSize - Offset;
    return pXData + Offset;
}