524 lines
16 KiB
C++
524 lines
16 KiB
C++
/***
|
|
*pdblkup.cpp - RTC support
|
|
*
|
|
* Copyright (c) 1998-2001, Microsoft Corporation. All rights reserved.
|
|
*
|
|
*
|
|
*Revision History:
|
|
* 07-28-98 JWM Module incorporated into CRTs (from KFrei)
|
|
* 05-11-99 KBF Error if RTC support define not enabled
|
|
* 05-26-99 KBF Minor Cleanup, _RTC_ prefix added to GetSrcLine
|
|
* 11-30-99 PML Compile /Wp64 clean.
|
|
* 06-20-00 KBF Major mods to use PDBOpenValidate3 & MSPDB70
|
|
* 03-19-01 KBF Fix buffer overruns (vs7#227306), eliminate all /GS
|
|
* checks (vs7#224261), use correct VS7 registry key.
|
|
* 03-28-01 PML Protect against GetModuleFileName overflow (vs7#231284)
|
|
*
|
|
****/
|
|
|
|
#ifndef _RTC
|
|
#error RunTime Check support not enabled!
|
|
#endif
|
|
|
|
#include "rtcpriv.h"
|
|
#include <tlhelp32.h>
|
|
|
|
#pragma warning(disable:4311 4312) // 32-bit specific, ignore /Wp64 warnings
|
|
|
|
#define REGISTRY_KEY_MASTER HKEY_LOCAL_MACHINE
|
|
#define REGISTRY_KEY_NAME "EnvironmentDirectory"
|
|
#define REGISTRY_KEY_LOCATION "SOFTWARE\\Microsoft\\VisualStudio\\7.0\\Setup\\VS"
|
|
|
|
static const char *mspdbName = "MSPDB70.DLL";
|
|
static const mspdbNameLen = 11;
|
|
|
|
// Here's some stuff from the PDB header
|
|
typedef char * SZ;
|
|
typedef ULONG SIG; // unique (across PDB instances) signature
|
|
typedef long EC; // error code
|
|
typedef USHORT ISECT; // section index
|
|
typedef LONG OFF; // offset
|
|
typedef LONG CB; // count of bytes
|
|
typedef BYTE* PB; // pointer to some bytes
|
|
struct PDB; // program database
|
|
struct DBI; // debug information within the PDB
|
|
struct Mod; // a module within the DBI
|
|
#define pdbRead "r"
|
|
|
|
// Here's some stuff from the psapi header
|
|
typedef struct _MODULEINFO {
|
|
LPVOID lpBaseOfDll;
|
|
DWORD SizeOfImage;
|
|
LPVOID EntryPoint;
|
|
} MODULEINFO, *LPMODULEINFO;
|
|
|
|
static HINSTANCE mspdb = 0;
|
|
static HINSTANCE psapi = 0;
|
|
static HINSTANCE imghlp = 0;
|
|
static HINSTANCE kernel = 0;
|
|
|
|
#define declare(rettype, call_type, name, parms)\
|
|
extern "C" { typedef rettype ( call_type * name ## Proc) parms; }
|
|
#define decldef(rettype, call_type, name, parms)\
|
|
declare(rettype, call_type, name, parms)\
|
|
static name ## Proc name = 0
|
|
|
|
#define GetProcedure(lib, name) name = (name ## Proc)GetProcAddress(lib, #name)
|
|
#define GetW9xProc(lib, name) name ## W9x = (name ## W9xProc)GetProcAddress(lib, #name)
|
|
|
|
#define GetReqProcedure(lib, name, err) {if (!(GetProcedure(lib, name))) return err;}
|
|
#define GetReqW9xProc(lib, name, err) {if (!(GetW9xProc(lib, name))) return err;}
|
|
|
|
|
|
/* PDB functions */
|
|
decldef(BOOL, __cdecl, PDBOpenValidate3,
|
|
(SZ szExe, SZ szPath, OUT EC* pec, OUT SZ szError, OUT SZ szDbgPath, OUT DWORD *pfo, OUT DWORD *pcb, OUT PDB** pppdb));
|
|
decldef(BOOL, __cdecl, PDBOpenDBI,
|
|
(PDB* ppdb, SZ szMode, SZ szTarget, OUT DBI** ppdbi));
|
|
decldef(BOOL, __cdecl, DBIQueryModFromAddr,
|
|
(DBI* pdbi, ISECT isect, OFF off, OUT Mod** ppmod, OUT ISECT* pisect, OUT OFF* poff, OUT CB* pcb));
|
|
decldef(BOOL, __cdecl, ModQueryLines,
|
|
(Mod* pmod, PB pbLines, CB* pcb));
|
|
decldef(BOOL, __cdecl, ModClose,
|
|
(Mod* pmod));
|
|
decldef(BOOL, __cdecl, DBIClose,
|
|
(DBI* pdbi));
|
|
decldef(BOOL, __cdecl, PDBClose,
|
|
(PDB* ppdb));
|
|
|
|
/* ImageHlp Functions */
|
|
decldef(PIMAGE_NT_HEADERS, __stdcall, ImageNtHeader,
|
|
(IN PVOID Base));
|
|
|
|
/* PSAPI Functions */
|
|
decldef(BOOL, WINAPI, GetModuleInformation,
|
|
(HANDLE hProcess, HMODULE hModule, LPMODULEINFO lpmodinfo, DWORD cb));
|
|
decldef(BOOL, WINAPI, EnumProcessModules,
|
|
(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded));
|
|
|
|
/* Win9X Functions */
|
|
decldef(HANDLE, WINAPI, CreateToolhelp32SnapshotW9x,
|
|
(DWORD dwFlags, DWORD th32ProcessID));
|
|
decldef(BOOL, WINAPI, Module32FirstW9x,
|
|
(HANDLE hSnapshot, LPMODULEENTRY32 lpme));
|
|
decldef(BOOL, WINAPI, Module32NextW9x,
|
|
(HANDLE hSnapshot, LPMODULEENTRY32 lpme));
|
|
|
|
/* AdvAPI32 Functions */
|
|
declare(WINADVAPI LONG, APIENTRY, RegOpenKeyExA,
|
|
(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult));
|
|
declare(WINADVAPI LONG, APIENTRY, RegQueryValueExA,
|
|
(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData));
|
|
declare(WINADVAPI LONG, APIENTRY, RegCloseKey,
|
|
(HKEY hKey));
|
|
|
|
struct ImageInfo {
|
|
DWORD sig;
|
|
DWORD BaseAddress;
|
|
DWORD BaseSize;
|
|
HMODULE hndl;
|
|
PIMAGE_NT_HEADERS img;
|
|
PIMAGE_SECTION_HEADER sectHdr;
|
|
char *imgName;
|
|
ImageInfo *next;
|
|
};
|
|
|
|
static ImageInfo *lImages = 0;
|
|
|
|
// I do lots of assignments in conditionals intentionally
|
|
#pragma warning(disable:4706)
|
|
|
|
static ImageInfo *
|
|
GetImageInfo(DWORD address)
|
|
{
|
|
ImageInfo *res, *cur;
|
|
// This will not run the first time, because lImages is null
|
|
for (res = lImages; res; res = res->next)
|
|
{
|
|
if (res->BaseAddress <= address && address - res->BaseAddress <= res->BaseSize)
|
|
return res;
|
|
}
|
|
|
|
// We didn't find the address in the images we already know about
|
|
// Let's refresh the image list, to see if it was delay-loaded
|
|
|
|
// Clear out the old list
|
|
while (lImages)
|
|
{
|
|
ImageInfo *next = lImages->next;
|
|
HeapFree(GetProcessHeap(), 0, lImages);
|
|
lImages = next;
|
|
}
|
|
|
|
if (!imghlp)
|
|
{
|
|
// We haven't already loaded all the DLL entrypoints we need
|
|
|
|
kernel = LoadLibrary("KERNEL32.DLL");
|
|
imghlp = LoadLibrary("IMAGEHLP.DLL");
|
|
|
|
if (!kernel || !imghlp)
|
|
return 0;
|
|
|
|
GetReqProcedure(imghlp, ImageNtHeader, 0);
|
|
|
|
GetW9xProc(kernel, CreateToolhelp32Snapshot);
|
|
|
|
if (!CreateToolhelp32SnapshotW9x)
|
|
{
|
|
// We're running under WinNT, use PSAPI DLL
|
|
|
|
psapi = LoadLibrary("PSAPI.DLL");
|
|
if (!psapi)
|
|
return 0;
|
|
|
|
GetReqProcedure(psapi, EnumProcessModules, 0);
|
|
GetReqProcedure(psapi, GetModuleInformation, 0);
|
|
} else
|
|
{
|
|
// We're running under Win9X, use the toolhelp functions
|
|
GetReqW9xProc(kernel, Module32First, 0);
|
|
GetReqW9xProc(kernel, Module32Next, 0);
|
|
}
|
|
}
|
|
|
|
// Now we have all the callbacks we need, so get the process information needed
|
|
if (!CreateToolhelp32SnapshotW9x)
|
|
{
|
|
// We're running under NT4
|
|
// Note that I "prefer" using toolhelp32 - it's supposed to show up in NT5...
|
|
HMODULE hModules[512];
|
|
HANDLE hProcess = GetCurrentProcess();
|
|
DWORD imageCount;
|
|
|
|
if (!EnumProcessModules(hProcess, hModules, 512 * sizeof(HMODULE), &imageCount))
|
|
return 0;
|
|
|
|
imageCount /= sizeof(HMODULE);
|
|
|
|
MODULEINFO info;
|
|
for (DWORD i = 0; i < imageCount; i++)
|
|
{
|
|
if (!GetModuleInformation(hProcess, hModules[i], &info, sizeof(MODULEINFO)))
|
|
return 0;
|
|
|
|
if (!(cur = (ImageInfo *)HeapAlloc(GetProcessHeap(), 0, sizeof(ImageInfo))))
|
|
goto CHOKE;
|
|
|
|
cur->hndl = hModules[i];
|
|
cur->BaseAddress = (DWORD)info.lpBaseOfDll;
|
|
cur->BaseSize = info.SizeOfImage;
|
|
cur->imgName = 0;
|
|
|
|
cur->next = lImages;
|
|
lImages = cur;
|
|
}
|
|
} else
|
|
{
|
|
HANDLE snap;
|
|
if ((snap = CreateToolhelp32SnapshotW9x(TH32CS_SNAPMODULE, 0)) == (HANDLE)-1)
|
|
return 0;
|
|
|
|
MODULEENTRY32 *info = (MODULEENTRY32*)_alloca(sizeof(MODULEENTRY32));
|
|
info->dwSize = sizeof(MODULEENTRY32);
|
|
if (Module32FirstW9x(snap, info))
|
|
{
|
|
do {
|
|
ImageInfo *newImg;
|
|
|
|
if (!(newImg = (ImageInfo *)HeapAlloc(GetProcessHeap(), 0, sizeof(ImageInfo))))
|
|
{
|
|
CloseHandle(snap);
|
|
goto CHOKE;
|
|
}
|
|
|
|
newImg->hndl = info->hModule;
|
|
newImg->BaseAddress = (DWORD)info->modBaseAddr;
|
|
newImg->BaseSize = info->modBaseSize;
|
|
newImg->imgName = 0;
|
|
|
|
newImg->next = lImages;
|
|
lImages = newImg;
|
|
|
|
} while (Module32NextW9x(snap, info));
|
|
}
|
|
CloseHandle(snap);
|
|
}
|
|
|
|
for (cur = lImages; cur; cur = cur->next)
|
|
{
|
|
cur->img = ImageNtHeader((void *)cur->BaseAddress);
|
|
cur->sectHdr = IMAGE_FIRST_SECTION(cur->img);
|
|
char *buf = (char*)_alloca(513);
|
|
buf[512] = '\0';
|
|
if (!GetModuleFileName(cur->hndl, buf, 512))
|
|
goto CHOKE;
|
|
int nmLen;
|
|
for (nmLen = 0; buf[nmLen]; nmLen++) {}
|
|
if (!(cur->imgName = (char*)HeapAlloc(GetProcessHeap(), 0, nmLen+1)))
|
|
goto CHOKE;
|
|
nmLen = 0;
|
|
do {
|
|
cur->imgName[nmLen] = buf[nmLen];
|
|
} while (buf[nmLen++]);
|
|
|
|
}
|
|
|
|
for (res = lImages; res; res = res->next)
|
|
{
|
|
if (res->BaseAddress <= address && address - res->BaseAddress <= res->BaseSize)
|
|
return res;
|
|
}
|
|
|
|
CHOKE:
|
|
while (lImages) {
|
|
ImageInfo *next = lImages->next;
|
|
|
|
if (lImages->imgName)
|
|
HeapFree(GetProcessHeap(), 0, lImages->imgName);
|
|
HeapFree(GetProcessHeap(), 0, lImages);
|
|
lImages = next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static HINSTANCE
|
|
GetPdbDll()
|
|
{
|
|
static BOOL alreadyTried = FALSE;
|
|
// If we already tried to load it, return
|
|
if (alreadyTried)
|
|
return (HINSTANCE)0;
|
|
alreadyTried = TRUE;
|
|
|
|
HINSTANCE res;
|
|
if (res = LoadLibrary(mspdbName))
|
|
return res;
|
|
|
|
// Load the AdvAPI32.DLL entrypoints
|
|
HINSTANCE advapi32;
|
|
if (!(advapi32 = LoadLibrary("ADVAPI32.DLL")))
|
|
return 0;
|
|
RegOpenKeyExAProc RegOpenKeyExA;
|
|
GetReqProcedure(advapi32, RegOpenKeyExA, 0);
|
|
RegQueryValueExAProc RegQueryValueExA;
|
|
GetReqProcedure(advapi32, RegQueryValueExA, 0);
|
|
RegCloseKeyProc RegCloseKey;
|
|
GetReqProcedure(advapi32, RegCloseKey, 0);
|
|
|
|
char *keyname = REGISTRY_KEY_LOCATION;
|
|
BYTE *buf;
|
|
HKEY key1;
|
|
long pos, err;
|
|
DWORD type, len;
|
|
|
|
err = RegOpenKeyExA(REGISTRY_KEY_MASTER, keyname, 0, KEY_QUERY_VALUE, &key1);
|
|
if (err != ERROR_SUCCESS)
|
|
{
|
|
FreeLibrary(advapi32);
|
|
return 0;
|
|
}
|
|
|
|
err = RegQueryValueExA(key1, REGISTRY_KEY_NAME, NULL, &type, 0, &len);
|
|
if (err != ERROR_SUCCESS)
|
|
return 0;
|
|
len += 2 + mspdbNameLen;
|
|
buf = (BYTE*)_alloca(len * sizeof(BYTE));
|
|
err = RegQueryValueExA(key1, REGISTRY_KEY_NAME, NULL, &type, buf, &len);
|
|
RegCloseKey(key1);
|
|
FreeLibrary(advapi32);
|
|
|
|
if (err != ERROR_SUCCESS)
|
|
return 0;
|
|
if (buf[len - 2] != '\\')
|
|
buf[len - 1] = '\\';
|
|
else
|
|
len--;
|
|
|
|
for (pos = 0; pos <= mspdbNameLen; pos++)
|
|
buf[len + pos] = mspdbName[pos];
|
|
|
|
return LoadLibrary((const char *)buf);
|
|
}
|
|
|
|
BOOL
|
|
_RTC_GetSrcLine(
|
|
DWORD address,
|
|
char* source,
|
|
int sourcelen,
|
|
int* pline,
|
|
char** moduleName
|
|
)
|
|
{
|
|
struct SSrcModuleHdr {
|
|
WORD cFile;
|
|
WORD cSeg;
|
|
};
|
|
struct SStartEnd {
|
|
DWORD start;
|
|
DWORD end;
|
|
};
|
|
SSrcModuleHdr *liHdr;
|
|
ULONG *baseSrcFile; // SSrcModuleHdr.cFile items
|
|
SStartEnd *startEnd; // SSrcModuleHdr.cSeg items
|
|
USHORT *contribSegs; // SSrcModuleHdr.cSeg items (+1 for alignement)
|
|
int i;
|
|
ImageInfo *iInf;
|
|
|
|
PDB *ppdb;
|
|
DBI *pdbi;
|
|
Mod *pmod;
|
|
|
|
EC err;
|
|
// CB_ERR_MAX from linker is 1024 - not particularly secure, but oh well.
|
|
// This whole thing should be rewritten using DIA instead of MSPDB for next rev...
|
|
char *errname = (char*)_alloca(1024);
|
|
|
|
OFF imageAddr;
|
|
OFF secAddr;
|
|
OFF offsetRes;
|
|
USHORT sectionIndex;
|
|
USHORT sectionIndexRes;
|
|
long size;
|
|
PB lineBuffer;
|
|
static BOOL PDBOK = FALSE;
|
|
|
|
BOOL res = FALSE;
|
|
|
|
*pline = 0;
|
|
*source = 0;
|
|
*moduleName = 0;
|
|
|
|
// First, find the image (DLL/EXE) in which this address occurs
|
|
iInf = GetImageInfo(address);
|
|
|
|
if (!iInf)
|
|
// We didn't find this address is the list of modules, so quit
|
|
goto DONE0;
|
|
|
|
|
|
// Now get the Relative virtual address of the address given
|
|
imageAddr = address - iInf->BaseAddress;
|
|
|
|
*moduleName = iInf->imgName;
|
|
|
|
res = TRUE;
|
|
|
|
// Try to load the PDB DLL
|
|
if (!PDBOK)
|
|
{
|
|
// If we already loaded it before, there must be some missing API function
|
|
if (mspdb || !(mspdb = GetPdbDll()))
|
|
goto DONE0;
|
|
|
|
GetReqProcedure(mspdb, PDBOpenValidate3, 0);
|
|
GetReqProcedure(mspdb, PDBOpenDBI, 0);
|
|
GetReqProcedure(mspdb, DBIQueryModFromAddr, 0);
|
|
GetReqProcedure(mspdb, ModQueryLines, 0);
|
|
GetReqProcedure(mspdb, ModClose, 0);
|
|
GetReqProcedure(mspdb, DBIClose, 0);
|
|
GetReqProcedure(mspdb, PDBClose, 0);
|
|
PDBOK = TRUE;
|
|
}
|
|
|
|
// Now find the section index & section-relative address
|
|
secAddr = -1;
|
|
for (sectionIndex = 0; sectionIndex < iInf->img->FileHeader.NumberOfSections; sectionIndex++)
|
|
{
|
|
if (iInf->sectHdr[sectionIndex].VirtualAddress < (unsigned)imageAddr &&
|
|
imageAddr - iInf->sectHdr[sectionIndex].VirtualAddress < iInf->sectHdr[sectionIndex].SizeOfRawData)
|
|
{
|
|
secAddr = imageAddr - iInf->sectHdr[sectionIndex].VirtualAddress;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (secAddr == -1)
|
|
goto DONE0;
|
|
|
|
// Open the PDB for this image
|
|
DWORD fo, cb;
|
|
char *path = (char*)_alloca(MAX_PATH);
|
|
// Rumor has it that I'll need to switch to OV5 instead of OV3 for this call soon...
|
|
if (!PDBOpenValidate3(iInf->imgName, "", &err, errname, path, &fo, &cb, &ppdb))
|
|
goto DONE0;
|
|
|
|
// Get the DBI interface for the PDB
|
|
if (!PDBOpenDBI(ppdb, pdbRead, 0, &pdbi))
|
|
goto DONE1;
|
|
|
|
// Now get the Mod from the section index & the section-relative address
|
|
if (!DBIQueryModFromAddr(pdbi, ++sectionIndex, secAddr, &pmod, §ionIndexRes, &offsetRes, &size))
|
|
goto DONE2;
|
|
|
|
// Get the size of the buffer we need
|
|
if (!ModQueryLines(pmod, 0, &size) || !size)
|
|
goto DONE3;
|
|
|
|
lineBuffer = (PB)HeapAlloc(GetProcessHeap(), 0, size);
|
|
if (!ModQueryLines(pmod, lineBuffer, &size))
|
|
goto DONE3;
|
|
|
|
// fill in the number of source files, and their corresponding regions
|
|
liHdr = (SSrcModuleHdr*)lineBuffer;
|
|
baseSrcFile = (ULONG *)(lineBuffer + sizeof(SSrcModuleHdr));
|
|
// I think I can actually ignore the rest of the module header info
|
|
startEnd = (SStartEnd *)&(baseSrcFile[liHdr->cFile]);
|
|
contribSegs = (USHORT *)&(startEnd[liHdr->cSeg]);
|
|
|
|
for (i = 0; i < liHdr->cFile; i++)
|
|
{
|
|
BYTE *srcBuff = lineBuffer + baseSrcFile[i];
|
|
USHORT segCount = *(USHORT *)srcBuff;
|
|
ULONG *baseSrcLn = &(((ULONG *)srcBuff)[1]);
|
|
SStartEnd *segStartEnd = (SStartEnd*)&(baseSrcLn[segCount]);
|
|
char *srcName = (char *)&segStartEnd[segCount];
|
|
|
|
// Step through the various bunch of segments this src file contributes
|
|
for (int j = 0; j < segCount; j++)
|
|
{
|
|
if (segStartEnd[j].start <= (unsigned)secAddr &&
|
|
(unsigned)secAddr <= segStartEnd[j].end)
|
|
{
|
|
// If this segment contains the section address,
|
|
// we've found the right one, so find the closest line number
|
|
BYTE *segLnBuf = &lineBuffer[baseSrcLn[j]];
|
|
USHORT pairCount = *(USHORT*)&(segLnBuf[sizeof(USHORT)]);
|
|
ULONG *offsets = (ULONG *)&(segLnBuf[sizeof(USHORT)*2]);
|
|
USHORT *linNums = (USHORT *)&(offsets[pairCount]);
|
|
int best = -1;
|
|
ULONG dist = 0xFFFFFFFF;
|
|
for (int k = 0; k < pairCount; k++)
|
|
{
|
|
if (secAddr - offsets[k] < dist)
|
|
{
|
|
best = k;
|
|
dist = secAddr - offsets[k];
|
|
}
|
|
}
|
|
if (best < 0)
|
|
// It shoulda been here, but it wasn't...
|
|
goto DONE4;
|
|
|
|
*pline = linNums[best];
|
|
for (j = 0; srcName[j] && j < sourcelen; j++)
|
|
source[j] = srcName[j];
|
|
source[(j < sourcelen) ? j : sourcelen-1] = 0;
|
|
|
|
goto DONE4;
|
|
}
|
|
}
|
|
}
|
|
|
|
DONE4:
|
|
HeapFree(GetProcessHeap(), 0, lineBuffer);
|
|
DONE3:
|
|
ModClose(pmod);
|
|
DONE2:
|
|
DBIClose(pdbi);
|
|
DONE1:
|
|
PDBClose(ppdb);
|
|
DONE0:
|
|
return res;
|
|
}
|