diff --git a/GNUmakefile b/GNUmakefile index be0bd9ba..c7ffdde3 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -25,7 +25,7 @@ toolchain_64 := x86_64-w64-mingw32- gitrev := $(shell git rev-parse HEAD) cppflags := -I src -I src/main -I src/test -DGITREV=$(gitrev) -cflags := -O2 -pipe -ffunction-sections -fdata-sections \ +cflags := -g -O2 -pipe -ffunction-sections -fdata-sections \ -Wall -std=c99 -DPSAPI_VERSION=1 cflags_release := -Werror ldflags := -Wl,--gc-sections -static-libgcc @@ -240,7 +240,6 @@ $$(dll_$1_$2_$3) $$(implib_$1_$2_$3): $$(obj_$1_$2_$3) $$(abslib_$1_$2_$3) \ $(V)$$(toolchain_$1)gcc -shared \ -o $$(dll_$1_$2_$3) -Wl,--out-implib,$$(implib_$1_$2_$3) \ -Wl,--start-group $$^ -Wl,--end-group $$(ldflags_$3) - $(V)$$(toolchain_$1)strip $$(dll_$1_$2_$3) $(V)$$(toolchain_$1)ranlib $$(implib_$1_$2_$3) endef @@ -257,7 +256,6 @@ $$(exe_$1_$2_$3): $$(obj_$1_$2_$3) $$(abslib_$1_$2_$3) $$(absdpl_$1_$2_$3) \ | $$(bindir_$1_$2) $(V)echo ... $$@ $(V)$$(toolchain_$1)gcc -o $$@ $$^ $$(ldflags_$3) - $(V)$$(toolchain_$1)strip $$@ endef diff --git a/Module.mk b/Module.mk index 4178a424..21b125d2 100644 --- a/Module.mk +++ b/Module.mk @@ -257,6 +257,7 @@ $(zipdir)/iidx-09-to-12.zip: \ build/bin/indep-32/iidxio.dll \ build/bin/indep-32/vefxio.dll \ build/bin/indep-32/inject.exe \ + dist/dwarfstack/32/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-09.bat \ dist/iidx/gamestart-10.bat \ @@ -282,6 +283,7 @@ $(zipdir)/iidx-13.zip: \ build/bin/indep-32/iidxio.dll \ build/bin/indep-32/vefxio.dll \ build/bin/indep-32/inject.exe \ + dist/dwarfstack/32/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-13.bat \ dist/iidx/iidxhook-13.conf \ @@ -298,6 +300,7 @@ $(zipdir)/iidx-14-to-17.zip: \ build/bin/indep-32/iidxio.dll \ build/bin/indep-32/vefxio.dll \ build/bin/indep-32/inject.exe \ + dist/dwarfstack/32/dwarfstack.dll \ dist/iidx/config.bat \ dist/iidx/gamestart-14.bat \ dist/iidx/gamestart-15.bat \ @@ -485,6 +488,7 @@ $(zipdir)/jb-01.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/jbio.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/jb/config.bat \ dist/jb/gamestart-01.bat \ dist/jb/jbhook-01.conf \ @@ -499,6 +503,7 @@ $(zipdir)/jb-02.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ build/bin/indep-32/jbio.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/jb/config.bat \ dist/jb/gamestart-02.bat \ dist/jb/jbhook-02.conf \ @@ -659,6 +664,7 @@ $(zipdir)/ddr-12-us.zip: \ build/bin/indep-32/ddrio-smx.dll \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/ddr/config.bat \ dist/ddr/gamestart-12-us.bat \ dist/ddr/gamestart-12-eu.bat \ @@ -776,6 +782,7 @@ $(zipdir)/popn-15-to-18.zip: \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/popnio.dll \ build/bin/indep-32/ezusb2-popn-shim.dll \ + dist/dwarfstack/32/dwarfstack.dll \ dist/popn/config.bat \ dist/popn/gamestart-15.bat \ dist/popn/gamestart-16.bat \ @@ -807,6 +814,7 @@ $(BUILDDIR)/tests.zip: \ build/bin/indep-32/iidxhook-util-config-gfx-test.exe \ build/bin/indep-32/iidxhook-util-config-misc-test.exe \ build/bin/indep-32/iidxhook-util-config-sec-test.exe \ + dist/dwarfstack/32/dwarfstack.dll \ build/bin/indep-32/inject.exe \ build/bin/indep-32/security-id-test.exe \ build/bin/indep-32/security-mcode-test.exe \ diff --git a/dist/dwarfstack/32/dwarfstack.dll b/dist/dwarfstack/32/dwarfstack.dll new file mode 100644 index 00000000..74b9796d Binary files /dev/null and b/dist/dwarfstack/32/dwarfstack.dll differ diff --git a/dist/dwarfstack/64/dwarfstack.dll b/dist/dwarfstack/64/dwarfstack.dll new file mode 100644 index 00000000..8944c726 Binary files /dev/null and b/dist/dwarfstack/64/dwarfstack.dll differ diff --git a/dist/dwarfstack/readme.md b/dist/dwarfstack/readme.md new file mode 100644 index 00000000..c8ce1e3c --- /dev/null +++ b/dist/dwarfstack/readme.md @@ -0,0 +1 @@ +Version/release: https://github.com/ssbssa/dwarfstack/releases/tag/2.2 \ No newline at end of file diff --git a/src/imports/dwarfstack.h b/src/imports/dwarfstack.h new file mode 100644 index 00000000..ba2d23c6 --- /dev/null +++ b/src/imports/dwarfstack.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2013-2019 Hannes Domani + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef __DWARFSTACK_H__ +#define __DWARFSTACK_H__ + +#include + + +#if defined(DWST_STATIC) +#define EXPORT +#elif defined(DWST_SHARED) +#define EXPORT __declspec(dllexport) +#else +#define EXPORT __declspec(dllimport) +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +// dwstCallback(): callback function +// addr: stack address +// filename: source file location +// lineno: line number +// funcname: function name +// context: user-provided pointer (callbackContext) +// columnno: column number +typedef void dwstCallback( + uint64_t addr,const char *filename,int lineno,const char *funcname, + void *context,int columnno ); + +typedef void dwstCallbackW( + uint64_t addr,const wchar_t *filename,int lineno,const char *funcname, + void *context,int columnno ); + +// special values for lineno: + +// DWST_BASE_ADDR: inform about the used image base address +// (important in case it's not the same as the preferred image base address) +// addr: used image base address +// filename: executable location +#define DWST_BASE_ADDR 0 + +// DWST_NO_DBG_SYM: no debug information available +// addr: stack address +// filename: executable location +#define DWST_NO_DBG_SYM -1 + +// DWST_NO_SRC_FILE: no source file information available +// addr: stack address +// filename: executable location +#define DWST_NO_SRC_FILE -2 + +// DWST_NOT_FOUND: no information available (invalid address?) +// addr: stack address +// filename: executable location +#define DWST_NOT_FOUND -3 + + +// dwstOfFile(): stack information of file +// name: executable location +// imageBase: used image base address +// addr: stack addresses +// count: number of addresses +// callbackFunc: callback function +// callbackContext: user-provided pointer (context) +// (for example see examples/addr2line/) +EXPORT int dwstOfFile( + const char *name,uint64_t imageBase, + uint64_t *addr,int count, + dwstCallback *callbackFunc,void *callbackContext ); + +EXPORT int dwstOfFileW( + const wchar_t *name,uint64_t imageBase, + uint64_t *addr,int count, + dwstCallbackW *callbackFunc,void *callbackContext ); + + +// dwstOfProcess(): stack information of current process +// addr: stack addresses +// count: number of addresses +// callbackFunc: callback function +// callbackContext: user-provided pointer (context) +EXPORT int dwstOfProcess( + uintptr_t *addr,int count, + dwstCallback *callbackFunc,void *callbackContext ); + +EXPORT int dwstOfProcessW( + uintptr_t *addr,int count, + dwstCallbackW *callbackFunc,void *callbackContext ); + + +// dwstOfLocation(): stack information of current location +// callbackFunc: callback function +// callbackContext: user-provided pointer (context) +// (for example see examples/location/) +EXPORT int dwstOfLocation( + dwstCallback *callbackFunc,void *callbackContext ); + +EXPORT int dwstOfLocationW( + dwstCallbackW *callbackFunc,void *callbackContext ); + + +// dwstOfException(): stack information of exception +// context: ContextRecord of exception +// callbackFunc: callback function +// callbackContext: user-provided pointer (context) +// (for example see examples/exception/) +EXPORT int dwstOfException( + void *context, + dwstCallback *callbackFunc,void *callbackContext ); + +EXPORT int dwstOfExceptionW( + void *context, + dwstCallbackW *callbackFunc,void *callbackContext ); + + +// dwstExceptionDialog(): show dialog on unhandled exception +// extraInfo: extra information shown in dialog +// (for example see examples/exception-dialog/) +EXPORT void dwstExceptionDialog( + const char *extraInfo ); + +EXPORT void dwstExceptionDialogW( + const wchar_t *extraInfo ); + + +#ifndef DWST_STATIC +// dwstDemangle(): demangle gcc style c++ symbols +// mangled: mangled name +// demangled: demangled name +// length: size of demangled buffer +EXPORT size_t dwstDemangle( + const char *mangled, + char *demangled,size_t length ); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/imports/import_32_indep_dwarfstack.def b/src/imports/import_32_indep_dwarfstack.def new file mode 100644 index 00000000..71f1aaf5 --- /dev/null +++ b/src/imports/import_32_indep_dwarfstack.def @@ -0,0 +1,13 @@ +LIBRARY dwarfstack + +EXPORTS + dwstOfFile @ 441 + dwstOfFileW @ 443 + dwstOfProcess @ 446 + dwstOfProcessW @ 448 + dwstOfLocation @ 444 + dwstOfLocationW @ 445 + dwstOfException @ 438 + dwstOfExceptionW @ 440 + dwstExceptionDialog @ 436 + dwstExceptionDialogW @ 437 \ No newline at end of file diff --git a/src/imports/import_64_indep_dwarfstack.def b/src/imports/import_64_indep_dwarfstack.def new file mode 100644 index 00000000..71f1aaf5 --- /dev/null +++ b/src/imports/import_64_indep_dwarfstack.def @@ -0,0 +1,13 @@ +LIBRARY dwarfstack + +EXPORTS + dwstOfFile @ 441 + dwstOfFileW @ 443 + dwstOfProcess @ 446 + dwstOfProcessW @ 448 + dwstOfLocation @ 444 + dwstOfLocationW @ 445 + dwstOfException @ 438 + dwstOfExceptionW @ 440 + dwstExceptionDialog @ 436 + dwstExceptionDialogW @ 437 \ No newline at end of file diff --git a/src/main/inject/Module.mk b/src/main/inject/Module.mk index 549f1792..648ea7f3 100644 --- a/src/main/inject/Module.mk +++ b/src/main/inject/Module.mk @@ -1,11 +1,14 @@ exes += inject +imps += dwarfstack ldflags_inject := \ -mconsole \ -lpsapi \ + -ldbghelp \ libs_inject := \ util \ + dwarfstack \ src_inject := \ main.c \ diff --git a/src/main/inject/debugger.c b/src/main/inject/debugger.c index 724ed9ba..77d92444 100644 --- a/src/main/inject/debugger.c +++ b/src/main/inject/debugger.c @@ -11,10 +11,10 @@ #include "inject/debugger.h" #include "inject/logger.h" +#include "util/debug.h" #include "util/log.h" #include "util/mem.h" #include "util/proc.h" -#include "util/signal.h" #include "util/str.h" #define MM_ALLOCATION_GRANULARITY 0x10000 @@ -271,7 +271,7 @@ static uint32_t debugger_loop() "EXCEPTION_DEBUG_EVENT(pid %ld, tid %ld): x%s 0x%p", de.dwProcessId, de.dwThreadId, - signal_exception_code_to_str( + debug_exception_code_to_str( de.u.Exception.ExceptionRecord.ExceptionCode), de.u.Exception.ExceptionRecord.ExceptionAddress); diff --git a/src/main/inject/main.c b/src/main/inject/main.c index bfb6c1be..475e5ab9 100644 --- a/src/main/inject/main.c +++ b/src/main/inject/main.c @@ -16,6 +16,7 @@ #include "inject/version.h" #include "util/cmdline.h" +#include "util/debug.h" #include "util/log.h" #include "util/mem.h" #include "util/os.h" @@ -166,6 +167,7 @@ int main(int argc, char **argv) os_version_log(); + debug_init(); signal_exception_handler_init(); // Cleanup remote process on CTRL+C signal_register_shutdown_handler(signal_shutdown_handler); diff --git a/src/main/util/Module.mk b/src/main/util/Module.mk index ab8d4956..af7851ec 100644 --- a/src/main/util/Module.mk +++ b/src/main/util/Module.mk @@ -5,6 +5,7 @@ src_util := \ cmdline.c \ crc.c \ crypto.c \ + debug.c \ fs.c \ hex.c \ iobuf.c \ diff --git a/src/main/util/debug.c b/src/main/util/debug.c new file mode 100644 index 00000000..f07e401c --- /dev/null +++ b/src/main/util/debug.c @@ -0,0 +1,184 @@ +#define LOG_MODULE "util-debug" + +#include + +#include + +#include "imports/dwarfstack.h" + +#include "util/debug.h" +#include "util/log.h" + +#define EX_DESC(name) \ + case EXCEPTION_##name: \ + desc = " (" #name ")"; \ + break + +static void _debug_stacktrace_printer( + uint64_t addr, + const char *filename, + int lineno, + const char *funcname, + void *context, + int columnno) +{ + int *count; + const char *delim; + void *ptr; + char buffer[512]; + char *buffer_ptr; + + count = context; + delim = strrchr(filename, '/'); + + if (delim) { + filename = delim + 1; + } + + delim = strrchr(filename, '\\'); + + if (delim) { + filename = delim + 1; + } + + ptr = (void *) (uintptr_t) addr; + + switch (lineno) { + case DWST_BASE_ADDR: + log_exception_handler("base address: 0x%p (%s)", ptr, filename); + break; + + case DWST_NOT_FOUND: + case DWST_NO_DBG_SYM: + case DWST_NO_SRC_FILE: + log_exception_handler( + " stack %02d: 0x%p (%s)", (*count)++, ptr, filename); + break; + + default: + buffer_ptr = buffer; + memset(buffer, 0, sizeof(buffer)); + + if (ptr) { + buffer_ptr += sprintf( + buffer_ptr, " stack %02d: 0x%p", (*count)++, ptr); + } else { + buffer_ptr += sprintf( + buffer_ptr, + " %*s", + (int) sizeof(void *) * 2, + ""); + } + + buffer_ptr += sprintf(buffer_ptr, " (%s:%d", filename, lineno); + + if (columnno > 0) { + buffer_ptr += sprintf(buffer_ptr, ":%d", columnno); + } + + buffer_ptr += sprintf(buffer_ptr, ")"); + + if (funcname) { + buffer_ptr += sprintf(buffer_ptr, " [%s]", funcname); + } + + log_exception_handler(buffer); + + break; + } +} + +static LONG WINAPI _debug_unhandled_exception_filter(LPEXCEPTION_POINTERS ep) +{ + DWORD code; + const char *desc; + ULONG_PTR flag; + ULONG_PTR addr; + int count; + + log_exception_handler( + "=========================================================="); + log_exception_handler( + "The application has crashed due to an unhandled exception!"); + + code = ep->ExceptionRecord->ExceptionCode; + desc = debug_exception_code_to_str(code); + + log_exception_handler("code: 0x%08lX", code); + log_exception_handler("desc: %s", desc); + + if (code == EXCEPTION_ACCESS_VIOLATION && + ep->ExceptionRecord->NumberParameters == 2) { + flag = ep->ExceptionRecord->ExceptionInformation[0]; + addr = ep->ExceptionRecord->ExceptionInformation[1]; + + log_exception_handler( + "%s violation at 0x%p", + flag == 8 ? "data execution prevention" : + (flag ? "write access" : "read access"), + (void *) addr); + } + + log_exception_handler("stacktrace:"); + + count = 0; + + dwstOfException(ep->ContextRecord, &_debug_stacktrace_printer, &count); + + return EXCEPTION_EXECUTE_HANDLER; +} + +void debug_init() +{ + SetUnhandledExceptionFilter(_debug_unhandled_exception_filter); + + log_info("Initialized"); +} + +void debug_print_stacktrace() +{ + int count; + + count = 0; + + log_exception_handler( + "=========================================================="); + log_exception_handler("Debug stacktrace"); + + dwstOfLocation(&_debug_stacktrace_printer, &count); +} + +const char *debug_exception_code_to_str(DWORD code) +{ + const char *desc = ""; + + switch (code) { + EX_DESC(ACCESS_VIOLATION); + EX_DESC(ARRAY_BOUNDS_EXCEEDED); + EX_DESC(BREAKPOINT); + EX_DESC(DATATYPE_MISALIGNMENT); + EX_DESC(FLT_DENORMAL_OPERAND); + EX_DESC(FLT_DIVIDE_BY_ZERO); + EX_DESC(FLT_INEXACT_RESULT); + EX_DESC(FLT_INVALID_OPERATION); + EX_DESC(FLT_OVERFLOW); + EX_DESC(FLT_STACK_CHECK); + EX_DESC(FLT_UNDERFLOW); + EX_DESC(ILLEGAL_INSTRUCTION); + EX_DESC(IN_PAGE_ERROR); + EX_DESC(INT_DIVIDE_BY_ZERO); + EX_DESC(INT_OVERFLOW); + EX_DESC(INVALID_DISPOSITION); + EX_DESC(NONCONTINUABLE_EXCEPTION); + EX_DESC(PRIV_INSTRUCTION); + EX_DESC(SINGLE_STEP); + EX_DESC(STACK_OVERFLOW); + case DBG_CONTROL_C: + return "DBG_CONTROL_C"; + default: + log_warning("Unknown exception code: %lX", code); + return "Unknown"; + } + + return desc; +} \ No newline at end of file diff --git a/src/main/util/debug.h b/src/main/util/debug.h new file mode 100644 index 00000000..80519050 --- /dev/null +++ b/src/main/util/debug.h @@ -0,0 +1,10 @@ +#ifndef UTIL_DEBUG_H +#define UTIL_DEBUG_H + +#include + +void debug_init(); +void debug_print_stacktrace(); +const char *debug_exception_code_to_str(DWORD code); + +#endif \ No newline at end of file diff --git a/src/main/util/log.c b/src/main/util/log.c index 7da1d63a..bd643293 100644 --- a/src/main/util/log.c +++ b/src/main/util/log.c @@ -64,6 +64,15 @@ static void log_builtin_format( } } +void log_exception_handler(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_builtin_format(LOG_LEVEL_FATAL, LOG_MODULE, fmt, ap); + va_end(ap); +} + void log_assert_body(const char *file, int line, const char *function) { log_impl_fatal("assert", "%s:%d: function `%s'", file, line, function); diff --git a/src/main/util/log.h b/src/main/util/log.h index 47603fa1..e8a0bd0c 100644 --- a/src/main/util/log.h +++ b/src/main/util/log.h @@ -66,6 +66,7 @@ enum log_level { LOG_LEVEL_MISC = 3, }; +void log_exception_handler(const char *fmt, ...); void log_assert_body(const char *file, int line, const char *function); void log_to_external( log_formatter_t misc, diff --git a/src/main/util/signal.c b/src/main/util/signal.c index 4c15acd5..c35cb270 100644 --- a/src/main/util/signal.c +++ b/src/main/util/signal.c @@ -1,5 +1,4 @@ #include -#include #include "util/hex.h" #include "util/log.h" @@ -45,40 +44,9 @@ static BOOL WINAPI console_ctrl_handler(DWORD dwCtrlType) return FALSE; } -static LONG WINAPI -unhandled_exception_filter(struct _EXCEPTION_POINTERS *ExceptionInfo) -{ - // no exception info provided - if (ExceptionInfo != NULL) { - struct _EXCEPTION_RECORD *ExceptionRecord = - ExceptionInfo->ExceptionRecord; - - log_warning( - "Exception raised: %s", - signal_exception_code_to_str(ExceptionRecord->ExceptionCode)); - - struct _EXCEPTION_RECORD *record_cause = - ExceptionRecord->ExceptionRecord; - - while (record_cause != NULL) { - log_warning( - "Caused by: %s", - signal_exception_code_to_str(record_cause->ExceptionCode)); - record_cause = record_cause->ExceptionRecord; - } - - // TODO print stacktrace - - log_fatal("End exception handler"); - } - - return EXCEPTION_CONTINUE_SEARCH; -} - void signal_exception_handler_init() { SetConsoleCtrlHandler(console_ctrl_handler, TRUE); - SetUnhandledExceptionFilter(unhandled_exception_filter); log_info("Initialized"); } @@ -88,55 +56,4 @@ void signal_register_shutdown_handler(signal_shutdown_handler_t handler) shutdown_handler = handler; log_misc("Registered shutdown handler"); -} - -const char *signal_exception_code_to_str(DWORD code) -{ - switch (code) { - case EXCEPTION_ACCESS_VIOLATION: - return "EXCEPTION_ACCESS_VIOLATION"; - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; - case EXCEPTION_BREAKPOINT: - return "EXCEPTION_BREAKPOINT"; - case EXCEPTION_DATATYPE_MISALIGNMENT: - return "EXCEPTION_DATATYPE_MISALIGNMENT"; - case EXCEPTION_FLT_DENORMAL_OPERAND: - return "EXCEPTION_FLT_DENORMAL_OPERAND"; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - return "EXCEPTION_FLT_DIVIDE_BY_ZERO"; - case EXCEPTION_FLT_INEXACT_RESULT: - return "EXCEPTION_FLT_INEXACT_RESULT"; - case EXCEPTION_FLT_INVALID_OPERATION: - return "EXCEPTION_FLT_INVALID_OPERATION"; - case EXCEPTION_FLT_OVERFLOW: - return "EXCEPTION_FLT_OVERFLOW"; - case EXCEPTION_FLT_STACK_CHECK: - return "EXCEPTION_FLT_STACK_CHECK"; - case EXCEPTION_FLT_UNDERFLOW: - return "EXCEPTION_FLT_UNDERFLOW"; - case EXCEPTION_ILLEGAL_INSTRUCTION: - return "EXCEPTION_ILLEGAL_INSTRUCTION"; - case EXCEPTION_IN_PAGE_ERROR: - return "EXCEPTION_IN_PAGE_ERROR"; - case EXCEPTION_INT_DIVIDE_BY_ZERO: - return "EXCEPTION_INT_DIVIDE_BY_ZERO"; - case EXCEPTION_INT_OVERFLOW: - return "EXCEPTION_INT_OVERFLOW"; - case EXCEPTION_INVALID_DISPOSITION: - return "EXCEPTION_INVALID_DISPOSITION"; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - return "EXCEPTION_NONCONTINUABLE_EXCEPTION"; - case EXCEPTION_PRIV_INSTRUCTION: - return "EXCEPTION_PRIV_INSTRUCTION"; - case EXCEPTION_SINGLE_STEP: - return "EXCEPTION_SINGLE_STEP"; - case EXCEPTION_STACK_OVERFLOW: - return "EXCEPTION_STACK_OVERFLOW"; - case DBG_CONTROL_C: - return "DBG_CONTROL_C"; - default: - log_warning("Unknown exception code: %lX", code); - return "Unknown"; - } } \ No newline at end of file