1357 lines
36 KiB
C
1357 lines
36 KiB
C
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
checksum.c
|
|
|
|
Abstract:
|
|
|
|
This module implements functions for splitting debugging information
|
|
out of an image file and into a separate .DBG file.
|
|
|
|
Author:
|
|
|
|
Steven R. Wood (stevewo) 4-May-1993
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <private.h>
|
|
#include <symbols.h>
|
|
#include <globals.h>
|
|
|
|
#define ROUNDUP(x, y) ((x + (y-1)) & ~(y-1))
|
|
|
|
typedef BOOL
|
|
(CALLBACK *PSEARCH_TREE_FOR_FILE_EX_CALLBACK)(
|
|
LPSTR FilePath,
|
|
PVOID CallerData
|
|
);
|
|
|
|
BOOL
|
|
IMAGEAPI
|
|
SearchTreeForFileEx(
|
|
PSTR RootPath,
|
|
PSTR InputPathName,
|
|
PSTR OutputPathBuffer,
|
|
PSEARCH_TREE_FOR_FILE_EX_CALLBACK Callback,
|
|
PVOID CallbackData
|
|
);
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
#if !defined(_WIN64)
|
|
|
|
PIMAGE_DEBUG_INFORMATION
|
|
IMAGEAPI
|
|
MapDebugInformation(
|
|
HANDLE FileHandle,
|
|
LPSTR FileName,
|
|
LPSTR SymbolPath,
|
|
ULONG ImageBase
|
|
)
|
|
|
|
// Here's what we're going to try. MapDebugInformation was only
|
|
// documented as returning COFF symbolic and every user I can find
|
|
// in the tree uses COFF exclusively. Rather than try to make this
|
|
// api do everything possible, let's just leave it as a COFF only thing.
|
|
|
|
// The new debug info api (GetDebugData) will be internal only.
|
|
|
|
{
|
|
PIMAGE_DEBUG_INFORMATION pIDI;
|
|
CHAR szName[_MAX_FNAME];
|
|
CHAR szExt[_MAX_EXT];
|
|
PIMGHLP_DEBUG_DATA pIDD;
|
|
PPIDI pPIDI;
|
|
DWORD sections;
|
|
BOOL SymbolsLoaded;
|
|
HANDLE hProcess;
|
|
LPSTR sz;
|
|
HANDLE hdb;
|
|
DWORD dw;
|
|
hProcess = GetCurrentProcess();
|
|
|
|
pIDD = GetDebugData(NULL, FileHandle, FileName, SymbolPath, ImageBase, NULL, NO_PE64_IMAGES);
|
|
if (!pIDD)
|
|
return NULL;
|
|
|
|
pPIDI = (PPIDI)MemAlloc(sizeof(PIDI));
|
|
if (!pPIDI)
|
|
return NULL;
|
|
|
|
ZeroMemory(pPIDI, sizeof(PIDI));
|
|
pIDI = &pPIDI->idi;
|
|
pPIDI->hdr.pIDD = pIDD;
|
|
|
|
pIDI->ReservedSize = sizeof(IMAGE_DEBUG_INFORMATION);
|
|
pIDI->ReservedMachine = pIDD->Machine;
|
|
pIDI->ReservedCharacteristics = (USHORT)pIDD->Characteristics;
|
|
pIDI->ReservedCheckSum = pIDD->CheckSum;
|
|
pIDI->ReservedTimeDateStamp = pIDD->TimeDateStamp;
|
|
pIDI->ReservedRomImage = pIDD->fROM;
|
|
|
|
// read info
|
|
|
|
InitializeListHead( &pIDI->List );
|
|
pIDI->ImageBase = (ULONG)pIDD->ImageBaseFromImage;
|
|
|
|
pIDI->ImageFilePath = (PSTR)MemAlloc(strlen(pIDD->ImageFilePath)+1);
|
|
if (pIDI->ImageFilePath) {
|
|
strcpy( pIDI->ImageFilePath, pIDD->ImageFilePath );
|
|
}
|
|
|
|
pIDI->ImageFileName = (PSTR)MemAlloc(strlen(pIDD->OriginalImageFileName)+1);
|
|
if (pIDI->ImageFileName) {
|
|
strcpy(pIDI->ImageFileName, pIDD->OriginalImageFileName);
|
|
}
|
|
|
|
if (pIDD->pMappedCoff) {
|
|
pIDI->CoffSymbols = (PIMAGE_COFF_SYMBOLS_HEADER)MemAlloc(pIDD->cMappedCoff);
|
|
if (pIDI->CoffSymbols) {
|
|
memcpy(pIDI->CoffSymbols, pIDD->pMappedCoff, pIDD->cMappedCoff);
|
|
}
|
|
pIDI->SizeOfCoffSymbols = pIDD->cMappedCoff;
|
|
}
|
|
|
|
if (pIDD->pFpo) {
|
|
pIDI->ReservedNumberOfFpoTableEntries = pIDD->cFpo;
|
|
pIDI->ReservedFpoTableEntries = (PFPO_DATA)pIDD->pFpo;
|
|
}
|
|
|
|
pIDI->SizeOfImage = pIDD->SizeOfImage;
|
|
|
|
if (pIDD->DbgFilePath && *pIDD->DbgFilePath) {
|
|
pIDI->ReservedDebugFilePath = (PSTR)MemAlloc(strlen(pIDD->DbgFilePath)+1);
|
|
if (pIDI->ReservedDebugFilePath) {
|
|
strcpy(pIDI->ReservedDebugFilePath, pIDD->DbgFilePath);
|
|
}
|
|
}
|
|
|
|
if (pIDD->pMappedCv) {
|
|
pIDI->ReservedCodeViewSymbols = pIDD->pMappedCv;
|
|
pIDI->ReservedSizeOfCodeViewSymbols = pIDD->cMappedCv;
|
|
}
|
|
|
|
// for backwards compatibility
|
|
if (pIDD->ImageMap) {
|
|
sections = (DWORD)((char *)pIDD->pCurrentSections - (char *)pIDD->ImageMap);
|
|
pIDI->ReservedMappedBase = MapItRO(pIDD->ImageFileHandle);
|
|
if (pIDI->ReservedMappedBase) {
|
|
pIDI->ReservedSections = (PIMAGE_SECTION_HEADER)pIDD->pCurrentSections;
|
|
pIDI->ReservedNumberOfSections = pIDD->cCurrentSections;
|
|
if (pIDD->ddva) {
|
|
pIDI->ReservedDebugDirectory = (PIMAGE_DEBUG_DIRECTORY)((PCHAR)pIDI->ReservedMappedBase + pIDD->ddva);
|
|
pIDI->ReservedNumberOfDebugDirectories = pIDD->cdd;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pIDI;
|
|
}
|
|
|
|
BOOL
|
|
UnmapDebugInformation(
|
|
PIMAGE_DEBUG_INFORMATION pIDI
|
|
)
|
|
{
|
|
PPIDI pPIDI;
|
|
|
|
if (!pIDI)
|
|
return TRUE;
|
|
|
|
if (pIDI->ImageFileName){
|
|
MemFree(pIDI->ImageFileName);
|
|
}
|
|
|
|
if (pIDI->ImageFilePath) {
|
|
MemFree(pIDI->ImageFilePath);
|
|
}
|
|
|
|
if (pIDI->ReservedDebugFilePath) {
|
|
MemFree(pIDI->ReservedDebugFilePath);
|
|
}
|
|
|
|
if (pIDI->CoffSymbols) {
|
|
MemFree(pIDI->CoffSymbols);
|
|
}
|
|
|
|
if (pIDI->ReservedMappedBase) {
|
|
UnmapViewOfFile(pIDI->ReservedMappedBase);
|
|
}
|
|
|
|
pPIDI = (PPIDI)(PCHAR)((PCHAR)pIDI - sizeof(PIDI_HEADER));
|
|
ReleaseDebugData(pPIDI->hdr.pIDD, IMGHLP_FREE_ALL);
|
|
MemFree(pPIDI);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
LPSTR
|
|
ExpandPath(
|
|
LPSTR lpPath
|
|
)
|
|
{
|
|
LPSTR p, newpath, p1, p2, p3;
|
|
CHAR envvar[MAX_PATH];
|
|
CHAR envstr[MAX_PATH];
|
|
ULONG i, PathMax;
|
|
|
|
if (!lpPath) {
|
|
return(NULL);
|
|
}
|
|
|
|
p = lpPath;
|
|
PathMax = strlen(lpPath) + MAX_PATH + 1;
|
|
p2 = newpath = (LPSTR) MemAlloc( PathMax );
|
|
|
|
if (!newpath) {
|
|
return(NULL);
|
|
}
|
|
|
|
while( p && *p) {
|
|
if (*p == '%') {
|
|
i = 0;
|
|
p++;
|
|
while (p && *p && *p != '%') {
|
|
envvar[i++] = *p++;
|
|
}
|
|
p++;
|
|
envvar[i] = '\0';
|
|
p1 = envstr;
|
|
*p1 = 0;
|
|
GetEnvironmentVariable( envvar, p1, MAX_PATH );
|
|
while (p1 && *p1) {
|
|
*p2++ = *p1++;
|
|
if (p2 >= newpath + PathMax) {
|
|
PathMax += MAX_PATH;
|
|
p3 = (LPSTR)MemReAlloc(newpath, PathMax);
|
|
if (!p3) {
|
|
MemFree(newpath);
|
|
return(NULL);
|
|
} else {
|
|
p2 = p3 + (p2 - newpath);
|
|
newpath = p3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*p2++ = *p++;
|
|
if (p2 >= newpath + PathMax) {
|
|
PathMax += MAX_PATH;
|
|
p3 = (LPSTR)MemReAlloc(newpath, PathMax);
|
|
if (!p3) {
|
|
MemFree(newpath);
|
|
return(NULL);
|
|
} else {
|
|
p2 = p3 + (p2 - newpath);
|
|
newpath = p3;
|
|
}
|
|
}
|
|
}
|
|
*p2 = '\0';
|
|
|
|
return newpath;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IMAGEAPI
|
|
SymFindFileInPath(
|
|
HANDLE hprocess,
|
|
LPSTR SearchPath,
|
|
LPSTR FileName,
|
|
PVOID id,
|
|
DWORD two,
|
|
DWORD three,
|
|
DWORD flags,
|
|
LPSTR FilePath,
|
|
PFINDFILEINPATHCALLBACK callback,
|
|
PVOID context
|
|
)
|
|
{
|
|
PPROCESS_ENTRY pe;
|
|
char path[MAX_PATH];
|
|
LPSTR emark;
|
|
LPSTR spath;
|
|
GUID guid;
|
|
GUID *pguid;
|
|
BOOL rc;
|
|
LPSTR p;
|
|
|
|
if (!FileName || !*FileName) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
// ignore the path...
|
|
|
|
for (p = FileName + strlen(FileName); p >= FileName; p--) {
|
|
if (*p == '\\') {
|
|
FileName = p + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (traceSubName(FileName)) // for setting debug breakpoints from DBGHELP_TOKEN
|
|
dprint("debug(%s)\n", FileName);
|
|
|
|
// prepare identifiers for symbol server
|
|
|
|
if (flags & SSRVOPT_GUIDPTR) {
|
|
pguid = (GUID *)id;
|
|
} else {
|
|
pguid = &guid;
|
|
ZeroMemory(pguid, sizeof(GUID));
|
|
if (!flags || (flags & SSRVOPT_DWORD))
|
|
pguid->Data1 = PtrToUlong(id);
|
|
else if (flags & SSRVOPT_DWORDPTR)
|
|
pguid->Data1 = *(DWORD *)id;
|
|
}
|
|
|
|
// setup local copy of the symbol path
|
|
|
|
*FilePath = 0;
|
|
spath = NULL;
|
|
|
|
if (!SearchPath || !*SearchPath) {
|
|
if (hprocess) {
|
|
pe = FindProcessEntry(hprocess);
|
|
if (pe && pe->SymbolSearchPath) {
|
|
spath = pe->SymbolSearchPath;
|
|
}
|
|
}
|
|
} else {
|
|
spath = SearchPath;
|
|
}
|
|
|
|
if (!spath || !*spath) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
// for each node in the search path, look
|
|
// for the file, or for it's symsrv entry
|
|
|
|
do {
|
|
emark = strchr(spath, ';');
|
|
|
|
if (emark) {
|
|
memcpy(path, spath, emark - spath);
|
|
path[emark - spath] = 0;
|
|
emark++;
|
|
} else {
|
|
strcpy(path, spath);
|
|
}
|
|
|
|
// look for the file in this node
|
|
|
|
if (!_strnicmp(path, "SYMSRV*", 7)) {
|
|
GetFileFromSymbolServer(path,
|
|
FileName,
|
|
pguid,
|
|
two,
|
|
three,
|
|
FilePath);
|
|
} else {
|
|
EnsureTrailingBackslash(path);
|
|
strcat(path, FileName);
|
|
if (fileexists(path))
|
|
strcpy(FilePath, path);
|
|
}
|
|
|
|
// if we find a file, process it.
|
|
|
|
if (*FilePath) {
|
|
// if no callback is specified, return with the filename filled in
|
|
if (!callback)
|
|
break;
|
|
// otherwise call the callback
|
|
rc = callback(FilePath, context);
|
|
// if it returns FALSE, quit...
|
|
if (!rc)
|
|
break;
|
|
// otherwise continue
|
|
*FilePath = 0;
|
|
}
|
|
|
|
// continue to the next node
|
|
|
|
spath = emark;
|
|
|
|
} while (emark);
|
|
|
|
return (*FilePath) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
IMAGEAPI
|
|
FindFileInPath(
|
|
HANDLE hprocess,
|
|
LPSTR SearchPath,
|
|
LPSTR FileName,
|
|
PVOID id,
|
|
DWORD two,
|
|
DWORD three,
|
|
DWORD flags,
|
|
LPSTR FilePath
|
|
)
|
|
{
|
|
return SymFindFileInPath(hprocess, SearchPath, FileName, id, two, three, flags, FilePath, NULL, NULL);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IMAGEAPI
|
|
FindFileInSearchPath(
|
|
HANDLE hprocess,
|
|
LPSTR SearchPath,
|
|
LPSTR FileName,
|
|
DWORD one,
|
|
DWORD two,
|
|
DWORD three,
|
|
LPSTR FilePath
|
|
)
|
|
{
|
|
return FindFileInPath(hprocess, SearchPath, FileName, UlongToPtr(one), two, three, SSRVOPT_DWORD, FilePath);
|
|
}
|
|
|
|
|
|
HANDLE
|
|
IMAGEAPI
|
|
FindExecutableImage(
|
|
LPSTR FileName,
|
|
LPSTR SymbolPath,
|
|
LPSTR ImageFilePath
|
|
)
|
|
{
|
|
return FindExecutableImageEx(FileName, SymbolPath, ImageFilePath, NULL, NULL);
|
|
}
|
|
|
|
|
|
HANDLE
|
|
CheckExecutableImage(
|
|
LPSTR Path,
|
|
PFIND_EXE_FILE_CALLBACK Callback,
|
|
PVOID CallerData,
|
|
DWORD flags
|
|
)
|
|
{
|
|
HANDLE FileHandle;
|
|
|
|
SetCriticalErrorMode();
|
|
|
|
dprint("FindExecutableImageEx-> Looking for %s... ", Path);
|
|
FileHandle = CreateFile( Path,
|
|
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
|
|
);
|
|
|
|
if (FileHandle != INVALID_HANDLE_VALUE) {
|
|
if (Callback) {
|
|
if (!Callback(FileHandle, Path, CallerData)) {
|
|
eprint("mismatched timestamp ");
|
|
if (!(flags & SYMOPT_LOAD_ANYTHING)) {
|
|
eprint("\n");
|
|
CloseHandle(FileHandle);
|
|
FileHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
}
|
|
if (FileHandle != INVALID_HANDLE_VALUE) {
|
|
eprint("OK\n");
|
|
}
|
|
} else {
|
|
eprint("no file\n");
|
|
}
|
|
|
|
ResetCriticalErrorMode();
|
|
|
|
return FileHandle;
|
|
}
|
|
|
|
typedef struct _FEIEP_STATE
|
|
{
|
|
PFIND_EXE_FILE_CALLBACK UserCb;
|
|
PVOID UserCbData;
|
|
DWORD flags;
|
|
HANDLE Return;
|
|
} FEIEP_STATE;
|
|
|
|
BOOL
|
|
CALLBACK
|
|
FindExSearchTreeCallback(
|
|
LPSTR FilePath,
|
|
PVOID CallerData
|
|
)
|
|
{
|
|
FEIEP_STATE* State = (FEIEP_STATE*)CallerData;
|
|
|
|
State->Return =
|
|
CheckExecutableImage(FilePath, State->UserCb, State->UserCbData,
|
|
State->flags);
|
|
return State->Return != INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
HANDLE
|
|
IMAGEAPI
|
|
FindExecutableImageExPass(
|
|
LPSTR FileName,
|
|
LPSTR SymbolPath,
|
|
LPSTR ImageFilePath,
|
|
PFIND_EXE_FILE_CALLBACK Callback,
|
|
PVOID CallerData,
|
|
DWORD flags
|
|
)
|
|
{
|
|
LPSTR Start, End;
|
|
HANDLE FileHandle = NULL;
|
|
CHAR DirectoryPath[ MAX_PATH ];
|
|
LPSTR NewSymbolPath = NULL;
|
|
|
|
__try {
|
|
__try {
|
|
if (GetFullPathName( FileName, MAX_PATH, ImageFilePath, &Start )) {
|
|
FileHandle = CheckExecutableImage(ImageFilePath, Callback, CallerData, flags);
|
|
if (FileHandle != INVALID_HANDLE_VALUE)
|
|
return FileHandle;
|
|
}
|
|
|
|
NewSymbolPath = ExpandPath(SymbolPath);
|
|
Start = NewSymbolPath;
|
|
while (Start && *Start != '\0') {
|
|
FEIEP_STATE SearchTreeState;
|
|
|
|
if (End = strchr( Start, ';' )) {
|
|
int Len = (int)(End - Start);
|
|
Len = (int)min(Len, sizeof(DirectoryPath)-1);
|
|
|
|
strncpy( (PCHAR) DirectoryPath, Start, Len );
|
|
DirectoryPath[ Len ] = '\0';
|
|
End += 1;
|
|
} else {
|
|
strcpy( (PCHAR) DirectoryPath, Start );
|
|
}
|
|
|
|
if (!_strnicmp(DirectoryPath, "SYMSRV*", 7)) {
|
|
goto next;
|
|
}
|
|
|
|
dprint("FindExecutableImageEx-> Searching %s for %s... ", DirectoryPath, FileName);
|
|
SearchTreeState.UserCb = Callback;
|
|
SearchTreeState.UserCbData = CallerData;
|
|
SearchTreeState.flags = flags;
|
|
if (SearchTreeForFileEx( (PCHAR) DirectoryPath, FileName, ImageFilePath, FindExSearchTreeCallback, &SearchTreeState )) {
|
|
eprint("found\n");
|
|
MemFree( NewSymbolPath );
|
|
return SearchTreeState.Return;
|
|
} else {
|
|
eprint("no file\n");
|
|
}
|
|
|
|
next:
|
|
Start = End;
|
|
}
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
ImageFilePath[0] = '\0';
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (FileHandle) {
|
|
CloseHandle(FileHandle);
|
|
}
|
|
|
|
if (NewSymbolPath) {
|
|
MemFree( NewSymbolPath );
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
HANDLE
|
|
IMAGEAPI
|
|
FindExecutable(
|
|
LPSTR FileName,
|
|
LPSTR SymbolPath,
|
|
LPSTR ImageFilePath,
|
|
PFIND_EXE_FILE_CALLBACK Callback,
|
|
PVOID CallerData,
|
|
DWORD flags
|
|
)
|
|
{
|
|
BOOL FullPath = FALSE;
|
|
BOOL PathComponents = FALSE;
|
|
HANDLE FileHandle;
|
|
|
|
if (!FileName || !*FileName) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// The filename may or may not contain path components.
|
|
// Determine what kind of path it is.
|
|
//
|
|
|
|
if ((((FileName[0] >= 'a' && FileName[0] <= 'z') ||
|
|
(FileName[0] >= 'A' && FileName[0] <= 'Z')) &&
|
|
FileName[1] == ':') ||
|
|
FileName[0] == '\\' ||
|
|
FileName[0] == '/') {
|
|
|
|
FullPath = TRUE;
|
|
PathComponents = TRUE;
|
|
|
|
} else if (strchr(FileName, '\\') ||
|
|
strchr(FileName, '/')) {
|
|
|
|
PathComponents = TRUE;
|
|
|
|
}
|
|
|
|
// If the filename is a full path then it can be checked
|
|
// for existence directly; there's no need to search
|
|
// along any paths.
|
|
if (FullPath) {
|
|
__try {
|
|
FileHandle = CheckExecutableImage(FileName, Callback, CallerData, flags);
|
|
if (FileHandle != INVALID_HANDLE_VALUE) {
|
|
strcpy(ImageFilePath, FileName);
|
|
return FileHandle;
|
|
}
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
// If it's not a full path we need to do a first pass
|
|
// with the filename as given. This handles relative
|
|
// paths and bare filenames.
|
|
FileHandle = FindExecutableImageExPass(FileName, SymbolPath,
|
|
ImageFilePath, Callback,
|
|
CallerData, flags);
|
|
if (FileHandle != NULL) {
|
|
return FileHandle;
|
|
}
|
|
}
|
|
|
|
// If we still haven't found it and the given filename
|
|
// has path components we need to strip off the path components
|
|
// and try again with just the base filename.
|
|
if (PathComponents) {
|
|
LPSTR BaseFile;
|
|
|
|
BaseFile = strrchr(FileName, '\\');
|
|
if (BaseFile == NULL) {
|
|
BaseFile = strrchr(FileName, '/');
|
|
if (BaseFile == NULL) {
|
|
// Must be <drive>:.
|
|
BaseFile = FileName + 1;
|
|
}
|
|
}
|
|
|
|
// Skip path character to point to base file.
|
|
BaseFile++;
|
|
|
|
return FindExecutableImageExPass(BaseFile, SymbolPath,
|
|
ImageFilePath, Callback,
|
|
CallerData, flags);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
HANDLE
|
|
IMAGEAPI
|
|
FindExecutableImageEx(
|
|
LPSTR FileName,
|
|
LPSTR SymbolPath,
|
|
LPSTR ImageFilePath,
|
|
PFIND_EXE_FILE_CALLBACK Callback,
|
|
PVOID CallerData
|
|
)
|
|
{
|
|
HANDLE hrc;
|
|
|
|
hrc = FindExecutable(FileName,
|
|
SymbolPath,
|
|
ImageFilePath,
|
|
Callback,
|
|
CallerData,
|
|
0);
|
|
if (hrc)
|
|
return hrc;
|
|
|
|
if (g.SymOptions & SYMOPT_LOAD_ANYTHING)
|
|
hrc = FindExecutable(FileName,
|
|
SymbolPath,
|
|
ImageFilePath,
|
|
Callback,
|
|
CallerData,
|
|
SYMOPT_LOAD_ANYTHING);
|
|
return hrc;
|
|
}
|
|
|
|
|
|
HANDLE
|
|
IMAGEAPI
|
|
FindDebugInfoFile(
|
|
LPSTR FileName,
|
|
LPSTR SymbolPath,
|
|
LPSTR DebugFilePath
|
|
)
|
|
{
|
|
return FindDebugInfoFileEx(FileName, SymbolPath, DebugFilePath, NULL, NULL);
|
|
}
|
|
|
|
|
|
HANDLE
|
|
IMAGEAPI
|
|
FindDebugInfoFileEx(
|
|
IN LPSTR FileName,
|
|
IN LPSTR SymbolPath,
|
|
OUT LPSTR DebugFilePath,
|
|
IN PFIND_DEBUG_FILE_CALLBACK Callback,
|
|
IN PVOID CallerData
|
|
)
|
|
{
|
|
DWORD flag;
|
|
|
|
if (g.SymOptions & SYMOPT_LOAD_ANYTHING)
|
|
flag = fdifRECURSIVE;
|
|
else
|
|
flag = 0;
|
|
if (flag)
|
|
dprint("RECURSIVE %s\n", FileName);
|
|
|
|
return fnFindDebugInfoFileEx(FileName,
|
|
SymbolPath,
|
|
DebugFilePath,
|
|
Callback,
|
|
CallerData,
|
|
flag);
|
|
}
|
|
|
|
|
|
HANDLE
|
|
IMAGEAPI
|
|
fnFindDebugInfoFileEx(
|
|
IN LPSTR FileName,
|
|
IN LPSTR SymbolPath,
|
|
OUT LPSTR DebugFilePath,
|
|
IN PFIND_DEBUG_FILE_CALLBACK Callback,
|
|
IN PVOID CallerData,
|
|
IN DWORD flag
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The rules are:
|
|
Look for
|
|
1. <SymbolPath>\Symbols\<ext>\<filename>.dbg
|
|
3. <SymbolPath>\<ext>\<filename>.dbg
|
|
5. <SymbolPath>\<filename>.dbg
|
|
7. <FileNamePath>\<filename>.dbg
|
|
|
|
Arguments:
|
|
FileName - Supplies a file name in one of three forms: fully qualified,
|
|
<ext>\<filename>.dbg, or just filename.dbg
|
|
SymbolPath - semi-colon delimited
|
|
|
|
DebugFilePath -
|
|
|
|
Callback - May be NULL. Callback that indicates whether the Symbol file is valid, or whether
|
|
the function should continue searching for another Symbol file.
|
|
The callback returns TRUE if the Symbol file is valid, or FALSE if the function should
|
|
continue searching.
|
|
|
|
CallerData - May be NULL. Data passed to the callback.
|
|
|
|
Flag - indicates that PDBs shouldn't be searched for
|
|
|
|
Return Value:
|
|
|
|
The name of the .dbg file and a handle to that file.
|
|
|
|
--*/
|
|
{
|
|
HANDLE FileHandle = INVALID_HANDLE_VALUE;
|
|
LPSTR ExpSymbolPath = NULL, SymPathStart, PathEnd;
|
|
DWORD ShareAttributes, cnt;
|
|
LPSTR InitialPath, Sub1, Sub2, FileExt;
|
|
CHAR FilePath[_MAX_PATH + 1];
|
|
CHAR Drive[_MAX_DRIVE], Dir[_MAX_DIR], SubDirPart[_MAX_DIR], FilePart[_MAX_FNAME], Ext[_MAX_EXT];
|
|
CHAR *ExtDir;
|
|
DWORD i;
|
|
PIMGHLP_DEBUG_DATA pIDD;
|
|
BOOL found = FALSE;
|
|
DWORD err = 0;
|
|
GUID guid;
|
|
BOOL ssrv;
|
|
|
|
assert(DebugFilePath);
|
|
*DebugFilePath = 0;
|
|
|
|
ZeroMemory(&guid, sizeof(GUID));
|
|
|
|
if (g.OSVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
|
|
ShareAttributes = (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE);
|
|
} else {
|
|
ShareAttributes = (FILE_SHARE_READ | FILE_SHARE_WRITE);
|
|
}
|
|
|
|
__try {
|
|
*DebugFilePath = '\0';
|
|
|
|
// Step 1. What do we have?
|
|
_splitpath(FileName, Drive, Dir, FilePart, Ext);
|
|
|
|
if (!_stricmp(Ext, ".dbg")) {
|
|
// We got a filename of the form: ext\filename.dbg. Dir holds the extension already.
|
|
ExtDir = Dir;
|
|
} else {
|
|
// Otherwise, skip the period and null out the Dir.
|
|
ExtDir = CharNext(Ext);
|
|
}
|
|
|
|
ExpSymbolPath = ExpandPath(SymbolPath);
|
|
if (!ExpSymbolPath)
|
|
return NULL;
|
|
|
|
SetCriticalErrorMode();
|
|
|
|
SymPathStart = ExpSymbolPath;
|
|
cnt = 0;
|
|
|
|
do {
|
|
if (PathEnd = strchr( SymPathStart, ';' )) {
|
|
*PathEnd = '\0';
|
|
}
|
|
|
|
ssrv = FALSE;
|
|
pIDD = (PIMGHLP_DEBUG_DATA)CallerData;
|
|
|
|
if (!_strnicmp(SymPathStart, "SYMSRV*", 7)) {
|
|
|
|
*DebugFilePath = 0;
|
|
if (!cnt && CallerData) {
|
|
ssrv = TRUE;
|
|
strcpy(FilePath, FilePart);
|
|
strcat(FilePath, ".dbg");
|
|
guid.Data1 = pIDD->TimeDateStamp;
|
|
GetFileFromSymbolServer(SymPathStart,
|
|
FilePath,
|
|
&guid,
|
|
pIDD->SizeOfImage,
|
|
0,
|
|
DebugFilePath);
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (cnt) {
|
|
|
|
case 0: // <SymbolPath>\symbols\<ext>\<filename>.ext
|
|
InitialPath = SymPathStart;
|
|
Sub1 = "symbols";
|
|
Sub2 = ExtDir;
|
|
break;
|
|
|
|
case 1: // <SymbolPath>\<ext>\<filename>.ext
|
|
InitialPath = SymPathStart;
|
|
Sub1 = "";
|
|
Sub2 = ExtDir;
|
|
break;
|
|
|
|
case 2: // <SymbolPath>\<filename>.ext
|
|
InitialPath = SymPathStart;
|
|
Sub1 = "";
|
|
Sub2 = "";
|
|
break;
|
|
|
|
case 3: // <FileNamePath>\<filename>.ext - A.K.A. what was passed to us
|
|
InitialPath = Drive;
|
|
Sub1 = "";
|
|
Sub2 = Dir;
|
|
// this stops us from checking out everything in the sympath
|
|
cnt++;
|
|
break;
|
|
}
|
|
|
|
// build fully-qualified filepath to look for
|
|
|
|
strcpy(FilePath, InitialPath);
|
|
EnsureTrailingBackslash(FilePath);
|
|
strcat(FilePath, Sub1);
|
|
EnsureTrailingBackslash(FilePath);
|
|
strcat(FilePath, Sub2);
|
|
EnsureTrailingBackslash(FilePath);
|
|
strcat(FilePath, FilePart);
|
|
|
|
strcpy(DebugFilePath, FilePath);
|
|
strcat(DebugFilePath, ".dbg");
|
|
}
|
|
|
|
// try to open the file
|
|
|
|
if (*DebugFilePath) {
|
|
dprint("FindDebugInfoFileEx-> Looking for %s... ", DebugFilePath);
|
|
FileHandle = CreateFile(DebugFilePath,
|
|
GENERIC_READ,
|
|
ShareAttributes,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
|
|
// if the file opens, bail from this loop
|
|
|
|
if (FileHandle != INVALID_HANDLE_VALUE) {
|
|
found = TRUE;
|
|
if (pIDD) {
|
|
pIDD->ImageSrc = (ssrv) ? srcSymSrv : srcSearchPath;
|
|
}
|
|
if (!Callback) {
|
|
break;
|
|
} else if (Callback(FileHandle, DebugFilePath, CallerData)) {
|
|
break;
|
|
} else {
|
|
eprint("mismatched timestamp\n");
|
|
CloseHandle(FileHandle);
|
|
FileHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
} else {
|
|
err = GetLastError();
|
|
switch (err)
|
|
{
|
|
case ERROR_FILE_NOT_FOUND:
|
|
eprint("file not found\n");
|
|
break;
|
|
case ERROR_PATH_NOT_FOUND:
|
|
eprint("path not found\n");
|
|
break;
|
|
default:
|
|
eprint("file error 0x%x\n", err);
|
|
break;
|
|
}
|
|
}
|
|
// if file is open, bail from this loop too - else continue
|
|
|
|
if (FileHandle != INVALID_HANDLE_VALUE)
|
|
break;
|
|
}
|
|
|
|
// go to next item in the sympath
|
|
|
|
if (PathEnd) {
|
|
*PathEnd = ';';
|
|
SymPathStart = PathEnd + 1;
|
|
} else {
|
|
SymPathStart = ExpSymbolPath;
|
|
cnt++;
|
|
}
|
|
} while (cnt < 4);
|
|
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
if (FileHandle != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(FileHandle);
|
|
}
|
|
FileHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
if (ExpSymbolPath) {
|
|
MemFree(ExpSymbolPath);
|
|
}
|
|
|
|
if (FileHandle == INVALID_HANDLE_VALUE) {
|
|
FileHandle = NULL;
|
|
DebugFilePath[0] = '\0';
|
|
} else {
|
|
eprint("OK\n");
|
|
}
|
|
|
|
if (!FileHandle // if we didn't get the right file...
|
|
&& found // but we found some file...
|
|
&& (flag & fdifRECURSIVE)) // and we were told to run recursively...
|
|
{
|
|
// try again without timestamp checking
|
|
FileHandle = fnFindDebugInfoFileEx(FileName,
|
|
SymbolPath,
|
|
FilePath,
|
|
NULL,
|
|
0,
|
|
flag);
|
|
if (FileHandle && FileHandle != INVALID_HANDLE_VALUE)
|
|
strcpy(DebugFilePath, FilePath);
|
|
}
|
|
|
|
ResetCriticalErrorMode();
|
|
|
|
return FileHandle;
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetImageNameFromMiscDebugData(
|
|
IN HANDLE FileHandle,
|
|
IN PVOID MappedBase,
|
|
IN PIMAGE_NT_HEADERS32 NtHeaders,
|
|
IN PIMAGE_DEBUG_DIRECTORY DebugDirectories,
|
|
IN ULONG NumberOfDebugDirectories,
|
|
OUT LPSTR ImageFilePath
|
|
)
|
|
{
|
|
IMAGE_DEBUG_MISC TempMiscData;
|
|
PIMAGE_DEBUG_MISC DebugMiscData;
|
|
ULONG BytesToRead, BytesRead;
|
|
BOOLEAN FoundImageName;
|
|
LPSTR ImageName;
|
|
PIMAGE_OPTIONAL_HEADER32 OptionalHeader32 = NULL;
|
|
PIMAGE_OPTIONAL_HEADER64 OptionalHeader64 = NULL;
|
|
|
|
while (NumberOfDebugDirectories) {
|
|
if (DebugDirectories->Type == IMAGE_DEBUG_TYPE_MISC) {
|
|
break;
|
|
} else {
|
|
DebugDirectories += 1;
|
|
NumberOfDebugDirectories -= 1;
|
|
}
|
|
}
|
|
|
|
if (NumberOfDebugDirectories == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
OptionalHeadersFromNtHeaders(NtHeaders, &OptionalHeader32, &OptionalHeader64);
|
|
|
|
if ((OPTIONALHEADER(MajorLinkerVersion) < 3) &&
|
|
(OPTIONALHEADER(MinorLinkerVersion) < 36) ) {
|
|
BytesToRead = FIELD_OFFSET( IMAGE_DEBUG_MISC, Reserved );
|
|
} else {
|
|
BytesToRead = FIELD_OFFSET( IMAGE_DEBUG_MISC, Data );
|
|
}
|
|
|
|
DebugMiscData = NULL;
|
|
FoundImageName = FALSE;
|
|
if (MappedBase == 0) {
|
|
if (SetFilePointer( FileHandle,
|
|
DebugDirectories->PointerToRawData,
|
|
NULL,
|
|
FILE_BEGIN
|
|
) == DebugDirectories->PointerToRawData
|
|
) {
|
|
if (ReadFile( FileHandle,
|
|
&TempMiscData,
|
|
BytesToRead,
|
|
&BytesRead,
|
|
NULL
|
|
) &&
|
|
BytesRead == BytesToRead
|
|
) {
|
|
DebugMiscData = &TempMiscData;
|
|
if (DebugMiscData->DataType == IMAGE_DEBUG_MISC_EXENAME) {
|
|
BytesToRead = DebugMiscData->Length - BytesToRead;
|
|
BytesToRead = BytesToRead > MAX_PATH ? MAX_PATH : BytesToRead;
|
|
if (ReadFile( FileHandle,
|
|
ImageFilePath,
|
|
BytesToRead,
|
|
&BytesRead,
|
|
NULL
|
|
) &&
|
|
BytesRead == BytesToRead
|
|
) {
|
|
FoundImageName = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
DebugMiscData = (PIMAGE_DEBUG_MISC)((PCHAR)MappedBase +
|
|
DebugDirectories->PointerToRawData );
|
|
if (DebugMiscData->DataType == IMAGE_DEBUG_MISC_EXENAME) {
|
|
ImageName = (PCHAR)DebugMiscData + BytesToRead;
|
|
BytesToRead = DebugMiscData->Length - BytesToRead;
|
|
BytesToRead = BytesToRead > MAX_PATH ? MAX_PATH : BytesToRead;
|
|
if (*ImageName != '\0' ) {
|
|
memcpy( ImageFilePath, ImageName, BytesToRead );
|
|
FoundImageName = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FoundImageName;
|
|
}
|
|
|
|
|
|
|
|
#define MAX_DEPTH 32
|
|
|
|
BOOL
|
|
IMAGEAPI
|
|
SearchTreeForFileEx(
|
|
LPSTR RootPath,
|
|
LPSTR InputPathName,
|
|
LPSTR OutputPathBuffer,
|
|
PSEARCH_TREE_FOR_FILE_EX_CALLBACK Callback,
|
|
PVOID CallbackData
|
|
)
|
|
{
|
|
// UnSafe...
|
|
|
|
PCHAR FileName;
|
|
PUCHAR Prefix = (PUCHAR) "";
|
|
CHAR PathBuffer[ MAX_PATH ];
|
|
ULONG Depth;
|
|
PCHAR PathTail[ MAX_DEPTH ];
|
|
PCHAR FindHandle[ MAX_DEPTH ];
|
|
LPWIN32_FIND_DATA FindFileData;
|
|
UCHAR FindFileBuffer[ MAX_PATH + sizeof( WIN32_FIND_DATA ) ];
|
|
BOOL Result;
|
|
|
|
SetCriticalErrorMode();;
|
|
|
|
strcpy( PathBuffer, RootPath );
|
|
FileName = InputPathName;
|
|
while (*InputPathName) {
|
|
if (*InputPathName == ':' || *InputPathName == '\\' || *InputPathName == '/') {
|
|
FileName = ++InputPathName;
|
|
} else {
|
|
InputPathName = CharNext(InputPathName);
|
|
}
|
|
}
|
|
FindFileData = (LPWIN32_FIND_DATA)FindFileBuffer;
|
|
Depth = 0;
|
|
Result = FALSE;
|
|
while (TRUE) {
|
|
startDirectorySearch:
|
|
PathTail[ Depth ] = strchr( PathBuffer, '\0' );
|
|
if (PathTail[ Depth ] > PathBuffer
|
|
&& *CharPrev(PathBuffer, PathTail[ Depth ]) != '\\') {
|
|
*(PathTail[ Depth ])++ = '\\';
|
|
}
|
|
|
|
strcpy( PathTail[ Depth ], "*.*" );
|
|
FindHandle[ Depth ] = (PCHAR) FindFirstFile( PathBuffer, FindFileData );
|
|
|
|
if (FindHandle[ Depth ] == INVALID_HANDLE_VALUE) {
|
|
goto nextDirectory;
|
|
}
|
|
|
|
do {
|
|
if (FindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
|
if (strcmp( FindFileData->cFileName, "." ) &&
|
|
strcmp( FindFileData->cFileName, ".." ) &&
|
|
Depth < MAX_DEPTH
|
|
) {
|
|
strcpy(PathTail[ Depth ], FindFileData->cFileName);
|
|
strcat(PathTail[ Depth ], "\\");
|
|
|
|
Depth++;
|
|
goto startDirectorySearch;
|
|
}
|
|
} else
|
|
if (!_stricmp( FindFileData->cFileName, FileName )) {
|
|
strcpy( PathTail[ Depth ], FindFileData->cFileName );
|
|
strcpy( OutputPathBuffer, PathBuffer );
|
|
if (Callback != NULL) {
|
|
Result = Callback(OutputPathBuffer, CallbackData);
|
|
} else {
|
|
Result = TRUE;
|
|
}
|
|
}
|
|
|
|
restartDirectorySearch:
|
|
if (Result) {
|
|
break;
|
|
}
|
|
}
|
|
while (FindNextFile( FindHandle[ Depth ], FindFileData ));
|
|
FindClose( FindHandle[ Depth ] );
|
|
|
|
nextDirectory:
|
|
if (Depth == 0) {
|
|
break;
|
|
}
|
|
|
|
Depth--;
|
|
goto restartDirectorySearch;
|
|
}
|
|
|
|
ResetCriticalErrorMode();
|
|
|
|
return Result;
|
|
}
|
|
|
|
BOOL
|
|
IMAGEAPI
|
|
SearchTreeForFile(
|
|
LPSTR RootPath,
|
|
LPSTR InputPathName,
|
|
LPSTR OutputPathBuffer
|
|
)
|
|
{
|
|
return SearchTreeForFileEx(RootPath, InputPathName, OutputPathBuffer,
|
|
NULL, NULL);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IMAGEAPI
|
|
MakeSureDirectoryPathExists(
|
|
LPCSTR DirPath
|
|
)
|
|
{
|
|
LPSTR p, DirCopy;
|
|
DWORD dw;
|
|
|
|
// Make a copy of the string for editing.
|
|
|
|
__try {
|
|
DirCopy = (LPSTR) MemAlloc(strlen(DirPath) + 1);
|
|
|
|
if (!DirCopy) {
|
|
return FALSE;
|
|
}
|
|
|
|
strcpy(DirCopy, DirPath);
|
|
|
|
p = DirCopy;
|
|
|
|
// If the second character in the path is "\", then this is a UNC
|
|
// path, and we should skip forward until we reach the 2nd \ in the path.
|
|
|
|
if ((*p == '\\') && (*(p+1) == '\\')) {
|
|
p++; // Skip over the first \ in the name.
|
|
p++; // Skip over the second \ in the name.
|
|
|
|
// Skip until we hit the first "\" (\\Server\).
|
|
|
|
while (*p && *p != '\\') {
|
|
p = CharNext(p);
|
|
}
|
|
|
|
// Advance over it.
|
|
|
|
if (*p) {
|
|
p++;
|
|
}
|
|
|
|
// Skip until we hit the second "\" (\\Server\Share\).
|
|
|
|
while (*p && *p != '\\') {
|
|
p = CharNext(p);
|
|
}
|
|
|
|
// Advance over it also.
|
|
|
|
if (*p) {
|
|
p++;
|
|
}
|
|
|
|
} else
|
|
// Not a UNC. See if it's <drive>:
|
|
if (*(p+1) == ':' ) {
|
|
|
|
p++;
|
|
p++;
|
|
|
|
// If it exists, skip over the root specifier
|
|
|
|
if (*p && (*p == '\\')) {
|
|
p++;
|
|
}
|
|
}
|
|
|
|
while( *p ) {
|
|
if ( *p == '\\' ) {
|
|
*p = '\0';
|
|
dw = fnGetFileAttributes(DirCopy);
|
|
// Nothing exists with this name. Try to make the directory name and error if unable to.
|
|
if ( dw == 0xffffffff ) {
|
|
if ( !CreateDirectory(DirCopy,NULL) ) {
|
|
if( GetLastError() != ERROR_ALREADY_EXISTS ) {
|
|
MemFree(DirCopy);
|
|
return FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
if ( (dw & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY ) {
|
|
// Something exists with this name, but it's not a directory... Error
|
|
MemFree(DirCopy);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*p = '\\';
|
|
}
|
|
p = CharNext(p);
|
|
}
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
ImagepSetLastErrorFromStatus( GetExceptionCode() );
|
|
MemFree(DirCopy);
|
|
return(FALSE);
|
|
}
|
|
|
|
MemFree(DirCopy);
|
|
return TRUE;
|
|
}
|
|
|
|
LPAPI_VERSION
|
|
IMAGEAPI
|
|
ImagehlpApiVersion(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// don't tell old apps about the new version. It will
|
|
// just scare them.
|
|
//
|
|
return &g.AppVersion;
|
|
}
|
|
|
|
LPAPI_VERSION
|
|
IMAGEAPI
|
|
ImagehlpApiVersionEx(
|
|
LPAPI_VERSION av
|
|
)
|
|
{
|
|
LPAPI_VERSION ver;
|
|
|
|
__try {
|
|
ver = av;
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
ver = &g.AppVersion;
|
|
}
|
|
|
|
if (ver->Revision < 6) {
|
|
//
|
|
// For older debuggers, just tell them what they want to hear.
|
|
//
|
|
return ver;
|
|
}
|
|
return &g.ApiVersion;
|
|
}
|
|
|