/// Error handling module. /// /// NOTE: Every thing that could go wrong should have an error code. /// Authors: dd86k <dd@dax.moe> /// Copyright: © dd86k <dd@dax.moe> /// License: BSD-3-Clause-Clear module adbg.error; version (Windows) { import core.sys.windows.winbase : GetLastError, FormatMessageA, FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_MAX_WIDTH_MASK; enum ADBG_OS_ERROR_FORMAT = "%08X"; /// Error code format } else { enum ADBG_OS_ERROR_FORMAT = "%d"; /// Error code format } import core.stdc.errno : errno; import core.stdc.string : strerror; import adbg.include.capstone : csh, cs_errno, cs_strerror; //TODO: Make module thread-safe // Either via TLS and/or atomic operations //TODO: More error should have context parameters // invalidArgument: string stating which argument //TODO: adbg_error_source -> alicedbg/crt/os/capstone, etc. // adbg_error_is_external -> bool //TODO: Maybe redo error code functions to reduce confusion between errno/external/system/current //TODO: Error utils // adbg_ensure_params(lvalue, "name") // - returns string if null found // - automatically set error code // adbg_oopsn(AdbgError) // - returns null extern (C): /// Error codes. enum AdbgError { // // 0-99: Generic // success = 0, invalidArgument = 1, /// Argument is null or zero emptyArgument = 2, /// Argument contains an empty dataset uninitiated = 4, /// Instance was not initiated invalidOption = 5, /// Invalid option invalidValue = 6, /// Invalid value for option offsetBounds = 7, /// File offset is outside of file size indexBounds = 8, /// Index is outside of bounds of list unavailable = 9, /// Feature or item is unavailable unfindable = 10, /// Item cannot be found in list // // 100-199: Debugger // debuggerUnattached = 100, debuggerUnpaused = 101, debuggerInvalidAction = 102, /// Wrong action from creation method. debuggerPresent = 103, /// Debugger already present in remote process // // 200-299: Disasembler // disasmUnsupportedMachine = 202, disasmIllegalInstruction = 220, disasmEndOfData = 221, disasmOpcodeLimit = 221, // // 300-399: Object server // objectUnknownFormat = 301, objectUnsupportedFormat = 302, objectTooSmall = 303, objectMalformed = 304, objectItemNotFound = 305, objectInvalidVersion = 310, objectInvalidMachine = 311, objectInvalidClass = 312, objectInvalidEndian = 313, objectInvalidType = 314, objectInvalidABI = 315, // // 400-499: System // systemLoadError = 402, systemBindError = 403, // // 800-899: Memory scanner // scannerDataEmpty = 800, scannerDataLimit = 801, // // 1000-1999: Misc // assertion = 1000, /// Soft assert unimplemented = 1001, /// Not implemented // // 2000-2999: External resources // os = 2001, crt = 2002, // // 3000-3999: External libraries // libCapstone = 3002, /// Capstone } /// Represents an error in alicedbg. struct adbg_error_t { const(char)* mod; /// Source module int line; /// Line source int code; /// Error code void *res; /// External handle or code } /// Last error in alicedbg. private __gshared adbg_error_t error; //TODO: Strongly consider string, provides .ptr and .length private struct adbg_error_msg_t { int code; const(char) *msg; } private immutable const(char) *defaultMsg = "Unknown error occured."; private immutable adbg_error_msg_t[] errors_msg = [ // // Generics // { AdbgError.invalidArgument, "Invalid or missing parameter value." }, { AdbgError.emptyArgument, "Parameter is empty." }, { AdbgError.uninitiated, "Object or structure requires to be initialized first." }, { AdbgError.invalidOption, "Option unknown." }, { AdbgError.invalidValue, "Option received invalid value." }, { AdbgError.offsetBounds, "File offset outside file size." }, { AdbgError.indexBounds, "Index outside of list." }, { AdbgError.unavailable, "Feature or item is unavailable." }, { AdbgError.unfindable, "Item was not found." }, // // Debugger // { AdbgError.debuggerUnattached, "Debugger needs to be attached for this feature." }, { AdbgError.debuggerUnpaused, "Debugger needs the process to be paused for this feature." }, { AdbgError.debuggerInvalidAction, "Debugger was given a wrong action for this process." }, { AdbgError.debuggerPresent, "Debugger already present on remote process." }, // // Disassembler // { AdbgError.disasmUnsupportedMachine, "Disassembler does not support this platform." }, { AdbgError.disasmIllegalInstruction, "Disassembler met an illegal instruction." }, { AdbgError.disasmEndOfData, "Disassembler reached end of data." }, { AdbgError.disasmOpcodeLimit, "Disassembler reached architectural opcode limit." }, // // Object server // { AdbgError.objectUnknownFormat, "Object format unknown." }, { AdbgError.objectUnsupportedFormat, "Object format unsupported." }, { AdbgError.objectTooSmall, "Object is too small to be valid." }, { AdbgError.objectMalformed, "Object potentially corrupted." }, { AdbgError.objectItemNotFound, "Object item was not found." }, { AdbgError.objectInvalidVersion, "Object has invalid version." }, { AdbgError.objectInvalidMachine, "Object has invalid machine or platform value." }, { AdbgError.objectInvalidClass, "Object has invalid class or bitness value." }, { AdbgError.objectInvalidEndian, "Object has invalid endian value." }, { AdbgError.objectInvalidType, "Object type invalid." }, { AdbgError.objectInvalidABI, "Object has Invalid ABI value." }, // // Symbols // { AdbgError.systemLoadError, "Dynamic library could not be loaded." }, { AdbgError.systemBindError, "Symbol could not be binded." }, // // Memory module // { AdbgError.scannerDataEmpty, "Memory scanner received empty data." }, { AdbgError.scannerDataLimit, "Memory scanner received too much data." }, // // Misc. // { AdbgError.assertion, "A soft debugging assertion was hit." }, { AdbgError.unimplemented, "Feature is not implemented." }, { AdbgError.success, "No errors occured." }, ]; /// Get error state instance. /// Returns: Pointer to the only error instance. //TODO: Deprecate as dangerous // Getting extra info such as source (string) and al. should be via functions const(adbg_error_t)* adbg_error_current() { return &error; } /// Get error message from the OS (or CRT) by providing the error code /// Params: code = Error code number from OS /// Returns: String const(char)* adbg_sys_error(int code) { version (Windows) { //TODO: Handle NTSTATUS codes enum ERR_BUF_SZ = 256; __gshared char [ERR_BUF_SZ]buffer = void; size_t len = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK, null, code, 0, // Default buffer.ptr, ERR_BUF_SZ, null); return len ? cast(char*)buffer : "Unknown error"; } else { return strerror(code); } } /// Get the last error code from the OS (or CRT) /// Returns: GetLastError from Windows, otherwise errno private int adbg_error_system() { version (Windows) { return error.res ? cast(uint)error.res : GetLastError(); } else return errno; } // // ANCHOR Error setters // /// Sets the last error code. The module path and line are automatically /// populated. /// Params: /// e = Error code. /// res = External resource (handle, etc.). /// m = Automatically set to `__MODULE__`. /// l = Automatically set to `__LINE__`. /// f = Automatically set to `__FUNCTION__`. /// Returns: Error code int adbg_oops(AdbgError e, void *res = null, string m = __MODULE__, int l = __LINE__, const(char)* f = __FUNCTION__.ptr) { version (Trace) trace("code=%d res=%p caller=%s:%d", e, res, f, l); error.mod = m.ptr; error.line = l; error.res = res; return error.code = e; } // // ANCHOR Error getters // /// Obtain the last set code. /// Returns: Error code. int adbg_errno() { return error.code; } /// Obtain the external error code. /// Returns: Subsystem, library, or OS error code. int adbg_errno_extern() { switch (error.code) with (AdbgError) { case crt: return errno; case os: return adbg_error_system; case libCapstone: if (error.res == null) return 0; return cs_errno(*cast(csh*)error.res); default: return error.code; } } /// Obtain an error message with the last error code set. /// Returns: Error message const(char)* adbg_error_msg(int code = error.code) { switch (error.code) with (AdbgError) { case crt: return strerror(errno); case os: return adbg_sys_error(adbg_error_system()); case libCapstone: if (error.res == null) break; return cs_strerror(cs_errno(*cast(csh*)error.res)); default: foreach (ref e; errors_msg) if (code == e.code) return e.msg; } return defaultMsg; } version (Trace) { import core.stdc.stdio, core.stdc.stdarg; private import adbg.include.d.config : D_FEATURE_PRAGMA_PRINTF; private extern (C) int putchar(int); static if (D_FEATURE_PRAGMA_PRINTF) { /// Trace application pragma(printf) void trace(string func = __FUNCTION__, int line = __LINE__)(const(char) *fmt, ...) { va_list va; va_start(va, fmt); printf("TRACE:%s:%u: ", func.ptr, line); vprintf(fmt, va); putchar('\n'); } } else { /// Trace application void trace(string func = __FUNCTION__, int line = __LINE__)(const(char) *fmt, ...) { va_list va; va_start(va, fmt); printf("TRACE:%s:%u: ", func.ptr, line); vprintf(fmt, va); putchar('\n'); } } }