@@ -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
14731577static 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" )
0 commit comments