From 2e2b3d09bfa290f62729309088444299ddc3d3cf Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Tue, 27 Aug 2002 22:02:30 +0000 Subject: [PATCH] Disasm/crash code. --- stepmania/src/Crash.cpp | 1417 +++++++++++++++++++++++++++++++++++++ stepmania/src/Crash.h | 58 ++ stepmania/src/CrashList.h | 174 +++++ stepmania/src/Disasm.cpp | 810 +++++++++++++++++++++ stepmania/src/Disasm.h | 88 +++ stepmania/src/Tls.cpp | 37 + stepmania/src/Tls.h | 31 + 7 files changed, 2615 insertions(+) create mode 100644 stepmania/src/Crash.cpp create mode 100644 stepmania/src/Crash.h create mode 100644 stepmania/src/CrashList.h create mode 100644 stepmania/src/Disasm.cpp create mode 100644 stepmania/src/Disasm.h create mode 100644 stepmania/src/Tls.cpp create mode 100644 stepmania/src/Tls.h diff --git a/stepmania/src/Crash.cpp b/stepmania/src/Crash.cpp new file mode 100644 index 0000000000..1f6bf0264b --- /dev/null +++ b/stepmania/src/Crash.cpp @@ -0,0 +1,1417 @@ +// from VirtualDub +// Copyright (C) 1998-2001 Avery Lee +// +// This program 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. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +// DO NOT USE stdio.h! printf() calls malloc()! +//#include +#include +#include + +#include +#include + +#include "resource.h" +#include "crash.h" +#include "disasm.h" +#include "CrashList.h" + +// GotoURL pulls in MFC stuff that we really shouldn't have here. +// #include "RageUtil.h" // for GotoURL +HINSTANCE GotoURL(const char *url); + +/////////////////////////////////////////////////////////////////////////// + +#define CODE_WINDOW (256) + +/////////////////////////////////////////////////////////////////////////// + +extern HINSTANCE g_hInstance; +extern unsigned long version_num; + +static CodeDisassemblyWindow *g_pcdw; + +struct VDDebugInfoContext { + void *pRawBlock; + + int nBuildNumber; + + const unsigned char *pRVAHeap; + unsigned nFirstRVA; + + const char *pClassNameHeap; + const char *pFuncNameHeap; + const unsigned long (*pSegments)[2]; + int nSegments; +}; + + +static VDDebugInfoContext g_debugInfo; + +/////////////////////////////////////////////////////////////////////////// + +BOOL APIENTRY CrashDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); +static void ReportCrashLog(HWND hwnd, HANDLE hFile); + +/////////////////////////////////////////////////////////////////////////// + +#ifdef _DEBUG +/* +void checkfpustack(const char *file, const int line) throw() { + static const char szFPUProblemCaption[]="FPU/MMX internal problem"; + static const char szFPUProblemMessage[]="The FPU stack wasn't empty! Tagword = %04x\nFile: %s, line %d"; + static bool seenmsg=false; + + char buf[128]; + unsigned short tagword; + + if (seenmsg) + return; + + __asm fnstenv buf + + tagword = *(unsigned short *)(buf + 8); + + if (tagword != 0xffff) { + wsprintf(buf, szFPUProblemMessage, tagword, file, line); + MessageBox(NULL, buf, szFPUProblemCaption, MB_OK); + seenmsg=true; + } + +} + +static void __declspec(naked) *operator new(size_t bytes) { + static const char fname[]="stack trace"; + + __asm { + push ebp + mov ebp,esp + + push [ebp+4] ;return address + push offset fname ;'filename' + push _NORMAL_BLOCK ;block type + push [ebp+8] ;allocation size + + call _malloc_dbg + add esp,16 + + pop ebp + ret + } +} +*/ +#endif + +#if 0 +void __declspec(naked) stackcheck(void *&sp) { + static const char g_szStackHemorrhage[]="WARNING: Thread is hemorrhaging stack space!\n"; + + __asm { + mov eax,[esp+4] + mov ecx,[eax] + or ecx,ecx + jnz started + mov [eax],esp + ret +started: + sub ecx,esp + mov eax,ecx + sar ecx,31 + xor eax,ecx + sub eax,ecx + cmp eax,128 + jb ok + push offset g_szStackHemorrhage + call dword ptr [OutputDebugString] + int 3 +ok: + ret + } +} +#endif + +/////////////////////////////////////////////////////////////////////////// +// +// Nina's per-thread debug logs are really handy, so I back-ported +// them to 1.x. These are lightweight in comparison, however. +// + +VirtualDubThreadState __declspec(thread) g_PerThreadState; +__declspec(thread) +struct { + ListNode node; + VirtualDubThreadState *pState; +} g_PerThreadStateNode; + +class VirtualDubThreadStateNode : public ListNode2 { +public: + VirtualDubThreadState *pState; +}; + +static CRITICAL_SECTION g_csPerThreadState; +static List2 g_listPerThreadState; +static LONG g_nThreadsTrackedMinusOne = -1; + +void VirtualDubInitializeThread(const char *pszName) { + DWORD dwThreadId = GetCurrentThreadId(); + + if (!InterlockedIncrement(&g_nThreadsTrackedMinusOne)) { + InitializeCriticalSection(&g_csPerThreadState); + } + + EnterCriticalSection(&g_csPerThreadState); + + if (DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), (HANDLE *)&g_PerThreadState.hThread, NULL, FALSE, DUPLICATE_SAME_ACCESS)) { + + g_PerThreadState.pszThreadName = pszName; + g_PerThreadState.dwThreadId = dwThreadId; + + g_PerThreadStateNode.pState = &g_PerThreadState; + g_listPerThreadState.AddTail((ListNode2 *)&g_PerThreadStateNode); + } + + LeaveCriticalSection(&g_csPerThreadState); +} + +void VirtualDubDeinitializeThread() { + EnterCriticalSection(&g_csPerThreadState); + + ((ListNode2 *)&g_PerThreadStateNode)->Remove(); + + LeaveCriticalSection(&g_csPerThreadState); + + if (g_PerThreadState.pszThreadName) + CloseHandle((HANDLE)g_PerThreadState.hThread); + + InterlockedDecrement(&g_nThreadsTrackedMinusOne); +} + +/////////////////////////////////////////////////////////////////////////// + +static const struct ExceptionLookup { + DWORD code; + const char *name; +} exceptions[]={ + { EXCEPTION_ACCESS_VIOLATION, "Access Violation" }, + { EXCEPTION_BREAKPOINT, "Breakpoint" }, + { EXCEPTION_FLT_DENORMAL_OPERAND, "FP Denormal Operand" }, + { EXCEPTION_FLT_DIVIDE_BY_ZERO, "FP Divide-by-Zero" }, + { EXCEPTION_FLT_INEXACT_RESULT, "FP Inexact Result" }, + { EXCEPTION_FLT_INVALID_OPERATION, "FP Invalid Operation" }, + { EXCEPTION_FLT_OVERFLOW, "FP Overflow", }, + { EXCEPTION_FLT_STACK_CHECK, "FP Stack Check", }, + { EXCEPTION_FLT_UNDERFLOW, "FP Underflow", }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Integer Divide-by-Zero", }, + { EXCEPTION_INT_OVERFLOW, "Integer Overflow", }, + { EXCEPTION_PRIV_INSTRUCTION, "Privileged Instruction", }, + { EXCEPTION_ILLEGAL_INSTRUCTION, "Illegal instruction" }, + { EXCEPTION_INVALID_HANDLE, "Invalid handle" }, + { 0xe06d7363, "Unhandled Microsoft C++ Exception", }, + // hmm... '_msc'... gee, who would have thought? + { NULL }, +}; + +long VDDebugInfoLookupRVA(VDDebugInfoContext *pctx, unsigned rva, char *buf, int buflen); +bool VDDebugInfoInitFromMemory(VDDebugInfoContext *pctx, const void *_src); +bool VDDebugInfoInitFromFile(VDDebugInfoContext *pctx, const char *pszFilename); +void VDDebugInfoDeinit(VDDebugInfoContext *pctx); + +static void SpliceProgramPath(char *buf, int bufsiz, const char *fn) { + char tbuf[MAX_PATH]; + char *pszFile; + + GetModuleFileName(NULL, tbuf, sizeof tbuf); + GetFullPathName(tbuf, bufsiz, buf, &pszFile); + strcpy(pszFile, fn); +} + +long CrashSymLookup(VDDisassemblyContext *pctx, unsigned long virtAddr, char *buf, int buf_len) { + if (!g_debugInfo.pRVAHeap) + return -1; + + return VDDebugInfoLookupRVA(&g_debugInfo, virtAddr, buf, buf_len); +} + +long __stdcall CrashHandler(EXCEPTION_POINTERS *pExc) { + SetUnhandledExceptionFilter(NULL); + ///////////////////////// + // + // QUICKLY: SUSPEND ALL THREADS THAT AREN'T US. + + EnterCriticalSection(&g_csPerThreadState); + + try { + DWORD dwCurrentThread = GetCurrentThreadId(); + + for(List2::fwit it = g_listPerThreadState.begin(); it; ++it) { + const VirtualDubThreadState *pState = it->pState; + + if (pState->dwThreadId && pState->dwThreadId != dwCurrentThread) { + SuspendThread((HANDLE)pState->hThread); + } + } + } catch(...) { + } + + LeaveCriticalSection(&g_csPerThreadState); + + ///////////////////////// + + static char buf[CODE_WINDOW+16]; + HANDLE hprMe = GetCurrentProcess(); + void *lpBaseAddress = pExc->ExceptionRecord->ExceptionAddress; + char *lpAddr = (char *)((long)lpBaseAddress & -32); + + memset(buf, 0, sizeof buf); + + if ((unsigned long)lpAddr > CODE_WINDOW/2) + lpAddr -= CODE_WINDOW/2; + else + lpAddr = NULL; + + if (!ReadProcessMemory(hprMe, lpAddr, buf, CODE_WINDOW, NULL)) { + int i; + + for(i=0; iExceptionRecord; + const CONTEXT *const pContext = (const CONTEXT *)pExc->ContextRecord; + int i, tos; + + Report(hwnd, hFile, "EAX = %08lx", pContext->Eax); + Report(hwnd, hFile, "EBX = %08lx", pContext->Ebx); + Report(hwnd, hFile, "ECX = %08lx", pContext->Ecx); + Report(hwnd, hFile, "EDX = %08lx", pContext->Edx); + Report(hwnd, hFile, "EBP = %08lx", pContext->Ebp); + Report(hwnd, hFile, "DS:ESI = %04x:%08lx", pContext->SegDs, pContext->Esi); + Report(hwnd, hFile, "ES:EDI = %04x:%08lx", pContext->SegEs, pContext->Edi); + Report(hwnd, hFile, "SS:ESP = %04x:%08lx", pContext->SegSs, pContext->Esp); + Report(hwnd, hFile, "CS:EIP = %04x:%08lx", pContext->SegCs, pContext->Eip); + Report(hwnd, hFile, "FS = %04x", pContext->SegFs); + Report(hwnd, hFile, "GS = %04x", pContext->SegGs); + Report(hwnd, hFile, "EFLAGS = %08lx", pContext->EFlags); + Report(hwnd, hFile, ""); + + // extract out MMX registers + + tos = (pContext->FloatSave.StatusWord & 0x3800)>>11; + + for(i=0; i<8; i++) { + long *pReg = (long *)(pContext->FloatSave.RegisterArea + 10*((i-tos) & 7)); + + Report(hwnd, hFile, "MM%c = %08lx%08lx", i+'0', pReg[1], pReg[0]); + } + + // fill out bomb reason + + const struct ExceptionLookup *pel = exceptions; + + while(pel->code) { + if (pel->code == pRecord->ExceptionCode) + break; + + ++pel; + } + + // Unfortunately, EXCEPTION_ACCESS_VIOLATION doesn't seem to provide + // us with the read/write flag and virtual address as the docs say... + // *sigh* + + if (!pel->code) { + if (hwndReason) + SetWindowTitlef(hwndReason, "Crash reason: unknown exception 0x%08lx", pRecord->ExceptionCode); + + if (hFile) + Report(NULL, hFile, "Crash reason: unknown exception 0x%08lx", pRecord->ExceptionCode); + } else { + if (hwndReason) + SetWindowTitlef(hwndReason, "Crash reason: %s", pel->name); + + if (hFile) + Report(NULL, hFile, "Crash reason: %s", pel->name); + } + + // Dump thread stacks + + Report(NULL, hFile, ""); + + EnterCriticalSection(&g_csPerThreadState); + + try { + for(List2::fwit it = g_listPerThreadState.begin(); it; ++it) { + const VirtualDubThreadState *pState = it->pState; + + Report(NULL, hFile, "Thread %08lx (%s)", pState->dwThreadId, pState->pszThreadName?pState->pszThreadName:"unknown"); + + for(int i=0; icp[(pState->nNextCP+i) & (CHECKPOINT_COUNT-1)]; + + if (cp.file) + Report(NULL, hFile, "\t%s(%d)", cp.file, cp.line); + } + } + } catch(...) { + } + + LeaveCriticalSection(&g_csPerThreadState); + + Report(NULL, hFile, ""); +} + +static const char *GetNameFromHeap(const char *heap, int idx) { + while(idx--) + while(*heap++); + + return heap; +} + +////////////////////////////////////////////////////////////////////////////// + +static bool IsValidCall(char *buf, int len) { + // Permissible CALL sequences that we care about: + // + // E8 xx xx xx xx CALL near relative + // FF (group 2) CALL near absolute indirect + // + // Minimum sequence is 2 bytes (call eax). + // Maximum sequence is 7 bytes (call dword ptr [eax+disp32]). + + if (len >= 5 && buf[-5] == (char)0xE8) + return true; + + // FF 14 xx CALL [reg32+reg32*scale] + + if (len >= 3 && buf[-3] == (char)0xFF && buf[-2]==0x14) + return true; + + // FF 15 xx xx xx xx CALL disp32 + + if (len >= 6 && buf[-6] == (char)0xFF && buf[-5]==0x15) + return true; + + // FF 00-3F(!14/15) CALL [reg32] + + if (len >= 2 && buf[-2] == (char)0xFF && (unsigned char)buf[-1] < 0x40) + return true; + + // FF D0-D7 CALL reg32 + + if (len >= 2 && buf[-2] == (char)0xFF && (buf[-1]&0xF8) == 0xD0) + return true; + + // FF 50-57 xx CALL [reg32+reg32*scale+disp8] + + if (len >= 3 && buf[-3] == (char)0xFF && (buf[-2]&0xF8) == 0x50) + return true; + + // FF 90-97 xx xx xx xx xx CALL [reg32+reg32*scale+disp32] + + if (len >= 7 && buf[-7] == (char)0xFF && (buf[-6]&0xF8) == 0x90) + return true; + + return false; +} + +////////////////////////////////////////////////////////////////////////////// + +struct ModuleInfo { + const char *name; + unsigned long base, size; +}; + +// ARRGH. Where's psapi.h?!? + +struct Win32ModuleInfo { + DWORD base, size, entry; +}; + +typedef BOOL (__stdcall *PENUMPROCESSMODULES)(HANDLE, HMODULE *, DWORD, LPDWORD); +typedef DWORD (__stdcall *PGETMODULEBASENAME)(HANDLE, HMODULE, LPTSTR, DWORD); +typedef BOOL (__stdcall *PGETMODULEINFORMATION)(HANDLE, HMODULE, Win32ModuleInfo *, DWORD); + +typedef HANDLE (__stdcall *PCREATETOOLHELP32SNAPSHOT)(DWORD, DWORD); +typedef BOOL (WINAPI *PMODULE32FIRST)(HANDLE, LPMODULEENTRY32); +typedef BOOL (WINAPI *PMODULE32NEXT)(HANDLE, LPMODULEENTRY32); + +static ModuleInfo *CrashGetModules(void *&ptr) { + void *pMem = VirtualAlloc(NULL, 65536, MEM_COMMIT, PAGE_READWRITE); + + if (!pMem) { + ptr = NULL; + return NULL; + } + + // This sucks. If we're running under Windows 9x, we must use + // TOOLHELP.DLL to get the module list. Under Windows NT, we must + // use PSAPI.DLL. With Windows 2000, we can use both (but prefer + // PSAPI.DLL). + + HMODULE hmodPSAPI = LoadLibrary("psapi.dll"); + + if (hmodPSAPI) { + // Using PSAPI.DLL. Call EnumProcessModules(), then GetModuleFileNameEx() + // and GetModuleInformation(). + + PENUMPROCESSMODULES pEnumProcessModules = (PENUMPROCESSMODULES)GetProcAddress(hmodPSAPI, "EnumProcessModules"); + PGETMODULEBASENAME pGetModuleBaseName = (PGETMODULEBASENAME)GetProcAddress(hmodPSAPI, "GetModuleBaseNameA"); + PGETMODULEINFORMATION pGetModuleInformation = (PGETMODULEINFORMATION)GetProcAddress(hmodPSAPI, "GetModuleInformation"); + HMODULE *pModules, *pModules0 = (HMODULE *)((char *)pMem + 0xF000); + DWORD cbNeeded; + + if (pEnumProcessModules && pGetModuleBaseName && pGetModuleInformation + && pEnumProcessModules(GetCurrentProcess(), pModules0, 0x1000, &cbNeeded)) { + + ModuleInfo *pMod, *pMod0; + char *pszHeap = (char *)pMem, *pszHeapLimit; + + if (cbNeeded > 0x1000) cbNeeded = 0x1000; + + pModules = (HMODULE *)((char *)pMem + 0x10000 - cbNeeded); + memmove(pModules, pModules0, cbNeeded); + + pMod = pMod0 = (ModuleInfo *)((char *)pMem + 0x10000 - sizeof(ModuleInfo) * (cbNeeded / sizeof(HMODULE) + 1)); + pszHeapLimit = (char *)pMod; + + do { + HMODULE hCurMod = *pModules++; + Win32ModuleInfo mi; + + if (pGetModuleBaseName(GetCurrentProcess(), hCurMod, pszHeap, pszHeapLimit - pszHeap) + && pGetModuleInformation(GetCurrentProcess(), hCurMod, &mi, sizeof mi)) { + + char *period = NULL; + + pMod->name = pszHeap; + + while(*pszHeap++) + if (pszHeap[-1] == '.') + period = pszHeap-1; + + if (period) { + *period = 0; + pszHeap = period+1; + } + + pMod->base = mi.base; + pMod->size = mi.size; + ++pMod; + } + } while((cbNeeded -= sizeof(HMODULE *)) > 0); + + pMod->name = NULL; + + FreeLibrary(hmodPSAPI); + ptr = pMem; + return pMod0; + } + + FreeLibrary(hmodPSAPI); + } else { + // No PSAPI. Use the ToolHelp functions in KERNEL. + + HMODULE hmodKERNEL32 = LoadLibrary("kernel32.dll"); + + PCREATETOOLHELP32SNAPSHOT pCreateToolhelp32Snapshot = (PCREATETOOLHELP32SNAPSHOT)GetProcAddress(hmodKERNEL32, "CreateToolhelp32Snapshot"); + PMODULE32FIRST pModule32First = (PMODULE32FIRST)GetProcAddress(hmodKERNEL32, "Module32First"); + PMODULE32NEXT pModule32Next = (PMODULE32NEXT)GetProcAddress(hmodKERNEL32, "Module32Next"); + HANDLE hSnap; + + if (pCreateToolhelp32Snapshot && pModule32First && pModule32Next) { + if ((HANDLE)-1 != (hSnap = pCreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0))) { + ModuleInfo *pModInfo = (ModuleInfo *)((char *)pMem + 65536); + char *pszHeap = (char *)pMem; + MODULEENTRY32 me; + + --pModInfo; + pModInfo->name = NULL; + + me.dwSize = sizeof(MODULEENTRY32); + + if (pModule32First(hSnap, &me)) + do { + if (pszHeap+strlen(me.szModule) >= (char *)(pModInfo - 1)) + break; + + strcpy(pszHeap, me.szModule); + + --pModInfo; + pModInfo->name = pszHeap; + + char *period = NULL; + + while(*pszHeap++); + if (pszHeap[-1]=='.') + period = pszHeap-1; + + if (period) { + *period = 0; + pszHeap = period+1; + } + + pModInfo->base = (unsigned long)me.modBaseAddr; + pModInfo->size = me.modBaseSize; + + } while(pModule32Next(hSnap, &me)); + + CloseHandle(hSnap); + + FreeLibrary(hmodKERNEL32); + + ptr = pMem; + return pModInfo; + } + } + + FreeLibrary(hmodKERNEL32); + } + + VirtualFree(pMem, 0, MEM_RELEASE); + + ptr = NULL; + return NULL; +} + +/////////////////////////////////////////////////////////////////////////// +// +// info from Portable Executable/Common Object File Format (PE/COFF) spec + +typedef unsigned short ushort; +typedef unsigned long ulong; + +struct PEHeader { + ulong signature; + ushort machine; + ushort sections; + ulong timestamp; + ulong symbol_table; + ulong symbols; + ushort opthdr_size; + ushort characteristics; +}; + +struct PESectionHeader { + char name[8]; + ulong virtsize; + ulong virtaddr; + ulong rawsize; + ulong rawptr; + ulong relocptr; + ulong linenoptr; + ushort reloc_cnt; + ushort lineno_cnt; + ulong characteristics; +}; + +struct PEExportDirectory { + ulong flags; + ulong timestamp; + ushort major; + ushort minor; + ulong nameptr; + ulong ordbase; + ulong addrtbl_cnt; + ulong nametbl_cnt; + ulong addrtbl_ptr; + ulong nametbl_ptr; + ulong ordtbl_ptr; +}; + +struct PE32OptionalHeader { + ushort magic; // 0 + char major_linker_ver; // 2 + char minor_linker_ver; // 3 + ulong codesize; // 4 + ulong idatasize; // 8 + ulong udatasize; // 12 + ulong entrypoint; // 16 + ulong codebase; // 20 + ulong database; // 24 + ulong imagebase; // 28 + ulong section_align; // 32 + ulong file_align; // 36 + ushort majoros; // 40 + ushort minoros; // 42 + ushort majorimage; // 44 + ushort minorimage; // 46 + ushort majorsubsys; // 48 + ushort minorsubsys; // 50 + ulong reserved; // 52 + ulong imagesize; // 56 + ulong hdrsize; // 60 + ulong checksum; // 64 + ushort subsystem; // 68 + ushort characteristics; // 70 + ulong stackreserve; // 72 + ulong stackcommit; // 76 + ulong heapreserve; // 80 + ulong heapcommit; // 84 + ulong loaderflags; // 88 + ulong dictentries; // 92 + + // Not part of header, but it's convienent here + + ulong export_RVA; // 96 + ulong export_size; // 100 +}; + +struct PE32PlusOptionalHeader { + ushort magic; // 0 + char major_linker_ver; // 2 + char minor_linker_ver; // 3 + ulong codesize; // 4 + ulong idatasize; // 8 + ulong udatasize; // 12 + ulong entrypoint; // 16 + ulong codebase; // 20 + __int64 imagebase; // 24 + ulong section_align; // 32 + ulong file_align; // 36 + ushort majoros; // 40 + ushort minoros; // 42 + ushort majorimage; // 44 + ushort minorimage; // 46 + ushort majorsubsys; // 48 + ushort minorsubsys; // 50 + ulong reserved; // 52 + ulong imagesize; // 56 + ulong hdrsize; // 60 + ulong checksum; // 64 + ushort subsystem; // 68 + ushort characteristics; // 70 + __int64 stackreserve; // 72 + __int64 stackcommit; // 80 + __int64 heapreserve; // 88 + __int64 heapcommit; // 96 + ulong loaderflags; // 104 + ulong dictentries; // 108 + + // Not part of header, but it's convienent here + + ulong export_RVA; // 112 + ulong export_size; // 116 +}; + +static const char *CrashLookupExport(HMODULE hmod, unsigned long addr, unsigned long &fnbase) { + char *pBase = (char *)hmod; + + // The PEheader offset is at hmod+0x3c. Add the size of the optional header + // to step to the section headers. + + PEHeader *pHeader = (PEHeader *)(pBase + ((long *)hmod)[15]); + + if (pHeader->signature != 'EP') + return NULL; + +#if 0 + PESectionHeader *pSHdrs = (PESectionHeader *)((char *)pHeader + sizeof(PEHeader) + pHeader->opthdr_size); + + // Scan down the section headers and look for ".edata" + + int i; + + for(i=0; isections; i++) { + MessageBox(NULL, pSHdrs[i].name, "section", MB_OK); + if (!memcmp(pSHdrs[i].name, ".edata", 6)) + break; + } + + if (i >= pHeader->sections) + return NULL; +#endif + + // Verify the optional structure. + + PEExportDirectory *pExportDir; + + if (pHeader->opthdr_size < 104) + return NULL; + + switch(*(short *)((char *)pHeader + sizeof(PEHeader))) { + case 0x10b: // PE32 + { + PE32OptionalHeader *pOpt = (PE32OptionalHeader *)((char *)pHeader + sizeof(PEHeader)); + + if (pOpt->dictentries < 1) + return NULL; + + pExportDir = (PEExportDirectory *)(pBase + pOpt->export_RVA); + } + break; + case 0x20b: // PE32+ + { + PE32PlusOptionalHeader *pOpt = (PE32PlusOptionalHeader *)((char *)pHeader + sizeof(PEHeader)); + + if (pOpt->dictentries < 1) + return NULL; + + pExportDir = (PEExportDirectory *)(pBase + pOpt->export_RVA); + } + break; + + default: + return NULL; + } + + // Hmmm... no exports? + + if ((char *)pExportDir == pBase) + return NULL; + + // Find the location of the export information. + + ulong *pNameTbl = (ulong *)(pBase + pExportDir->nametbl_ptr); + ulong *pAddrTbl = (ulong *)(pBase + pExportDir->addrtbl_ptr); + ushort *pOrdTbl = (ushort *)(pBase + pExportDir->ordtbl_ptr); + + // Scan exports. + + const char *pszName = NULL; + ulong bestdelta = 0xFFFFFFFF; + unsigned i; + + addr -= (ulong)pBase; + + for(i=0; inametbl_cnt; i++) { + ulong fnaddr; + int idx; + + idx = pOrdTbl[i]; + fnaddr = pAddrTbl[idx]; + + if (addr >= fnaddr) { + ulong delta = addr - fnaddr; + + if (delta < bestdelta) { + bestdelta = delta; + fnbase = fnaddr; + + if (pNameTbl[i]) + pszName = pBase + pNameTbl[i]; + else { + static char buf[8]; + + wsprintf(buf, "ord%d", pOrdTbl[i]); + pszName = buf; + } + + } + } + } + + return pszName; +} + +/////////////////////////////////////////////////////////////////////////// + +static bool IsExecutableProtection(DWORD dwProtect) { + MEMORY_BASIC_INFORMATION meminfo; + + // Windows NT/2000 allows Execute permissions, but Win9x seems to + // rip it off. So we query the permissions on our own code block, + // and use it to determine if READONLY/READWRITE should be + // considered 'executable.' + + VirtualQuery(IsExecutableProtection, &meminfo, sizeof meminfo); + + switch((unsigned char)dwProtect) { + case PAGE_READONLY: // *sigh* Win9x... + case PAGE_READWRITE: // *sigh* + return meminfo.Protect==PAGE_READONLY || meminfo.Protect==PAGE_READWRITE; + + case PAGE_EXECUTE: + case PAGE_EXECUTE_READ: + case PAGE_EXECUTE_READWRITE: + case PAGE_EXECUTE_WRITECOPY: + return true; + } + return false; +} + +static const char *CrashGetModuleBaseName(HMODULE hmod, char *pszBaseName) { + char szPath1[MAX_PATH]; + char szPath2[MAX_PATH]; + + __try { + DWORD dw; + char *pszFile, *period = NULL; + + if (!GetModuleFileName(hmod, szPath1, sizeof szPath1)) + return NULL; + + dw = GetFullPathName(szPath1, sizeof szPath2, szPath2, &pszFile); + + if (!dw || dw>sizeof szPath2) + return NULL; + + strcpy(pszBaseName, pszFile); + + pszFile = pszBaseName; + + while(*pszFile++) + if (pszFile[-1]=='.') + period = pszFile-1; + + if (period) + *period = 0; + } __except(1) { + return NULL; + } + + return pszBaseName; +} + + +/////////////////////////////////////////////////////////////////////////// + +bool VDDebugInfoInitFromMemory(VDDebugInfoContext *pctx, const void *_src) { + const unsigned char *src = (const unsigned char *)_src; + + pctx->pRVAHeap = NULL; + + // Check type string + + if (!memcmp((char *)src + 6, "] VirtualDub disasm", 19)) + src += *(long *)(src + 64) + 72; + + if (src[0] != '[' || src[3] != '|') + return false; + + if (memcmp((char *)src + 6, "] VirtualDub symbolic debug information", 39)) + return false; + + // Check version number + + int write_version = (src[1]-'0')*10 + (src[2] - '0'); + int compat_version = (src[4]-'0')*10 + (src[5] - '0'); + + if (compat_version > 1) + return false; // resource is too new for us to load + + // Extract fields + + src += 64; + + pctx->nBuildNumber = *(int *)src; + pctx->pRVAHeap = (const unsigned char *)(src + 24); + pctx->nFirstRVA = *(const long *)(src + 20); + pctx->pClassNameHeap = (const char *)pctx->pRVAHeap - 4 + *(const long *)(src + 4); + pctx->pFuncNameHeap = pctx->pClassNameHeap + *(const long *)(src + 8); + pctx->pSegments = (unsigned long (*)[2])(pctx->pFuncNameHeap + *(const long *)(src + 12)); + pctx->nSegments = *(const long *)(src + 16); + + return true; +} + +void VDDebugInfoDeinit(VDDebugInfoContext *pctx) { + if (pctx->pRawBlock) { + VirtualFree(pctx->pRawBlock, 0, MEM_RELEASE); + pctx->pRawBlock = NULL; + } +} + +bool VDDebugInfoInitFromFile(VDDebugInfoContext *pctx, const char *pszFilename) { + pctx->pRawBlock = NULL; + pctx->pRVAHeap = NULL; + + HANDLE h = CreateFile(pszFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (INVALID_HANDLE_VALUE == h) { + return false; + } + + do { + DWORD dwFileSize = GetFileSize(h, NULL); + + if (dwFileSize == 0xFFFFFFFF) + break; + + pctx->pRawBlock = VirtualAlloc(NULL, dwFileSize, MEM_COMMIT, PAGE_READWRITE); + if (!pctx->pRawBlock) + break; + + DWORD dwActual; + if (!ReadFile(h, pctx->pRawBlock, dwFileSize, &dwActual, NULL) || dwActual != dwFileSize) + break; + + if (VDDebugInfoInitFromMemory(pctx, pctx->pRawBlock)) { + CloseHandle(h); + return true; + } + + VirtualFree(pctx->pRawBlock, 0, MEM_RELEASE); + + } while(false); + + VDDebugInfoDeinit(pctx); + CloseHandle(h); + return false; +} + +long VDDebugInfoLookupRVA(VDDebugInfoContext *pctx, unsigned rva, char *buf, int buflen) { + int i; + + for(i=0; inSegments; ++i) { + if (rva >= pctx->pSegments[i][0] && rva < pctx->pSegments[i][0] + pctx->pSegments[i][1]) + break; + } + + if (i >= pctx->nSegments) + return -1; + + const unsigned char *pr = pctx->pRVAHeap; + const unsigned char *pr_limit = (const unsigned char *)pctx->pClassNameHeap; + int idx = 0; + + // Linearly unpack RVA deltas and find lower_bound + + rva -= pctx->nFirstRVA; + + if ((signed)rva < 0) + return -1; + + while(pr < pr_limit) { + unsigned char c; + unsigned diff = 0; + + do { + c = *pr++; + + diff = (diff << 7) | (c & 0x7f); + } while(c & 0x80); + + rva -= diff; + + if ((signed)rva < 0) { + rva += diff; + break; + } + + ++idx; + } + + // Decompress name for RVA + + if (pr < pr_limit) { + const char *fn_name = GetNameFromHeap(pctx->pFuncNameHeap, idx); + const char *class_name = NULL; + const char *prefix = ""; + + if (*fn_name == 0) + return -1; + + if (*fn_name < 32) { + int class_idx; + + class_idx = ((unsigned)fn_name[0] - 1)*128 + ((unsigned)fn_name[1] - 1); + class_name = GetNameFromHeap(pctx->pClassNameHeap, class_idx); + + fn_name += 2; + + if (*fn_name == 1) { + fn_name = class_name; + } else if (*fn_name == 2) { + fn_name = class_name; + prefix = "~"; + } else if (*fn_name < 32) + fn_name = "(special)"; + } + + // ehh... where's my wsnprintf? _snprintf() might allocate memory or locks.... + return wsprintf(buf, "%s%s%s%s", class_name?class_name:"", class_name?"::":"", prefix, fn_name) >= 0 + ? rva + : -1; + } + + return -1; +} + +/////////////////////////////////////////////////////////////////////////// + +static bool ReportCrashCallStack(HWND hwnd, HANDLE hFile, const EXCEPTION_POINTERS *const pExc, const void *pDebugSrc) { + const CONTEXT *const pContext = (const CONTEXT *)pExc->ContextRecord; + HANDLE hprMe = GetCurrentProcess(); + char *lpAddr = (char *)pContext->Esp; + int limit = 100; + unsigned long data; + char buf[512]; + + if (!g_debugInfo.pRVAHeap) { + Report(hwnd, hFile, "Could not open debug resource file (VirtualDub.vdi)."); + return false; + } + + if (g_debugInfo.nBuildNumber != int(version_num)) { + Report(hwnd, hFile, "Incorrect VirtualDub.vdi file (build %d, expected %d) for this version of VirtualDub -- call stack unavailable.", g_debugInfo.nBuildNumber, int(version_num)); + return false; + } + + // Get some module names. + + void *pModuleMem; + ModuleInfo *pModules = CrashGetModules(pModuleMem); + + // Retrieve stack pointers. + // Not sure if NtCurrentTeb() is available on Win95.... + + NT_TIB *pTib; + + __asm { + mov eax, fs:[0]_NT_TIB.Self + mov pTib, eax + } + + char *pStackBase = (char *)pTib->StackBase; + + // Walk up the stack. Hopefully it wasn't fscked. + + data = pContext->Eip; + do { + bool fValid = true; + int len; + MEMORY_BASIC_INFORMATION meminfo; + + VirtualQuery((void *)data, &meminfo, sizeof meminfo); + + if (!IsExecutableProtection(meminfo.Protect) || meminfo.State!=MEM_COMMIT) { +// Report(hwnd, hFile, "Rejected: %08lx (%08lx)", data, meminfo.Protect); + fValid = false; + } + + if (data != pContext->Eip) { + len = 7; + + *(long *)(buf + 0) = *(long *)(buf + 4) = 0; + + while(len > 0 && !ReadProcessMemory(GetCurrentProcess(), (void *)(data-len), buf+7-len, len, NULL)) + --len; + + fValid &= IsValidCall(buf+7, len); + } + + if (fValid) { + if (VDDebugInfoLookupRVA(&g_debugInfo, data, buf, sizeof buf) >= 0) { + Report(hwnd, hFile, "%08lx: %s()", data, buf); + --limit; + } else { + ModuleInfo *pMods = pModules; + ModuleInfo mi; + char szName[MAX_PATH]; + + mi.name = NULL; + + if (pMods) { + while(pMods->name) { + if (data >= pMods->base && (data - pMods->base) < pMods->size) + break; + + ++pMods; + } + + mi = *pMods; + } else { + + // Well, something failed, or we didn't have either PSAPI.DLL or ToolHelp + // to play with. So we'll use a nastier method instead. + + mi.base = (unsigned long)meminfo.AllocationBase; + mi.name = CrashGetModuleBaseName((HMODULE)mi.base, szName); + } + + if (mi.name) { + unsigned long fnbase; + const char *pExportName = CrashLookupExport((HMODULE)mi.base, data, fnbase); + + if (pExportName) + Report(hwnd, hFile, "%08lx: %s!%s [%08lx+%lx+%lx]", data, mi.name, pExportName, mi.base, fnbase, (data-mi.base-fnbase)); + else + Report(hwnd, hFile, "%08lx: %s!%08lx", data, mi.name, data - mi.base); + } else + Report(hwnd, hFile, "%08lx: %08lx", data, data); + + --limit; + } + } + + if (lpAddr >= pStackBase) + break; + + lpAddr += 4; + } while(limit > 0 && ReadProcessMemory(hprMe, lpAddr-4, &data, 4, NULL)); + + // All done, close up shop and exit. + + if (pModuleMem) + VirtualFree(pModuleMem, 0, MEM_RELEASE); + + return true; +} + +void DoSave(const EXCEPTION_POINTERS *pExc) { + HANDLE hFile; + char szModName2[MAX_PATH]; + char tbuf[2048]; + long idx; + + SpliceProgramPath(szModName2, sizeof szModName2, "crashinfo.txt"); + + hFile = CreateFile(szModName2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (INVALID_HANDLE_VALUE == hFile) + return; + + Report(NULL, hFile, + "VirtualDub crash report -- build %d\r\n" + "--------------------------------------\r\n" + "\r\n" + "Disassembly:", version_num); + + idx = 0; + + while(idx = g_pcdw->getInstruction(tbuf, idx)) { + Report(NULL, hFile, "%s", tbuf); + } + + Report(NULL, hFile, ""); + + // Detect operating system. + + OSVERSIONINFO ovi; + ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + if (GetVersionEx(&ovi)) { + Report(NULL, hFile, "Windows %d.%d (Win%s build %d) [%s]" + ,ovi.dwMajorVersion + ,ovi.dwMinorVersion + ,ovi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS + ? (ovi.dwMinorVersion>0 ? "98" : "95") + : ovi.dwPlatformId == VER_PLATFORM_WIN32_NT + ? (ovi.dwMajorVersion >= 5 ? "2000" : "NT") + : "?" + ,ovi.dwBuildNumber & 0xffff + ,ovi.szCSDVersion); + } + + Report(NULL, hFile, ""); + + ReportCrashData(NULL, NULL, hFile, pExc); + + Report(NULL, hFile, ""); + + ReportCrashCallStack(NULL, hFile, pExc, g_pcdw->vdc.pExtraData); + + Report(NULL, hFile, ""); + + ReportCrashLog(NULL, hFile); + + Report(NULL, hFile, "\r\n-- End of report"); + + CloseHandle(hFile); +} + +BOOL APIENTRY CrashDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + static const EXCEPTION_POINTERS *s_pExc; + static bool s_bHaveCallstack; + + switch(msg) { + + case WM_INITDIALOG: + { + HWND hwndList1 = GetDlgItem(hDlg, IDC_ASMBOX); + HWND hwndList2 = GetDlgItem(hDlg, IDC_REGDUMP); + HWND hwndList3 = GetDlgItem(hDlg, IDC_CALL_STACK); + HWND hwndReason = GetDlgItem(hDlg, IDC_STATIC_BOMBREASON); + const EXCEPTION_POINTERS *const pExc = (const EXCEPTION_POINTERS *)lParam; + const EXCEPTION_RECORD *const pRecord = (const EXCEPTION_RECORD *)pExc->ExceptionRecord; + const CONTEXT *const pContext = (const CONTEXT *)pExc->ContextRecord; + + s_pExc = pExc; + + g_pcdw->DoInitListbox(hwndList1); + + SendMessage(hwndList2, WM_SETFONT, SendMessage(hwndList1, WM_GETFONT, 0, 0), MAKELPARAM(TRUE, 0)); + SendMessage(hwndList3, WM_SETFONT, SendMessage(hwndList1, WM_GETFONT, 0, 0), MAKELPARAM(TRUE, 0)); + + ReportCrashData(hwndList2, hwndReason, NULL, pExc); + s_bHaveCallstack = ReportCrashCallStack(hwndList3, NULL, pExc, g_pcdw->vdc.pExtraData); + + } + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDCANCEL: case IDOK: + EndDialog(hDlg, FALSE); + return TRUE; + case IDC_CRASH_SAVE: + if (!s_bHaveCallstack) + if (IDOK != MessageBox(hDlg, + "VirtualDub cannot load its crash resource file, and thus the crash dump will be " + "missing the most important part, the call stack. Crash dumps are much less useful " + "without the call stack.", + "VirtualDub warning", MB_OK|MB_ICONEXCLAMATION)) + return TRUE; + + DoSave(s_pExc); + return TRUE; + case IDC_BUTTON_RESTART: + { + // Launch StepMania + PROCESS_INFORMATION pi; + STARTUPINFO si; + ZeroMemory( &si, sizeof(si) ); + + CreateProcess( + NULL, // pointer to name of executable module + "stepmania.exe", // pointer to command line string + NULL, // process security attributes + NULL, // thread security attributes + false, // handle inheritance flag + 0, // creation flags + NULL, // pointer to new environment block + NULL, // pointer to current directory name + &si, // pointer to STARTUPINFO + &pi // pointer to PROCESS_INFORMATION + ); + } + EndDialog( hDlg, FALSE ); + break; + case IDC_BUTTON_REPORT: + GotoURL( "http://sourceforge.net/tracker/?func=add&group_id=37892&atid=421366" ); + break; + } + break; + + case WM_MEASUREITEM: + return g_pcdw->DoMeasureItem(lParam); + + case WM_DRAWITEM: + return g_pcdw->DoDrawItem(lParam); + } + + return FALSE; +} + +void crash() { + __try { + __asm xor ebx,ebx + __asm mov eax,dword ptr [ebx] +// __asm mov dword ptr [ebx],eax +// __asm lock add dword ptr cs:[00000000h], 12345678h + } __except(CrashHandler((EXCEPTION_POINTERS*)_exception_info())) { + } +} + +/* RageLog feeds us logs; keep track of recent logs in a circular + * buffer. This lets us include recent logs in the crash report. + * We don't want to include RageLog, since we don't want to pull + * in MFC. */ +static const int BACKLOG_LINES = 10; +static char backlog[BACKLOG_LINES][1024]; +static int backlog_start=0, backlog_cnt=0; + +void CrashLog(const char *str) +{ + strncpy(backlog[backlog_start], str, sizeof(backlog[backlog_start])); + backlog[backlog_start] [ sizeof(backlog[backlog_start])-1 ] = 0; + + backlog_start++; + backlog_start %= BACKLOG_LINES; + if(backlog_start > backlog_cnt) + backlog_cnt=backlog_start; +} + +static void ReportCrashLog(HWND hwnd, HANDLE hFile) +{ + Report(NULL, hFile, "Partial log:"); + + int i; + for(i = backlog_start; i < BACKLOG_LINES && i < backlog_cnt; ++i) + Report(hwnd, hFile, "%s", backlog[i]); + for(i = 0; i < backlog_start && i < backlog_cnt; ++i) + Report(hwnd, hFile, "%s", backlog[i]); +} + diff --git a/stepmania/src/Crash.h b/stepmania/src/Crash.h new file mode 100644 index 0000000000..4eadc35a9a --- /dev/null +++ b/stepmania/src/Crash.h @@ -0,0 +1,58 @@ +// from VirtualDub +// Copyright (C) 1998-2001 Avery Lee +// +// This program 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. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifndef f_CRASH_H +#define f_CRASH_H + +#ifdef f_CRASH_CPP +#define EXTERN +#else +#define EXTERN extern +#endif + +struct VirtualDubCheckpoint { + const char *file; + int line; + + inline void set(const char *f, int l) { file=f; line=l; } +}; + +#define CHECKPOINT_COUNT (16) + +struct VirtualDubThreadState { + const char *pszThreadName; + unsigned long dwThreadId; + void * hThread; + + VirtualDubCheckpoint cp[CHECKPOINT_COUNT]; + int nNextCP; +}; + +EXTERN __declspec(thread) VirtualDubThreadState g_PerThreadState; + +#define VDCHECKPOINT (g_PerThreadState.cp[g_PerThreadState.nNextCP++&(CHECKPOINT_COUNT-1)].set(__FILE__, __LINE__)) + +void VirtualDubInitializeThread(const char *pszName); +void VirtualDubDeinitializeThread(); +extern long __stdcall CrashHandler(struct _EXCEPTION_POINTERS *ExceptionInfo); + +void CrashLog(const char *str); + +/* Exactly as advertised. */ +void crash(); + +#endif diff --git a/stepmania/src/CrashList.h b/stepmania/src/CrashList.h new file mode 100644 index 0000000000..91a0a4c21a --- /dev/null +++ b/stepmania/src/CrashList.h @@ -0,0 +1,174 @@ +// from VirtualDub +// Copyright (C) 1998-2001 Avery Lee, All Rights Reserved. +// +// This program 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. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +// Don't use this. It's only here for the crash code. +#ifndef f_DISASM_LIST_H +#define f_DISASM_LIST_H + +class ListNode { +public: + ListNode *next, *prev; + + void Remove() { + next->prev = prev; + prev->next = next; +#ifdef _DEBUG + prev = next = 0; +#endif + } + + void InsertBefore(ListNode *node) { + next = node->next; + prev = node; + if (node->next) node->next->prev = this; + node->next = this; + } + + ListNode *NextFromHead() const { + return prev; + } +}; + +class List { +private: +public: + ListNode head, tail; + + // <--- next prev ---> + // + // head <-> node <-> node <-> tail + + List() { + head.next = tail.prev = 0; + head.prev = &tail; + tail.next = &head; + } + + void AddTail(ListNode *node) { + node->InsertBefore(&tail); + } + + bool IsEmpty() const throw() { + return !head.prev->prev; + } + + ListNode *AtHead() const throw() { + return head.prev; + } + + ListNode *AtTail() const throw() { + return tail.next; + } +}; + +// Templated classes... templated classes good. + +template class List2; + +template +class ListNode2 : public ListNode { +friend List2; +public: + void InsertBefore(ListNode2 *node) { ListNode::InsertBefore(node); } + + void Remove() { ListNode::Remove(); } + T *NextFromHead() const { return (T *)ListNode::NextFromHead(); } + T *NextFromTail() const { return (T *)ListNode::NextFromTail(); } +}; + +template +class List2 : public List { +public: + List2() {} + + void AddHead(ListNode2 *node) { List::AddHead(node); } + void AddTail(ListNode2 *node) { List::AddTail(node); } + T *AtHead() const { return (T *)List::AtHead(); } + T *AtTail() const { return (T *)List::AtTail(); } + + // I must admit to being pampered by STL (end is different though!!) + + T *begin() const { return AtHead(); } + T *end() const { return AtTail(); } + + class iterator { + protected: + ListNode2 *node; + ListNode2 *next; + + public: + iterator() {} + iterator(const iterator& src) throw() : node(src.node), next(src.next) {} + + bool operator!() const throw() { return 0 == next; } + T *operator->() const throw() { return (T *)node; } + operator bool() const throw() { return 0 != next; } + T& operator *() const throw() { return *(T *)node; } + }; + + // fwit: forward iterator (SAFE if node disappears) + // rvit: reverse iterator (SAFE if node disappears) + + class fwit : public iterator { + public: + fwit() throw() {} + fwit(const fwit& src) throw() : iterator(src) {} + fwit(ListNode2 *start) throw() { + node = start; + next = start->NextFromHead(); + } + + const fwit& operator=(ListNode2 *start) throw() { + node = start; + next = start->NextFromHead(); + + return *this; + } + + fwit& operator++() throw() { + node = next; + next = node->NextFromHead(); + + return *this; + } + + const fwit& operator+=(int v) throw() { + while(next && v--) { + node = next; + next = node->NextFromHead(); + } + + return *this; + } + + fwit operator+(int v) const throw() { + fwit t(*this); + + t += v; + + return t; + } + + // This one's for my sanity. + + void operator++(int) throw() { + ++*this; + } + }; +}; + +#endif diff --git a/stepmania/src/Disasm.cpp b/stepmania/src/Disasm.cpp new file mode 100644 index 0000000000..e8e6c137ce --- /dev/null +++ b/stepmania/src/Disasm.cpp @@ -0,0 +1,810 @@ +// from VirtualDub +// Copyright (C) 1998-2001 Avery Lee +// +// This program 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. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include +#include +#include + +#include + +#include "resource.h" +#include "disasm.h" + +extern HINSTANCE g_hInstance; + +#define MAX_INSTRUCTIONS (1024) + +// WARNING: This is called from crash-time conditions! No malloc() or new!!! + +#define malloc not_allowed_here +#define new not_allowed_here + +// Also, we keep as much of our initialized data as possible as const. That way, +// it is in a write-locked code segment, which can't be overwritten. + +static char *VDDisasmMatchRule(VDDisassemblyContext *pContext, const unsigned char *source, const unsigned char *pattern, int pattern_len, int bytes, int *sp, char *hp, const unsigned char *&source_end); + +char *strtack(char *s, const char *t) { + while(*s = *t) + ++s, ++t; + + return s; +} + +// 1-15 are static lookups +static const char kTarget_r32 = 1; +static const char kTarget_r16 = 2; +static const char kTarget_r8 = 3; +static const char kTarget_rm = 4; +static const char kTarget_rx = 5; +static const char kTarget_rc = 6; +static const char kTarget_rd = 7; +static const char kTarget_rs = 8; +static const char kTarget_rf = 9; + +// 16-31 are dynamic translations +static const char kTarget_r1632 = 16; +static const char kTarget_rmx = 17; +static const char kTarget_x = 18; +static const char kTarget_hx = 19; +static const char kTarget_lx = 20; +static const char kTarget_s = 21; +static const char kTarget_o = 22; +static const char kTarget_ho = 23; +static const char kTarget_lo = 24; +static const char kTarget_a = 25; +static const char kTarget_ha = 26; +static const char kTarget_la = 27; + +static const char kTarget_ap = (char)224; +static const char kTarget_p_cs = (char)225; +static const char kTarget_p_ss = (char)226; +static const char kTarget_p_ds = (char)227; +static const char kTarget_p_es = (char)228; +static const char kTarget_p_fs = (char)229; +static const char kTarget_p_gs = (char)230; +static const char kTarget_p_66 = (char)231; +static const char kTarget_p_67 = (char)232; +static const char kTarget_p_F2 = (char)233; +static const char kTarget_p_F3 = (char)234; + +static void *VDDisasmDecompress(void *_dst, const unsigned char *src, int src_len) { + const unsigned char *src_limit = src + src_len; + unsigned char *dst = (unsigned char *)_dst; + + // read ruleset count + int rulesets = *src++; + unsigned char **prstab = (unsigned char **)dst; + + dst += sizeof(unsigned char *) * (rulesets + 1); + + // decompress rulesets sequentially + for(int rs=0; rs> 6; + + int prematch = (packctl & 7) * 2; + int postmatch = ((packctl>>3) & 7) * 2; + int literal = (*src++ - 1) * 2; + + *dst++ = literal + prematch + postmatch; + + const unsigned char *pattern_start = dst; + + for(cnt=0; cnt0; --cnt) { + pattern_cache[cnt][0] = pattern_cache[cnt-1][0]; + pattern_cache[cnt][1] = pattern_cache[cnt-1][1]; + } + + pattern_cache[0][0] = pattern_start; + pattern_cache[0][1] = dst; + + // read pack control byte and copy prematch-literal-postmatch for result + + packctl = *src++; + packsrc = packctl >> 6; + + prematch = (packctl & 7); + postmatch = ((packctl>>3) & 7); + literal = (*src++ - 1); + + *dst++ = prematch + postmatch + literal; + + const unsigned char *result_start = dst; + + for(cnt=0; cnt0; --cnt) { + result_cache[cnt][0] = result_cache[cnt-1][0]; + result_cache[cnt][1] = result_cache[cnt-1][1]; + } + + result_cache[0][0] = result_start; + result_cache[0][1] = dst; + } + + src += 2; + + *dst++ = 0; + *dst++ = 0; + } + + prstab[0] = prstab[rulesets]; + + return dst; +} + +static long VDDisasmPack32(const int *src) { + return src[0] + (src[1]<<8) + (src[2]<<16) + (src[3]<<24); +} + +static void VDDisasmExpandRule(VDDisassemblyContext *pContext, char *s, const unsigned char *result, const int *sp_base, const unsigned char *source) { + static const char *const reg32[8]={"eax","ecx","edx","ebx","esp","ebp","esi","edi"}; + static const char *const reg16[8]={"ax","cx","dx","bx","sp","bp","si","di"}; + static const char *const reg8[8]={"al","cl","dl","bl","ah","ch","dh","bh"}; + static const char *const regmmx[8]={"mm0","mm1","mm2","mm3","mm4","mm5","mm6","mm7"}; + static const char *const regxmm[8]={"xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7"}; + static const char *const regcrn[8]={"cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7"}; + static const char *const regdrn[8]={"dr0","dr1","dr2","dr3","dr4","dr5","dr6","dr7"}; + static const char *const regseg[8]={"es","cs","ss","ds","fs","gs","?6s","?7s"}; + static const char *const regf[8]={"st(0)","st(1)","st(2)","st(3)","st(4)","st(5)","st(6)","st(7)"}; + + static const char *const *const sStaticLabels[]={ + reg32, + reg16, + reg8, + regmmx, + regxmm, + regcrn, + regdrn, + regseg, + regf + }; + + const unsigned char *result_limit = result + result[0]+1; + + ++result; + + while(result < result_limit) { + char c = *result++; + + if ((unsigned char)(c&0x7f) < 32) { + if (c & 0x80) { + c &= 0x7f; + *s++ = ' '; + } + + static const unsigned char static_bitfields[8]={ + 0x80, 0x30, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + unsigned char control_byte = (unsigned char)*result++; + unsigned char bitfield = static_bitfields[control_byte >> 5]; + + if (!bitfield) + bitfield = (unsigned char)*result++; + + int bf_start = bitfield & 15; + int bf_siz = bitfield>>4; + const char *arg_s = (const char *)sp_base[c-1]; + int arg = (sp_base[c-1] >> bf_start) & ((1<bSizeOverride); + break; + case kTarget_rmx: + s = strtack(s, (pContext->bSizeOverride ? regxmm : regmmx)[arg]); + break; + case kTarget_lx: + symoffset = VDDisasmPack32(sp_base + c - 1); + s += sprintf(s, "%08lx", symoffset); + break; + case kTarget_hx: + s += sprintf(s, "%02x%02x" + , (unsigned char)sp_base[c] + , (unsigned char)sp_base[c-1] + ); + break; + case kTarget_x: + s += sprintf(s, "%02x", arg); + break; + case kTarget_lo: + symoffset = VDDisasmPack32(sp_base + c - 1); + s += sprintf(s, "%c%02lx", symoffset<0 ? '-' : '+', abs(symoffset)); + break; + case kTarget_ho: + { + short x = ((unsigned char)sp_base[c ] << 8) + + (unsigned char)sp_base[c-1]; + + s += sprintf(s, "%c%02lx", x<0 ? '-' : '+', abs(x)); + } + break; + case kTarget_o: + s += sprintf(s, "%c%02x", arg&0x80?'-':'+', abs((signed char)arg)); + break; + case kTarget_la: + symoffset = (long)source + VDDisasmPack32(sp_base + c - 1) + pContext->physToVirtOffset; + s += sprintf(s, "%08lx", symoffset); + break; + case kTarget_ha: + symoffset = (long)source + (signed short)(sp_base[c-1] + (sp_base[c]<<8)) + pContext->physToVirtOffset; + s += sprintf(s, "%08lx", symoffset); + break; + case kTarget_a: + symoffset = (long)source + (signed char)arg + pContext->physToVirtOffset; + s += sprintf(s, "%08lx", symoffset); + break; + case kTarget_s: + s = strtack(s, arg_s); + break; + } + + if (symoffset && pContext->pSymLookup) { + char buf[1024]; + + symoffset = pContext->pSymLookup(pContext, (unsigned long)symoffset, buf, sizeof buf - 16); + + if (symoffset >= 0) { + int l = strlen(buf); + int l2 = s - s_base; + + if (symoffset) + l += sprintf(buf+l, "+%02x", symoffset); + + memmove(s_base+l+2, s_base, l2); + memcpy(s_base, buf, l); + s_base[l] = ' '; + s_base[l+1] = '('; + s = s_base + l+l2+2; + *s++ = ')'; + } + } + } + } else if ((unsigned char)c >= 0xe0) { + switch(c) { + case kTarget_ap: + if (pContext->pszSegmentOverride) { + s = strtack(s, pContext->pszSegmentOverride); + *s++ = ':'; + } + break; + case kTarget_p_cs: pContext->pszSegmentOverride = regseg[1]; break; + case kTarget_p_ss: pContext->pszSegmentOverride = regseg[2]; break; + case kTarget_p_ds: pContext->pszSegmentOverride = regseg[3]; break; + case kTarget_p_es: pContext->pszSegmentOverride = regseg[0]; break; + case kTarget_p_fs: pContext->pszSegmentOverride = regseg[4]; break; + case kTarget_p_gs: pContext->pszSegmentOverride = regseg[5]; break; + case kTarget_p_66: pContext->bSizeOverride = true; break; + case kTarget_p_67: pContext->bAddressOverride = true; break; + case kTarget_p_F2: pContext->bRepnePrefix = true; break; + case kTarget_p_F3: pContext->bRepePrefix = true; break; + } + } else + *s++ = c; + } + + *s = 0; +} + +static char *VDDisasmApplyRuleset(VDDisassemblyContext *pContext, const unsigned char *rs, int *sp, char *hp, const unsigned char *source, int bytes, const unsigned char *&source_end) { + char *hpr; + + while(rs[0] || rs[1]) { + const unsigned char *src_end; + const unsigned char *result = rs + rs[0] + 1; + const unsigned char *match_next = result + result[0] + 1; + + hpr = VDDisasmMatchRule(pContext, source, rs+1, rs[0]>>1, bytes, sp, hp, src_end); + + if (hpr) { + VDDisasmExpandRule(pContext, hpr, result, sp, src_end); + + source_end = src_end; + return hpr; + } + + rs = match_next; + } + + return NULL; +} + +static char *VDDisasmMatchRule(VDDisassemblyContext *pContext, const unsigned char *source, const unsigned char *pattern, int pattern_len, int bytes, int *sp, char *hp, const unsigned char *&source_end) { + while(pattern_len) { + if (!pattern[1] && pattern[0]) { + if (pattern[0] & 0x80) { + int count = pattern[0] & 0x3f; + + if (pattern[0] & 0x40) { + --source; + ++bytes; + } + + const unsigned char *src_end; + + hp = VDDisasmApplyRuleset(pContext, pContext->pRuleSystem[count+1], sp, hp, source, bytes, src_end); + + if (!hp) + return NULL; + + *sp++ = *source; + *sp++ = (int)hp; + + while(*hp++); + + bytes -= (src_end - source)-1; + source = src_end; + } else if (pattern[0] < 16) { + if (pattern[0] > bytes) + return NULL; + + for(int i=0; ibSizeOverride) return NULL; break; + case 17: if (!pContext->bAddressOverride) return NULL; break; + case 18: if (!pContext->bRepnePrefix) return NULL; break; + case 19: if (!pContext->bRepePrefix) return NULL; break; + case 20: if (pContext->pszSegmentOverride) return NULL; break; + } + } + } else { + if (!bytes) + return NULL; + + unsigned char b = *source++; + + if ((b & pattern[1]) != pattern[0]) + return NULL; + + *sp++ = b; + } + pattern += 2; + --bytes; + --pattern_len; + } + + if (!pattern_len) { + source_end = source; + return hp; + } + + return NULL; +} + +char *VDDisassemble(VDDisassemblyContext *pvdc, const unsigned char *source, int bytes, int& count) { + const unsigned char *src2 = source; + const unsigned char *src_end; + char *s; + + pvdc->bAddressOverride = false; + pvdc->bSizeOverride = false; + pvdc->bRepePrefix = false; + pvdc->bRepnePrefix = false; + pvdc->pszSegmentOverride = NULL; + + do { + s = VDDisasmApplyRuleset(pvdc, pvdc->pRuleSystem[0], pvdc->stack, pvdc->heap, src2, bytes, src_end); + + bytes -= (src_end - src2); + src2 = src_end; + } while(!*s && bytes); + + if (!*s) + return NULL; + + count = src2 - source; + + char *dst = s, *dst_base; + + while(*dst++) + ; + + dst_base = dst; + + char *t = s; + while(*t && *t != ' ') + ++t; + + if (*t) + *t++ = 0; + + sprintf(dst, "%-6s %s", s, t); + + return dst_base; +} + +bool VDDisasmInit(VDDisassemblyContext *pvdc, const char *pszFilename) { + HANDLE h; + + pvdc->pRawBlock = NULL; + pvdc->pExtraData = NULL; + + h = CreateFile(pszFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + return false; + + DWORD dwSize = GetFileSize(h, NULL), dwActual; + + pvdc->pRawBlock = VirtualAlloc(NULL, dwSize + 65536, MEM_COMMIT, PAGE_READWRITE); + + if (!pvdc->pRawBlock || !ReadFile(h, pvdc->pRawBlock, dwSize, &dwActual, NULL) || dwActual != dwSize) { + CloseHandle(h); + return false; + } + + CloseHandle(h); + + const char *src = (const char *)pvdc->pRawBlock; + + if (src[0] != '[' || src[3] != '|') + return false; + + if (memcmp((char *)src + 6, "] VirtualDub disasm module", 26)) + return false; + + // Check version number + + int write_version = (src[1]-'0')*10 + (src[2] - '0'); + int compat_version = (src[4]-'0')*10 + (src[5] - '0'); + + if (compat_version > 1) + return false; // resource is too new for us to load + + + long packSize = *(long *)((char *)pvdc->pRawBlock + 64); + long depackSize = *(long *)((char *)pvdc->pRawBlock + 68); + + pvdc->pRuleSystem = (const unsigned char **)((char *)pvdc->pRawBlock + dwSize); + pvdc->stack = (int *)((char *)pvdc->pRuleSystem + depackSize); + pvdc->heap = (char *)(pvdc->stack + 32); + + pvdc->cbExtraData = dwSize - (packSize+72); + pvdc->pExtraData = NULL; + + if (pvdc->cbExtraData) + pvdc->pExtraData = (char *)pvdc->pRawBlock + 72 + packSize; + + VDDisasmDecompress(pvdc->pRuleSystem, (unsigned char *)pvdc->pRawBlock + 72, packSize); + + return true; +} + +void VDDisasmDeinit(VDDisassemblyContext *pvdc) { + if (pvdc->pRawBlock) { + VirtualFree(pvdc->pRawBlock, 0, MEM_RELEASE); + pvdc->pRawBlock = NULL; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +static void SpliceProgramPath(char *buf, int bufsiz, const char *fn) { + char tbuf[MAX_PATH]; + char *pszFile; + + GetModuleFileName(NULL, tbuf, sizeof tbuf); + GetFullPathName(tbuf, bufsiz, buf, &pszFile); + strcpy(pszFile, fn); +} + +CodeDisassemblyWindow::CodeDisassemblyWindow(void *_code, long _length, void *_rbaseptr, void *_abaseptr) +: code(_code) +, rbase(_rbaseptr) +, abase(_abaseptr) +, length(_length) +, pFault(0) +{ +// lbents = new lbent[MAX_INSTRUCTIONS]; + + char buf[MAX_PATH]; + + vdc.pRuleSystem = NULL; + vdc.pSymLookup = NULL; + vdc.physToVirtOffset = -(long)_rbaseptr; + + SpliceProgramPath(buf, sizeof buf, "ia32.vdi"); + if (!VDDisasmInit(&vdc, buf)) { + SpliceProgramPath(buf, sizeof buf, "StepMania.vdi"); + if (!VDDisasmInit(&vdc, buf)) { + SpliceProgramPath(buf, sizeof buf, "StepMani.vdi"); + VDDisasmInit(&vdc, buf); + } + } + + lbents = (lbent *)VirtualAlloc(NULL, sizeof(lbent)*MAX_INSTRUCTIONS, MEM_COMMIT, PAGE_READWRITE); + + hFontMono = CreateFont( + 10, // nHeight + 0, // nWidth + 0, // nEscapement + 0, // nOrientation + FW_DONTCARE, // fnWeight + FALSE, // fdwItalic + FALSE, // fdwUnderline + FALSE, // fdwStrikeOut + ANSI_CHARSET, // fdwCharSet + OUT_DEFAULT_PRECIS, // fdwOutputPrecision + CLIP_DEFAULT_PRECIS, // fdwClipPrecision + DEFAULT_QUALITY, // fdwQuality + DEFAULT_PITCH | FF_DONTCARE, // fdwPitchAndFamily + "Lucida Console" + ); + + if (!hFontMono) hFontMono = (HFONT)GetStockObject(ANSI_FIXED_FONT); + +} + +CodeDisassemblyWindow::~CodeDisassemblyWindow() { + + VDDisasmDeinit(&vdc); + + if (hFontMono) DeleteObject(hFontMono); + + if (lbents) + VirtualFree(lbents, 0, MEM_RELEASE); +} + +///////////////////////////////////////////////////////////////////////// + + +void CodeDisassemblyWindow::parse() { + if (!vdc.pRuleSystem) + return; + + unsigned char *ip = (unsigned char *)code; + unsigned char *ipend = ip + length; + lbent *ipd = lbents; + int cnt=0; + + if (!ipd) { + num_ents = 0; + return; + } + + while(ip < ipend && cnt++ip = ip; + ipd->len = count; + + ip += count; + + ++ipd; + } + + num_ents = ipd-lbents; +} + +BOOL CodeDisassemblyWindow::post(HWND hWnd) { + if (!lbents) return FALSE; + + DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_DISASM), hWnd, CodeDisassemblyWindow::DlgProc, (LPARAM)this); + + return TRUE; +} + +long CodeDisassemblyWindow::getInstruction(char *buf, long val) { + lbent *ipd; + unsigned char *ip; + + if ((val >> 16) >= num_ents) + return 0; + + ipd = &lbents[val >> 16]; + ip = ipd->ip; + + int count; + + unsigned virtAddr = (unsigned)ip - (unsigned)code + (unsigned)abase; + int subIndex = val & 0xffff; + int left = ipd->len - subIndex * 7; + + buf += sprintf(buf, subIndex ? " " : "%08lx: ", virtAddr); + + for(int i=0; i<7; ++i) { + if (--left >= 0) { + buf += wsprintf(buf, "%02x", (unsigned char)ip[subIndex*7+i]); + } else { + *buf++ = ' '; + *buf++ = ' '; + } + } + + *buf++ = ' '; + *buf++ = ' '; + *buf = 0; + + if (!subIndex) { + strcpy(buf, VDDisassemble(&vdc, ip, ipd->len, count)); + + if (virtAddr == (unsigned)pFault) + strcat(buf, " <-- FAULT"); + } + + ++subIndex; + + if (subIndex >= (ipd->len+6) / 7) + return (val&0xffff0000) + 0x10000; + else + return val+1; +} + +void CodeDisassemblyWindow::DoInitListbox(HWND hwndList) { +// SendMessage(hwndList, LB_SETCOUNT, num_ents, 0); + + for(int i=0; iCtlType != ODT_LISTBOX) return FALSE; + + lbent *pent = &lbents[pinfo->itemID]; + + pinfo->itemHeight = 11 * ((pent->len + 6)/7); + return TRUE; +} + +BOOL CodeDisassemblyWindow::DoDrawItem(LPARAM lParam) { + LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam; + lbent *ipd; + + if (lpdis->CtlType != ODT_LISTBOX) return FALSE; + if (!(lpdis->itemAction & ODA_DRAWENTIRE)) return FALSE; + if (lpdis->itemID < 0) return FALSE; + +// ipd = (lbent *)lpdis->itemData; + ipd = &lbents[lpdis->itemID]; + + if (ipd->ip) { + SetBkMode(lpdis->hDC, TRANSPARENT); + SetTextColor(lpdis->hDC, RGB(0x00,0x00,0x00)); + + if (ipd->ip - (unsigned char *)code + (unsigned char *)abase == pFault) { + HBRUSH hbr = CreateSolidBrush(RGB(0xff,0xa0,0x60)); + if (hbr) { + FillRect(lpdis->hDC, &lpdis->rcItem, hbr); + DeleteObject(hbr); + } + } + + int count; + char *dst = buf; + const unsigned char *src = (const unsigned char *)ipd->ip; + int i, left = ipd->len; + + dst += sprintf(dst, "%08lx: ", (long)ipd->ip + vdc.physToVirtOffset); + + for(i=0; i<7; ++i) + if (--left>=0) { + dst += sprintf(dst, "%02x", *src++); + } else { + dst[0] = dst[1] = ' '; + dst += 2; + } + + *dst++ = ' '; + *dst++ = ' '; + + strcpy(dst, VDDisassemble(&vdc, ipd->ip, ipd->len, count)); + + int x = lpdis->rcItem.left; + int y = lpdis->rcItem.top; + + ExtTextOut(lpdis->hDC, x, y, 0, NULL, buf, strlen(buf), NULL); + + while(left>0) { + dst = buf+sprintf(buf, " "); + for(i=0; i<7; ++i) { + if (--left>=0) { + dst += sprintf(dst, "%02x", *src++); + } + } + + y += 11; + ExtTextOut(lpdis->hDC, x, y, 0, NULL, buf, dst-buf, NULL); + } + } + + return TRUE; +} + +BOOL CALLBACK CodeDisassemblyWindow::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + CodeDisassemblyWindow *thisPtr = (CodeDisassemblyWindow *)GetWindowLong(hDlg, DWL_USER); + + switch(msg) { + + case WM_INITDIALOG: + SetWindowLong(hDlg, DWL_USER, lParam); + thisPtr = (CodeDisassemblyWindow *)lParam; + + { + HWND hwndList = GetDlgItem(hDlg, IDC_ASMBOX); + + thisPtr->DoInitListbox(hwndList); + } + return TRUE; + + case WM_COMMAND: + switch(LOWORD(wParam)) { + case IDCANCEL: case IDOK: + EndDialog(hDlg, FALSE); + return TRUE; + } + break; + + case WM_MEASUREITEM: + return thisPtr->DoMeasureItem(lParam); + + case WM_DRAWITEM: + return thisPtr->DoDrawItem(lParam); + } + + return FALSE; +} diff --git a/stepmania/src/Disasm.h b/stepmania/src/Disasm.h new file mode 100644 index 0000000000..2a26996919 --- /dev/null +++ b/stepmania/src/Disasm.h @@ -0,0 +1,88 @@ +// from VirtualDub +// Copyright (C) 1998-2001 Avery Lee +// +// This program 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. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifndef f_DISASM_H +#define f_DISASM_H + +#include + +struct VDDisassemblyContext { + const unsigned char **pRuleSystem; + long (*pSymLookup)(VDDisassemblyContext *pctx, unsigned long virtAddr, char *buf, int buf_len); + + bool bSizeOverride; // 66 + bool bAddressOverride; // 67 + bool bRepnePrefix; // F2 + bool bRepePrefix; // F3 + const char *pszSegmentOverride; + + long physToVirtOffset; + + void *pRawBlock; + char *heap; + char *heap_limit; + int *stack; + + void *pExtraData; + int cbExtraData; +}; + + +bool VDDisasmInit(VDDisassemblyContext *, const char *, const char *); +void VDDisasmDeinit(VDDisassemblyContext *); +char *VDDisassemble(VDDisassemblyContext *pvdc, const unsigned char *source, int bytes, int& count); + + + +class CodeDisassemblyWindow { +private: + static BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM); + + void *code, *rbase, *abase; + long length; + void *pFault; + + class lbent { + public: + unsigned char *ip; + int len; + } *lbents; + int num_ents; + + HFONT hFontMono; + + char buf[256]; + +public: + VDDisassemblyContext vdc; + + CodeDisassemblyWindow(void *code, long, void *, void *); + ~CodeDisassemblyWindow(); + + void DoInitListbox(HWND hwndList); + BOOL DoMeasureItem(LPARAM lParam); + BOOL DoDrawItem(LPARAM lParam); + void parse(); + BOOL post(HWND); + long getInstruction(char *buf, long val); + + void setFaultAddress(void *_pFault) { + pFault = _pFault; + } +}; + +#endif diff --git a/stepmania/src/Tls.cpp b/stepmania/src/Tls.cpp new file mode 100644 index 0000000000..082e669684 --- /dev/null +++ b/stepmania/src/Tls.cpp @@ -0,0 +1,37 @@ +// from VirtualDub +// Copyright (C) 1998-2001 Avery Lee +// +// This program 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. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include "tls.h" +#include "crash.h" + +__declspec(thread) VirtualDubTLSData g_tlsdata; + +void InitThreadData(const char *pszName) { + VirtualDubTLSData *pData = &g_tlsdata; + + __asm { + mov eax,pData + mov dword ptr fs:[14h],eax + } + + VirtualDubInitializeThread(pszName); +} + +void DeinitThreadData() { + VirtualDubDeinitializeThread(); +} + diff --git a/stepmania/src/Tls.h b/stepmania/src/Tls.h new file mode 100644 index 0000000000..ff14f4cfa6 --- /dev/null +++ b/stepmania/src/Tls.h @@ -0,0 +1,31 @@ +// from VirtualDub +// Copyright (C) 1998-2001 Avery Lee +// +// This program 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. +// +// This program 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 this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#ifndef f_VIRTUALDUB_TLS_H +#define f_VIRTUALDUB_TLS_H + +struct VirtualDubTLSData { + int tmp[16]; + void *ESPsave; +}; + +extern __declspec(thread) VirtualDubTLSData g_tlsdata; + +void InitThreadData(const char *pszName); +void DeinitThreadData(); + +#endif