Home | History | Annotate | Line # | Download | only in gdb
      1 /* Native-dependent code for FreeBSD/aarch64.
      2 
      3    Copyright (C) 2017-2024 Free Software Foundation, Inc.
      4 
      5    This file is part of GDB.
      6 
      7    This program is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3 of the License, or
     10    (at your option) any later version.
     11 
     12    This program is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15    GNU General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     19 
     20 #include "arch-utils.h"
     21 #include "inferior.h"
     22 #include "regcache.h"
     23 #include "target.h"
     24 #include "nat/aarch64-hw-point.h"
     25 
     26 #include "elf/common.h"
     27 
     28 #include <sys/param.h>
     29 #include <sys/ptrace.h>
     30 #include <machine/armreg.h>
     31 #include <machine/reg.h>
     32 
     33 #include "fbsd-nat.h"
     34 #include "aarch64-tdep.h"
     35 #include "aarch64-fbsd-tdep.h"
     36 #include "aarch64-nat.h"
     37 #include "inf-ptrace.h"
     38 
     39 #if __FreeBSD_version >= 1400005
     40 #define	HAVE_DBREG
     41 
     42 #include <unordered_set>
     43 #endif
     44 
     45 #ifdef HAVE_DBREG
     46 struct aarch64_fbsd_nat_target final
     47   : public aarch64_nat_target<fbsd_nat_target>
     48 #else
     49 struct aarch64_fbsd_nat_target final : public fbsd_nat_target
     50 #endif
     51 {
     52   void fetch_registers (struct regcache *, int) override;
     53   void store_registers (struct regcache *, int) override;
     54 
     55   const struct target_desc *read_description () override;
     56 
     57 #ifdef HAVE_DBREG
     58   /* Hardware breakpoints and watchpoints.  */
     59   bool stopped_by_watchpoint () override;
     60   bool stopped_data_address (CORE_ADDR *) override;
     61   bool stopped_by_hw_breakpoint () override;
     62   bool supports_stopped_by_hw_breakpoint () override;
     63 
     64   void post_startup_inferior (ptid_t) override;
     65   void post_attach (int pid) override;
     66 
     67   void low_new_fork (ptid_t parent, pid_t child) override;
     68   void low_delete_thread (thread_info *) override;
     69   void low_prepare_to_resume (thread_info *) override;
     70 
     71 private:
     72   void probe_debug_regs (int pid);
     73   static bool debug_regs_probed;
     74 #endif
     75 };
     76 
     77 static aarch64_fbsd_nat_target the_aarch64_fbsd_nat_target;
     78 
     79 /* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
     80    for all registers.  */
     81 
     82 void
     83 aarch64_fbsd_nat_target::fetch_registers (struct regcache *regcache,
     84 					  int regnum)
     85 {
     86   fetch_register_set<struct reg> (regcache, regnum, PT_GETREGS,
     87 				  &aarch64_fbsd_gregset);
     88   fetch_register_set<struct fpreg> (regcache, regnum, PT_GETFPREGS,
     89 				    &aarch64_fbsd_fpregset);
     90 
     91   gdbarch *gdbarch = regcache->arch ();
     92   aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
     93   if (tdep->has_tls ())
     94     fetch_regset<uint64_t> (regcache, regnum, NT_ARM_TLS,
     95 			    &aarch64_fbsd_tls_regset, tdep->tls_regnum_base);
     96 }
     97 
     98 /* Store register REGNUM back into the inferior.  If REGNUM is -1, do
     99    this for all registers.  */
    100 
    101 void
    102 aarch64_fbsd_nat_target::store_registers (struct regcache *regcache,
    103 					  int regnum)
    104 {
    105   store_register_set<struct reg> (regcache, regnum, PT_GETREGS, PT_SETREGS,
    106 				  &aarch64_fbsd_gregset);
    107   store_register_set<struct fpreg> (regcache, regnum, PT_GETFPREGS,
    108 				    PT_SETFPREGS, &aarch64_fbsd_fpregset);
    109 
    110   gdbarch *gdbarch = regcache->arch ();
    111   aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch);
    112   if (tdep->has_tls ())
    113     store_regset<uint64_t> (regcache, regnum, NT_ARM_TLS,
    114 			    &aarch64_fbsd_tls_regset, tdep->tls_regnum_base);
    115 }
    116 
    117 /* Implement the target read_description method.  */
    118 
    119 const struct target_desc *
    120 aarch64_fbsd_nat_target::read_description ()
    121 {
    122   if (inferior_ptid == null_ptid)
    123     return this->beneath ()->read_description ();
    124 
    125   aarch64_features features;
    126   features.tls = have_regset (inferior_ptid, NT_ARM_TLS)? 1 : 0;
    127   return aarch64_read_description (features);
    128 }
    129 
    130 #ifdef HAVE_DBREG
    131 bool aarch64_fbsd_nat_target::debug_regs_probed;
    132 
    133 /* Set of threads which need to update debug registers on next resume.  */
    134 
    135 static std::unordered_set<lwpid_t> aarch64_debug_pending_threads;
    136 
    137 /* Implement the "stopped_data_address" target_ops method.  */
    138 
    139 bool
    140 aarch64_fbsd_nat_target::stopped_data_address (CORE_ADDR *addr_p)
    141 {
    142   siginfo_t siginfo;
    143   struct aarch64_debug_reg_state *state;
    144 
    145   if (!fbsd_nat_get_siginfo (inferior_ptid, &siginfo))
    146     return false;
    147 
    148   /* This must be a hardware breakpoint.  */
    149   if (siginfo.si_signo != SIGTRAP
    150       || siginfo.si_code != TRAP_TRACE
    151       || siginfo.si_trapno != EXCP_WATCHPT_EL0)
    152     return false;
    153 
    154   const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
    155 
    156   /* Check if the address matches any watched address.  */
    157   state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
    158   return aarch64_stopped_data_address (state, addr_trap, addr_p);
    159 }
    160 
    161 /* Implement the "stopped_by_watchpoint" target_ops method.  */
    162 
    163 bool
    164 aarch64_fbsd_nat_target::stopped_by_watchpoint ()
    165 {
    166   return stopped_data_address (nullptr);
    167 }
    168 
    169 /* Implement the "stopped_by_hw_breakpoint" target_ops method.  */
    170 
    171 bool
    172 aarch64_fbsd_nat_target::stopped_by_hw_breakpoint ()
    173 {
    174   siginfo_t siginfo;
    175   struct aarch64_debug_reg_state *state;
    176 
    177   if (!fbsd_nat_get_siginfo (inferior_ptid, &siginfo))
    178     return false;
    179 
    180   /* This must be a hardware breakpoint.  */
    181   if (siginfo.si_signo != SIGTRAP
    182       || siginfo.si_code != TRAP_TRACE
    183       || siginfo.si_trapno != EXCP_WATCHPT_EL0)
    184     return false;
    185 
    186   return !stopped_by_watchpoint();
    187 }
    188 
    189 /* Implement the "supports_stopped_by_hw_breakpoint" target_ops method.  */
    190 
    191 bool
    192 aarch64_fbsd_nat_target::supports_stopped_by_hw_breakpoint ()
    193 {
    194   return true;
    195 }
    196 
    197 /* Fetch the hardware debug register capability information.  */
    198 
    199 void
    200 aarch64_fbsd_nat_target::probe_debug_regs (int pid)
    201 {
    202   if (!debug_regs_probed)
    203     {
    204       struct dbreg reg;
    205 
    206       debug_regs_probed = true;
    207       aarch64_num_bp_regs = 0;
    208       aarch64_num_wp_regs = 0;
    209 
    210       if (ptrace(PT_GETDBREGS, pid, (PTRACE_TYPE_ARG3) &reg, 0) == 0)
    211 	{
    212 	  switch (reg.db_debug_ver)
    213 	    {
    214 	    case AARCH64_DEBUG_ARCH_V8:
    215 	    case AARCH64_DEBUG_ARCH_V8_1:
    216 	    case AARCH64_DEBUG_ARCH_V8_2:
    217 	    case AARCH64_DEBUG_ARCH_V8_4:
    218 	    case AARCH64_DEBUG_ARCH_V8_8:
    219 	    case AARCH64_DEBUG_ARCH_V8_9:
    220 	      break;
    221 	    default:
    222 	      return;
    223 	    }
    224 
    225 	  aarch64_num_bp_regs = reg.db_nbkpts;
    226 	  if (aarch64_num_bp_regs > AARCH64_HBP_MAX_NUM)
    227 	    {
    228 	      warning (_("Unexpected number of hardware breakpoint registers"
    229 			 " reported by ptrace, got %d, expected %d."),
    230 		       aarch64_num_bp_regs, AARCH64_HBP_MAX_NUM);
    231 	      aarch64_num_bp_regs = AARCH64_HBP_MAX_NUM;
    232 	    }
    233 	  aarch64_num_wp_regs = reg.db_nwtpts;
    234 	  if (aarch64_num_wp_regs > AARCH64_HWP_MAX_NUM)
    235 	    {
    236 	      warning (_("Unexpected number of hardware watchpoint registers"
    237 			 " reported by ptrace, got %d, expected %d."),
    238 		       aarch64_num_wp_regs, AARCH64_HWP_MAX_NUM);
    239 	      aarch64_num_wp_regs = AARCH64_HWP_MAX_NUM;
    240 	    }
    241 	}
    242     }
    243 }
    244 
    245 /* Implement the virtual inf_ptrace_target::post_startup_inferior method.  */
    246 
    247 void
    248 aarch64_fbsd_nat_target::post_startup_inferior (ptid_t ptid)
    249 {
    250   aarch64_remove_debug_reg_state (ptid.pid ());
    251   probe_debug_regs (ptid.pid ());
    252   fbsd_nat_target::post_startup_inferior (ptid);
    253 }
    254 
    255 /* Implement the "post_attach" target_ops method.  */
    256 
    257 void
    258 aarch64_fbsd_nat_target::post_attach (int pid)
    259 {
    260   aarch64_remove_debug_reg_state (pid);
    261   probe_debug_regs (pid);
    262   fbsd_nat_target::post_attach (pid);
    263 }
    264 
    265 /* Implement the virtual fbsd_nat_target::low_new_fork method.  */
    266 
    267 void
    268 aarch64_fbsd_nat_target::low_new_fork (ptid_t parent, pid_t child)
    269 {
    270   struct aarch64_debug_reg_state *parent_state, *child_state;
    271 
    272   /* If there is no parent state, no watchpoints nor breakpoints have
    273      been set, so there is nothing to do.  */
    274   parent_state = aarch64_lookup_debug_reg_state (parent.pid ());
    275   if (parent_state == nullptr)
    276     return;
    277 
    278   /* The kernel clears debug registers in the new child process after
    279      fork, but GDB core assumes the child inherits the watchpoints/hw
    280      breakpoints of the parent, and will remove them all from the
    281      forked off process.  Copy the debug registers mirrors into the
    282      new process so that all breakpoints and watchpoints can be
    283      removed together.  */
    284 
    285   child_state = aarch64_get_debug_reg_state (child);
    286   *child_state = *parent_state;
    287 }
    288 
    289 /* Mark debug register state "dirty" for all threads belonging to the
    290    current inferior.  */
    291 
    292 void
    293 aarch64_notify_debug_reg_change (ptid_t ptid,
    294 				 int is_watchpoint, unsigned int idx)
    295 {
    296   for (thread_info *tp : current_inferior ()->non_exited_threads ())
    297     {
    298       if (tp->ptid.lwp_p ())
    299 	aarch64_debug_pending_threads.emplace (tp->ptid.lwp ());
    300     }
    301 }
    302 
    303 /* Implement the virtual fbsd_nat_target::low_delete_thread method.  */
    304 
    305 void
    306 aarch64_fbsd_nat_target::low_delete_thread (thread_info *tp)
    307 {
    308   gdb_assert(tp->ptid.lwp_p ());
    309   aarch64_debug_pending_threads.erase (tp->ptid.lwp ());
    310 }
    311 
    312 /* Implement the virtual fbsd_nat_target::low_prepare_to_resume method.  */
    313 
    314 void
    315 aarch64_fbsd_nat_target::low_prepare_to_resume (thread_info *tp)
    316 {
    317   gdb_assert(tp->ptid.lwp_p ());
    318 
    319   if (aarch64_debug_pending_threads.erase (tp->ptid.lwp ()) == 0)
    320     return;
    321 
    322   struct aarch64_debug_reg_state *state =
    323     aarch64_lookup_debug_reg_state (tp->ptid.pid ());
    324   gdb_assert(state != nullptr);
    325 
    326   struct dbreg reg;
    327   memset (&reg, 0, sizeof(reg));
    328   for (int i = 0; i < aarch64_num_bp_regs; i++)
    329     {
    330       reg.db_breakregs[i].dbr_addr = state->dr_addr_bp[i];
    331       reg.db_breakregs[i].dbr_ctrl = state->dr_ctrl_bp[i];
    332     }
    333   for (int i = 0; i < aarch64_num_wp_regs; i++)
    334     {
    335       reg.db_watchregs[i].dbw_addr = state->dr_addr_wp[i];
    336       reg.db_watchregs[i].dbw_ctrl = state->dr_ctrl_wp[i];
    337     }
    338   if (ptrace(PT_SETDBREGS, tp->ptid.lwp (), (PTRACE_TYPE_ARG3) &reg, 0) != 0)
    339     error (_("Failed to set hardware debug registers"));
    340 }
    341 #else
    342 /* A stub that should never be called.  */
    343 void
    344 aarch64_notify_debug_reg_change (ptid_t ptid,
    345 				 int is_watchpoint, unsigned int idx)
    346 {
    347   gdb_assert (true);
    348 }
    349 #endif
    350 
    351 void _initialize_aarch64_fbsd_nat ();
    352 void
    353 _initialize_aarch64_fbsd_nat ()
    354 {
    355 #ifdef HAVE_DBREG
    356   aarch64_initialize_hw_point ();
    357 #endif
    358   add_inf_child_target (&the_aarch64_fbsd_nat_target);
    359 }
    360