Add Alpha linux native debugger (#6447)

* add alpha to rz_types
* fix register read
* add register write
* add breakpoint instruction
* fix register sp
This commit is contained in:
well-mannered-goat 2026-06-09 00:27:19 -04:00 committed by GitHub
parent fb9b15eac6
commit 25886f4ccf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 436 additions and 0 deletions

View file

@ -102,6 +102,11 @@ static bool fini(void *u) {
return true;
}
static bool alpha_sw_breakpoint(const RzAsm *a, ut64 addr, const RzAsmOp *original, RzAsmOp *breakpoint) {
rz_asm_op_set_buf(breakpoint, (const ut8 *)"\x80\x00\x00\x00", 4);
return true;
}
RzAsmPlugin rz_asm_plugin_alpha_cs = {
.name = "alpha",
.desc = "DEC Alpha Capstone-based disassembler",
@ -112,6 +117,7 @@ RzAsmPlugin rz_asm_plugin_alpha_cs = {
.disassemble = &disassemble,
.init = &init,
.fini = &fini,
.sw_breakpoint = &alpha_sw_breakpoint,
};
#ifndef RZ_PLUGIN_INCORE

View file

@ -105,6 +105,8 @@ if has_debugger
rz_debug_sources += ['p/native/linux_riscv64.c']
elif host_machine.cpu_family() == 'riscv32'
rz_debug_sources += ['p/native/linux_riscv32.c']
elif host_machine.cpu_family() == 'alpha'
rz_debug_sources += ['p/native/linux_alpha.c']
else
rz_debug_sources += ['p/native/linux_other.c']
endif

View file

@ -153,6 +153,11 @@ struct powerpc_regs_t {
#include <sys/ucontext.h>
typedef ut64 mips64_regs_t[274];
#define RZ_DEBUG_REG_T mips64_regs_t
#elif __alpha__
#include <asm/ptrace.h>
#define RZ_DEBUG_REG_T struct pt_regs
#define RZ_DEBUG_FPREG_T fpreg_t
#endif
#endif

View file

@ -0,0 +1,342 @@
// SPDX-FileCopyrightText: 2026 well-mannered-goat <takshformal@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include "rz_util/rz_log.h"
#include <errno.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <signal.h>
#include <linux/elf.h>
#include <sys/mman.h>
#include "linux/linux_debug.h"
#include "procfs.h"
#include "bt.c"
#ifdef __WALL
#define WAITPID_FLAGS __WALL
#else
#define WAITPID_FLAGS 0
#endif
#define ALPHA_PTRACE_REG_COUNT 66
static char *rz_debug_native_reg_profile(RzDebug *dbg) {
#include "reg/linux-alpha.h"
}
static bool rz_debug_native_step(RzDebug *dbg) {
return linux_step(dbg);
}
static int rz_debug_native_attach(RzDebug *dbg, int pid) {
return linux_attach(dbg, pid);
}
static int rz_debug_native_detach(RzDebug *dbg, int pid) {
return rz_debug_ptrace(dbg, PTRACE_DETACH, pid, NULL, (rz_ptrace_data_t)(size_t)0);
}
static int rz_debug_native_select(RzDebug *dbg, int pid, int tid) {
return linux_select(dbg, pid, tid);
}
static int rz_debug_native_continue_syscall(RzDebug *dbg, int pid, int num) {
linux_set_options(dbg, pid);
return rz_debug_ptrace(dbg, PTRACE_SYSCALL, pid, 0, 0);
}
static void interrupt_process(RzDebug *dbg) {
rz_debug_kill(dbg, dbg->pid, dbg->tid, SIGINT);
rz_cons_break_pop();
}
static int rz_debug_native_stop(RzDebug *dbg) {
return linux_stop_threads(dbg, dbg->reason.tid);
}
static int rz_debug_native_continue(RzDebug *dbg, int pid, int tid, int sig) {
int contsig = dbg->reason.signum;
int ret = -1;
if (sig != -1) {
contsig = sig;
}
/* SIGINT handler for attached processes: dbg.consbreak (disabled by default) */
if (dbg->consbreak) {
rz_cons_break_push((RzConsBreak)interrupt_process, dbg);
}
if (dbg->continue_all_threads && dbg->n_threads && dbg->threads) {
RzDebugPid *th;
RzListIter *it;
rz_list_foreach (dbg->threads, it, th) {
ret = rz_debug_ptrace(dbg, PTRACE_CONT, th->pid, 0, 0);
if (ret) {
RZ_LOG_ERROR("(%d) is running or dead.\n", th->pid);
}
}
} else {
ret = rz_debug_ptrace(dbg, PTRACE_CONT, tid, NULL, (rz_ptrace_data_t)(size_t)contsig);
if (ret) {
rz_sys_perror("PTRACE_CONT");
}
}
return tid;
}
static RzDebugInfo *rz_debug_native_info(RzDebug *dbg, const char *arg) {
return linux_info(dbg, arg);
}
static RzDebugReasonType rz_debug_native_wait(RzDebug *dbg, int pid) {
RzDebugReasonType reason = RZ_DEBUG_REASON_UNKNOWN;
if (pid < 0) {
RZ_LOG_ERROR("rz_debug_native_wait called with pid -1\n");
return RZ_DEBUG_REASON_ERROR;
}
reason = linux_dbg_wait(dbg, dbg->tid);
dbg->reason.type = reason;
return reason;
}
#undef MAXPID
#define MAXPID 99999
static RzList /*<RzDebugPid *>*/ *rz_debug_native_pids(RzDebug *dbg, int pid) {
RzList *list = rz_list_new();
if (!list) {
return NULL;
}
return linux_pid_list(pid, list);
}
RZ_API RZ_OWN RzList /*<RzDebugPid *>*/ *rz_debug_native_threads(RzDebug *dbg, int pid) {
RzList *list = rz_list_new();
if (!list) {
RZ_LOG_ERROR("Cannot create list\n");
return NULL;
}
return linux_thread_list(dbg, pid, list);
}
RZ_API ut64 rz_debug_get_tls(RZ_NONNULL RzDebug *dbg, int tid) {
rz_return_val_if_fail(dbg, 0);
return get_linux_tls_val(dbg, tid);
}
static int rz_debug_native_reg_read(RzDebug *dbg, int type, ut8 *buf, int size) {
if (size < 1) {
return false;
}
int pid = dbg->tid;
switch (type) {
case RZ_REG_TYPE_DRX:
case RZ_REG_TYPE_MMX:
case RZ_REG_TYPE_XMM:
case RZ_REG_TYPE_YMM:
return false;
case RZ_REG_TYPE_SEG:
case RZ_REG_TYPE_FLG:
case RZ_REG_TYPE_FPU:
case RZ_REG_TYPE_GPR: {
ut64 regs[ALPHA_PTRACE_REG_COUNT];
memset(&regs, 0, sizeof(regs));
memset(buf, 0, size);
for (int i = 0; i < ALPHA_PTRACE_REG_COUNT; i++) {
errno = 0;
long v = rz_debug_ptrace(dbg, PTRACE_PEEKUSER, pid, (void *)i, NULL);
if (errno) {
RZ_LOG_DEBUG("reg[%02d] failed errno=%d\n", i, errno);
regs[i] = UT64_MAX;
continue;
}
regs[i] = (ut64)v;
}
size = RZ_MIN(sizeof(regs), size);
memcpy(buf, &regs, size);
return size;
} break;
}
return false;
}
static int rz_debug_native_reg_write(RzDebug *dbg, int type, const ut8 *buf, int size) {
int pid = dbg->pid;
switch (type) {
case RZ_REG_TYPE_DRX:
return false;
case RZ_REG_TYPE_GPR:
int ret;
for (int i = 0; i < ALPHA_PTRACE_REG_COUNT; i++) {
ut64 value = rz_read_at_le64(buf, i * 8);
ret = rz_debug_ptrace(dbg, PTRACE_POKEUSER, pid, (void *)i, (void *)value);
if (ret < 0) {
RZ_LOG_ERROR("Writing to register for value : %lx", value);
return false;
}
}
break;
default:
RZ_LOG_DEBUG("TODO: reg_write_non-gpr (%d)\n", type);
return false;
}
return true;
}
static RzDebugMap *rz_debug_native_map_alloc(RzDebug *dbg, ut64 addr, int size, bool thp) {
return linux_map_alloc(dbg, addr, size, thp);
}
static int rz_debug_native_map_dealloc(RzDebug *dbg, ut64 addr, int size) {
return linux_map_dealloc(dbg, addr, size);
}
static RzList /*<RzDebugMap *>*/ *rz_debug_native_map_get(RzDebug *dbg) {
if (dbg->pid == -1) {
return NULL;
}
RzList *map_list = linux_map_get(dbg);
if (!map_list) {
RZ_LOG_ERROR("Cannot create process map list.\n");
return NULL;
}
return map_list;
}
static RzList /*<RzDebugMap *>*/ *rz_debug_native_modules_get(RzDebug *dbg) {
char *lastname = NULL;
RzDebugMap *map;
RzListIter *iter, *iter2;
RzList *list, *last;
bool must_delete;
if (!(list = rz_debug_native_map_get(dbg))) {
return NULL;
}
if (!(last = rz_list_newf((RzListFree)rz_debug_map_free))) {
rz_list_free(list);
return NULL;
}
rz_list_foreach_safe (list, iter, iter2, map) {
const char *file = map->file;
if (!map->file) {
file = map->file = rz_str_dup(map->name);
}
must_delete = true;
if (file && *file == '/') {
if (!lastname || strcmp(lastname, file)) {
must_delete = false;
}
}
if (must_delete) {
rz_list_delete(list, iter);
} else {
rz_list_append(last, map);
free(lastname);
lastname = rz_str_dup(file);
}
}
list->free = NULL;
free(lastname);
rz_list_free(list);
return last;
}
static bool rz_debug_native_kill(RzDebug *dbg, int pid, int tid, int sig) {
bool ret = false;
if (pid == 0) {
pid = dbg->pid;
}
if (sig == SIGKILL && dbg->threads) {
rz_list_free(dbg->threads);
dbg->threads = NULL;
}
if ((rz_sys_kill(pid, sig) != -1)) {
ret = true;
}
if (errno == 1) {
ret = -true; // EPERM
}
return ret;
}
static int rz_debug_native_drx(RzDebug *dbg, int n, ut64 addr, int sz, int rwx, int g, int api_type) {
RZ_LOG_ERROR("drx: Unsupported platform\n");
return -1;
}
static int rz_debug_native_bp(RzBreakpoint *bp, RzBreakpointItem *b, bool set) {
return false;
}
static RzList /*<RzDebugDesc *>*/ *rz_debug_desc_native_list(int pid) {
return linux_desc_list(pid);
}
static int rz_debug_native_map_protect(RzDebug *dbg, ut64 addr, int size, int perms) {
return linux_map_protect(dbg, addr, size, perms);
}
static int rz_debug_desc_native_open(const char *path) {
return 0;
}
static bool rz_debug_gcore(RzDebug *dbg, char *path, RzBuffer *dest) {
RZ_LOG_ERROR("Unsupported on this platform\n");
return false;
}
struct rz_debug_desc_plugin_t rz_debug_desc_plugin_native = {
.open = rz_debug_desc_native_open,
.list = rz_debug_desc_native_list,
};
bool rz_debug_native_init(RzDebug *dbg, void **user) {
dbg->cur->desc = rz_debug_desc_plugin_native;
return true;
}
void rz_debug_native_fini(RzDebug *dbg, void *user) {
if (!user) {
return;
}
free(user);
}
RzDebugPlugin rz_debug_plugin_native = {
.name = "native",
.license = "LGPL3",
.bits = RZ_SYS_BITS_64,
.arch = "alpha",
.canstep = 0,
.init = &rz_debug_native_init,
.fini = &rz_debug_native_fini,
.step = &rz_debug_native_step,
.cont = &rz_debug_native_continue,
.stop = &rz_debug_native_stop,
.contsc = &rz_debug_native_continue_syscall,
.attach = &rz_debug_native_attach,
.detach = &rz_debug_native_detach,
.select = &rz_debug_native_select,
.pids = &rz_debug_native_pids,
.threads = &rz_debug_native_threads,
.wait = &rz_debug_native_wait,
.kill = &rz_debug_native_kill,
.frames = &rz_debug_native_frames, // rename to backtrace ?
.reg_profile = rz_debug_native_reg_profile,
.reg_read = rz_debug_native_reg_read,
.info = rz_debug_native_info,
.reg_write = (void *)&rz_debug_native_reg_write,
.map_alloc = rz_debug_native_map_alloc,
.map_dealloc = rz_debug_native_map_dealloc,
.map_get = rz_debug_native_map_get,
.modules_get = rz_debug_native_modules_get,
.map_protect = rz_debug_native_map_protect,
.breakpoint = rz_debug_native_bp,
.drx = rz_debug_native_drx,
.gcore = rz_debug_gcore,
};

View file

@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: 2026 well-mannered-goat
// SPDX-License-Identifier: LGPL-3.0-only
return rz_str_dup(
"=PC pc\n"
"=SP sp\n"
"=BP s6\n"
"=A0 a0\n"
"=A1 a1\n"
"=A2 a2\n"
"=R0 v0\n"
"gpr v0 .64 0 0\n"
"gpr t0 .64 8 0\n"
"gpr t1 .64 16 0\n"
"gpr t2 .64 24 0\n"
"gpr t3 .64 32 0\n"
"gpr t4 .64 40 0\n"
"gpr t5 .64 48 0\n"
"gpr t6 .64 56 0\n"
"gpr t7 .64 64 0\n"
"gpr s0 .64 72 0\n"
"gpr s1 .64 80 0\n"
"gpr s2 .64 88 0\n"
"gpr s3 .64 96 0\n"
"gpr s4 .64 104 0\n"
"gpr s5 .64 112 0\n"
"gpr s6 .64 120 0\n"
"gpr a0 .64 128 0\n"
"gpr a1 .64 136 0\n"
"gpr a2 .64 144 0\n"
"gpr a3 .64 152 0\n"
"gpr a4 .64 160 0\n"
"gpr a5 .64 168 0\n"
"gpr t8 .64 176 0\n"
"gpr t9 .64 184 0\n"
"gpr t10 .64 192 0\n"
"gpr t11 .64 200 0\n"
"gpr ra .64 208 0\n"
"gpr t12 .64 216 0\n"
"gpr at .64 224 0\n"
"gpr gp .64 232 0\n"
"gpr sp .64 240 0\n"
"gpr zero .64 248 0\n"
"fpu f0 .64 256 0\n"
"fpu f1 .64 264 0\n"
"fpu f2 .64 272 0\n"
"fpu f3 .64 280 0\n"
"fpu f4 .64 288 0\n"
"fpu f5 .64 296 0\n"
"fpu f6 .64 304 0\n"
"fpu f7 .64 312 0\n"
"fpu f8 .64 320 0\n"
"fpu f9 .64 328 0\n"
"fpu f10 .64 336 0\n"
"fpu f11 .64 344 0\n"
"fpu f12 .64 352 0\n"
"fpu f13 .64 360 0\n"
"fpu f14 .64 368 0\n"
"fpu f15 .64 376 0\n"
"fpu f16 .64 384 0\n"
"fpu f17 .64 392 0\n"
"fpu f18 .64 400 0\n"
"fpu f19 .64 408 0\n"
"fpu f20 .64 416 0\n"
"fpu f21 .64 424 0\n"
"fpu f22 .64 432 0\n"
"fpu f23 .64 440 0\n"
"fpu f24 .64 448 0\n"
"fpu f25 .64 456 0\n"
"fpu f26 .64 464 0\n"
"fpu f27 .64 472 0\n"
"fpu f28 .64 480 0\n"
"fpu f29 .64 488 0\n"
"fpu f30 .64 496 0\n"
"fpu f31 .64 504 0\n"
"gpr pc .64 512 0\n"
"gpr unique .64 520 0\n");

View file

@ -289,6 +289,10 @@ extern "C" {
#else
#define RZ_SYS_BITS (RZ_SYS_BITS_32 | RZ_SYS_BITS_64)
#endif
#elif __alpha__
#define RZ_SYS_ARCH "alpha"
#define RZ_SYS_BITS RZ_SYS_BITS_64
#define RZ_SYS_ENDIAN RZ_SYS_ENDIAN_LITTLE
#else
#ifdef _MSC_VER
#if defined(_M_X64) || defined(_M_AMD64)