diff --git a/librz/arch/p/asm/asm_alpha_cs.c b/librz/arch/p/asm/asm_alpha_cs.c index 13dee63f62..a84440c652 100644 --- a/librz/arch/p/asm/asm_alpha_cs.c +++ b/librz/arch/p/asm/asm_alpha_cs.c @@ -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 diff --git a/librz/debug/meson.build b/librz/debug/meson.build index b6367881f2..e1c0f2a182 100644 --- a/librz/debug/meson.build +++ b/librz/debug/meson.build @@ -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 diff --git a/librz/debug/p/native/linux/linux_debug.h b/librz/debug/p/native/linux/linux_debug.h index e20acf6261..c4725446eb 100644 --- a/librz/debug/p/native/linux/linux_debug.h +++ b/librz/debug/p/native/linux/linux_debug.h @@ -153,6 +153,11 @@ struct powerpc_regs_t { #include typedef ut64 mips64_regs_t[274]; #define RZ_DEBUG_REG_T mips64_regs_t + +#elif __alpha__ +#include +#define RZ_DEBUG_REG_T struct pt_regs +#define RZ_DEBUG_FPREG_T fpreg_t #endif #endif diff --git a/librz/debug/p/native/linux_alpha.c b/librz/debug/p/native/linux_alpha.c new file mode 100644 index 0000000000..e12374bd94 --- /dev/null +++ b/librz/debug/p/native/linux_alpha.c @@ -0,0 +1,342 @@ +// SPDX-FileCopyrightText: 2026 well-mannered-goat +// SPDX-License-Identifier: LGPL-3.0-only + +#include "rz_util/rz_log.h" +#include +#include +#include +#include +#include + +#include +#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 /**/ *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 /**/ *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(®s, 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, ®s, 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 /**/ *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 /**/ *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 /**/ *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, +}; \ No newline at end of file diff --git a/librz/debug/p/native/reg/linux-alpha.h b/librz/debug/p/native/reg/linux-alpha.h new file mode 100644 index 0000000000..6b19396642 --- /dev/null +++ b/librz/debug/p/native/reg/linux-alpha.h @@ -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"); \ No newline at end of file diff --git a/librz/include/rz_types.h b/librz/include/rz_types.h index eca6478b4d..1650376f01 100644 --- a/librz/include/rz_types.h +++ b/librz/include/rz_types.h @@ -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)