From c6ad0bbefbeeb4ca17d3deedf7324bb53feb0235 Mon Sep 17 00:00:00 2001
From: dd86k
Date: Thu, 31 Oct 2024 16:21:49 -0400
Subject: [PATCH] Introduce stackframes, "error" command
---
debugger/shell.d | 99 ++++++++++++++++++++-------
src/adbg/debugger.d | 4 +-
src/adbg/process/frame.d | 136 +++++++++++++++++++++++++++++++++++++
src/adbg/process/package.d | 1 +
src/adbg/process/thread.d | 93 ++++++++++++++++++-------
src/adbg/utils/list.d | 5 +-
6 files changed, 283 insertions(+), 55 deletions(-)
create mode 100644 src/adbg/process/frame.d
diff --git a/debugger/shell.d b/debugger/shell.d
index 95f893b..f12afe8 100644
--- a/debugger/shell.d
+++ b/debugger/shell.d
@@ -241,10 +241,9 @@ struct command2_t {
command2_help_section_t[] doc_sections;
int function(int, const(char)**) entry;
}
-// NOTE: Called "commands_list" to avoid conflict with future "command_list" function
// TODO: New commands
// - b|breakpoint: Breakpoint management
-// - sym: Symbol management
+// - sym|symbols: Symbol management
immutable command2_t[] shell_commands = [
//
// Debugger
@@ -476,17 +475,24 @@ immutable command2_t[] shell_commands = [
[ "[ITEM]" ],
MODULE_SHELL, CATEGORY_SHELL,
[
-
],
&command_help,
},
+ {
+ [ "error" ],
+ "Show last error in greater details",
+ [],
+ MODULE_SHELL, CATEGORY_SHELL,
+ [
+ ],
+ &command_error,
+ },
{
[ "version" ],
"Show Alicedbg version.",
[],
MODULE_SHELL, CATEGORY_SHELL,
[
-
],
&command_version,
},
@@ -629,27 +635,39 @@ void shell_event_exception(adbg_process_t *proc, void *udata, adbg_exception_t *
event_tid = adbg_exception_tid(exception);
printf("* Process %d (thread %lld) stopped\n"~
- " Reason : %s ("~ERR_OSFMT~")\n",
+ "* Reason : %s ("~ERR_OSFMT~")\n",
pid, event_tid,
adbg_exception_name(exception), adbg_exception_orig_code(exception));
- // No fault address available
- if (exception.fault_address == 0)
- return;
-
- printf(" Address : 0x%llx\n", exception.fault_address);
-
- char[32] machbuf = void;
- const(char)* mnemonic = void, operands = void;
- if (shell_disassemble(cast(size_t)exception.fault_address,
- null,
- null, 0,
- machbuf.ptr, 32,
- &mnemonic, &operands))
- return;
+ // Fault address available, print it
+ if (exception.fault_address) {
+ printf(" Address : 0x%llx\n", exception.fault_address);
+ char[32] machbuf = void;
+ const(char)* mnemonic = void, operands = void;
+ // Disassembling instruction at fault address passed
+ if (shell_disassemble(cast(size_t)exception.fault_address,
+ null,
+ null, 0,
+ machbuf.ptr, 32,
+ &mnemonic, &operands) == 0) {
+ printf("* Machine : %s\n", machbuf.ptr);
+ printf("* Mnemonic: %s %s\n", mnemonic, operands);
+ }
+ }
- printf(" Machine : %s\n", machbuf.ptr);
- printf(" Mnemonic: %s %s\n", mnemonic, operands);
+ // Print callstack, if available
+ adbg_thread_t *thread = adbg_thread_new(event_tid);
+ if (thread) {
+ void *frames = adbg_frame_list(process, thread);
+ if (frames) {
+ puts("\n* Callstack (WIP):");
+ adbg_stackframe_t *frame = void;
+ for (size_t i; (frame = adbg_frame_list_at(frames, i)) != null; ++i) {
+ printf("%3zu. %llx\n", i, frame.address);
+ }
+ adbg_frame_list_close(frames);
+ }
+ }
}
void shell_event_process_exit(adbg_process_t *proc, void *udata, int code) {
printf("* Process %d exited with code %d\n", adbg_process_id(proc), code);
@@ -1121,7 +1139,7 @@ int command_thread(int argc, const(char) **argv) {
// thread list - get a list of threads
if (strcmp(action, "list") == 0) {
printf("Threads:");
- for (size_t i; (thread = adbg_thread_list_get(process, i)) != null; ++i) {
+ for (size_t i; (thread = adbg_thread_list_get(thrlist, i)) != null; ++i) {
if (i) putchar(',');
printf(" %lld", adbg_thread_id(thread));
}
@@ -1139,7 +1157,7 @@ int command_thread(int argc, const(char) **argv) {
return ShellError.alicedbg;
action = argv[2];
- if (strcmp(action, "registers") == 0 || strcmp(action, "regs") == 0) {
+ if (*action == 'r' || strcmp(action, "registers") == 0) {
int id;
adbg_register_t *register = void;
while ((register = adbg_register_by_id(thread, id++)) != null) {
@@ -1151,10 +1169,28 @@ int command_thread(int argc, const(char) **argv) {
-16, hex.ptr,
dec.ptr);
}
- } else
- return ShellError.invalidParameter;
+ return 0;
+ } else if (*action == 's' || strcmp(action, "stack") == 0) {
+ if (argc < 4) // stack action
+ return ShellError.missingArgument;
+
+ action = argv[3];
+ if (strcmp(action, "show") == 0) { // show thread id stack
+ void *frames = adbg_frame_list(process, thread);
+ if (frames == null)
+ return ShellError.alicedbg;
+
+ adbg_stackframe_t *frame = void;
+ for (size_t i; (frame = adbg_frame_list_at(frames, i)) != null; ++i) {
+ printf("%3zu. %llx\n", i, frame.address);
+ }
+
+ adbg_frame_list_close(frames);
+ return 0;
+ }
+ }
- return 0;
+ return ShellError.invalidCommand;
}
int command_cd(int argc, const(char) **argv) {
@@ -1174,6 +1210,17 @@ int command_pwd(int argc, const(char) **argv) {
return 0;
}
+int command_error(int argc, const(char) **argv) {
+ printf(
+ "Message : %s\n"~
+ "Code : %d\n"~
+ "Caller : %s:L%d\n",
+ adbg_error_message(),
+ adbg_error_code(),
+ adbg_error_function(), adbg_error_line());
+ return 0;
+}
+
int command_version(int argc, const(char) **argv) {
static immutable string LINE = `Alicedbg `~ADBG_VERSION~` `~TARGET_PLATFORM~`-`~TARGET_OS~`-`~TARGET_ENV;
puts(LINE.ptr);
diff --git a/src/adbg/debugger.d b/src/adbg/debugger.d
index 0866b8c..ffacc6f 100644
--- a/src/adbg/debugger.d
+++ b/src/adbg/debugger.d
@@ -238,7 +238,7 @@ version (Windows) {
adbg_oops(AdbgError.assertion);
return null;
}
- version(Trace) trace("args='%s'", proc.args);
+ version(Trace) trace("args='%s'", proc.orig_args);
}
// TODO: Parse envp
@@ -664,7 +664,7 @@ int adbg_debugger_udata(adbg_process_t *proc, void *udata) {
/// Params: proc = Process instancied by the debugger.
/// Returns: Error code.
int adbg_debugger_wait(adbg_process_t *proc) {
- version(Trace) trace("proc=%p udata=%p", proc, udata);
+ version(Trace) trace("proc=%p", proc);
if (proc == null)
return adbg_oops(AdbgError.invalidArgument);
diff --git a/src/adbg/process/frame.d b/src/adbg/process/frame.d
new file mode 100644
index 0000000..e00a7c9
--- /dev/null
+++ b/src/adbg/process/frame.d
@@ -0,0 +1,136 @@
+/// Process frame management.
+///
+/// Stack frames, unwinding operations, etc.
+///
+/// Authors: dd86k
+/// Copyright: © dd86k
+/// License: BSD-3-Clause-Clear
+module adbg.process.frame;
+
+import adbg.error;
+import adbg.machines;
+import adbg.process.base; // for machine info
+import adbg.process.thread; // for accessing thread information
+import adbg.utils.list;
+
+// NOTE: Stack frame layouts
+//
+// # Windows
+//
+// There are basically two layouts: EBP frames and FPO frames.
+//
+// With EBP, EBP points to the previous value, EBP+4 points to the return
+// address, and EBP+8 points to the first stack argument.
+//
+// With FPO (POGO?)... To find out.
+//
+// # Linux
+//
+
+extern (C):
+
+struct adbg_stackframe_t {
+ int level;
+ ulong address;
+ // TODO: Function information
+ // TODO: Line information
+}
+
+private
+struct __machine_pc_reg {
+ AdbgMachine machine;
+ AdbgRegister[] set;
+}
+// Level 0: Current location, typically Program Counter
+// Level 1: Frame Pointer if available
+private
+static immutable __machine_pc_reg[] stackregs = [
+ { AdbgMachine.i386, [ AdbgRegister.x86_eip, AdbgRegister.x86_ebp ] },
+ { AdbgMachine.amd64, [ AdbgRegister.amd64_rip, AdbgRegister.amd64_rbp ] },
+ { AdbgMachine.arm, [ AdbgRegister.arm_pc, AdbgRegister.arm_fp ] },
+ { AdbgMachine.aarch64, [ AdbgRegister.aarch64_pc, AdbgRegister.aarch64_fp ] },
+];
+
+void* adbg_frame_list(adbg_process_t *process, adbg_thread_t *thread) {
+ if (process == null || thread == null) {
+ adbg_oops(AdbgError.invalidArgument);
+ return null;
+ }
+
+ if (adbg_thread_context_update(process, thread))
+ return null;
+
+ // Map a set of registers to use at primary stackframe levels
+ AdbgMachine mach = adbg_process_machine(process);
+ immutable(AdbgRegister)[] registers;
+ foreach (ref regs; stackregs) {
+ if (mach == regs.machine) {
+ registers = regs.set;
+ break;
+ }
+ }
+ if (registers.length == 0) {
+ adbg_oops(AdbgError.unavailable);
+ return null;
+ }
+
+ // New frame list
+ list_t *list = adbg_list_new(adbg_stackframe_t.sizeof, 8);
+ if (list == null)
+ return null;
+
+ // Start with the first frame, which is always PC
+ // If we can't have that, then we cannot even obtain frames at all
+ adbg_register_t *reg = adbg_register_by_id(thread, registers[0]);
+ if (reg == null) {
+ adbg_oops(AdbgError.unavailable);
+ adbg_list_close(list);
+ return null;
+ }
+ ulong *address = cast(ulong*)adbg_register_value(reg);
+ if (address == null || *address == 0) {
+ adbg_list_close(list);
+ return null;
+ }
+ adbg_stackframe_t frame = void;
+ frame.level = 0;
+ frame.address = *address;
+ list = adbg_list_add(list, &frame);
+ if (list == null) {
+ adbg_list_close(list);
+ return null;
+ }
+
+ // Add additional frames from additional registers
+ // As best as we can, otherwise just return the list
+ foreach (AdbgRegister r; registers[1..$]) {
+ reg = adbg_register_by_id(thread, r);
+ if (reg == null)
+ break;
+ address = cast(ulong*)adbg_register_value(reg);
+ if (address == null || *address == 0)
+ break;
+
+ ++frame.level; // frame[0] is level=0, so increment
+ frame.address = *address;
+ list = adbg_list_add(list, &frame);
+ if (list == null) {
+ adbg_list_close(list);
+ return null;
+ }
+ }
+
+ return list;
+}
+
+size_t adbg_frame_list_count(void *list) {
+ return adbg_list_count(cast(list_t*)list);
+}
+
+adbg_stackframe_t* adbg_frame_list_at(void *list, size_t index) {
+ return cast(adbg_stackframe_t*)adbg_list_get(cast(list_t*)list, index);
+}
+
+void adbg_frame_list_close(void *list) {
+ adbg_list_close(cast(list_t*)list);
+}
\ No newline at end of file
diff --git a/src/adbg/process/package.d b/src/adbg/process/package.d
index 0c6e4fb..59013ad 100644
--- a/src/adbg/process/package.d
+++ b/src/adbg/process/package.d
@@ -10,4 +10,5 @@ public import
adbg.process.exception,
adbg.process.breakpoint,
adbg.process.memory,
+ adbg.process.frame,
adbg.process.thread;
\ No newline at end of file
diff --git a/src/adbg/process/thread.d b/src/adbg/process/thread.d
index 984fc18..bf8831b 100644
--- a/src/adbg/process/thread.d
+++ b/src/adbg/process/thread.d
@@ -11,7 +11,8 @@ import adbg.machines : AdbgMachine;
import adbg.error;
import adbg.process.base;
import adbg.utils.list;
-import core.stdc.stdio : snprintf;
+import core.stdc.stdio; // snprintf
+import core.stdc.stdlib;
// TODO: Consider NOT attaching thread list to process
// Affects: adbg_thread_list_update
@@ -45,6 +46,47 @@ struct adbg_thread_t {
adbg_thread_context_t context;
}
+/// Make a new single thread instance by its id.
+/// Params: id = Thread ID.
+/// Returns: New thread instance
+adbg_thread_t* adbg_thread_new(long id) {
+ adbg_thread_t *thread = cast(adbg_thread_t*)malloc(adbg_thread_t.sizeof);
+ if (thread == null) {
+ adbg_oops(AdbgError.crt);
+ return null;
+ }
+ thread.id = id;
+ return thread;
+}
+
+void adbg_thread_close(adbg_thread_t *thread) {
+ if (thread == null) return;
+ free(thread);
+}
+
+/// Get the thread ID out of this thread instance.
+/// Params: thread = Thread instance.
+/// Returns: Thread ID. On error, zero.
+long adbg_thread_id(adbg_thread_t *thread) {
+ version (Trace) trace("thread=%p", thread);
+version (Windows) {
+ if (thread == null) {
+ adbg_oops(AdbgError.invalidArgument);
+ return 0;
+ }
+ return thread.id;
+} else version (Posix) {
+ if (thread == null) {
+ adbg_oops(AdbgError.invalidArgument);
+ return 0;
+ }
+ return thread.id;
+} else {
+ adbg_oops(AdbgError.unimplemented);
+ return 0;
+}
+}
+
/// Update the list of threads for the target process.
/// Params: process = Process instance.
/// Returns: List instance; Otherwise null on error.
@@ -212,7 +254,7 @@ adbg_thread_t* adbg_thread_list_get(void *list, size_t index) {
/// id = Thread ID
/// Returns: Thread instance. On error, null.
adbg_thread_t* adbg_thread_list_by_id(void *list, long id) {
- version (Trace) trace("process=%p id=%lld", process, id);
+ version (Trace) trace("list=%p id=%lld", list, id);
if (list == null) {
adbg_oops(AdbgError.invalidArgument);
return null;
@@ -227,29 +269,6 @@ adbg_thread_t* adbg_thread_list_by_id(void *list, long id) {
return null;
}
-/// Get the thread ID out of this thread instance.
-/// Params: thread = Thread instance.
-/// Returns: Thread ID. On error, zero.
-long adbg_thread_id(adbg_thread_t *thread) {
- version (Trace) trace("thread=%p", thread);
-version (Windows) {
- if (thread == null) {
- adbg_oops(AdbgError.invalidArgument);
- return 0;
- }
- return thread.id;
-} else version (Posix) {
- if (thread == null) {
- adbg_oops(AdbgError.invalidArgument);
- return 0;
- }
- return thread.id;
-} else {
- adbg_oops(AdbgError.unimplemented);
- return 0;
-}
-}
-
//
// Context management
//
@@ -366,6 +385,7 @@ enum AdbgRegister {
/// Register size
enum AdbgRegisterType : ubyte {
+ none,
u8, u16, u32, u64,
f32, f64
}
@@ -547,6 +567,7 @@ adbg_register_t* adbg_register_by_id(adbg_thread_t *thread, int id) {
return &thread.context.items[index];
}
+// get register name
const(char)* adbg_register_name(adbg_register_t *register) {
version (Trace) trace("register=%p", register);
if (register == null) {
@@ -558,6 +579,28 @@ const(char)* adbg_register_name(adbg_register_t *register) {
return register.info.name;
}
+// get register data type
+AdbgRegisterType adbg_register_type(adbg_register_t *register) {
+ version (Trace) trace("register=%p", register);
+ if (register == null) {
+ adbg_oops(AdbgError.invalidArgument);
+ return AdbgRegisterType.none;
+ }
+ assert(register.info);
+ assert(register.info.type);
+ return register.info.type;
+}
+
+// get register data value
+void* adbg_register_value(adbg_register_t *register) {
+ version (Trace) trace("register=%p", register);
+ if (register == null) {
+ adbg_oops(AdbgError.invalidArgument);
+ return null;
+ }
+ return ®ister.u64;
+}
+
// Configure register set, used internally
private
int adbg_thread_context_config(adbg_thread_context_t *ctx, AdbgMachine mach) {
diff --git a/src/adbg/utils/list.d b/src/adbg/utils/list.d
index e2b3034..a85b99f 100644
--- a/src/adbg/utils/list.d
+++ b/src/adbg/utils/list.d
@@ -310,8 +310,9 @@ extern (D) unittest {
/// Free the list.
/// Params: list = List instance.
-void adbg_list_free(list_t *list) {
+void adbg_list_close(list_t *list) {
if (list == null)
return;
free(list);
-}
\ No newline at end of file
+}
+alias adbg_list_free = adbg_list_close;
\ No newline at end of file