1262 lines
35 KiB
C++
1262 lines
35 KiB
C++
/*
|
||
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-2010 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"
|
||
// FIXME: #include from src/
|
||
#include "src/version.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
|
||
|
||
// Declare the static variables
|
||
static TCHAR szLogFileName[MAX_PATH] = _T("");
|
||
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>
|
||
extern "C"
|
||
{
|
||
#include "include/demangle.h"
|
||
#include "include/coff/internal.h"
|
||
#include "include/libcoff.h"
|
||
}
|
||
|
||
// 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;
|
||
|
||
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;
|
||
|
||
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))
|
||
{
|
||
bfd_vma adjust_section_vma = 0;
|
||
|
||
/* If we are adjusting section VMA's, change them all now. Changing
|
||
the BFD information is a hack. However, we must do it, or
|
||
bfd_find_nearest_line will not do the right thing. */
|
||
if ((adjust_section_vma = (bfd_vma) hModule - pe_data(abfd)->pe_opthdr.ImageBase))
|
||
{
|
||
asection *s;
|
||
|
||
for (s = abfd->sections; s != NULL; s = s->next)
|
||
{
|
||
s->vma += adjust_section_vma;
|
||
s->lma += adjust_section_vma;
|
||
}
|
||
}
|
||
|
||
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 = CreateFile(
|
||
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;
|
||
LPVOID lpDisplayBuf;
|
||
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);
|
||
LocalFree(lpDisplayBuf);
|
||
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)
|
||
{
|
||
TCHAR szBuffer[4196];
|
||
int err;
|
||
|
||
SetFilePointer(hReportFile, 0, 0, FILE_END);
|
||
|
||
// FIXME: We don't return from the below function call
|
||
GenerateExceptionReport(pExceptionInfo);
|
||
CloseHandle(hReportFile);
|
||
|
||
wsprintf(szBuffer, _T("Warzone has crashed.\r\nSee %s for more details\r\n"), szLogFileName);
|
||
err = MessageBox((HWND)MB_ICONERROR, szBuffer, _T("Warzone Crashed!"), MB_OK | MB_ICONERROR);
|
||
if (err == 0)
|
||
{
|
||
LPVOID lpMsgBuf;
|
||
LPVOID lpDisplayBuf;
|
||
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);
|
||
LocalFree(lpDisplayBuf);
|
||
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()
|
||
{
|
||
# if defined(WZ_CC_MINGW)
|
||
TCHAR miniDumpPath[PATH_MAX] = {'\0'};
|
||
// Install the unhandled exception filter function
|
||
prevExceptionFilter = SetUnhandledExceptionFilter(TopLevelExceptionFilter);
|
||
|
||
// Retrieve the current version
|
||
formattedVersionString = strdup(version_getFormattedVersionString());
|
||
|
||
// 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( SHGetFolderPathA( NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, miniDumpPath ) ))
|
||
{
|
||
PathAppend( miniDumpPath, TEXT( "Warzone 2100 master\\logs" ) );
|
||
|
||
if( !PathFileExists( miniDumpPath ) )
|
||
{
|
||
if( ERROR_SUCCESS != SHCreateDirectoryEx( NULL, miniDumpPath, NULL ) )
|
||
{
|
||
_tcscpy(miniDumpPath, _T("c:\\temp"));
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{ // should never fail, but if it does, we fall back to this
|
||
_tcscpy(miniDumpPath, _T("c:\\temp"));
|
||
}
|
||
|
||
_tcscat(szLogFileName, _T("Warzone2100.RPT"));
|
||
_tcscat(miniDumpPath, _T("\\"));
|
||
_tcscat(miniDumpPath,szLogFileName);
|
||
_tcscpy(szLogFileName, miniDumpPath);
|
||
|
||
atexit(ExchndlShutdown);
|
||
#endif
|
||
}
|
||
void ResetRPTDirectory(TCHAR *newPath)
|
||
{
|
||
debug(LOG_WZ, "New RPT directory is %s, was %s", newPath, szLogFileName);
|
||
_tcscpy(szLogFileName, newPath);
|
||
}
|
||
void ExchndlShutdown(void)
|
||
{
|
||
if (prevExceptionFilter)
|
||
SetUnhandledExceptionFilter(prevExceptionFilter);
|
||
|
||
prevExceptionFilter = NULL;
|
||
free(formattedVersionString);
|
||
formattedVersionString = NULL;
|
||
}
|