Skip to content

Commit

Permalink
Improve adbg_process_machine
Browse files Browse the repository at this point in the history
- Support AArch64
- Support Linux
  • Loading branch information
dd86k committed Sep 17, 2024
1 parent 2e9c3b7 commit b7a9901
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 55 deletions.
4 changes: 2 additions & 2 deletions debugger/shell.d
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ int shell_proc_spawn(const(char) *exec, const(char) **argv) {
putchar('\n');

// Open disassembler for process machine type
dis = adbg_dis_open(adbg_process_get_machine(process));
dis = adbg_dis_open(adbg_process_machine(process));
if (dis == null)
logwarn("Disassembler not available (%s)", adbg_error_message());

Expand All @@ -546,7 +546,7 @@ int shell_proc_attach(int pid) {
puts("Debugger attached.");

// Open disassembler for process machine type
dis = adbg_dis_open(adbg_process_get_machine(process));
dis = adbg_dis_open(adbg_process_machine(process));
if (dis) {
if (opt_syntax)
adbg_dis_options(dis, AdbgDisOpt.syntax, opt_syntax, 0);
Expand Down
2 changes: 1 addition & 1 deletion examples/simple.d
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ int main(int argc, const(char) **argv) {
if (process == null)
die;

dis = adbg_dis_open(adbg_process_get_machine(process));
dis = adbg_dis_open(adbg_process_machine(process));
if (dis == null)
printf("warning: Disassembler unavailable (%s)\n", adbg_error_message());

Expand Down
52 changes: 18 additions & 34 deletions src/adbg/debugger.d
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ version (Windows) {
import adbg.include.windows.psapi_dyn;
import adbg.include.windows.winnt;
import core.sys.windows.winbase;
import adbg.machines;
} else version (Posix) {
import adbg.include.posix.ptrace;
import adbg.include.posix.unistd;
Expand Down Expand Up @@ -232,23 +233,6 @@ version (Windows) {
proc.pid = pi.dwProcessId;
proc.tid = pi.dwThreadId;

// Microsoft recommends getting function pointer with
// GetProcAddress("kernel32", "IsWow64Process"), but so far
// all 64-bit versions of Windows have WOW64 (does Embedded too?).
// Nevertheless, required to support 32-bit processes under
// 64-bit builds.
//TODO: IsWow64Process2 support
// with GetProcAddress("kernel32", "IsWow64Process2")
// Introduced in Windows 10, version 1511
// IsWow64Process: 32-bit proc. under aarch64 returns FALSE
// NOTE: Could be moved to adbg_process_get_machine
version (Win64)
if (IsWow64Process(proc.hpid, &proc.wow64) == FALSE) {
adbg_process_free(proc);
adbg_oops(AdbgError.os);
return null;
}

proc.status = AdbgProcStatus.standby;
proc.creation = AdbgCreation.spawned;
return proc;
Expand Down Expand Up @@ -773,49 +757,49 @@ version (Windows) {
}

/// Performs an instruction step for the debuggee process.
/// Params: tracee = Process being debugged.
/// Params: proc = Process being debugged.
/// Returns: Error code.
int adbg_debugger_stepi(adbg_process_t *tracee) {
if (tracee == null)
int adbg_debugger_stepi(adbg_process_t *proc) {
if (proc == null)
return adbg_oops(AdbgError.invalidArgument);
if (tracee.creation == AdbgCreation.unloaded)
if (proc.creation == AdbgCreation.unloaded)
return adbg_oops(AdbgError.debuggerUnattached);

version (Windows) {
enum EFLAGS_TF = 0x100;

// Enable single-stepping via Trap flag
version (Win64)
if (tracee.wow64) {
if (adbg_process_machine(proc) == AdbgMachine.i386) {
WOW64_CONTEXT winctxwow64 = void;
winctxwow64.ContextFlags = CONTEXT_CONTROL;
Wow64GetThreadContext(tracee.htid, &winctxwow64);
Wow64GetThreadContext(proc.htid, &winctxwow64);
winctxwow64.EFlags |= EFLAGS_TF;
Wow64SetThreadContext(tracee.htid, &winctxwow64);
FlushInstructionCache(tracee.hpid, null, 0);
Wow64SetThreadContext(proc.htid, &winctxwow64);
FlushInstructionCache(proc.hpid, null, 0);

return adbg_debugger_continue(tracee);
return adbg_debugger_continue(proc);
}

// X86, AMD64
CONTEXT winctx = void;
winctx.ContextFlags = CONTEXT_ALL;
GetThreadContext(tracee.htid, cast(LPCONTEXT)&winctx);
GetThreadContext(proc.htid, cast(LPCONTEXT)&winctx);
winctx.EFlags |= EFLAGS_TF;
SetThreadContext(tracee.htid, cast(LPCONTEXT)&winctx);
FlushInstructionCache(tracee.hpid, null, 0);
SetThreadContext(proc.htid, cast(LPCONTEXT)&winctx);
FlushInstructionCache(proc.hpid, null, 0);

return adbg_debugger_continue(tracee);
return adbg_debugger_continue(proc);
} else version (linux) {
if (ptrace(PT_SINGLESTEP, tracee.pid, null, null) < 0) {
tracee.status = AdbgProcStatus.unknown;
if (ptrace(PT_SINGLESTEP, proc.pid, null, null) < 0) {
proc.status = AdbgProcStatus.unknown;
return adbg_oops(AdbgError.os);
}

return 0;
} else {
if (ptrace(PT_STEP, tracee.pid, null, 0) < 0) {
tracee.status = AdbgProcStatus.unknown;
if (ptrace(PT_STEP, proc.pid, null, 0) < 0) {
proc.status = AdbgProcStatus.unknown;
return adbg_oops(AdbgError.os);
}

Expand Down
71 changes: 71 additions & 0 deletions src/adbg/include/linux/personality.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/// Bindings for personality header (personality.h).
///
/// Source: include/uapi/linux/personality.h
///
/// Authors: dd86k <dd@dax.moe>
/// Copyright: © dd86k <dd@dax.moe>
/// License: BSD-3-Clause-Clear
module adbg.include.linux.personality;

version (linux):

/*
* Flags for bug emulation.
*
* These occupy the top three bytes.
*/
enum {
UNAME26 = 0x0020000,
ADDR_NO_RANDOMIZE = 0x0040000, /* disable randomization of VA space */
FDPIC_FUNCPTRS = 0x0080000, /* userspace function ptrs point to descriptors
* (signal handling)
*/
MMAP_PAGE_ZERO = 0x0100000,
ADDR_COMPAT_LAYOUT = 0x0200000,
READ_IMPLIES_EXEC = 0x0400000,
ADDR_LIMIT_32BIT = 0x0800000,
SHORT_INODE = 0x1000000,
WHOLE_SECONDS = 0x2000000,
STICKY_TIMEOUTS = 0x4000000,
ADDR_LIMIT_3GB = 0x8000000,
}

/*
* Security-relevant compatibility flags that must be
* cleared upon setuid or setgid exec:
*/
enum PER_CLEAR_ON_SETID = READ_IMPLIES_EXEC | ADDR_NO_RANDOMIZE |
ADDR_COMPAT_LAYOUT | MMAP_PAGE_ZERO;

/*
* Personality types.
*
* These go in the low byte. Avoid using the top bit, it will
* conflict with error returns.
*/
enum {
PER_LINUX = 0x0000,
PER_LINUX_32BIT = 0x0000 | ADDR_LIMIT_32BIT,
PER_LINUX_FDPIC = 0x0000 | FDPIC_FUNCPTRS,
PER_SVR4 = 0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
PER_SVR3 = 0x0002 | STICKY_TIMEOUTS | SHORT_INODE,
PER_SCOSVR3 = 0x0003 | STICKY_TIMEOUTS |
WHOLE_SECONDS | SHORT_INODE,
PER_OSR5 = 0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS,
PER_WYSEV386 = 0x0004 | STICKY_TIMEOUTS | SHORT_INODE,
PER_ISCR4 = 0x0005 | STICKY_TIMEOUTS,
PER_BSD = 0x0006,
PER_SUNOS = 0x0006 | STICKY_TIMEOUTS,
PER_XENIX = 0x0007 | STICKY_TIMEOUTS | SHORT_INODE,
PER_LINUX32 = 0x0008,
PER_LINUX32_3GB = 0x0008 | ADDR_LIMIT_3GB,
PER_IRIX32 = 0x0009 | STICKY_TIMEOUTS,/* IRIX5 32-bit */
PER_IRIXN32 = 0x000a | STICKY_TIMEOUTS,/* IRIX6 new 32-bit */
PER_IRIX64 = 0x000b | STICKY_TIMEOUTS,/* IRIX6 64-bit */
PER_RISCOS = 0x000c,
PER_SOLARIS = 0x000d | STICKY_TIMEOUTS,
PER_UW7 = 0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
PER_OSF4 = 0x000f, /* OSF/1 v4 */
PER_HPUX = 0x0010,
PER_MASK = 0x00ff,
}
58 changes: 43 additions & 15 deletions src/adbg/process/base.d
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ version (Windows) {
import core.sys.posix.libgen : basename;
import adbg.include.c.stdio; // snprintf;
import adbg.platform : ADBG_CHILD_STACK_SIZE;
import adbg.include.linux.personality;
}

//version (CRuntime_Glibc)
Expand Down Expand Up @@ -71,13 +72,11 @@ enum ADBG_PROCESS_NAME_LENGTH = 256;
struct adbg_process_t {
version (Windows) { // Original identifiers; Otherwise informal
int pid; /// Process identificiation number
deprecated int tid; /// Thread identification number
HANDLE hpid; /// Process 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) deprecated int wow64;
// TODO: Deprecate tid and htid to rely on adbg_thread_t
int tid; /// Thread identification number
HANDLE htid; /// Thread handle
}
version (Posix) {
pid_t pid; /// Process ID
Expand Down Expand Up @@ -206,18 +205,37 @@ version (Windows) {
///
/// This is useful when the debugger is dealing with a process running
/// under a subsystem such as WoW or lib32-on-linux64 programs.
/// Params: tracee = Debuggee process.
/// Params: proc = Debuggee process.
/// Returns: Machine platform.
AdbgMachine adbg_process_get_machine(adbg_process_t *tracee) {
if (tracee == null)
AdbgMachine adbg_process_machine(adbg_process_t *proc) {
if (proc == null)
return AdbgMachine.unknown;

//TODO: There's probably a way to remotely check this
// Windows: IsWow64Process/IsWow64Process2 with process handle
version (Win64) {
if (tracee.wow64) return AdbgMachine.i386;
}

version (Win64) {
// TODO: Check with IsWow64Process2 when able
// Important for AArch32 support on AArch64
// with GetProcAddress("kernel32", "IsWow64Process2")
// Introduced in Windows 10, version 1511
// IsWow64Process: 32-bit proc. under aarch64 returns FALSE
BOOL w64 = void;
version (X86_64) if (IsWow64Process(proc.hpid, &w64) && w64) return AdbgMachine.i386;
version (AArch64) if (IsWow64Process(proc.hpid, &w64) && w64) return AdbgMachine.arm;
}
version (linux) {
char[64] path = void;
if (snprintf(path.ptr, 64, "/proc/%d/personality", proc.pid) <= 0)
return adbg_machine_default();
int fd = open(path.ptr, O_RDONLY);
if (fd < 0)
return adbg_machine_default();
if (read(fd, path.ptr, 8)) // re-use buffer that's no longer needed
return adbg_machine_default();
path[8] = 0;
char *end = void;
uint personality = cast(uint)strtol(path.ptr, &end, 16);
version (X86_64) if (personality & ADDR_LIMIT_32BIT) return AdbgMachine.i386;
version (AArch64) if (personality & ADDR_LIMIT_32BIT) return AdbgMachine.arm;
}
return adbg_machine_default();
}

Expand Down Expand Up @@ -313,5 +331,15 @@ adbg_process_t* adbg_process_list_get(void *proclist, size_t index) {
/// Close the process list created by `adbg_process_list_new`.
/// Params: proclist = List instance.
void adbg_process_list_close(void *proclist) {
if (proclist) adbg_list_free(cast(list_t*)proclist);
if (proclist == null) return;

version (Windows) {
list_t *list = cast(list_t*)proclist;
adbg_process_t *proc = void;
for (size_t i; (proc = cast(adbg_process_t*)adbg_list_get(list, i)) != null; ++i) {
CloseHandle(proc.hpid);
}
}

adbg_list_free(cast(list_t*)proclist);
}
6 changes: 3 additions & 3 deletions src/adbg/process/thread.d
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ version (Windows) {

adbg_list_clear(process.thread_list);
adbg_thread_t t = void;
adbg_thread_context_config(&t.context, adbg_process_get_machine(process));
adbg_thread_context_config(&t.context, adbg_process_machine(process));
do {
if (te32.th32OwnerProcessID != process.pid)
continue;
Expand Down Expand Up @@ -138,7 +138,7 @@ version (Windows) {
// 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));
adbg_thread_context_config(&t.context, adbg_process_machine(process));
for (dirent *entry = void; (entry = readdir(procfd)) != null;) {
// readdir() includes "." and "..", skip them
if (isdigit(entry.d_name[0]) == 0)
Expand Down Expand Up @@ -523,7 +523,7 @@ int adbg_thread_context_update(adbg_process_t *proc, adbg_thread_t *thread) {
return adbg_oops(AdbgError.invalidArgument);

version (Win64) {
AdbgMachine mach = adbg_process_get_machine(proc);
AdbgMachine mach = adbg_process_machine(proc);
version (X86_64) switch (mach) {
case AdbgMachine.amd64:
CONTEXT_X64 winctx = void; // CONTEXT
Expand Down

0 comments on commit b7a9901

Please sign in to comment.