Skip to content

Commit 4ec6ed2

Browse files
authored
Implement native breakpoints support for XNU/ARM64 ##debug
1 parent d4203a9 commit 4ec6ed2

File tree

4 files changed

+121
-4
lines changed

4 files changed

+121
-4
lines changed

libr/core/cmd_debug.inc.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3653,8 +3653,12 @@ static void add_breakpoint(RCore *core, const char *input, bool hwbp, bool watch
36533653
RBreakpointItem *bpi;
36543654
ut64 addr;
36553655
int i = 0;
3656-
3657-
char *str = strdup (r_str_trim_head_ro (input + 1));
3656+
const char *inp = r_str_trim_head_ro (input + 1);
3657+
if (R_STR_ISEMPTY (inp)) {
3658+
R_LOG_ERROR ("No argument provided, use: dbH [addr] [rwx]");
3659+
return;
3660+
}
3661+
char *str = strdup (inp);
36583662
int sl = r_str_word_set0 (str);
36593663
// For dbw every second argument is 'rw', so we need to skip it.
36603664
for (; i < sl; i += 1 + (watch ? 1 : 0)) {

libr/debug/p/debug_native.c

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1469,6 +1469,110 @@ static bool arm64_hwbp_del(RDebug *dbg, RBreakpoint *bp, RBreakpointItem *b) {
14691469
#endif // __arm64__
14701470
#endif // __linux__
14711471

1472+
#if (__arm64__ || __arm64e__ || __aarch64__) && __APPLE__
1473+
1474+
#include "native/xnu/xnu_debug.h"
1475+
#include "native/xnu/xnu_threads.h"
1476+
1477+
// BCR definitions for ARM64
1478+
#define BCR_M_IMVA_MATCH ((uint32_t)(0u << 21))
1479+
#define BCR_M_IMVA_MISMATCH ((uint32_t)(2u << 21))
1480+
#define BCR_ENABLE ((uint32_t)(1u))
1481+
#define BAS_IMVA_ALL ((uint32_t)(0xfu << 5))
1482+
#define S_USER ((uint32_t)(2u << 1))
1483+
1484+
static bool darwin_arm64_hwbp_add(RDebug *dbg, RBreakpoint *bp, RBreakpointItem *b) {
1485+
RList *threads = xnu_thread_list (dbg, dbg->pid, NULL);
1486+
if (!threads) {
1487+
return false;
1488+
}
1489+
RListIter *it;
1490+
xnu_thread_t *thread;
1491+
bool ret = false;
1492+
// Find an available breakpoint slot that is free on all threads
1493+
int slot = -1;
1494+
int i;
1495+
for (i = 0; i < 16; i++) {
1496+
bool available = true;
1497+
r_list_foreach (threads, it, thread) {
1498+
if (!xnu_thread_get_drx (dbg, thread)) {
1499+
available = false;
1500+
break;
1501+
}
1502+
if (thread->debug.drx64.__bcr[i] != 0) {
1503+
available = false;
1504+
break;
1505+
}
1506+
}
1507+
if (available) {
1508+
slot = i;
1509+
break;
1510+
}
1511+
}
1512+
if (slot == -1) {
1513+
r_list_free (threads);
1514+
return false; // No available slots
1515+
}
1516+
// Set breakpoint on all threads
1517+
r_list_foreach (threads, it, thread) {
1518+
if (!xnu_thread_get_drx (dbg, thread)) {
1519+
continue;
1520+
}
1521+
arm_debug_state64_t *state = &thread->debug.drx64;
1522+
state->__bvr[slot] = b->addr;
1523+
state->__bcr[slot] = BCR_M_IMVA_MATCH | S_USER | BCR_ENABLE | BAS_IMVA_ALL;
1524+
if (xnu_thread_set_drx (dbg, thread)) {
1525+
ret = true;
1526+
}
1527+
}
1528+
r_list_free (threads);
1529+
return ret;
1530+
}
1531+
1532+
static bool darwin_arm64_hwbp_del(RDebug *dbg, RBreakpoint *bp, RBreakpointItem *b) {
1533+
RList *threads = xnu_thread_list (dbg, dbg->pid, NULL);
1534+
if (!threads) {
1535+
return false;
1536+
}
1537+
RListIter *it;
1538+
xnu_thread_t *thread;
1539+
bool ret = false;
1540+
// Find the breakpoint slot with matching address
1541+
int i, slot = -1;
1542+
r_list_foreach (threads, it, thread) {
1543+
if (!xnu_thread_get_drx (dbg, thread)) {
1544+
continue;
1545+
}
1546+
for (i = 0; i < 16; i++) {
1547+
if (thread->debug.drx64.__bvr[i] == b->addr && (thread->debug.drx64.__bcr[i] & BCR_ENABLE)) {
1548+
slot = i;
1549+
goto found;
1550+
}
1551+
}
1552+
}
1553+
found:
1554+
if (slot == -1) {
1555+
r_list_free (threads);
1556+
return false; // Breakpoint not found
1557+
}
1558+
// Clear breakpoint on all threads
1559+
r_list_foreach (threads, it, thread) {
1560+
if (!xnu_thread_get_drx (dbg, thread)) {
1561+
continue;
1562+
}
1563+
arm_debug_state64_t *state = &thread->debug.drx64;
1564+
state->__bvr[slot] = 0;
1565+
state->__bcr[slot] = 0;
1566+
if (xnu_thread_set_drx (dbg, thread)) {
1567+
ret = true;
1568+
}
1569+
}
1570+
r_list_free (threads);
1571+
return ret;
1572+
}
1573+
1574+
#endif // __APPLE__
1575+
14721576
// Set or unset breakpoints... only handle hardware breakpoints here. otherwise, the caller must do the work
14731577
static bool r_debug_native_bp(RBreakpoint *bp, RBreakpointItem *b, bool set) {
14741578
if (b && b->hw) {
@@ -1490,6 +1594,11 @@ static bool r_debug_native_bp(RBreakpoint *bp, RBreakpointItem *b, bool set) {
14901594
#elif __riscv && __linux__
14911595
// no hw bps afaik
14921596
return false;
1597+
#elif (__arm64__ || __arm64e__ || __aarch64__) && __APPLE__
1598+
RDebug *dbg = bp->user;
1599+
return set
1600+
? darwin_arm64_hwbp_add (dbg, bp, b)
1601+
: darwin_arm64_hwbp_del (dbg, bp, b);
14931602
#else
14941603
#ifdef _MSC_VER
14951604
#pragma message ( "r_debug_native_bp not implemented for this platform" )

libr/debug/p/native/xnu/xnu_debug.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ typedef struct {
188188
coredump_thread_state_flavor_t *flavors;
189189
} tir_t;
190190

191+
#include "xnu_threads.h"
192+
191193
task_t pid_to_task (int pid);
192194
int xnu_get_vmmap_entries_for_pid (pid_t pid);
193195
char *xnu_corefile_default_location(void);
@@ -209,5 +211,7 @@ RDebugPid *xnu_get_pid(int pid);
209211
RList *xnu_dbg_maps(RDebug *dbg, int only_modules);
210212
RList *xnu_thread_list(RDebug *dbg, int pid, RList *list);
211213
RDebugInfo *xnu_info(RDebug *dbg, const char *arg);
214+
bool xnu_thread_get_drx(RDebug *dbg, xnu_thread_t *thread);
215+
bool xnu_thread_set_drx(RDebug *dbg, xnu_thread_t *thread);
212216

213217
#endif

libr/debug/p/native/xnu/xnu_threads.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ static kern_return_t xnu_convert_thread_state(mach_port_t thread, int direction,
5252
}
5353

5454
// XXX this should work as long as in arm trace bit relies on this
55-
static bool xnu_thread_get_drx(RDebug *dbg, xnu_thread_t *thread) {
55+
bool xnu_thread_get_drx(RDebug *dbg, xnu_thread_t *thread) {
5656
R_RETURN_VAL_IF_FAIL (dbg && thread, false);
5757
kern_return_t rc;
5858
#if __x86_64__ || __i386__
@@ -96,7 +96,7 @@ static bool xnu_thread_get_drx(RDebug *dbg, xnu_thread_t *thread) {
9696
return true;
9797
}
9898

99-
static bool xnu_thread_set_drx(RDebug *dbg, xnu_thread_t *thread) {
99+
bool xnu_thread_set_drx(RDebug *dbg, xnu_thread_t *thread) {
100100
R_RETURN_VAL_IF_FAIL (dbg && thread, false);
101101
kern_return_t rc;
102102
#if __i386__ || __x86_64__

0 commit comments

Comments
 (0)