From 769ef81fec526f3c7513c88e82f98045f8971d14 Mon Sep 17 00:00:00 2001 From: Antoine Tremblay Date: Mon, 30 Nov 2015 15:08:04 -0500 Subject: [PATCH] Fix breakpoint size when stepping over a permanent breakpoint in GDBServer. When manually stepping over a permanent breakpoint on ARM we need to fetch the right breakpoint size based on the current instruction set used. Since this is not encoded in the stop_pc, the instruction mode needs to be fetched from the CPSR register. This is done by introducing a new target operation called : breakpoint_kind_from_current_state. For other targets that do not need this, breakpoint_kind_from_pc is used. No regressions, tested on ubuntu 14.04 ARMv7 and x86. With gdbserver-{native,extended} / { -marm -mthumb } gdb/gdbserver/ChangeLog: * linux-arm-low.c (arm_is_thumb_mode): New function. (arm_breakpoint_at): Use arm_is_thumb_mode. (arm_breakpoint_kind_from_current_state): New function. (struct linux_target_ops) : Initialize. * linux-low.c (linux_wait_1): Call breakpoint_kind_from_current_state. (linux_breakpoint_kind_from_current_state): New function. (struct target_ops : Initialize. * linux-low.h (struct linux_target_ops) : New field. * target.h (struct target_ops): Likewise. (target_breakpoint_kind_from_current_state): New macro. --- gdb/gdbserver/ChangeLog | 15 +++++++++++++ gdb/gdbserver/linux-arm-low.c | 40 ++++++++++++++++++++++++++++++++++- gdb/gdbserver/linux-low.c | 16 +++++++++++++- gdb/gdbserver/linux-low.h | 3 +++ gdb/gdbserver/target.h | 11 ++++++++++ 5 files changed, 83 insertions(+), 2 deletions(-) diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 6330c03236..87ce9e7eef 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,18 @@ +2015-11-30 Antoine Tremblay + + * linux-arm-low.c (arm_is_thumb_mode): New function. + (arm_breakpoint_at): Use arm_is_thumb_mode. + (arm_breakpoint_kind_from_current_state): New function. + (struct linux_target_ops) : + Initialize. + * linux-low.c (linux_wait_1): Call breakpoint_kind_from_current_state. + (linux_breakpoint_kind_from_current_state): New function. + (struct target_ops : Initialize. + * linux-low.h (struct linux_target_ops) + : New field. + * target.h (struct target_ops): Likewise. + (target_breakpoint_kind_from_current_state): New macro. + 2015-11-30 Pedro Alves * linux-low.c (linux_resume): Wake up the event loop before diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c index dda37cb36a..6f37f58109 100644 --- a/gdb/gdbserver/linux-arm-low.c +++ b/gdb/gdbserver/linux-arm-low.c @@ -264,8 +264,10 @@ static const unsigned short thumb_breakpoint = 0xde01; static const unsigned short thumb2_breakpoint[] = { 0xf7f0, 0xa000 }; #define thumb2_breakpoint_len 4 +/* Returns 1 if the current instruction set is thumb, 0 otherwise. */ + static int -arm_breakpoint_at (CORE_ADDR where) +arm_is_thumb_mode (void) { struct regcache *regcache = get_thread_regcache (current_thread, 1); unsigned long cpsr; @@ -273,6 +275,17 @@ arm_breakpoint_at (CORE_ADDR where) collect_register_by_name (regcache, "cpsr", &cpsr); if (cpsr & 0x20) + return 1; + else + return 0; +} + +/* Returns 1 if there is a software breakpoint at location. */ + +static int +arm_breakpoint_at (CORE_ADDR where) +{ + if (arm_is_thumb_mode ()) { /* Thumb mode. */ unsigned short insn; @@ -996,6 +1009,23 @@ arm_sw_breakpoint_from_kind (int kind , int *size) return NULL; } +/* Implementation of the linux_target_ops method + "breakpoint_kind_from_current_state". */ + +static int +arm_breakpoint_kind_from_current_state (CORE_ADDR *pcptr) +{ + if (arm_is_thumb_mode ()) + { + *pcptr = MAKE_THUMB_ADDR (*pcptr); + return arm_breakpoint_kind_from_pc (pcptr); + } + else + { + return arm_breakpoint_kind_from_pc (pcptr); + } +} + struct linux_target_ops the_low_target = { arm_arch_setup, arm_regs_info, @@ -1021,6 +1051,14 @@ struct linux_target_ops the_low_target = { arm_new_thread, arm_new_fork, arm_prepare_to_resume, + NULL, /* process_qsupported */ + NULL, /* supports_tracepoints */ + NULL, /* get_thread_area */ + NULL, /* install_fast_tracepoint_jump_pad */ + NULL, /* emit_ops */ + NULL, /* get_min_fast_tracepoint_insn_len */ + NULL, /* supports_range_stepping */ + arm_breakpoint_kind_from_current_state }; void diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 086b202d94..207a5ba90b 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -3084,7 +3084,8 @@ linux_wait_1 (ptid_t ptid, int breakpoint_kind = 0; CORE_ADDR stop_pc = event_child->stop_pc; - breakpoint_kind = the_target->breakpoint_kind_from_pc (&stop_pc); + breakpoint_kind = + the_target->breakpoint_kind_from_current_state (&stop_pc); the_target->sw_breakpoint_from_kind (breakpoint_kind, &increment_pc); if (debug_threads) @@ -7036,6 +7037,18 @@ linux_sw_breakpoint_from_kind (int kind, int *size) return (*the_low_target.sw_breakpoint_from_kind) (kind, size); } +/* Implementation of the target_ops method + "breakpoint_kind_from_current_state". */ + +static int +linux_breakpoint_kind_from_current_state (CORE_ADDR *pcptr) +{ + if (the_low_target.breakpoint_kind_from_current_state != NULL) + return (*the_low_target.breakpoint_kind_from_current_state) (pcptr); + else + return linux_breakpoint_kind_from_pc (pcptr); +} + static struct target_ops linux_target_ops = { linux_create_inferior, linux_arch_setup, @@ -7133,6 +7146,7 @@ static struct target_ops linux_target_ops = { linux_breakpoint_kind_from_pc, linux_sw_breakpoint_from_kind, linux_proc_tid_get_name, + linux_breakpoint_kind_from_current_state }; static void diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index f1d4f0f5a4..e2bfeb70c1 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -233,6 +233,9 @@ struct linux_target_ops /* Returns true if the low target supports range stepping. */ int (*supports_range_stepping) (void); + + /* See target.h. */ + int (*breakpoint_kind_from_current_state) (CORE_ADDR *pcptr); }; extern struct linux_target_ops the_low_target; diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index a09ba2fd54..d3fcb36033 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -457,6 +457,12 @@ struct target_ops /* Return the thread's name, or NULL if the target is unable to determine it. The returned value must not be freed by the caller. */ const char *(*thread_name) (ptid_t thread); + + /* Return the breakpoint kind for this target based on the current + processor state (e.g. the current instruction mode on ARM) and the + PC. The PCPTR is adjusted to the real memory location in case a flag + (e.g., the Thumb bit on ARM) is present in the PC. */ + int (*breakpoint_kind_from_current_state) (CORE_ADDR *pcptr); }; extern struct target_ops *the_target; @@ -644,6 +650,11 @@ int kill_inferior (int); ? (*the_target->breakpoint_kind_from_pc) (pcptr) \ : default_breakpoint_kind_from_pc (pcptr)) +#define target_breakpoint_kind_from_current_state(pcptr) \ + (the_target->breakpoint_kind_from_current_state \ + ? (*the_target->breakpoint_kind_from_current_state) (pcptr) \ + : target_breakpoint_kind_from_pc (pcptr)) + /* Start non-stop mode, returns 0 on success, -1 on failure. */ int start_non_stop (int nonstop);