warzone2100/lib/exceptionhandler/exchndl.cpp

1269 lines
35 KiB
C++
Raw Normal View History

/*
This file is part of Warzone 2100.
Copyright (C) 1997-XXXX José Fonseca <j_r_fonseca@yahoo.co.uk>
* Originally based on Matt Pietrek's MSJEXHND.CPP in Microsoft Systems Journal, April 1997.
Copyright (C) 2008 Giel van Schijndel
Copyright (C) 2008-2013 Warzone 2100 Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#if (_WIN32_WINNT < 0x0500) // must force win 2k or higher
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0500
#endif
#include "lib/framework/frame.h"
#include "dumpinfo.h"
#include "exchndl.h"
#include <assert.h>
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <shlobj.h>
#include <shlwapi.h>
#if !defined(WZ_CC_MSVC)
#define HAVE_BFD 1
#endif
#define WSTRING(x) CONCAT(L, x)
// Declare the static variables
static wchar_t szLogFileName[MAX_PATH] = L"";
static LPTOP_LEVEL_EXCEPTION_FILTER prevExceptionFilter = NULL;
static HANDLE hReportFile;
static char* formattedVersionString = NULL;
static
int __cdecl rprintf(const TCHAR * format, ...)
{
TCHAR szBuff[4096];
int retValue;
DWORD cbWritten;
va_list argptr;
va_start(argptr, format);
retValue = wvsprintf(szBuff, format, argptr);
va_end(argptr);
WriteFile(hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0);
return retValue;
}
// The GetModuleBase function retrieves the base address of the module that contains the specified address.
static
DWORD GetModuleBase(DWORD dwAddress)
{
MEMORY_BASIC_INFORMATION Buffer;
return VirtualQuery((LPCVOID) dwAddress, &Buffer, sizeof(Buffer)) ? (DWORD) Buffer.AllocationBase : 0;
}
#ifdef HAVE_BFD
#include <bfd.h>
2010-12-22 17:58:16 -08:00
extern "C"
{
#include "include/demangle.h"
#include "include/coff/internal.h"
#include "include/libcoff.h"
2010-12-22 17:58:16 -08:00
}
// Read in the symbol table.
static bfd_boolean
slurp_symtab (bfd *abfd, asymbol ***syms, long *symcount)
{
long storage;
if ((bfd_get_file_flags (abfd) & HAS_SYMS) == 0)
return FALSE;
storage = bfd_get_symtab_upper_bound (abfd);
if (storage < 0)
return FALSE;
*syms = (asymbol **) GlobalAlloc(GMEM_FIXED, storage);
if (*syms == NULL)
return FALSE;
if((*symcount = bfd_canonicalize_symtab (abfd, *syms)) < 0)
return FALSE;
return TRUE;
}
// This stucture is used to pass information between translate_addresses and find_address_in_section.
struct find_handle
{
asymbol **syms;
bfd_vma pc;
const char *filename;
const char *functionname;
unsigned int line;
bfd_boolean found;
};
// Look for an address in a section. This is called via bfd_map_over_sections.
static void find_address_in_section (bfd *abfd, asection *section, PTR data)
{
struct find_handle *info = (struct find_handle *) data;
bfd_vma vma;
bfd_size_type size;
if (info->found)
return;
if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0)
return;
vma = bfd_get_section_vma (abfd, section);
size = bfd_get_section_size (section);
if (info->pc < (vma = bfd_get_section_vma (abfd, section)))
return;
if (info->pc >= vma + (size = bfd_get_section_size (section)))
return;
info->found = bfd_find_nearest_line (abfd, section, info->syms, info->pc - vma, &info->filename, &info->functionname, &info->line);
}
static
bool BfdDemangleSymName(LPCTSTR lpName, LPTSTR lpDemangledName, DWORD nSize)
{
char *res;
assert(lpName != NULL);
if((res = cplus_demangle(lpName, DMGL_ANSI /*| DMGL_PARAMS*/)) == NULL)
{
lstrcpyn(lpDemangledName, lpName, nSize);
return FALSE;
}
else
{
lstrcpyn(lpDemangledName, res, nSize);
free (res);
return TRUE;
}
}
static
bool BfdGetSymFromAddr(bfd *abfd, asymbol **syms, long symcount, DWORD dwAddress, LPTSTR lpSymName, DWORD nSize)
{
HMODULE hModule;
struct find_handle info;
if(!(hModule = (HMODULE) GetModuleBase(dwAddress)))
return FALSE;
info.pc = dwAddress;
if(!(bfd_get_file_flags (abfd) & HAS_SYMS) || !symcount)
return FALSE;
info.syms = syms;
info.found = FALSE;
bfd_map_over_sections (abfd, find_address_in_section, (PTR) &info);
if (info.found == FALSE || info.line == 0)
return FALSE;
assert(lpSymName);
if(info.functionname == NULL && *info.functionname == '\0')
return FALSE;
lstrcpyn(lpSymName, info.functionname, nSize);
return TRUE;
}
static
bool BfdGetLineFromAddr(bfd *abfd, asymbol **syms, long symcount, DWORD dwAddress, LPTSTR lpFileName, DWORD nSize, LPDWORD lpLineNumber)
{
HMODULE hModule;
struct find_handle info;
if(!(hModule = (HMODULE) GetModuleBase(dwAddress)))
return FALSE;
info.pc = dwAddress;
if(!(bfd_get_file_flags (abfd) & HAS_SYMS) || !symcount)
return FALSE;
info.syms = syms;
info.found = FALSE;
bfd_map_over_sections (abfd, find_address_in_section, (PTR) &info);
if (info.found == FALSE || info.line == 0)
return FALSE;
assert(lpFileName && lpLineNumber);
lstrcpyn(lpFileName, info.filename, nSize);
*lpLineNumber = info.line;
return TRUE;
}
#endif /* HAVE_BFD */
#include <imagehlp.h>
static bool bSymInitialized = FALSE;
static HMODULE hModule_Imagehlp = NULL;
typedef bool (WINAPI *PFNSYMINITIALIZE)(HANDLE, LPSTR, bool);
static PFNSYMINITIALIZE pfnSymInitialize = NULL;
static
bool WINAPI j_SymInitialize(HANDLE hProcess, PSTR UserSearchPath, bool fInvadeProcess)
{
if(
(hModule_Imagehlp || (hModule_Imagehlp = LoadLibrary(_T("IMAGEHLP.DLL")))) &&
(pfnSymInitialize || (pfnSymInitialize = (PFNSYMINITIALIZE) GetProcAddress(hModule_Imagehlp, "SymInitialize")))
)
return pfnSymInitialize(hProcess, UserSearchPath, fInvadeProcess);
else
return FALSE;
}
typedef bool (WINAPI *PFNSYMCLEANUP)(HANDLE);
static PFNSYMCLEANUP pfnSymCleanup = NULL;
static
bool WINAPI j_SymCleanup(HANDLE hProcess)
{
if(
(hModule_Imagehlp || (hModule_Imagehlp = LoadLibrary(_T("IMAGEHLP.DLL")))) &&
(pfnSymCleanup || (pfnSymCleanup = (PFNSYMCLEANUP) GetProcAddress(hModule_Imagehlp, "SymCleanup")))
)
return pfnSymCleanup(hProcess);
else
return FALSE;
}
typedef DWORD (WINAPI *PFNSYMSETOPTIONS)(DWORD);
static PFNSYMSETOPTIONS pfnSymSetOptions = NULL;
static
DWORD WINAPI j_SymSetOptions(DWORD SymOptions)
{
if(
(hModule_Imagehlp || (hModule_Imagehlp = LoadLibrary(_T("IMAGEHLP.DLL")))) &&
(pfnSymSetOptions || (pfnSymSetOptions = (PFNSYMSETOPTIONS) GetProcAddress(hModule_Imagehlp, "SymSetOptions")))
)
return pfnSymSetOptions(SymOptions);
else
return FALSE;
}
typedef bool (WINAPI *PFNSYMUNDNAME)(PIMAGEHLP_SYMBOL, PSTR, DWORD);
static PFNSYMUNDNAME pfnSymUnDName = NULL;
static
bool WINAPI j_SymUnDName(PIMAGEHLP_SYMBOL Symbol, PSTR UnDecName, DWORD UnDecNameLength)
{
if(
(hModule_Imagehlp || (hModule_Imagehlp = LoadLibrary(_T("IMAGEHLP.DLL")))) &&
(pfnSymUnDName || (pfnSymUnDName = (PFNSYMUNDNAME) GetProcAddress(hModule_Imagehlp, "SymUnDName")))
)
return pfnSymUnDName(Symbol, UnDecName, UnDecNameLength);
else
return FALSE;
}
typedef PFUNCTION_TABLE_ACCESS_ROUTINE PFNSYMFUNCTIONTABLEACCESS;
static PFNSYMFUNCTIONTABLEACCESS pfnSymFunctionTableAccess = NULL;
static
PVOID WINAPI j_SymFunctionTableAccess(HANDLE hProcess, DWORD AddrBase)
{
if(
(hModule_Imagehlp || (hModule_Imagehlp = LoadLibrary(_T("IMAGEHLP.DLL")))) &&
(pfnSymFunctionTableAccess || (pfnSymFunctionTableAccess = (PFNSYMFUNCTIONTABLEACCESS) GetProcAddress(hModule_Imagehlp, "SymFunctionTableAccess")))
)
return pfnSymFunctionTableAccess(hProcess, AddrBase);
else
return NULL;
}
typedef PGET_MODULE_BASE_ROUTINE PFNSYMGETMODULEBASE;
static PFNSYMGETMODULEBASE pfnSymGetModuleBase = NULL;
static
DWORD WINAPI j_SymGetModuleBase(HANDLE hProcess, DWORD dwAddr)
{
if(
(hModule_Imagehlp || (hModule_Imagehlp = LoadLibrary(_T("IMAGEHLP.DLL")))) &&
(pfnSymGetModuleBase || (pfnSymGetModuleBase = (PFNSYMGETMODULEBASE) GetProcAddress(hModule_Imagehlp, "SymGetModuleBase")))
)
return pfnSymGetModuleBase(hProcess, dwAddr);
else
return 0;
}
typedef bool (WINAPI *PFNSTACKWALK)(DWORD, HANDLE, HANDLE, LPSTACKFRAME, LPVOID, PREAD_PROCESS_MEMORY_ROUTINE, PFUNCTION_TABLE_ACCESS_ROUTINE, PGET_MODULE_BASE_ROUTINE, PTRANSLATE_ADDRESS_ROUTINE);
static PFNSTACKWALK pfnStackWalk = NULL;
static
bool WINAPI j_StackWalk(
DWORD MachineType,
HANDLE hProcess,
HANDLE hThread,
LPSTACKFRAME StackFrame,
PVOID ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE TranslateAddress
)
{
if(
(hModule_Imagehlp || (hModule_Imagehlp = LoadLibrary(_T("IMAGEHLP.DLL")))) &&
(pfnStackWalk || (pfnStackWalk = (PFNSTACKWALK) GetProcAddress(hModule_Imagehlp, "StackWalk")))
)
return pfnStackWalk(
MachineType,
hProcess,
hThread,
StackFrame,
ContextRecord,
ReadMemoryRoutine,
FunctionTableAccessRoutine,
GetModuleBaseRoutine,
TranslateAddress
);
else
return FALSE;
}
typedef bool (WINAPI *PFNSYMGETSYMFROMADDR)(HANDLE, DWORD, LPDWORD, PIMAGEHLP_SYMBOL);
static PFNSYMGETSYMFROMADDR pfnSymGetSymFromAddr = NULL;
static
bool WINAPI j_SymGetSymFromAddr(HANDLE hProcess, DWORD Address, PDWORD Displacement, PIMAGEHLP_SYMBOL Symbol)
{
if(
(hModule_Imagehlp || (hModule_Imagehlp = LoadLibrary(_T("IMAGEHLP.DLL")))) &&
(pfnSymGetSymFromAddr || (pfnSymGetSymFromAddr = (PFNSYMGETSYMFROMADDR) GetProcAddress(hModule_Imagehlp, "SymGetSymFromAddr")))
)
return pfnSymGetSymFromAddr(hProcess, Address, Displacement, Symbol);
else
return FALSE;
}
typedef bool (WINAPI *PFNSYMGETLINEFROMADDR)(HANDLE, DWORD, LPDWORD, PIMAGEHLP_LINE);
static PFNSYMGETLINEFROMADDR pfnSymGetLineFromAddr = NULL;
static
bool WINAPI j_SymGetLineFromAddr(HANDLE hProcess, DWORD dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line)
{
if(
(hModule_Imagehlp || (hModule_Imagehlp = LoadLibrary(_T("IMAGEHLP.DLL")))) &&
(pfnSymGetLineFromAddr || (pfnSymGetLineFromAddr = (PFNSYMGETLINEFROMADDR) GetProcAddress(hModule_Imagehlp, "SymGetLineFromAddr")))
)
return pfnSymGetLineFromAddr(hProcess, dwAddr, pdwDisplacement, Line);
else
return FALSE;
}
static
bool ImagehlpDemangleSymName(LPCTSTR lpName, LPTSTR lpDemangledName, DWORD nSize)
{
BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512];
PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL) symbolBuffer;
memset( symbolBuffer, 0, sizeof(symbolBuffer) );
pSymbol->SizeOfStruct = sizeof(symbolBuffer);
pSymbol->MaxNameLength = 512;
2010-12-21 07:28:28 -08:00
lstrcpyn((LPTSTR)pSymbol->Name, lpName, pSymbol->MaxNameLength);
if(!j_SymUnDName(pSymbol, (PSTR)lpDemangledName, nSize))
return FALSE;
return TRUE;
}
static
bool ImagehlpGetSymFromAddr(HANDLE hProcess, DWORD dwAddress, LPTSTR lpSymName, DWORD nSize)
{
// IMAGEHLP is wacky, and requires you to pass in a pointer to a
// IMAGEHLP_SYMBOL structure. The problem is that this structure is
// variable length. That is, you determine how big the structure is
// at runtime. This means that you can't use sizeof(struct).
// So...make a buffer that's big enough, and make a pointer
// to the buffer. We also need to initialize not one, but TWO
// members of the structure before it can be used.
BYTE symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512];
PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL) symbolBuffer;
DWORD dwDisplacement = 0; // Displacement of the input address, relative to the start of the symbol
pSymbol->SizeOfStruct = sizeof(symbolBuffer);
pSymbol->MaxNameLength = 512;
assert(bSymInitialized);
if(!j_SymGetSymFromAddr(hProcess, dwAddress, &dwDisplacement, pSymbol))
return FALSE;
2010-12-21 07:28:28 -08:00
lstrcpyn(lpSymName, (LPCTSTR)pSymbol->Name, nSize);
return TRUE;
}
static
bool ImagehlpGetLineFromAddr(HANDLE hProcess, DWORD dwAddress, LPTSTR lpFileName, DWORD nSize, LPDWORD lpLineNumber)
{
IMAGEHLP_LINE Line;
DWORD dwDisplacement = 0; // Displacement of the input address, relative to the start of the symbol
// Do the source and line lookup.
memset(&Line, 0, sizeof(IMAGEHLP_LINE));
Line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
assert(bSymInitialized);
#if 1
{
// The problem is that the symbol engine only finds those source
// line addresses (after the first lookup) that fall exactly on
// a zero displacement. I will walk backwards 100 bytes to
// find the line and return the proper displacement.
DWORD dwTempDisp = 0 ;
while (dwTempDisp < 100 && !j_SymGetLineFromAddr(hProcess, dwAddress - dwTempDisp, &dwDisplacement, &Line))
++dwTempDisp;
if(dwTempDisp >= 100)
return FALSE;
// It was found and the source line information is correct so
// change the displacement if it was looked up multiple times.
if (dwTempDisp < 100 && dwTempDisp != 0 )
dwDisplacement = dwTempDisp;
}
#else
if(!j_SymGetLineFromAddr(hProcess, dwAddress, &dwDisplacement, &Line))
return FALSE;
#endif
assert(lpFileName && lpLineNumber);
lstrcpyn(lpFileName, (LPCTSTR)Line.FileName, nSize);
*lpLineNumber = Line.LineNumber;
return TRUE;
}
static
bool PEGetSymFromAddr(HANDLE hProcess, DWORD dwAddress, LPTSTR lpSymName, DWORD nSize)
{
HMODULE hModule;
PIMAGE_NT_HEADERS pNtHdr;
IMAGE_NT_HEADERS NtHdr;
PIMAGE_SECTION_HEADER pSection;
DWORD dwNearestAddress = 0, dwNearestName;
int i;
if(!(hModule = (HMODULE) GetModuleBase(dwAddress)))
return FALSE;
{
PIMAGE_DOS_HEADER pDosHdr;
LONG e_lfanew;
// Point to the DOS header in memory
pDosHdr = (PIMAGE_DOS_HEADER)hModule;
// From the DOS header, find the NT (PE) header
if(!ReadProcessMemory(hProcess, &pDosHdr->e_lfanew, &e_lfanew, sizeof(e_lfanew), NULL))
return FALSE;
pNtHdr = (PIMAGE_NT_HEADERS)((DWORD)hModule + (DWORD)e_lfanew);
if(!ReadProcessMemory(hProcess, pNtHdr, &NtHdr, sizeof(IMAGE_NT_HEADERS), NULL))
return FALSE;
}
pSection = (PIMAGE_SECTION_HEADER) ((DWORD)pNtHdr + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + NtHdr.FileHeader.SizeOfOptionalHeader);
// Look for export section
for (i = 0; i < NtHdr.FileHeader.NumberOfSections; i++, pSection++)
{
IMAGE_SECTION_HEADER Section;
PIMAGE_EXPORT_DIRECTORY pExportDir = NULL;
BYTE ExportSectionName[IMAGE_SIZEOF_SHORT_NAME] = {'.', 'e', 'd', 'a', 't', 'a', '\0', '\0'};
if(!ReadProcessMemory(hProcess, pSection, &Section, sizeof(IMAGE_SECTION_HEADER), NULL))
return FALSE;
if(memcmp(Section.Name, ExportSectionName, IMAGE_SIZEOF_SHORT_NAME) == 0)
pExportDir = (PIMAGE_EXPORT_DIRECTORY) Section.VirtualAddress;
else if ((NtHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress >= Section.VirtualAddress) && (NtHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress < (Section.VirtualAddress + Section.SizeOfRawData)))
pExportDir = (PIMAGE_EXPORT_DIRECTORY) NtHdr.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
if(pExportDir)
{
IMAGE_EXPORT_DIRECTORY ExportDir;
if(!ReadProcessMemory(hProcess, (PVOID)((DWORD)hModule + (DWORD)pExportDir), &ExportDir, sizeof(IMAGE_EXPORT_DIRECTORY), NULL))
return FALSE;
{
PDWORD *AddressOfFunctions = (PDWORD *)alloca(ExportDir.NumberOfFunctions*sizeof(PDWORD));
int j;
if(!ReadProcessMemory(hProcess, (PVOID)((DWORD)hModule + (DWORD)ExportDir.AddressOfFunctions), AddressOfFunctions, ExportDir.NumberOfFunctions*sizeof(PDWORD), NULL))
return FALSE;
for(j = 0; j < ExportDir.NumberOfNames; ++j)
{
DWORD pFunction = (DWORD)hModule + (DWORD)AddressOfFunctions[j];
//ReadProcessMemory(hProcess, (DWORD) hModule + (DWORD) (&ExportDir.AddressOfFunctions[j]), &pFunction, sizeof(pFunction), NULL);
if(pFunction <= dwAddress && pFunction > dwNearestAddress)
{
dwNearestAddress = pFunction;
if(!ReadProcessMemory(hProcess, (PVOID)((DWORD)hModule + (DWORD)(&ExportDir.AddressOfNames)[j]), &dwNearestName, sizeof(dwNearestName), NULL))
return FALSE;
dwNearestName = (DWORD) hModule + dwNearestName;
}
}
}
}
}
if(!dwNearestAddress)
return FALSE;
if(!ReadProcessMemory(hProcess, (PVOID)dwNearestName, lpSymName, nSize, NULL))
return FALSE;
lpSymName[nSize - 1] = 0;
return TRUE;
}
// Cross platform compatibility.
// If you change this, make sure it doesn't break the cross compile or the native compile.
// Don't ask why this is needed, have no idea.
struct ItDoesntMatterIfItsADWORDOrAVoidPointer_JustCompileTheDamnThing
{
ItDoesntMatterIfItsADWORDOrAVoidPointer_JustCompileTheDamnThing(void *whatever) : whatever(whatever) {}
operator void *() const { return whatever; } // Oooh, look compiler1, I'm a void *, just what you wanted!
operator DWORD() const { return (DWORD)whatever; } // Oooh, look compiler2, I'm a DWORD, just what you wanted!
void *whatever;
};
static
bool WINAPI IntelStackWalk(
DWORD MachineType,
HANDLE hProcess,
WZ_DECL_UNUSED HANDLE hThread,
LPSTACKFRAME StackFrame,
PCONTEXT ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
WZ_DECL_UNUSED PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
WZ_DECL_UNUSED PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
WZ_DECL_UNUSED PTRANSLATE_ADDRESS_ROUTINE TranslateAddress
)
{
assert(MachineType == IMAGE_FILE_MACHINE_I386);
if(ReadMemoryRoutine == NULL)
ReadMemoryRoutine = (PREAD_PROCESS_MEMORY_ROUTINE)ReadProcessMemory;
if(!StackFrame->Reserved[0])
{
StackFrame->Reserved[0] = 1;
StackFrame->AddrPC.Mode = AddrModeFlat;
StackFrame->AddrPC.Offset = ContextRecord->Eip;
StackFrame->AddrStack.Mode = AddrModeFlat;
StackFrame->AddrStack.Offset = ContextRecord->Esp;
StackFrame->AddrFrame.Mode = AddrModeFlat;
StackFrame->AddrFrame.Offset = ContextRecord->Ebp;
StackFrame->AddrReturn.Mode = AddrModeFlat;
// Error 26 error C2664: 'bool (HANDLE,DWORD,PVOID,DWORD,PDWORD)' :
// cannot convert parameter 2 from 'void *' to 'DWORD'
// c:\warzone\lib\exceptionhandler\exchndl.cpp 599
// ../../../../lib/exceptionhandler/exchndl.cpp: In function bool IntelStackWalk(DWORD, void*, void*, _tagSTACKFRAME*, CONTEXT*, bool (*)(void*, const void*, void*, DWORD, DWORD*), void* (*)(void*, DWORD), DWORD (*)(void*, DWORD), DWORD (*)(void*, void*, _tagADDRESS*)):
// ../../../../lib/exceptionhandler/exchndl.cpp:599: error: invalid conversion from long unsigned int to const void*
if(!ReadMemoryRoutine((HANDLE)hProcess, ItDoesntMatterIfItsADWORDOrAVoidPointer_JustCompileTheDamnThing((void *) (StackFrame->AddrFrame.Offset + sizeof(DWORD))), (void *)&StackFrame->AddrReturn.Offset, sizeof(DWORD), NULL))
return FALSE;
}
else
{
StackFrame->AddrPC.Offset = StackFrame->AddrReturn.Offset;
//AddrStack = AddrFrame + 2*sizeof(DWORD);
if(!ReadMemoryRoutine((HANDLE)hProcess, ItDoesntMatterIfItsADWORDOrAVoidPointer_JustCompileTheDamnThing((void *) StackFrame->AddrFrame.Offset), (void *)&StackFrame->AddrFrame.Offset, sizeof(DWORD), NULL))
return FALSE;
if(!ReadMemoryRoutine((HANDLE)hProcess, ItDoesntMatterIfItsADWORDOrAVoidPointer_JustCompileTheDamnThing((void *) (StackFrame->AddrFrame.Offset + sizeof(DWORD))), (void *)&StackFrame->AddrReturn.Offset, sizeof(DWORD), NULL))
return FALSE;
}
ReadMemoryRoutine((HANDLE)hProcess, ItDoesntMatterIfItsADWORDOrAVoidPointer_JustCompileTheDamnThing((void *) (StackFrame->AddrFrame.Offset + 2*sizeof(DWORD))), (void *)StackFrame->Params, sizeof(StackFrame->Params), NULL);
return TRUE;
}
static
bool StackBackTrace(HANDLE hProcess, HANDLE hThread, PCONTEXT pContext)
{
STACKFRAME StackFrame;
HMODULE hModule = NULL;
TCHAR szModule[MAX_PATH];
#ifdef HAVE_BFD
bfd *abfd = NULL;
asymbol **syms = NULL; // The symbol table.
long symcount = 0; // Number of symbols in `syms'.
#endif /* HAVE_BFD */
assert(!bSymInitialized);
j_SymSetOptions(/* SYMOPT_UNDNAME | */ SYMOPT_LOAD_LINES);
if(j_SymInitialize(hProcess, NULL, TRUE))
bSymInitialized = TRUE;
memset( &StackFrame, 0, sizeof(StackFrame) );
// Initialize the STACKFRAME structure for the first call. This is only
// necessary for Intel CPUs, and isn't mentioned in the documentation.
StackFrame.AddrPC.Offset = pContext->Eip;
StackFrame.AddrPC.Mode = AddrModeFlat;
StackFrame.AddrStack.Offset = pContext->Esp;
StackFrame.AddrStack.Mode = AddrModeFlat;
StackFrame.AddrFrame.Offset = pContext->Ebp;
StackFrame.AddrFrame.Mode = AddrModeFlat;
rprintf( _T("Call stack:\r\n") );
if(0)
rprintf( _T("AddrPC AddrReturn AddrFrame AddrStack\r\n") );
while ( 1 )
{
bool bSuccess = FALSE;
#ifdef HAVE_BFD
const HMODULE hPrevModule = hModule;
#endif /* HAVE_BFD */
TCHAR szSymName[512] = _T("");
TCHAR szFileName[MAX_PATH] = _T("");
DWORD LineNumber = 0;
if(bSymInitialized)
{
if(!j_StackWalk(
IMAGE_FILE_MACHINE_I386,
hProcess,
hThread,
&StackFrame,
pContext,
NULL,
j_SymFunctionTableAccess,
j_SymGetModuleBase,
NULL
)
)
break;
}
else
{
if(!IntelStackWalk(
IMAGE_FILE_MACHINE_I386,
hProcess,
hThread,
&StackFrame,
pContext,
NULL,
NULL,
NULL,
NULL
)
)
break;
}
// Basic sanity check to make sure the frame is OK. Bail if not.
if ( 0 == StackFrame.AddrFrame.Offset )
break;
if(0)
{
rprintf(
_T("%08lX %08lX %08lX %08lX\r\n"),
StackFrame.AddrPC.Offset,
StackFrame.AddrReturn.Offset,
StackFrame.AddrFrame.Offset,
StackFrame.AddrStack.Offset
);
rprintf(
_T("%08lX %08lX %08lX %08lX\r\n"),
StackFrame.Params[0],
StackFrame.Params[1],
StackFrame.Params[2],
StackFrame.Params[3]
);
}
rprintf( _T("%08lX"), StackFrame.AddrPC.Offset);
if((hModule = (HMODULE) GetModuleBase(StackFrame.AddrPC.Offset)) && GetModuleFileName(hModule, szModule, sizeof(szModule)))
{
#ifndef HAVE_BFD
rprintf( _T(" %s:ModulBase %08lX"), szModule, hModule);
#else /* HAVE_BFD */
rprintf( _T(" %s:%08lX"), szModule, StackFrame.AddrPC.Offset);
if(hModule != hPrevModule)
{
if(syms)
{
GlobalFree(syms);
syms = NULL;
symcount = 0;
}
if(abfd)
bfd_close(abfd);
if((abfd = bfd_openr (szModule, NULL)))
if(bfd_check_format(abfd, bfd_object))
if(bfd_get_file_flags(abfd) & HAS_SYMS)
/* Read in the symbol table. */
slurp_symtab(abfd, &syms, &symcount);
}
if(!bSuccess && abfd && syms && symcount)
if((bSuccess = BfdGetSymFromAddr(abfd, syms, symcount, StackFrame.AddrPC.Offset, szSymName, 512)))
{
/*
framepointer = StackFrame.AddrFrame.Offset;
hprocess = hProcess;
*/
BfdDemangleSymName(szSymName, szSymName, 512);
rprintf( _T(" %s"), szSymName);
if(BfdGetLineFromAddr(abfd, syms, symcount, StackFrame.AddrPC.Offset, szFileName, MAX_PATH, &LineNumber))
rprintf( _T(" %s:%ld"), szFileName, LineNumber);
}
#endif /* HAVE_BFD */
if(!bSuccess && bSymInitialized)
if((bSuccess = ImagehlpGetSymFromAddr(hProcess, StackFrame.AddrPC.Offset, szSymName, 512)))
{
rprintf( _T(" %s"), szSymName);
ImagehlpDemangleSymName(szSymName, szSymName, 512);
if(ImagehlpGetLineFromAddr(hProcess, StackFrame.AddrPC.Offset, szFileName, MAX_PATH, &LineNumber))
rprintf( _T(" %s:%ld"), szFileName, LineNumber);
}
if(!bSuccess)
if((bSuccess = PEGetSymFromAddr(hProcess, StackFrame.AddrPC.Offset, szSymName, 512)))
rprintf( _T(" %s"), szSymName);
}
rprintf(_T("\r\n"));
}
#ifdef HAVE_BFD
if(syms)
{
GlobalFree(syms);
syms = NULL;
symcount = 0;
}
if(abfd)
bfd_close(abfd);
#endif /* HAVE_BFD */
if(bSymInitialized)
{
if(!j_SymCleanup(hProcess))
assert(0);
bSymInitialized = FALSE;
}
return TRUE;
}
static
void GenerateExceptionReport(PEXCEPTION_POINTERS pExceptionInfo)
{
PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
TCHAR szModule[MAX_PATH];
HMODULE hModule;
PCONTEXT pContext;
// Start out with a banner
rprintf(_T("-------------------\r\n\r\n"));
{
const TCHAR *lpDayOfWeek[] = {
_T("Sunday"),
_T("Monday"),
_T("Tuesday"),
_T("Wednesday"),
_T("Thursday"),
_T("Friday"),
_T("Saturday")
};
const TCHAR *lpMonth[] = {
NULL,
_T("January"),
_T("February"),
_T("March"),
_T("April"),
_T("May"),
_T("June"),
_T("July"),
_T("August"),
_T("September"),
_T("October"),
_T("November"),
_T("December")
};
SYSTEMTIME SystemTime;
GetLocalTime(&SystemTime);
rprintf(_T("Error occured on %s, %s %i, %i at %02i:%02i:%02i.\r\n\r\n"),
lpDayOfWeek[SystemTime.wDayOfWeek],
lpMonth[SystemTime.wMonth],
SystemTime.wDay,
SystemTime.wYear,
SystemTime.wHour,
SystemTime.wMinute,
SystemTime.wSecond
);
}
// Dump a generic info header
dbgDumpHeader(hReportFile);
// First print information about the type of fault
rprintf(_T("\r\n%s caused "), GetModuleFileName(NULL, szModule, MAX_PATH) ? szModule : _T("Application"));
switch(pExceptionRecord->ExceptionCode)
{
case EXCEPTION_ACCESS_VIOLATION:
rprintf(_T("an Access Violation"));
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
rprintf(_T("an Array Bound Exceeded"));
break;
case EXCEPTION_BREAKPOINT:
rprintf(_T("a Breakpoint"));
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
rprintf(_T("a Datatype Misalignment"));
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
rprintf(_T("a Float Denormal Operand"));
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
rprintf(_T("a Float Divide By Zero"));
break;
case EXCEPTION_FLT_INEXACT_RESULT:
rprintf(_T("a Float Inexact Result"));
break;
case EXCEPTION_FLT_INVALID_OPERATION:
rprintf(_T("a Float Invalid Operation"));
break;
case EXCEPTION_FLT_OVERFLOW:
rprintf(_T("a Float Overflow"));
break;
case EXCEPTION_FLT_STACK_CHECK:
rprintf(_T("a Float Stack Check"));
break;
case EXCEPTION_FLT_UNDERFLOW:
rprintf(_T("a Float Underflow"));
break;
case EXCEPTION_GUARD_PAGE:
rprintf(_T("a Guard Page"));
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
rprintf(_T("an Illegal Instruction"));
break;
case EXCEPTION_IN_PAGE_ERROR:
rprintf(_T("an In Page Error"));
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
rprintf(_T("an Integer Divide By Zero"));
break;
case EXCEPTION_INT_OVERFLOW:
rprintf(_T("an Integer Overflow"));
break;
case EXCEPTION_INVALID_DISPOSITION:
rprintf(_T("an Invalid Disposition"));
break;
case EXCEPTION_INVALID_HANDLE:
rprintf(_T("an Invalid Handle"));
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
rprintf(_T("a Noncontinuable Exception"));
break;
case EXCEPTION_PRIV_INSTRUCTION:
rprintf(_T("a Privileged Instruction"));
break;
case EXCEPTION_SINGLE_STEP:
rprintf(_T("a Single Step"));
break;
case EXCEPTION_STACK_OVERFLOW:
rprintf(_T("a Stack Overflow"));
break;
case DBG_CONTROL_C:
rprintf(_T("a Control+C"));
break;
case DBG_CONTROL_BREAK:
rprintf(_T("a Control+Break"));
break;
case DBG_TERMINATE_THREAD:
rprintf(_T("a Terminate Thread"));
break;
case DBG_TERMINATE_PROCESS:
rprintf(_T("a Terminate Process"));
break;
case RPC_S_UNKNOWN_IF:
rprintf(_T("an Unknown Interface"));
break;
case RPC_S_SERVER_UNAVAILABLE:
rprintf(_T("a Server Unavailable"));
break;
default:
/*
static TCHAR szBuffer[512] = { 0 };
// If not one of the "known" exceptions, try to get the string
// from NTDLL.DLL's message table.
FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
GetModuleHandle(_T("NTDLL.DLL")),
dwCode, 0, szBuffer, sizeof(szBuffer), 0);
*/
rprintf(_T("an Unknown [0x%lX] Exception"), pExceptionRecord->ExceptionCode);
break;
}
// Now print information about where the fault occured
rprintf(_T(" at location %08x"), (DWORD) pExceptionRecord->ExceptionAddress);
if((hModule = (HMODULE) GetModuleBase((DWORD) pExceptionRecord->ExceptionAddress)) && GetModuleFileName(hModule, szModule, sizeof(szModule)))
rprintf(_T(" in module %s"), szModule);
// If the exception was an access violation, print out some additional information, to the error log and the debugger.
if(pExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && pExceptionRecord->NumberParameters >= 2)
rprintf(_T(" %s location %08x"), pExceptionRecord->ExceptionInformation[0] ? "Writing to" : "Reading from", pExceptionRecord->ExceptionInformation[1]);
rprintf(_T(".\r\n\r\n"));
dbgDumpLog(hReportFile);
pContext = pExceptionInfo->ContextRecord;
#ifdef _M_IX86 // Intel Only!
// Show the registers
rprintf(_T("Registers:\r\n"));
if(pContext->ContextFlags & CONTEXT_INTEGER)
rprintf(
_T("eax=%08lx ebx=%08lx ecx=%08lx edx=%08lx esi=%08lx edi=%08lx\r\n"),
pContext->Eax,
pContext->Ebx,
pContext->Ecx,
pContext->Edx,
pContext->Esi,
pContext->Edi
);
if(pContext->ContextFlags & CONTEXT_CONTROL)
rprintf(
_T("eip=%08lx esp=%08lx ebp=%08lx iopl=%1lx %s %s %s %s %s %s %s %s %s %s\r\n"),
pContext->Eip,
pContext->Esp,
pContext->Ebp,
(pContext->EFlags >> 12) & 3, // IOPL level value
pContext->EFlags & 0x00100000 ? "vip" : " ", // VIP (virtual interrupt pending)
pContext->EFlags & 0x00080000 ? "vif" : " ", // VIF (virtual interrupt flag)
pContext->EFlags & 0x00000800 ? "ov" : "nv", // VIF (virtual interrupt flag)
pContext->EFlags & 0x00000400 ? "dn" : "up", // OF (overflow flag)
pContext->EFlags & 0x00000200 ? "ei" : "di", // IF (interrupt enable flag)
pContext->EFlags & 0x00000080 ? "ng" : "pl", // SF (sign flag)
pContext->EFlags & 0x00000040 ? "zr" : "nz", // ZF (zero flag)
pContext->EFlags & 0x00000010 ? "ac" : "na", // AF (aux carry flag)
pContext->EFlags & 0x00000004 ? "po" : "pe", // PF (parity flag)
pContext->EFlags & 0x00000001 ? "cy" : "nc" // CF (carry flag)
);
if(pContext->ContextFlags & CONTEXT_SEGMENTS)
{
rprintf(
_T("cs=%04lx ss=%04lx ds=%04lx es=%04lx fs=%04lx gs=%04lx"),
pContext->SegCs,
pContext->SegSs,
pContext->SegDs,
pContext->SegEs,
pContext->SegFs,
pContext->SegGs,
pContext->EFlags
);
if(pContext->ContextFlags & CONTEXT_CONTROL)
rprintf(
_T(" efl=%08lx"),
pContext->EFlags
);
}
else
if(pContext->ContextFlags & CONTEXT_CONTROL)
rprintf(
_T(" efl=%08lx"),
pContext->EFlags
);
rprintf(_T("\r\n\r\n"));
#endif
// FIXME: We *never* return from the below call!
StackBackTrace(GetCurrentProcess(), GetCurrentThread(), pContext);
rprintf(_T("\r\n\r\n"));
}
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
// Entry point where control comes on an unhandled exception
static
LONG WINAPI TopLevelExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
{
static bool bBeenHere = FALSE;
if(!bBeenHere)
{
UINT fuOldErrorMode;
bBeenHere = TRUE;
fuOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
hReportFile = CreateFileW(
szLogFileName,
GENERIC_WRITE,
0,
0,
OPEN_ALWAYS,
FILE_FLAG_WRITE_THROUGH,
0
);
if (hReportFile == INVALID_HANDLE_VALUE)
{
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
TCHAR szBuffer[4196];
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
wsprintf(szBuffer, _T("Exception handler failed with error %d: %s\n"), dw, lpMsgBuf);
MessageBox((HWND)MB_ICONEXCLAMATION, szBuffer, _T("Error"), MB_OK);
LocalFree(lpMsgBuf);
debug(LOG_ERROR, "Exception handler failed to create file!");
}
#ifdef HAVE_BFD
bfd_set_error_handler((bfd_error_handler_type) rprintf);
#endif /* HAVE_BFD */
if (hReportFile)
{
wchar_t szBuffer[4196];
int err;
SetFilePointer(hReportFile, 0, 0, FILE_END);
// FIXME: We don't return from the below function call
GenerateExceptionReport(pExceptionInfo);
CloseHandle(hReportFile);
wsprintfW(szBuffer, L"Warzone has crashed.\r\nSee %s for more details\r\n", szLogFileName);
err = MessageBoxW((HWND)MB_ICONERROR, szBuffer, L"Warzone Crashed!", MB_OK | MB_ICONERROR);
if (err == 0)
{
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
wchar_t szBuffer[4196];
FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR) &lpMsgBuf,
0, NULL );
wsprintfW(szBuffer, L"Exception handler failed with error %d: %s\n", dw, lpMsgBuf);
MessageBoxW((HWND)MB_ICONEXCLAMATION, szBuffer, L"Error", MB_OK);
LocalFree(lpMsgBuf);
debug(LOG_ERROR, "Exception handler failed to create file!");
}
hReportFile = 0;
}
SetErrorMode(fuOldErrorMode);
}
if(prevExceptionFilter)
return prevExceptionFilter(pExceptionInfo);
else
return EXCEPTION_CONTINUE_SEARCH;
}
void ExchndlSetup(const char *packageVersion)
{
# if defined(WZ_CC_MINGW)
wchar_t miniDumpPath[PATH_MAX] = {'\0'};
DWORD dwRetVal = 0;
#ifdef HAVE_BFD
bfd_init();
#endif /* HAVE_BFD */
// Install the unhandled exception filter function
prevExceptionFilter = SetUnhandledExceptionFilter(TopLevelExceptionFilter);
// Retrieve the current version
formattedVersionString = strdup(packageVersion);
#ifndef WZ_PORTABLE
// Because of UAC on vista / win7 we use this to write our dumps to (unless we override it via OverrideRPTDirectory())
// NOTE: CSIDL_PERSONAL = C:\Users\user name\Documents
if ( SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, miniDumpPath ) ))
{
PathAppendW( miniDumpPath, WSTRING(WZ_WRITEDIR));
PathAppendW( miniDumpPath, L"\\logs" );
if( !PathFileExistsW( miniDumpPath ) )
{
if( ERROR_SUCCESS != SHCreateDirectoryExW( NULL, miniDumpPath, NULL ) )
{
//last attempt to get a path
dwRetVal = GetTempPathW(PATH_MAX, miniDumpPath);
if (dwRetVal > MAX_PATH || (dwRetVal == 0))
{
MessageBox((HWND)MB_ICONEXCLAMATION, "Could not created a temporary directory!\nProgram will now exit." , _T("Error"), MB_OK);
exit(1);
}
}
}
}
#else
// we use where they installed it on, since this is a removeable drive (most likely), we will use where the program is located in.
if (dwRetVal = GetCurrentDirectoryW(MAX_PATH, miniDumpPath))
{
if(dwRetVal > MAX_PATH)
{
MessageBox((HWND)MB_ICONEXCLAMATION, "Buffer exceeds maximum path to create directory. Exiting.", _T("Error"), MB_OK);
exit(1);
}
PathAppendW( miniDumpPath, WSTRING(WZ_WRITEDIR));
PathAppendW( miniDumpPath, L"\\logs" );
}
#endif
else
{ // should never fail, but if it does, we fall back to this
dwRetVal = GetTempPathW(PATH_MAX, miniDumpPath);
if (dwRetVal > MAX_PATH || (dwRetVal == 0))
{
MessageBox((HWND)MB_ICONEXCLAMATION, "Could not created a temporary directory!\nProgram will now exit." , _T("Error!"), MB_OK);
exit(1);
}
}
wcscat(szLogFileName, L"Warzone2100.RPT");
wcscat(miniDumpPath, L"\\");
wcscat(miniDumpPath,szLogFileName);
wcscpy(szLogFileName, miniDumpPath);
atexit(ExchndlShutdown);
#endif
}
void ResetRPTDirectory(wchar_t *newPath)
{
wcscpy(szLogFileName, newPath);
}
void ExchndlShutdown(void)
{
if (prevExceptionFilter)
SetUnhandledExceptionFilter(prevExceptionFilter);
prevExceptionFilter = NULL;
free(formattedVersionString);
formattedVersionString = NULL;
}