/* Return whether current thread is stopped due to a watchpoint. */ static int arm_stopped_by_watchpoint (void) { struct lwp_info *lwp = get_thread_lwp (current_thread); siginfo_t siginfo; /* We must be able to set hardware watchpoints. */ if (arm_linux_get_hw_watchpoint_count () == 0) return 0; /* Retrieve siginfo. */ errno = 0; ptrace (PTRACE_GETSIGINFO, lwpid_of (current_thread), 0, &siginfo); if (errno != 0) return 0; /* This must be a hardware breakpoint. */ if (siginfo.si_signo != SIGTRAP || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */) return 0; /* If we are in a positive slot then we're looking at a breakpoint and not a watchpoint. */ if (siginfo.si_errno >= 0) return 0; /* Cache stopped data address for use by arm_stopped_data_address. */ lwp->arch_private->stopped_data_address = (CORE_ADDR) (uintptr_t) siginfo.si_addr; return 1; }
/* Called when resuming a thread. If the debug regs have changed, update the thread's copies. */ static void arm_prepare_to_resume (struct lwp_info *lwp) { struct thread_info *thread = get_lwp_thread (lwp); int pid = lwpid_of (thread); struct process_info *proc = find_process_pid (pid_of (thread)); struct arch_process_info *proc_info = proc->priv->arch_private; struct arch_lwp_info *lwp_info = lwp->arch_private; int i; for (i = 0; i < arm_linux_get_hw_breakpoint_count (); i++) if (lwp_info->bpts_changed[i]) { errno = 0; if (arm_hwbp_control_is_enabled (proc_info->bpts[i].control)) if (ptrace (PTRACE_SETHBPREGS, pid, (PTRACE_TYPE_ARG3) ((i << 1) + 1), &proc_info->bpts[i].address) < 0) perror_with_name ("Unexpected error setting breakpoint address"); if (arm_hwbp_control_is_initialized (proc_info->bpts[i].control)) if (ptrace (PTRACE_SETHBPREGS, pid, (PTRACE_TYPE_ARG3) ((i << 1) + 2), &proc_info->bpts[i].control) < 0) perror_with_name ("Unexpected error setting breakpoint"); lwp_info->bpts_changed[i] = 0; } for (i = 0; i < arm_linux_get_hw_watchpoint_count (); i++) if (lwp_info->wpts_changed[i]) { errno = 0; if (arm_hwbp_control_is_enabled (proc_info->wpts[i].control)) if (ptrace (PTRACE_SETHBPREGS, pid, (PTRACE_TYPE_ARG3) -((i << 1) + 1), &proc_info->wpts[i].address) < 0) perror_with_name ("Unexpected error setting watchpoint address"); if (arm_hwbp_control_is_initialized (proc_info->wpts[i].control)) if (ptrace (PTRACE_SETHBPREGS, pid, (PTRACE_TYPE_ARG3) -((i << 1) + 2), &proc_info->wpts[i].control) < 0) perror_with_name ("Unexpected error setting watchpoint"); lwp_info->wpts_changed[i] = 0; } }
/* Remove hardware break-/watchpoint. */ static int arm_remove_point (enum raw_bkpt_type type, CORE_ADDR addr, int len, struct raw_breakpoint *bp) { struct process_info *proc = current_process (); struct arm_linux_hw_breakpoint p, *pts; int watch, i, count; watch = arm_linux_hw_point_initialize (type, addr, len, &p); if (watch < 0) { /* Unsupported. */ return -1; } if (watch) { count = arm_linux_get_hw_watchpoint_count (); pts = proc->priv->arch_private->wpts; } else { count = arm_linux_get_hw_breakpoint_count (); pts = proc->priv->arch_private->bpts; } for (i = 0; i < count; i++) if (arm_linux_hw_breakpoint_equal (&p, pts + i)) { pts[i].control = arm_hwbp_control_disable (pts[i].control); /* Only update the threads of the current process. */ for_each_thread (current_thread->id.pid (), [&] (thread_info *thread) { update_registers_callback (thread, watch, i); }); return 0; } /* No watchpoint matched. */ return -1; }
/* Remove hardware break-/watchpoint. */ static int arm_remove_point (enum raw_bkpt_type type, CORE_ADDR addr, int len, struct raw_breakpoint *bp) { struct process_info *proc = current_process (); struct arm_linux_hw_breakpoint p, *pts; int watch, i, count; watch = arm_linux_hw_point_initialize (type, addr, len, &p); if (watch < 0) { /* Unsupported. */ return -1; } if (watch) { count = arm_linux_get_hw_watchpoint_count (); pts = proc->priv->arch_private->wpts; } else { count = arm_linux_get_hw_breakpoint_count (); pts = proc->priv->arch_private->bpts; } for (i = 0; i < count; i++) if (arm_linux_hw_breakpoint_equal (&p, pts + i)) { struct update_registers_data data = { watch, i }; pts[i].control = arm_hwbp_control_disable (pts[i].control); find_inferior (&all_threads, update_registers_callback, &data); return 0; } /* No watchpoint matched. */ return -1; }
/* Insert hardware break-/watchpoint. */ static int arm_insert_point (enum raw_bkpt_type type, CORE_ADDR addr, int len, struct raw_breakpoint *bp) { struct process_info *proc = current_process (); struct arm_linux_hw_breakpoint p, *pts; int watch, i, count; watch = arm_linux_hw_point_initialize (type, addr, len, &p); if (watch < 0) { /* Unsupported. */ return watch == -1 ? 1 : -1; } if (watch) { count = arm_linux_get_hw_watchpoint_count (); pts = proc->priv->arch_private->wpts; } else { count = arm_linux_get_hw_breakpoint_count (); pts = proc->priv->arch_private->bpts; } for (i = 0; i < count; i++) if (!arm_hwbp_control_is_enabled (pts[i].control)) { struct update_registers_data data = { watch, i }; pts[i] = p; find_inferior (&all_threads, update_registers_callback, &data); return 0; } /* We're out of watchpoints. */ return -1; }