From 2d52f710e49d60dea5cb9304dc86bc6a00c63f5b Mon Sep 17 00:00:00 2001
From: dd86k
Date: Sat, 14 Sep 2024 16:06:43 -0400
Subject: [PATCH] Rework Context API (+)
- shell: Remove "regs" command
- shell: Add "registers" subcommand for "thread"
- Remove register module and merge it into thread module
- utils: Add adbg_list_clear
---
debugger/shell.d | 136 ++++------
src/adbg/process/base.d | 23 +-
src/adbg/process/package.d | 1 -
src/adbg/process/register.d | 392 ----------------------------
src/adbg/process/thread.d | 501 ++++++++++++++++++++++++++++++++----
src/adbg/utils/list.d | 32 ++-
6 files changed, 546 insertions(+), 539 deletions(-)
delete mode 100644 src/adbg/process/register.d
diff --git a/debugger/shell.d b/debugger/shell.d
index 88f2889..e6ef921 100644
--- a/debugger/shell.d
+++ b/debugger/shell.d
@@ -165,7 +165,6 @@ __gshared:
adbg_process_t *process;
adbg_disassembler_t *dis;
-adbg_registers_t *registers;
const(char)* last_spawn_exec;
const(char)** last_spawn_argv;
@@ -378,22 +377,6 @@ immutable command2_t[] shell_commands = [
&command_stepi,
},
//
- // Context
- //
- {
- [ "regs" ],
- "Lists register values.",
- [ "[NAME]" ],
- MODULE_DEBUGGER, CATEGORY_CONTEXT,
- [
- {
- SECTION_DESCRIPTION,
- [ "Get list of registers and values from process." ]
- }
- ],
- &command_regs,
- },
- //
// Memory
//
{
@@ -476,7 +459,7 @@ immutable command2_t[] shell_commands = [
{
[ "t", "thread" ],
"Manage process threads.",
- [ "ACTION" ],
+ [ "list", "TID registers" ],
MODULE_DEBUGGER, CATEGORY_PROCESS,
[
{ SECTION_DESCRIPTION,
@@ -797,8 +780,6 @@ int command_detach(int argc, const(char) **argv) {
return ShellError.alicedbg;
}
adbg_dis_close(dis);
- free(registers);
-
return 0;
}
@@ -843,7 +824,6 @@ int command_kill(int argc, const(char) **argv) {
return ShellError.alicedbg;
puts("Process killed");
adbg_dis_close(dis);
- free(registers);
return 0;
}
@@ -856,42 +836,6 @@ int command_stepi(int argc, const(char) **argv) {
return 0;
}
-int command_regs(int argc, const(char) **argv) {
- if (process == null)
- return ShellError.pauseRequired;
-
- if (registers == null) {
- registers = adbg_registers_new(adbg_process_get_machine(process));
- if (registers == null)
- return ShellError.alicedbg;
- }
-
- adbg_registers_fill(registers, process);
-
- if (registers.count == 0) {
- logerror("No registers available");
- return ShellError.unavailable;
- }
-
- adbg_register_t *reg = registers.items.ptr;
- const(char) *rselect = argc >= 2 ? argv[1] : null;
- bool found;
- for (size_t i; i < registers.count; ++i, ++reg) {
- bool show = rselect == null || strcmp(rselect, reg.info.name) == 0;
- if (show == false) continue;
- char[20] normal = void, hexdec = void;
- adbg_register_format(normal.ptr, 20, reg, AdbgRegFormat.dec);
- adbg_register_format(hexdec.ptr, 20, reg, AdbgRegFormat.hexPadded);
- printf("%-8s 0x%8s %s\n", reg.info.name, hexdec.ptr, normal.ptr);
- found = true;
- }
- if (rselect && found == false) {
- logerror("Register not found");
- return ShellError.invalidParameter;
- }
- return 0;
-}
-
int command_memory(int argc, const(char) **argv) {
if (argc < 2) {
return ShellError.missingOption;
@@ -991,27 +935,15 @@ int command_disassemble(int argc, const(char) **argv) {
if (dis == null)
return ShellError.unavailable;
+ // Need address
+ if (argc < 2)
+ return ShellError.missingArgument;
+
long uaddress = void;
- if (argc > 1) { // Address given
- if (unformat64(&uaddress, argv[1]))
- return ShellError.unformat;
- } else { // Address not given, get PC/eIP value
- //TODO: dedicated function to get PC/IP/EIP/RIP value
- // HACK: register list should be allocated on-demand in global space
- AdbgMachine mach = adbg_process_get_machine(process);
- adbg_registers_t regs = void;
- //if (adbg_registers_config(®s, ))
- // return ShellError.alicedbg;
- if (adbg_registers_fill(®s, process))
- return ShellError.alicedbg;
- // Assume EIP/RIP is in regs[0]
- switch (mach) with (AdbgMachine) {
- case i386: uaddress = regs.items[0].u32; break;
- case amd64: uaddress = regs.items[0].u64; break;
- default: return ShellError.unavailable;
- }
- }
+ if (unformat64(&uaddress, argv[1]))
+ return ShellError.unformat;
+ // Number of instruction, default to 10
int ucount = 10;
if (argc >= 3) {
if (unformat(&ucount, argv[2]))
@@ -1226,21 +1158,57 @@ int command_thread(int argc, const(char) **argv) {
if (argc < 2)
return ShellError.missingArgument;
+ adbg_thread_t *thread = void;
+
const(char) *action = argv[1];
+ // thread list - get a list of threads
if (strcmp(action, "list") == 0) {
- void *list = adbg_thread_list(process);
- if (list == null)
+ if (adbg_thread_list_update(process))
return ShellError.alicedbg;
puts("Threads:");
- int tid = void;
- for (size_t i; (tid = adbg_thread_list_get(list, i)) != 0; ++i)
- printf("%d\n", tid);
+ size_t i;
+ while ((thread = adbg_thread_list_get(process, i++)) != null) {
+ printf("%*d\n", -10, adbg_thread_id(thread));
+ }
+ return 0;
+ }
+
+ // Else: thread TID subcommand
+ if (argc < 3) // thread id
+ return ShellError.missingArgument;
+
+ // Select thread
+ int tid = atoi(argv[1]);
+ size_t i;
+ adbg_thread_t *selected;
+ while ((thread = adbg_thread_list_get(process, i++)) != null) {
+ if (adbg_thread_id(thread) == tid) {
+ selected = thread;
+ break;
+ }
+ }
+ if (selected == null)
+ return ShellError.alicedbg;
+
+ action = argv[2];
+ if (strcmp(action, "registers") == 0) {
+ // Update its context
+ adbg_thread_context_update(process, selected);
- adbg_thread_list_free(list);
- } else {
+ i = 0;
+ adbg_register_t *register = void;
+ while ((register = adbg_register_get(thread, i++)) != null) {
+ char[20] dec = void, hex = void;
+ adbg_register_format(dec.ptr, 20, register, AdbgRegisterFormat.dec);
+ adbg_register_format(hex.ptr, 20, register, AdbgRegisterFormat.hexPadded);
+ printf("%*s 0x%*s %s\n",
+ -8, adbg_register_name(register),
+ 8, hex.ptr,
+ dec.ptr);
+ }
+ } else
return ShellError.invalidParameter;
- }
return 0;
}
diff --git a/src/adbg/process/base.d b/src/adbg/process/base.d
index f17787a..25ef6f9 100644
--- a/src/adbg/process/base.d
+++ b/src/adbg/process/base.d
@@ -5,17 +5,18 @@
/// License: BSD-3-Clause-Clear
module adbg.process.base;
-//TODO: Process Pause/Resume
-// Windows: NtSuspendProcess/NtResumeProcess or SuspendThread/ResumeThread
-// Linux: Send SIGSTOP/SIGCONT signals via kill(2)
-//TODO: List threads of process (maybe in a module called threading.d)
+// TODO: Process Pause/Resume
+// Windows: NtSuspendProcess/NtResumeProcess or SuspendThread/ResumeThread
+// Linux: Send SIGSTOP/SIGCONT signals via kill(2)
+// TODO: List threads of process (maybe in a module called threading.d)
import adbg.include.c.stdlib; // malloc, calloc, free, exit;
import adbg.include.c.stdarg;
import adbg.error;
-import adbg.utils.strings;
import adbg.process.exception : adbg_exception_t, adbg_exception_translate;
import adbg.machines;
+import adbg.utils.strings;
+import adbg.utils.list : list_t, adbg_list_free;
import core.stdc.string;
version (Windows) {
@@ -68,13 +69,13 @@ enum ADBG_PROCESS_NAME_LENGTH = 256;
struct adbg_process_t {
version (Windows) { // Original identifiers; Otherwise informal
int pid; /// Process identificiation number
- int tid; /// Thread identification number
+ deprecated int tid; /// Thread identification number
HANDLE hpid; /// Process handle
- HANDLE htid; /// Thread handle
+ deprecated HANDLE htid; /// Thread handle
char *args; /// Saved arguments when process was launched
// TODO: Deprecate and remove wow64 field
// Make function to query process machine type
- version (Win64) int wow64;
+ version (Win64) deprecated int wow64;
}
version (Posix) {
pid_t pid; /// Process ID
@@ -88,6 +89,8 @@ struct adbg_process_t {
AdbgProcStatus status;
/// Process' creation source.
AdbgCreation creation;
+ /// List of threads
+ list_t *thread_list;
//TODO: Deprecate and remove static buffer in process struct
/// Process base module name.
char[ADBG_PROCESS_NAME_LENGTH] name;
@@ -102,6 +105,7 @@ void adbg_process_free(adbg_process_t *proc) {
version (Posix) {
if (proc.argv) free(proc.argv);
}
+ adbg_list_free(proc.thread_list);
free(proc);
}
@@ -283,9 +287,6 @@ version (Windows) {
return 0;
}
}
-unittest {
- //TODO: Test one character buffers
-}
/// Get the current runtime machine platform.
///
diff --git a/src/adbg/process/package.d b/src/adbg/process/package.d
index 3f01295..0c6e4fb 100644
--- a/src/adbg/process/package.d
+++ b/src/adbg/process/package.d
@@ -7,7 +7,6 @@ module adbg.process;
public import
adbg.process.base,
- adbg.process.register,
adbg.process.exception,
adbg.process.breakpoint,
adbg.process.memory,
diff --git a/src/adbg/process/register.d b/src/adbg/process/register.d
deleted file mode 100644
index bbc91d4..0000000
--- a/src/adbg/process/register.d
+++ /dev/null
@@ -1,392 +0,0 @@
-/// Thread register and context handling.
-///
-/// Authors: dd86k
-/// Copyright: © dd86k
-/// License: BSD-3-Clause-Clear
-module adbg.process.register;
-
-import adbg.process.base : AdbgCreation, adbg_process_t;
-import adbg.include.c.stdio : snprintf;
-import adbg.include.c.stdlib;
-import adbg.error;
-import adbg.machines;
-
-// TODO: Support FPU registers (f32, f64, f80 types)
-// TODO: Thread struct type, to hold its context, and other info
-
-version (Windows) {
- import adbg.include.windows.wow64apiset;
- import adbg.include.windows.winnt;
- import core.sys.windows.winbase;
-} else version (Posix) {
- import adbg.include.linux.user;
- import adbg.include.posix.ptrace;
-}
-
-/// Number of registers, used for buffer
-private enum REG_COUNT = 18; // Only x86 support for now
-
-extern (C):
-
-/*
-enum AdbgRegister {
- unknown,
- pc,
- ip = pc,
- eip = pc,
- rip = pc,
-}
-*/
-
-/// Register size
-enum AdbgRegType : ubyte {
- u8, u16, u32, u64,
- f32, f64
-}
-
-/// Register
-enum AdbgRegFormat {
- dec,
- hex,
- hexPadded,
-}
-
-/// Register name and type.
-struct adbg_register_info_t {
- const(char) *name; /// Register name
- AdbgRegType type; /// Register type (size)
-}
-
-/// Register structure, designs a single register for UI ends to understand
-struct adbg_register_t {
- /// Register name and type.
- adbg_register_info_t info;
- union { // Data
- ulong u64; /// Register data: ulong (u64)
- uint u32; /// Register data: uint (u32)
- ushort u16; /// Register data: ushort (u16)
- ubyte u8; /// Register data: ubyte (u8)
- double f64; /// Register data: double (f64)
- float f32; /// Register data: float (f32)
- }
-}
-
-/// Represents a thread context structure with the register values once a
-/// process is paused.
-struct adbg_registers_t {
- /// Register count in registers field.
- ushort count;
- /// Register population, this may depends by platform.
- adbg_register_t[REG_COUNT] items;
- // Associated machine with register set.
- //AdbgMachine machine;
-}
-
-/// Allocate a new set of registers to be populated.
-/// Params: mach = Machine type, determining which set to allocate.
-/// Returns: Register set, or null on errror.
-adbg_registers_t* adbg_registers_new(AdbgMachine mach) {
- adbg_registers_t* regs = cast(adbg_registers_t*)malloc(adbg_registers_t.sizeof);
- if (regs == null) {
- adbg_oops(AdbgError.crt);
- return null;
- }
- if (adbg_registers_config(regs, mach)) {
- free(regs);
- return null;
- }
- return regs;
-}
-
-void adbg_registers_free(adbg_registers_t *regs) {
- if (regs) free(regs);
-}
-
-int adbg_registers_config(adbg_registers_t *ctx, AdbgMachine mach) {
- if (ctx == null)
- return adbg_oops(AdbgError.invalidArgument);
-
- static immutable adbg_register_info_t[] regs_x86 = [
- { "eip", AdbgRegType.u32 },
- { "eflags", AdbgRegType.u32 },
- { "eax", AdbgRegType.u32 },
- { "ebx", AdbgRegType.u32 },
- { "ecx", AdbgRegType.u32 },
- { "edx", AdbgRegType.u32 },
- { "esp", AdbgRegType.u32 },
- { "ebp", AdbgRegType.u32 },
- { "esi", AdbgRegType.u32 },
- { "edi", AdbgRegType.u32 },
- ];
- static immutable adbg_register_info_t[] regs_x86_64 = [
- { "rip", AdbgRegType.u64 },
- { "rflags", AdbgRegType.u64 },
- { "rax", AdbgRegType.u64 },
- { "rbx", AdbgRegType.u64 },
- { "rcx", AdbgRegType.u64 },
- { "rdx", AdbgRegType.u64 },
- { "rsp", AdbgRegType.u64 },
- { "rbp", AdbgRegType.u64 },
- { "rsi", AdbgRegType.u64 },
- { "rdi", AdbgRegType.u64 },
- { "r8", AdbgRegType.u64 },
- { "r9", AdbgRegType.u64 },
- { "r10", AdbgRegType.u64 },
- { "r11", AdbgRegType.u64 },
- { "r12", AdbgRegType.u64 },
- { "r13", AdbgRegType.u64 },
- { "r14", AdbgRegType.u64 },
- { "r15", AdbgRegType.u64 },
- ];
-
- immutable(adbg_register_info_t)[] regs = void;
-
- switch (mach) with (AdbgMachine) {
- case i386:
- regs = regs_x86;
- break;
- case amd64:
- regs = regs_x86_64;
- break;
- default:
- return adbg_oops(AdbgError.unimplemented);
- }
-
- version (Trace) trace("regs.length=%d", cast(int)regs.length);
-
- for (size_t i; i < regs.length; ++i)
- ctx.items[i].info = regs[i];
- ctx.count = cast(ushort)regs.length;
-
- return 0;
-}
-
-/*
-ulong* adbg_registers_get_u64(adbg_registers_t *ctx, AdbgRegister reg) {
-
-}
-*/
-
-int adbg_registers_fill(adbg_registers_t *ctx, adbg_process_t *tracee) {
- version (Trace) trace("tracee=%p ctx=%p", ctx, tracee);
-
- if (tracee == null || ctx == null)
- return adbg_oops(AdbgError.invalidArgument);
-
- if (tracee.creation == AdbgCreation.unloaded)
- return adbg_oops(AdbgError.debuggerUnattached);
-
-version (Windows) {
- CONTEXT winctx = void;
- version (Win64) {
- WOW64_CONTEXT winctxwow64 = void;
- if (tracee.wow64) {
- winctxwow64.ContextFlags = CONTEXT_ALL;
- if (Wow64GetThreadContext(tracee.htid, &winctxwow64) == FALSE) {
- return adbg_oops(AdbgError.os);
- }
- adbg_context_fill_wow64(ctx, &winctxwow64);
- } else {
- winctx.ContextFlags = CONTEXT_ALL;
- if (GetThreadContext(tracee.htid, cast(LPCONTEXT)&winctx) == FALSE) {
- return adbg_oops(AdbgError.os);
- }
- adbg_context_fill_win(ctx, &winctx);
- }
- } else {
- winctx.ContextFlags = CONTEXT_ALL;
- if (GetThreadContext(tracee.htid, cast(LPCONTEXT)&winctx) == FALSE) {
- return adbg_oops(AdbgError.os);
- }
- adbg_context_fill_win(ctx, &winctx);
- }
- return 0;
-} else version (linux) {
- //TODO: PT_GETFPREGS
- // PT_GETWMMXREGS
- // PT_GET_THREAD_AREA
- // PT_GETCRUNCHREGS
- // PT_GETVFPREGS
- // PT_GETHBPREGS
- user_regs_struct u = void;
- if (ptrace(PT_GETREGS, tracee.pid, null, &u) < 0)
- return adbg_oops(AdbgError.os);
-
- adbg_context_fill_linux(ctx, &u);
- return 0;
-} else {
- return adbg_oops(AdbgError.unimplemented);
-}
-}
-
-/// Format a register's value into a string buffer.
-/// Errors: invalidOption for format.
-/// Params:
-/// buffer = Reference to text buffer.
-/// len = Size of buffer.
-/// reg = Register.
-/// format = String format.
-/// Returns: Number of characters written.
-int adbg_register_format(char *buffer, size_t len, adbg_register_t *reg, AdbgRegFormat format) {
- if (reg == null || buffer == null || len == 0)
- return 0;
-
- // Get value
- ulong n = void;
- switch (reg.info.type) with (AdbgRegType) {
- case u8: n = reg.u8; break;
- case u16: n = reg.u16; break;
- case u32: n = reg.u32; break;
- case u64: n = reg.u64; break;
- case f32: *cast(double*)n = reg.f32; break;
- case f64: *cast(double*)n = reg.f64; break;
- default:
- adbg_oops(AdbgError.invalidOption);
- return 0;
- }
-
- // Get format
- const(char) *sformat = void;
- switch (format) with (AdbgRegFormat) {
- case dec:
- switch (reg.info.type) with (AdbgRegType) {
- case u8, u16, u32, u64:
- sformat = "%llu"; break;
- case f32, f64:
- sformat = "%f"; break;
- default:
- adbg_oops(AdbgError.assertion);
- return 0;
- }
- break;
- case hex:
- sformat = "%llx";
- break;
- case hexPadded:
- switch (reg.info.type) with (AdbgRegType) {
- case u8: sformat = "%02x"; break;
- case u16: sformat = "%04x"; break;
- case u32, f32: sformat = "%08x"; break;
- case u64, f64: sformat = "%016llx"; break;
- default:
- adbg_oops(AdbgError.assertion);
- return 0;
- }
- break;
- default:
- adbg_oops(AdbgError.invalidOption);
- return 0;
- }
-
- return snprintf(buffer, len, sformat, n);
-}
-unittest {
- adbg_register_t reg = void;
- reg.info.type = AdbgRegType.u16;
- reg.u16 = 0x1234;
- enum BUFSZ = 16;
- char[BUFSZ] buffer = void;
- int r = adbg_register_format(buffer.ptr, BUFSZ, ®, AdbgRegFormat.hex);
- assert(r == 4);
- assert(buffer[r] == 0);
- assert(buffer[0..r] == "1234");
-}
-
-private:
-
-version (Windows) {
-
-// Populate exception_t.registers array from Windows' CONTEXT
-void adbg_context_fill_win(adbg_registers_t *ctx, CONTEXT *winctx) {
- version (Trace) trace("ctx=%p win=%p", ctx, winctx);
- version (X86) {
- ctx.items[0].u32 = winctx.Eip;
- ctx.items[1].u32 = winctx.EFlags;
- ctx.items[2].u32 = winctx.Eax;
- ctx.items[3].u32 = winctx.Ebx;
- ctx.items[4].u32 = winctx.Ecx;
- ctx.items[5].u32 = winctx.Edx;
- ctx.items[6].u32 = winctx.Esp;
- ctx.items[7].u32 = winctx.Ebp;
- ctx.items[8].u32 = winctx.Esi;
- ctx.items[9].u32 = winctx.Edi;
- } else version (X86_64) {
- ctx.items[0].u64 = winctx.Rip;
- ctx.items[1].u64 = winctx.EFlags;
- ctx.items[2].u64 = winctx.Rax;
- ctx.items[3].u64 = winctx.Rbx;
- ctx.items[4].u64 = winctx.Rcx;
- ctx.items[5].u64 = winctx.Rdx;
- ctx.items[6].u64 = winctx.Rsp;
- ctx.items[7].u64 = winctx.Rbp;
- ctx.items[8].u64 = winctx.Rsi;
- ctx.items[9].u64 = winctx.Rdi;
- ctx.items[10].u64 = winctx.R8;
- ctx.items[11].u64 = winctx.R9;
- ctx.items[12].u64 = winctx.R10;
- ctx.items[13].u64 = winctx.R11;
- ctx.items[14].u64 = winctx.R12;
- ctx.items[15].u64 = winctx.R13;
- ctx.items[16].u64 = winctx.R14;
- ctx.items[17].u64 = winctx.R15;
- }
-}
-
-version (Win64) {
- version (X86_64)
- void adbg_context_fill_wow64(adbg_registers_t *ctx, WOW64_CONTEXT *winctx) {
- version (Trace) trace("ctx=%p win=%p", ctx, winctx);
- ctx.items[0].u32 = winctx.Eip;
- ctx.items[1].u32 = winctx.EFlags;
- ctx.items[2].u32 = winctx.Eax;
- ctx.items[3].u32 = winctx.Ebx;
- ctx.items[4].u32 = winctx.Ecx;
- ctx.items[5].u32 = winctx.Edx;
- ctx.items[6].u32 = winctx.Esp;
- ctx.items[7].u32 = winctx.Ebp;
- ctx.items[8].u32 = winctx.Esi;
- ctx.items[9].u32 = winctx.Edi;
- }
-
- //TODO: Windows WoW64 AArch64 filler
-}
-
-} else version (linux) {
-
-/// Populate exception_t.registers array from user_regs_struct
-void adbg_context_fill_linux(adbg_registers_t *ctx, user_regs_struct *u) {
- version (Trace) trace("ctx=%p u=%p", ctx, u);
- version (X86) {
- ctx.items[0].u32 = u.eip;
- ctx.items[1].u32 = u.eflags;
- ctx.items[2].u32 = u.eax;
- ctx.items[3].u32 = u.ebx;
- ctx.items[4].u32 = u.ecx;
- ctx.items[5].u32 = u.edx;
- ctx.items[6].u32 = u.esp;
- ctx.items[7].u32 = u.ebp;
- ctx.items[8].u32 = u.esi;
- ctx.items[9].u32 = u.edi;
- } else version (X86_64) {
- ctx.items[0].u64 = u.rip;
- ctx.items[1].u64 = u.eflags;
- ctx.items[2].u64 = u.rax;
- ctx.items[3].u64 = u.rbx;
- ctx.items[4].u64 = u.rcx;
- ctx.items[5].u64 = u.rdx;
- ctx.items[6].u64 = u.rsp;
- ctx.items[7].u64 = u.rbp;
- ctx.items[8].u64 = u.rsi;
- ctx.items[9].u64 = u.rdi;
- ctx.items[10].u64 = u.r8;
- ctx.items[11].u64 = u.r9;
- ctx.items[12].u64 = u.r10;
- ctx.items[13].u64 = u.r11;
- ctx.items[14].u64 = u.r12;
- ctx.items[15].u64 = u.r13;
- ctx.items[16].u64 = u.r14;
- ctx.items[17].u64 = u.r15;
- }
-}
-
-} // version linux
diff --git a/src/adbg/process/thread.d b/src/adbg/process/thread.d
index 31958ad..d512c52 100644
--- a/src/adbg/process/thread.d
+++ b/src/adbg/process/thread.d
@@ -1,76 +1,106 @@
/// Thread management.
///
+/// Including stack frame and context information.
+///
/// Authors: dd86k
/// Copyright: © dd86k
/// License: BSD-3-Clause-Clear
module adbg.process.thread;
+import adbg.machines : AdbgMachine;
import adbg.error;
import adbg.process.base;
import adbg.utils.list;
+import core.stdc.stdlib : malloc, free;
+import core.stdc.stdio : snprintf;
version (Windows) {
import adbg.include.windows.tlhelp32;
- import core.sys.windows.basetsd : HANDLE;
- import core.sys.windows.winbase : INVALID_HANDLE_VALUE, CloseHandle;
- import core.sys.windows.windef : FALSE;
+ import adbg.include.windows.wow64apiset;
+ import adbg.include.windows.winnt;
+ import core.sys.windows.basetsd;
+ import core.sys.windows.winbase;
+ import core.sys.windows.windef;
} else version (linux) {
import core.stdc.ctype : isdigit;
import core.stdc.stdio : snprintf;
import core.stdc.stdlib : atoi;
import core.sys.posix.dirent;
import core.sys.posix.libgen : basename;
+ import adbg.include.linux.user;
+ import adbg.include.posix.ptrace;
+}
+
+extern (C):
+
+struct adbg_thread_t {
+version (Windows) {
+ HANDLE handle;
+ int id;
+}
+version (linux) {
+ pid_t handle;
+}
+ adbg_thread_context_t context;
}
/// Get a list of threads for target process.
/// Params: process = Process.
/// Returns: Thread list.
-void* adbg_thread_list(adbg_process_t *process) {
+int adbg_thread_list_update(adbg_process_t *process) {
version (Windows) {
- if (process == null) {
- adbg_oops(AdbgError.invalidArgument);
- return null;
- }
- if (process.pid == 0) {
- adbg_oops(AdbgError.assertion);
- return null;
- }
+ if (process == null)
+ return adbg_oops(AdbgError.invalidArgument);
+ if (process.pid == 0)
+ return adbg_oops(AdbgError.uninitiated);
- list_t *list = adbg_list_new(int.sizeof, 16);
- if (list == null) {
- adbg_oops(AdbgError.crt);
- return null;
- }
+ if (process.thread_list == null)
+ process.thread_list = adbg_list_new(adbg_thread_t.sizeof, 32);
+ if (process.thread_list == null)
+ return adbg_errno();
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, process.pid);
if (snap == INVALID_HANDLE_VALUE) {
- adbg_oops(AdbgError.os);
- adbg_list_free(list);
- return null;
+ adbg_list_free(process.thread_list);
+ return adbg_oops(AdbgError.os);
}
scope(exit) CloseHandle(snap);
THREADENTRY32 te32 = void;
te32.dwSize = THREADENTRY32.sizeof;
if (Thread32First(snap, &te32) == FALSE) {
- adbg_oops(AdbgError.os);
- adbg_list_free(list);
- return null;
+ adbg_list_free(process.thread_list);
+ return adbg_oops(AdbgError.os);
}
+ adbg_list_clear(process.thread_list);
+ adbg_thread_t t = void;
+ adbg_thread_context_config(&t.context, adbg_process_get_machine(process));
do {
if (te32.th32OwnerProcessID != process.pid)
continue;
- list = adbg_list_add(list, &te32.th32ThreadID);
- if (list == null) {
- adbg_oops(AdbgError.crt);
- adbg_list_free(list);
- return null;
+ enum THREAD_ACCESS = SYNCHRONIZE | THREAD_QUERY_INFORMATION |
+ THREAD_GET_CONTEXT | THREAD_SET_CONTEXT |
+ THREAD_SUSPEND_RESUME;
+
+ // Get thread handle
+ t.handle = OpenThread(THREAD_ACCESS, FALSE, te32.th32ThreadID);
+ if (t.handle == INVALID_HANDLE_VALUE) {
+ adbg_list_free(process.thread_list);
+ return adbg_oops(AdbgError.os);
+ }
+
+ // Set thread ID
+ t.id = te32.th32ThreadID;
+ process.thread_list = adbg_list_add(process.thread_list, &t);
+ if (process.thread_list == null) {
+ adbg_list_free(process.thread_list);
+ return adbg_errno();
}
} while (Thread32Next(snap, &te32));
- return list;
+ return 0;
} else version (linux) {
if (process == null) {
adbg_oops(AdbgError.invalidArgument);
@@ -81,8 +111,9 @@ version (Windows) {
return null;
}
- list_t *list = adbg_list_new(int.sizeof, 16);
- if (list == null) {
+ if (process.thread_list == null)
+ process.thread_list = adbg_list_new(adbg_thread_t.sizeof, 32);
+ if (process.thread_list == null) {
adbg_oops(AdbgError.crt);
return null;
}
@@ -100,42 +131,61 @@ version (Windows) {
adbg_oops(AdbgError.crt);
return null;
}
- scope (exit) closedir(procfd);
+ scope(exit) closedir(procfd);
// Go through kernel thread IDs
+ adbg_list_clear(process.thread_list);
+ adbg_thread_t t = void;
+ adbg_thread_context_config(&t.context, adbg_process_get_machine(process));
for (dirent *entry = void; (entry = readdir(procfd)) != null;) {
version (Trace) trace("entry=%s", entry.d_name.ptr);
- // readdir() includes "." and ".."
+ // readdir() includes "." and "..", skip them
if (isdigit(entry.d_name[0]) == 0)
continue;
int tid = atoi( basename(entry.d_name.ptr) );
- list = adbg_list_add(list, &tid);
- if (list == null) {
- adbg_oops(AdbgError.crt);
- adbg_list_free(list);
+ process.thread_list = adbg_list_add(process.thread_list, &tid);
+ if (process.thread_list == null) {
+ adbg_list_free(process.thread_list);
return null;
}
}
- return list;
+ return 0;
} else {
- adbg_oops(AdbgError.unimplemented);
- return null;
+ return adbg_oops(AdbgError.unimplemented);
}
}
-/// Get thread ID from list using index.
+/// Get thread from list using index.
/// Params:
-/// list = Thread list instance.
+/// proc = Process instance.
/// index = Zero-based index.
-/// Returns: Thread ID. Zero means an error occured or end of list.
-int adbg_thread_list_get(void *list, size_t index) {
- if (list == null)
+/// Returns: Thread instance. On error, null.
+adbg_thread_t* adbg_thread_list_get(adbg_process_t *proc, size_t index) {
+ if (proc == null) {
+ adbg_oops(AdbgError.invalidArgument);
+ return null;
+ }
+ // NOTE: adbg_list_get checks both list pointer and index and sets error
+ return cast(adbg_thread_t*)adbg_list_get(proc.thread_list, index);
+}
+
+/// Get the thread ID out of this thread instance.
+/// Params: thread = Thread instance.
+/// Returns: Thread ID. On error, zero.
+int adbg_thread_id(adbg_thread_t *thread) {
+ if (thread == null) {
+ adbg_oops(AdbgError.invalidArgument);
return 0;
- int *tid = cast(int*)adbg_list_get(cast(list_t*)list, index);
- return tid ? *tid : 0;
+ }
+ version (Windows) return thread.id;
+ else version (linux) return cast(int)thread.handle;
+ else {
+ adbg_oops(AdbgError.unimplemented);
+ return 0;
+ }
}
/// Close the thread list instance.
@@ -145,3 +195,360 @@ void adbg_thread_list_free(void *list) {
return;
adbg_list_free(cast(list_t*)list);
}
+
+//
+// Context management
+//
+
+// TODO: Support FPU registers (f32, f64, f80 types)
+// TODO: Thread struct type, to hold its context, and other info
+
+enum AdbgRegister {
+ // x86
+ eip = 0, /// (x86) Extended Instruction Pointer
+ eflags = 1, /// (x86) Extended FLAGS
+ eax = 2, /// (x86)
+ ebx = 3, /// (x86)
+ ecx = 4, /// (x86)
+ edx = 5, /// (x86)
+ esp = 6, /// (x86) Extended Stack Pointer (top of stack)
+ ebp = 7, /// (x86) Extended Base Pointer (start of function)
+ esi = 8, /// (x86) Extended Source Index
+ edi = 9, /// (x86) Extended Destination Index
+
+ // x86-64
+ rip = 0, /// (x86-64) Re-extended Intruction Pointer
+ rflags = 1, /// (x86-64) Re-extended FLAGS
+ rax = 2, /// (x86-64)
+ rbx = 3, /// (x86-64)
+ rcx = 4, /// (x86-64)
+ rdx = 5, /// (x86-64)
+ rsp = 6, /// (x86-64) Re-extended Stack Pointer (top of stack)
+ rbp = 7, /// (x86-64) Re-extended Base Pointer (start of function)
+ rsi = 8, /// (x86-64) Re-extended Source Index
+ rdi = 9, /// (x86-64) Re-extended Destination Index
+ r8 = 10, /// (x86-64)
+ r9 = 11, /// (x86-64)
+ r10 = 12, /// (x86-64)
+ r11 = 13, /// (x86-64)
+ r12 = 14, /// (x86-64)
+ r13 = 15, /// (x86-64)
+ r14 = 16, /// (x86-64)
+ r15 = 17, /// (x86-64)
+}
+
+/// Buffer size for register set.
+private enum REG_COUNT = AdbgRegister.max + 1;
+
+/// Register size
+enum AdbgRegisterType : ubyte {
+ u8, u16, u32, u64,
+ f32, f64
+}
+
+/// Register
+enum AdbgRegisterFormat {
+ dec,
+ hex,
+ hexPadded,
+}
+
+/// Register name and type.
+struct adbg_register_info_t {
+ const(char) *name; /// Register name
+ AdbgRegisterType type; /// Register type (size)
+}
+
+/// Register structure, designs a single register for UI ends to understand
+struct adbg_register_t {
+ /// Register name and type.
+ adbg_register_info_t info;
+ union { // Data
+ ulong u64; /// Register data: ulong (u64)
+ uint u32; /// Register data: uint (u32)
+ ushort u16; /// Register data: ushort (u16)
+ ubyte u8; /// Register data: ubyte (u8)
+ double f64; /// Register data: double (f64)
+ float f32; /// Register data: float (f32)
+ }
+}
+
+/// Represents a thread context structure with the register values once a
+/// process is paused.
+struct adbg_thread_context_t {
+ /// Register count in registers field.
+ size_t count;
+ /// Register population, this may depends by platform.
+ adbg_register_t[REG_COUNT] items;
+}
+
+// Register sets
+private immutable adbg_register_info_t[] regset_x86 = [
+ { "eip", AdbgRegisterType.u32 },
+ { "eflags", AdbgRegisterType.u32 },
+ { "eax", AdbgRegisterType.u32 },
+ { "ebx", AdbgRegisterType.u32 },
+ { "ecx", AdbgRegisterType.u32 },
+ { "edx", AdbgRegisterType.u32 },
+ { "esp", AdbgRegisterType.u32 },
+ { "ebp", AdbgRegisterType.u32 },
+ { "esi", AdbgRegisterType.u32 },
+ { "edi", AdbgRegisterType.u32 },
+];
+private immutable adbg_register_info_t[] regset_x86_64 = [
+ { "rip", AdbgRegisterType.u64 },
+ { "rflags", AdbgRegisterType.u64 },
+ { "rax", AdbgRegisterType.u64 },
+ { "rbx", AdbgRegisterType.u64 },
+ { "rcx", AdbgRegisterType.u64 },
+ { "rdx", AdbgRegisterType.u64 },
+ { "rsp", AdbgRegisterType.u64 },
+ { "rbp", AdbgRegisterType.u64 },
+ { "rsi", AdbgRegisterType.u64 },
+ { "rdi", AdbgRegisterType.u64 },
+ { "r8", AdbgRegisterType.u64 },
+ { "r9", AdbgRegisterType.u64 },
+ { "r10", AdbgRegisterType.u64 },
+ { "r11", AdbgRegisterType.u64 },
+ { "r12", AdbgRegisterType.u64 },
+ { "r13", AdbgRegisterType.u64 },
+ { "r14", AdbgRegisterType.u64 },
+ { "r15", AdbgRegisterType.u64 },
+];
+
+adbg_register_t* adbg_register_get(adbg_thread_t *thread, size_t i) {
+ if (thread == null) {
+ adbg_oops(AdbgError.invalidArgument);
+ return null;
+ }
+
+ if (i >= thread.context.count) {
+ adbg_oops(AdbgError.indexBounds);
+ return null;
+ }
+
+ return &thread.context.items[i];
+}
+
+const(char)* adbg_register_name(adbg_register_t *register) {
+ if (register == null) {
+ adbg_oops(AdbgError.invalidArgument);
+ return null;
+ }
+ return register.info.name;
+}
+
+// Configure register set, used internally
+private
+int adbg_thread_context_config(adbg_thread_context_t *ctx, AdbgMachine mach) {
+ if (ctx == null)
+ return adbg_oops(AdbgError.invalidArgument);
+
+ // Select register set
+ immutable(adbg_register_info_t)[] regs = void;
+ switch (mach) with (AdbgMachine) {
+ case i386: regs = regset_x86; break;
+ case amd64: regs = regset_x86_64; break;
+ default: return adbg_oops(AdbgError.unimplemented);
+ }
+
+ version (Trace) trace("regs.length=%d", cast(int)regs.length);
+ for (size_t i; i < regs.length; ++i)
+ ctx.items[i].info = regs[i];
+ ctx.count = regs.length;
+ return 0;
+}
+
+// Update the context for thread
+int adbg_thread_context_update(adbg_process_t *proc, adbg_thread_t *thread) {
+ version (Trace) trace("tracee=%p ctx=%p", ctx, tracee);
+
+ if (proc == null || thread == null)
+ return adbg_oops(AdbgError.invalidArgument);
+
+version (Win64) {
+ AdbgMachine mach = adbg_process_get_machine(proc);
+ switch (mach) {
+ case AdbgMachine.amd64:
+ CONTEXT_X64 winctx = void; // CONTEXT
+ winctx.ContextFlags = CONTEXT_ALL;
+ if (GetThreadContext(thread.handle, cast(LPCONTEXT)&winctx) == FALSE) {
+ return adbg_oops(AdbgError.os);
+ }
+ thread.context.items[0].u64 = winctx.Rip;
+ thread.context.items[1].u64 = winctx.EFlags;
+ thread.context.items[2].u64 = winctx.Rax;
+ thread.context.items[3].u64 = winctx.Rbx;
+ thread.context.items[4].u64 = winctx.Rcx;
+ thread.context.items[5].u64 = winctx.Rdx;
+ thread.context.items[6].u64 = winctx.Rsp;
+ thread.context.items[7].u64 = winctx.Rbp;
+ thread.context.items[8].u64 = winctx.Rsi;
+ thread.context.items[9].u64 = winctx.Rdi;
+ thread.context.items[10].u64 = winctx.R8;
+ thread.context.items[11].u64 = winctx.R9;
+ thread.context.items[12].u64 = winctx.R10;
+ thread.context.items[13].u64 = winctx.R11;
+ thread.context.items[14].u64 = winctx.R12;
+ thread.context.items[15].u64 = winctx.R13;
+ thread.context.items[16].u64 = winctx.R14;
+ thread.context.items[17].u64 = winctx.R15;
+ break;
+ case AdbgMachine.i386: // WoW64 process
+ WOW64_CONTEXT winctxwow64 = void;
+ winctxwow64.ContextFlags = CONTEXT_ALL;
+ if (Wow64GetThreadContext(thread.handle, &winctxwow64) == FALSE) {
+ return adbg_oops(AdbgError.os);
+ }
+ thread.context.items[0].u32 = winctxwow64.Eip;
+ thread.context.items[1].u32 = winctxwow64.EFlags;
+ thread.context.items[2].u32 = winctxwow64.Eax;
+ thread.context.items[3].u32 = winctxwow64.Ebx;
+ thread.context.items[4].u32 = winctxwow64.Ecx;
+ thread.context.items[5].u32 = winctxwow64.Edx;
+ thread.context.items[6].u32 = winctxwow64.Esp;
+ thread.context.items[7].u32 = winctxwow64.Ebp;
+ thread.context.items[8].u32 = winctxwow64.Esi;
+ thread.context.items[9].u32 = winctxwow64.Edi;
+ break;
+ default:
+ return adbg_oops(AdbgError.objectInvalidMachine);
+ }
+ return 0;
+} else version (Win32) {
+ X86_NT_CONTEXT winctx = void; // CONTEXT
+ winctx.ContextFlags = CONTEXT_ALL;
+ if (GetThreadContext(tracee.htid, cast(LPCONTEXT)&winctx) == FALSE) {
+ return adbg_oops(AdbgError.os);
+ }
+ thread.context.items[0].u32 = winctx.Eip;
+ thread.context.items[1].u32 = winctx.EFlags;
+ thread.context.items[2].u32 = winctx.Eax;
+ thread.context.items[3].u32 = winctx.Ebx;
+ thread.context.items[4].u32 = winctx.Ecx;
+ thread.context.items[5].u32 = winctx.Edx;
+ thread.context.items[6].u32 = winctx.Esp;
+ thread.context.items[7].u32 = winctx.Ebp;
+ thread.context.items[8].u32 = winctx.Esi;
+ thread.context.items[9].u32 = winctx.Edi;
+ return 0;
+} else version (linux) {
+ //TODO: PT_GETFPREGS
+ // PT_GETWMMXREGS
+ // PT_GET_THREAD_AREA
+ // PT_GETCRUNCHREGS
+ // PT_GETVFPREGS
+ // PT_GETHBPREGS
+ user_regs_struct u = void;
+ if (ptrace(PT_GETREGS, tracee.pid, null, &u) < 0)
+ return adbg_oops(AdbgError.os);
+
+ version (X86) {
+ ctx.items[0].u32 = u.eip;
+ ctx.items[1].u32 = u.eflags;
+ ctx.items[2].u32 = u.eax;
+ ctx.items[3].u32 = u.ebx;
+ ctx.items[4].u32 = u.ecx;
+ ctx.items[5].u32 = u.edx;
+ ctx.items[6].u32 = u.esp;
+ ctx.items[7].u32 = u.ebp;
+ ctx.items[8].u32 = u.esi;
+ ctx.items[9].u32 = u.edi;
+ } else version (X86_64) {
+ ctx.items[0].u64 = u.rip;
+ ctx.items[1].u64 = u.eflags;
+ ctx.items[2].u64 = u.rax;
+ ctx.items[3].u64 = u.rbx;
+ ctx.items[4].u64 = u.rcx;
+ ctx.items[5].u64 = u.rdx;
+ ctx.items[6].u64 = u.rsp;
+ ctx.items[7].u64 = u.rbp;
+ ctx.items[8].u64 = u.rsi;
+ ctx.items[9].u64 = u.rdi;
+ ctx.items[10].u64 = u.r8;
+ ctx.items[11].u64 = u.r9;
+ ctx.items[12].u64 = u.r10;
+ ctx.items[13].u64 = u.r11;
+ ctx.items[14].u64 = u.r12;
+ ctx.items[15].u64 = u.r13;
+ ctx.items[16].u64 = u.r14;
+ ctx.items[17].u64 = u.r15;
+ }
+ return 0;
+} else {
+ return adbg_oops(AdbgError.unimplemented);
+}
+}
+
+/// Format a register's value into a string buffer.
+/// Errors: invalidOption for format.
+/// Params:
+/// buffer = Reference to text buffer.
+/// len = Size of buffer.
+/// reg = Register.
+/// format = String format.
+/// Returns: Number of characters written.
+int adbg_register_format(char *buffer, size_t len, adbg_register_t *reg, AdbgRegisterFormat format) {
+ if (reg == null || buffer == null || len == 0)
+ return 0;
+
+ // Get value
+ ulong n = void;
+ switch (reg.info.type) with (AdbgRegisterType) {
+ case u8: n = reg.u8; break;
+ case u16: n = reg.u16; break;
+ case u32: n = reg.u32; break;
+ case u64: n = reg.u64; break;
+ case f32: *cast(double*)n = reg.f32; break;
+ case f64: *cast(double*)n = reg.f64; break;
+ default:
+ adbg_oops(AdbgError.invalidOption);
+ return 0;
+ }
+
+ // Get format
+ const(char) *sformat = void;
+ switch (format) with (AdbgRegisterFormat) {
+ case dec:
+ switch (reg.info.type) with (AdbgRegisterType) {
+ case u8, u16, u32, u64:
+ sformat = "%llu"; break;
+ case f32, f64:
+ sformat = "%f"; break;
+ default:
+ adbg_oops(AdbgError.assertion);
+ return 0;
+ }
+ break;
+ case hex:
+ sformat = "%llx";
+ break;
+ case hexPadded:
+ switch (reg.info.type) with (AdbgRegisterType) {
+ case u8: sformat = "%02x"; break;
+ case u16: sformat = "%04x"; break;
+ case u32, f32: sformat = "%08x"; break;
+ case u64, f64: sformat = "%016llx"; break;
+ default:
+ adbg_oops(AdbgError.assertion);
+ return 0;
+ }
+ break;
+ default:
+ adbg_oops(AdbgError.invalidOption);
+ return 0;
+ }
+
+ return snprintf(buffer, len, sformat, n);
+}
+unittest {
+ adbg_register_t reg = void;
+ reg.info.type = AdbgRegType.u16;
+ reg.u16 = 0x1234;
+ enum BUFSZ = 16;
+ char[BUFSZ] buffer = void;
+ int r = adbg_register_format(buffer.ptr, BUFSZ, ®, AdbgRegFormat.hex);
+ assert(r == 4);
+ assert(buffer[r] == 0);
+ assert(buffer[0..r] == "1234");
+}
diff --git a/src/adbg/utils/list.d b/src/adbg/utils/list.d
index 0ec4181..e334d29 100644
--- a/src/adbg/utils/list.d
+++ b/src/adbg/utils/list.d
@@ -15,9 +15,14 @@ struct list_t {
size_t capacity; /// Capacity in number of items
size_t itemsize; /// Size of one item
size_t count; /// Current item count
- void *buffer;
+ void *buffer; /// Item buffer
}
+/// Allocate a new dynamic list.
+/// Params:
+/// itemsize = Size of one item, in bytes.
+/// capacity = Initial capacity size, in number of items.
+/// Returns: List instance. On error, null is returned.
list_t* adbg_list_new(size_t itemsize, size_t capacity) {
size_t isize = itemsize * capacity; // initial size
list_t *list = cast(list_t*)malloc(list_t.sizeof + isize);
@@ -32,16 +37,28 @@ list_t* adbg_list_new(size_t itemsize, size_t capacity) {
return list;
}
+/// Set the counter to zero.
+///
+/// For performance reasons, this function does not clear memory.
+/// Params: list = List instance.
+void adbg_list_clear(list_t *list) {
+ if (list == null)
+ return;
+ list.count = 0;
+}
+
/// Add an item to the list.
///
/// Item data is copied into the list.
///
/// If the returned list pointer is null, no content was changed, but an error occured.
/// You are free to either close the list or continue with the current, unchanged list.
+///
+/// Due to realloc(3), it is important to set the new list pointer.
/// Params:
/// list = List instance.
/// item = Item.
-/// Returns: List instance pointer.
+/// Returns: List instance pointer. On error, null is returned.
list_t* adbg_list_add(list_t *list, void *item) {
if (list == null) {
adbg_oops(AdbgError.invalidArgument);
@@ -51,15 +68,15 @@ list_t* adbg_list_add(list_t *list, void *item) {
// Increase capacity
if (list.count >= list.capacity) {
size_t newcapacity = list.capacity << 1; // double capacity
+ // NOTE: MSVC will always assign a new memory block
list = cast(list_t*)realloc(list, list_t.sizeof + (list.itemsize * newcapacity));
if (list == null) {
adbg_oops(AdbgError.crt);
return null;
}
- // Assuming realloc(3) copied data to new region if address changes...
+ // realloc(3) should have copied data to new block
// Only need to readjust buffer pointer
- list.capacity = newcapacity;
list.buffer = cast(void*)list + list_t.sizeof;
}
@@ -69,6 +86,11 @@ list_t* adbg_list_add(list_t *list, void *item) {
return list;
}
+/// Get an item at this index.
+/// Params:
+/// list = List instance.
+/// index = Item index.
+/// Returns: Item pointer. On error, null is returned.
void* adbg_list_get(list_t *list, size_t index) {
if (list == null) {
adbg_oops(AdbgError.invalidArgument);
@@ -81,6 +103,8 @@ void* adbg_list_get(list_t *list, size_t index) {
return list.buffer + (list.itemsize * index);
}
+/// Free the list.
+/// Params: list = List instance.
void adbg_list_free(list_t *list) {
if (list == null)
return;