Logo Search packages:      
Sourcecode: warzone2100 version File versions

exchndl.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       Warzone Resurrection 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
*/

#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>


#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>
#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(pSymbol->Name, lpName, pSymbol->MaxNameLength);

      if(!j_SymUnDName(pSymbol, 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, 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, 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 = 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;
}

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 = 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;
            if(!ReadMemoryRoutine(hProcess, (LPCVOID) (StackFrame->AddrFrame.Offset + sizeof(DWORD)), &StackFrame->AddrReturn.Offset, sizeof(DWORD), NULL))
                  return FALSE;
      }
      else
      {
            StackFrame->AddrPC.Offset = StackFrame->AddrReturn.Offset;
            //AddrStack = AddrFrame + 2*sizeof(DWORD);
            if(!ReadMemoryRoutine(hProcess, (LPCVOID) StackFrame->AddrFrame.Offset, &StackFrame->AddrFrame.Offset, sizeof(DWORD), NULL))
                  return FALSE;
            if(!ReadMemoryRoutine(hProcess, (LPCVOID) (StackFrame->AddrFrame.Offset + sizeof(DWORD)), &StackFrame->AddrReturn.Offset, sizeof(DWORD), NULL))
                  return FALSE;
      }

      ReadMemoryRoutine(hProcess, (LPCVOID) (StackFrame->AddrFrame.Offset + 2*sizeof(DWORD)), 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("%s caused "),  GetModuleFileName(NULL, szModule, MAX_PATH) ? szModule : "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(" %s location %08x", pExceptionRecord->ExceptionInformation[0] ? "Writing to" : "Reading from", pExceptionRecord->ExceptionInformation[1]);

      rprintf(".\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

      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
            );

#ifdef HAVE_BFD
            bfd_set_error_handler((bfd_error_handler_type) rprintf);
#endif /* HAVE_BFD */

            if (hReportFile)
            {
                  SetFilePointer(hReportFile, 0, 0, FILE_END);

                  GenerateExceptionReport(pExceptionInfo);

                  CloseHandle(hReportFile);
                  hReportFile = 0;
            }

            if(fuOldErrorMode & SEM_NOGPFAULTERRORBOX)
            {
                  TCHAR szBuffer[4196];

                  wsprintf(szBuffer, _T("An unhandled exception ocurred\r\nSee %s for more details\r\n"), szLogFileName);

                  MessageBox(
                        NULL,
                        szBuffer,
                        _T("Error"),
                        MB_OK | MB_ICONERROR
                  );
            }

            SetErrorMode(fuOldErrorMode);
      }

      if(prevExceptionFilter)
            return prevExceptionFilter(pExceptionInfo);
      else
            return EXCEPTION_CONTINUE_SEARCH;
}

void ExchndlSetup()
{
      // Install the unhandled exception filter function
      prevExceptionFilter = SetUnhandledExceptionFilter(TopLevelExceptionFilter);

      // Retrieve the current version
      formattedVersionString = strdup(version_getFormattedVersionString());

      // Figure out what the report file will be named, and store it away
      if(GetModuleFileName(NULL, szLogFileName, MAX_PATH))
      {
            LPTSTR lpszDot;

            // Look for the '.' before the "EXE" extension.  Replace the extension
            // with "RPT"
            if((lpszDot = _tcsrchr(szLogFileName, _T('.'))))
            {
                  lpszDot++;  // Advance past the '.'
                  _tcscpy(lpszDot, _T("RPT"));  // "RPT" -> "Report"
            }
            else
                  _tcscat(szLogFileName, _T(".RPT"));
      }
      else if(GetWindowsDirectory(szLogFileName, MAX_PATH))
      {
            _tcscat(szLogFileName, _T("EXCHNDL.RPT"));
      }

      atexit(ExchndlShutdown);
}

void ExchndlShutdown(void)
{
      if (prevExceptionFilter)
            SetUnhandledExceptionFilter(prevExceptionFilter);

      prevExceptionFilter = NULL;
      free(formattedVersionString);
      formattedVersionString = NULL;
}

Generated by  Doxygen 1.6.0   Back to index