diff --git a/common/errormgmt.d b/common/errormgmt.d index 397ee0f..7512169 100644 --- a/common/errormgmt.d +++ b/common/errormgmt.d @@ -77,15 +77,15 @@ void crashed(adbg_process_t *proc, adbg_exception_t *ex) { // TODO: Get thread context // Fault address & disasm if available - if (ex.faultz) { - printf("Address : %#zx\n", ex.faultz); + if (ex.fault_address) { + printf("Address : %#llx\n", ex.fault_address); ubyte[OPCODE_BUFSIZE] buffer = void; adbg_opcode_t op = void; adbg_disassembler_t *dis = adbg_disassembler_open(adbg_machine_current()); if (dis == null) goto Lunavail; - if (adbg_memory_read(proc, ex.faultz, buffer.ptr, OPCODE_BUFSIZE)) + if (adbg_memory_read(proc, cast(size_t)ex.fault_address, buffer.ptr, OPCODE_BUFSIZE)) goto Lunavail; if (adbg_disassemble(dis, &op, buffer.ptr, OPCODE_BUFSIZE)) goto Lunavail; diff --git a/debugger/shell.d b/debugger/shell.d index ad8e672..63fa96e 100644 --- a/debugger/shell.d +++ b/debugger/shell.d @@ -636,7 +636,7 @@ void shell_event_debugger(adbg_process_t *proc, int event, void *edata, void *ud adbg_exception_name(ex), ex.oscode); // No fault address available - if (ex.faultz == 0) + if (ex.fault_address == 0) return; printf(" Address : 0x%llx\n", ex.fault_address); @@ -644,7 +644,7 @@ void shell_event_debugger(adbg_process_t *proc, int event, void *edata, void *ud char[32] machbuf = void; const(char) *mnemonic = void; const(char) *operands = void; - if (shell_disassemble(ex.faultz, null, null, 0, machbuf.ptr, 32, &mnemonic, &operands)) + if (shell_disassemble(cast(size_t)ex.fault_address, null, null, 0, machbuf.ptr, 32, &mnemonic, &operands)) return; printf(" Machine : %s\n", machbuf.ptr); diff --git a/src/adbg/debugger.d b/src/adbg/debugger.d index b2d08f0..848ec13 100644 --- a/src/adbg/debugger.d +++ b/src/adbg/debugger.d @@ -656,7 +656,8 @@ Lwait: else enum WBASE = 0; int wstatus = void; Lwait: - // TODO: Check process flag to debug all subprocesses + // TODO: Check process flag to debug all subprocesses instead of -1 + // TODO: Check process creation via checking given PID and returned pid if ((proc.tracee.pid = waitpid(-1, &wstatus, WBASE)) < 0) { proc.status = AdbgProcStatus.unknown; return adbg_oops(AdbgError.crt); @@ -692,6 +693,74 @@ Lwait: return 0; } +// Used internally to translate OS codes into exception +private +void adbg_exception_translate(adbg_exception_t *exception, void *os1, void *os2) { +version (Windows) { + assert(os1); + DEBUG_EVENT *de = cast(DEBUG_EVENT*)os1; + + exception.fault_address = cast(ulong)de.Exception.ExceptionRecord.ExceptionAddress; + exception.oscode = de.Exception.ExceptionRecord.ExceptionCode; + + switch (exception.oscode) { + case EXCEPTION_IN_PAGE_ERROR: + case EXCEPTION_ACCESS_VIOLATION: + exception.type = adbg_exception_from_os(exception.oscode, + cast(uint)de.Exception.ExceptionRecord.ExceptionInformation[0]); + break; + default: + exception.type = adbg_exception_from_os(exception.oscode); + } +} else version (linux) { + assert(os1); + assert(os2); + int pid = *cast(int*)os1; + int signo = *cast(int*)os2; + int si_code = void; + + siginfo_t siginfo = void; + if (ptrace(PT_GETSIGINFO, pid, null, &siginfo) < 0) { + si_code = 0; + exception.fault_address = 0; + } else { + si_code = siginfo.si_code; + // Get fault address + switch (signo) { + case SIGILL, SIGSEGV, SIGFPE, SIGBUS: + // NOTE: .si_addr() emits linker errors on Musl platforms. + exception.fault_address = cast(ulong)siginfo._sifields._sigfault.si_addr; + break; + default: + exception.fault_address = 0; + } + } + + exception.oscode = signo; + exception.type = adbg_exception_from_os(signo, si_code); +} else version (FreeBSD) { + assert(os1); + assert(os2); + int pid = *cast(int*)os1; + int signo = *cast(int*)os2; + int si_code = void; + + ptrace_lwpinfo lwp = void; + if (ptrace(PT_LWPINFO, pid, &lwp, 0) < 0) { + si_code = 0; + exception.fault_address = 0; + } else { + si_code = lwp.pl_siginfo.si_code; + exception.fault_address = cast(ulong)lwp.pl_siginfo.si_addr; + } + + exception.oscode = signo; + exception.type = adbg_exception_from_os(signo, si_code); +} else { + static assert(false, "Implement exception translation code"); +} +} + /// Disconnect and terminate the debuggee process. /// Params: proc = Process. /// Returns: Error code. diff --git a/src/adbg/process/exception.d b/src/adbg/process/exception.d index 6152e1e..ac52789 100644 --- a/src/adbg/process/exception.d +++ b/src/adbg/process/exception.d @@ -11,18 +11,25 @@ module adbg.process.exception; version (Windows) { import core.sys.windows.winbase; - private enum { // missing values for WoW64 (NTSTATUS, winbase.h) + private enum { + // ntstatus.h + // Missing WoW64 status codes STATUS_WX86_UNSIMULATE = 0x4000001C, /// WOW64 exception code - STATUS_WX86_CONTINUE = 0x4000001D, /// WOW64 exception code - STATUS_WX86_SINGLE_STEP = 0x4000001E, /// WOW64 exception code - STATUS_WX86_BREAKPOINT = 0x4000001F, /// WOW64 exception code - STATUS_WX86_EXCEPTION_CONTINUE = 0x40000020, /// WOW64 exception code - STATUS_WX86_EXCEPTION_LASTCHANCE = 0x40000021, /// WOW64 exception code - STATUS_WX86_EXCEPTION_CHAIN = 0x40000022, /// WOW64 exception code + STATUS_WX86_CONTINUE = 0x4000001D, /// Ditto + STATUS_WX86_SINGLE_STEP = 0x4000001E, /// Ditto + STATUS_WX86_BREAKPOINT = 0x4000001F, /// Ditto + STATUS_WX86_EXCEPTION_CONTINUE = 0x40000020, /// Ditto + STATUS_WX86_EXCEPTION_LASTCHANCE = 0x40000021, /// Ditto + STATUS_WX86_EXCEPTION_CHAIN = 0x40000022, /// Ditto + // Thread Information Block? + STATUS_WX86_CREATEWX86TIB = 0x40000028, /// Ditto + STATUS_WX86_INTERNAL_ERROR = 0xC000026F, /// Ditto + STATUS_WX86_FLOAT_STACK_CHECK = 0xC0000270, /// Ditto // See https://devblogs.microsoft.com/oldnewthing/20190108-00/?p=100655 // tl;dr: Soft stack overflow (with a cookie on stack) within MSCRT, // for prevention measures. Implies /GS (MSVC) STATUS_STACK_BUFFER_OVERRUN = 0xC0000409, /// Soft stack overflow + STATUS_EMULATION_BREAKPOINT = 0x40000038, /// ARM64EC Breakpoint } } else version (Posix) { import core.sys.posix.signal; @@ -41,10 +48,10 @@ extern (C): /// Unhandled exception type of process/program enum AdbgException { Unknown, /// Unknown exception type. - Exit, /// Program was terminated, typically by the user. Breakpoint, /// A software breakpoint was hint. Step, /// Single step was performed. - Fault, /// An access violations or segmentation fault occured. + AccessViolation, /// An access violations or segmentation fault occured. + Fault = AccessViolation, /// Alias to AccessViolation. BoundExceeded, /// Array bounds exceeded. Misalignment, /// Data type misaligned. IllegalInstruction, /// Illegal opcode. @@ -63,24 +70,8 @@ enum AdbgException { FPUOverflow, /// Overflow in FPU operation. FPUUnderflow, /// FPU's stack overflow. FPUStackOverflow, /// FPU's stack overflowed. - // OS specific-ish - Disposition, /// OS reports invalid disposition to exception handler. Internal error. - NoContinue, /// Debugger tried to continue on after non-continuable error. } -//TODO: Severity levels depending on exception type -// Essentially it'll be for a "can or cannot continue" -// This would also allow the removal of Disposition/NoContinue -// OR -// Focus on providing "Exit" translations, which debugger should rely upon -//TODO: Redo some of the codes (Disposition, NoContinue, PageError) -// - While POSIX environments have no such notion, it should still be -// translated in some other form. -// - It could be posible to provide a "second chance" system translating -// some of the signals (e.g., faults aren't illigeable, but page errors could). -// - Rename PageError to something else like IoError, or Paging -// Maybe some SIGBUS and SIGIO subcodes are meant for this - /// Represents an exception. Upon creation, these are populated depending on /// the platform with their respective function. struct adbg_exception_t { @@ -88,15 +79,8 @@ struct adbg_exception_t { AdbgException type; /// Original OS code (exception or signal value). uint oscode; - union { - /// Faulting address, if available; Otherwise zero. - ulong fault_address; - /// 32-bit Faulting address, if available; Otherwise zero. - /// Useful for LP32 environments. - uint fault_address32; - /// Used internally. - size_t faultz; - } + /// Faulting address, if available; Otherwise zero. + ulong fault_address; } /// (Internal) Translate an oscode to an ExceptionType enum value. @@ -112,21 +96,20 @@ struct adbg_exception_t { /// /// Returns: Adjusted exception type. AdbgException adbg_exception_from_os(uint code, uint subcode = 0) { -version (Windows) { // NOTE: Prefer STATUS over EXCEPTION names when possible +version (Windows) { switch (code) with (AdbgException) { // NOTE: A step may also indicate a trace operation case STATUS_SINGLE_STEP, STATUS_WX86_SINGLE_STEP: return Step; - // Instruction breakpoint - case STATUS_BREAKPOINT, STATUS_WX86_BREAKPOINT: + // Instruction + case STATUS_BREAKPOINT, STATUS_WX86_BREAKPOINT, STATUS_EMULATION_BREAKPOINT: return Breakpoint; case STATUS_ILLEGAL_INSTRUCTION: return IllegalInstruction; - // Memory access violation - case STATUS_ACCESS_VIOLATION: // no similar sigcode for sub-operation - return Fault; - // Specifically to swap + // Memory + case STATUS_ACCESS_VIOLATION, STATUS_GUARD_PAGE_VIOLATION: + return AccessViolation; case STATUS_IN_PAGE_ERROR: // no similar sigcode for sub-operation // NOTE: The third array element specifies the underlying // NTSTATUS code that resulted in the exception. @@ -148,9 +131,7 @@ version (Windows) { case EXCEPTION_FLT_OVERFLOW: return FPUOverflow; case EXCEPTION_FLT_STACK_CHECK: return FPUStackOverflow; case EXCEPTION_FLT_UNDERFLOW: return FPUUnderflow; - // Misc - case STATUS_INVALID_DISPOSITION: return Disposition; - case STATUS_NONCONTINUABLE_EXCEPTION: return NoContinue; + case STATUS_WX86_FLOAT_STACK_CHECK: return FPUOverflow; default: } } else { @@ -174,7 +155,7 @@ version (Windows) { case SEGV_BNDERR: return BoundExceeded; default: } - return Fault; + return AccessViolation; case SIGBUS: switch (subcode) { case BUS_ADRALN: return Misalignment; @@ -185,18 +166,12 @@ version (Windows) { return Breakpoint; case SIGCHLD: switch (subcode) { - case CLD_EXITED: return Exit; - case CLD_KILLED: return Exit; - case CLD_DUMPED: return Unknown; case CLD_TRAPPED: return Breakpoint; - case CLD_STOPPED: return Unknown; - case CLD_CONTINUED: return Unknown; default: } break; // Because Windows' DebugBreak uses a regular breakpoint (int3) case SIGSTOP: return Breakpoint; - case SIGKILL: return Exit; default: } } // Posix @@ -206,17 +181,16 @@ version (Windows) { /// Get a short descriptive string for an exception type value. /// Params: ex = Exception. -/// Returns: String or null. Names are uppercased. -const(char) *adbg_exception_name(adbg_exception_t *ex) { +/// Returns: String or null on error. +const(char)* adbg_exception_name(adbg_exception_t *ex) { if (ex == null) return null; switch (ex.type) with (AdbgException) { case Unknown: return "UNKNOWN"; - case Exit: return "TERMINATED"; case Breakpoint: return "BREAKPOINT"; case Step: return "INSTRUCTION STEP"; // NOTE: Also known as a segmentation fault, // "access violation" remains a better term. - case Fault: return "ACCESS VIOLATION"; + case AccessViolation: return "ACCESS VIOLATION"; case BoundExceeded: return "INDEX OUT OF BOUNDS"; case Misalignment: return "DATA MISALIGNMENT"; case IllegalInstruction: return "ILLEGAL INSTRUCTION"; @@ -232,81 +206,6 @@ const(char) *adbg_exception_name(adbg_exception_t *ex) { case FPUOverflow: return "FPU: OVERFLOW"; case FPUUnderflow: return "FPU: UNDERFLOW"; case FPUStackOverflow: return "FPU: STACK OVERFLOW"; - case Disposition: return "OS: DISPOSITION"; - case NoContinue: return "OS: COULD NOT CONTINUE"; default: return "UNKNOWN"; } } - -// Used internally for debugger -void adbg_exception_translate(adbg_exception_t *exception, void *os1, void *os2) { -version (Windows) { - assert(os1); - DEBUG_EVENT *de = cast(DEBUG_EVENT*)os1; - - exception.faultz = cast(size_t)de.Exception.ExceptionRecord.ExceptionAddress; - exception.oscode = de.Exception.ExceptionRecord.ExceptionCode; - - switch (exception.oscode) { - case EXCEPTION_IN_PAGE_ERROR: - case EXCEPTION_ACCESS_VIOLATION: - exception.type = adbg_exception_from_os(exception.oscode, - cast(uint)de.Exception.ExceptionRecord.ExceptionInformation[0]); - break; - default: - exception.type = adbg_exception_from_os(exception.oscode); - } -} else { - assert(os1); - assert(os2); - int pid = *cast(int*)os1; - int signo = *cast(int*)os2; - - version (linux) { - // Get fault address - switch (signo) { - // NOTE: si_addr is NOT populated under ptrace for SIGTRAP - // - linux does not fill si_addr on a SIGTRAP from a ptrace event - // - see sigaction(2) - // - linux *only* fills user_regs_struct for "user area" - // - see arch/x86/include/asm/user_64.h - // - "ptrace does not yet supply these. Someday...." - // - So yeah, debug registers and "fault_address" not filled - // - No access to ucontext_t from ptrace either - // - using EIP/RIP is NOT a good idea - // - IP ALWAYS point to NEXT instruction - // - First SIGTRAP does NOT contain int3 - // - Windows does, though, and points to it - // - gdbserver and lldb never attempt to do such thing anyway - // - RIP-1 (x86) could *maybe* point to int3 or similar. - // - User area might have DR3, it does have "fault_address" - // NOTE: Newer D compilers fixed siginfo_t as a whole - // for version (linux). Noticed on DMD 2.103.1. - // Old glibc: ._sifields._sigfault.si_addr - // Old musl: .__si_fields.__sigfault.si_addr - // New: ._sifields._sigfault.si_addr & .si_addr() - // NOTE: .si_addr() emits linker errors on Musl platforms. - case SIGILL, SIGSEGV, SIGFPE, SIGBUS: - siginfo_t siginfo = void; - if (ptrace(PT_GETSIGINFO, pid, null, &siginfo) < 0) { - exception.fault_address = 0; - break; - } - exception.fault_address = cast(size_t)siginfo._sifields._sigfault.si_addr; - break; - default: - exception.fault_address = 0; - } - } else version (FreeBSD) { - ptrace_lwpinfo lwp = void; - exception.fault_address = - ptrace(PT_LWPINFO, pid, &lwp, 0) < 0 ? - 0 : cast(ulong)lwp.pl_siginfo.si_addr; - } else { - exception.fault_address = 0; - } - - exception.oscode = signo; - exception.type = adbg_exception_from_os(signo); -} -} diff --git a/src/adbg/self.d b/src/adbg/self.d index 308599e..0b219c8 100644 --- a/src/adbg/self.d +++ b/src/adbg/self.d @@ -169,7 +169,7 @@ uint adbg_internal_handler(EXCEPTION_POINTERS *e) { // Setup exception info adbg_exception_t ex = void; ex.oscode = e.ExceptionRecord.ExceptionCode; - ex.faultz = cast(size_t)e.ExceptionRecord.ExceptionAddress; + ex.fault_address = cast(ulong)e.ExceptionRecord.ExceptionAddress; with (e.ExceptionRecord) switch (ex.oscode) { case EXCEPTION_IN_PAGE_ERROR: case EXCEPTION_ACCESS_VIOLATION: @@ -197,11 +197,13 @@ void adbg_internal_handler(int sig, siginfo_t *si, void *p) { version (linux) { switch (sig) { case SIGILL, SIGSEGV, SIGFPE, SIGBUS: - ex.fault_address = cast(size_t)si._sifields._sigfault.si_addr; + ex.fault_address = cast(ulong)si._sifields._sigfault.si_addr; break; default: ex.fault_address = 0; } +} else version (FreeBSD) { + ex.fault_address = cast(ulong)si.si_addr; } else { ex.fault_address = 0; }