|
|
/***
*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; }
|