diff --git a/SOURCES/_gdb.spec.Patch.include b/SOURCES/_gdb.spec.Patch.include index f9e20b1..d2efe9e 100644 --- a/SOURCES/_gdb.spec.Patch.include +++ b/SOURCES/_gdb.spec.Patch.include @@ -751,3 +751,35 @@ Patch183: gdb-rhbz1905701-DWARF-data_location.patch # Andreas Arnaz (RH BZ 1903374) Patch184: gdb-rhbz1903374-s390x-store-on-condition.patch +# Fix segfault with nameless fortran modules. +# Bernhard Heckel, RH BZ 1943673 +Patch185: gdb-rhbz1934673-fortran-nameless-modules.patch + +# Backport "Add low_new_clone method to linux_nat_target." +# (Pedro Franco de Carvalho, RH BZ 1854784) +Patch186: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-1of7.patch + +# Backport "Add linux_get_hwcap" +# (Alan Hayward, RH BZ 1854784) +Patch187: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-2of7.patch + +# Backport "[PowerPC] Move up some register access routines" +# (Pedro Franco de Carvalho, RH BZ 1854784) +Patch188: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-3of7.patch + +# Backport "[PowerPC] Fix debug register issues in ppc-linux-nat" +# (Pedro Franco de Carvalho, RH BZ 1854784) +Patch189: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-4of7.patch + +# Backport "[PowerPC] Use < 0 and >= 0 for watchpoint ptrace calls" +# (Pedro Franco de Carvalho, RH BZ 1854784) +Patch190: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-5of7.patch + +# Backport "[PowerPC] Always clear watchpoint with PTRACE_SET_DEBUGREG" +# (Pedro Franco de Carvalho, RH BZ 1854784) +Patch191: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-6of7.patch + +# Backport "PowerPC remove 512 bytes region limit if 2nd DAWR is available." +# (Rogerio Alves, RH BZ 1854784) +Patch192: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-7of7.patch + diff --git a/SOURCES/_gdb.spec.patch.include b/SOURCES/_gdb.spec.patch.include index f14d36a..13ee90b 100644 --- a/SOURCES/_gdb.spec.patch.include +++ b/SOURCES/_gdb.spec.patch.include @@ -182,3 +182,11 @@ %patch182 -p1 %patch183 -p1 %patch184 -p1 +%patch185 -p1 +%patch186 -p1 +%patch187 -p1 +%patch188 -p1 +%patch189 -p1 +%patch190 -p1 +%patch191 -p1 +%patch192 -p1 diff --git a/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-1of7.patch b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-1of7.patch new file mode 100644 index 0000000..096e92b --- /dev/null +++ b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-1of7.patch @@ -0,0 +1,49 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Pedro Franco de Carvalho +Date: Wed, 7 Jul 2021 15:44:29 -0400 +Subject: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-1of7.patch + +;; Backport "Add low_new_clone method to linux_nat_target." +;; (Pedro Franco de Carvalho, RH BZ 1854784) + +This patch adds a low_new_clone method to linux_nat_target, called after +a PTRACE_EVENT_CLONE is detected, similar to how low_new_fork is called +after PTRACE_EVENT_(V)FORK. + +This is useful for targets that need to copy state associated with a +thread that is inherited across clones. + +gdb/ChangeLog: +2020-03-30 Pedro Franco de Carvalho + + * linux-nat.h (low_new_clone): New method. + * linux-nat.c (linux_handle_extended_wait): Call low_new_clone. + +diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c +--- a/gdb/linux-nat.c ++++ b/gdb/linux-nat.c +@@ -2020,6 +2020,10 @@ linux_handle_extended_wait (struct lwp_info *lp, int status) + inferior. */ + linux_target->low_new_fork (lp, new_pid); + } ++ else if (event == PTRACE_EVENT_CLONE) ++ { ++ linux_target->low_new_clone (lp, new_pid); ++ } + + if (event == PTRACE_EVENT_FORK + && linux_fork_checkpointing_p (lp->ptid.pid ())) +diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h +--- a/gdb/linux-nat.h ++++ b/gdb/linux-nat.h +@@ -160,6 +160,10 @@ public: + virtual void low_new_fork (struct lwp_info *parent, pid_t child_pid) + {} + ++ /* The method to call, if any, when a new clone event is detected. */ ++ virtual void low_new_clone (struct lwp_info *parent, pid_t child_lwp) ++ {} ++ + /* The method to call, if any, when a process is no longer + attached. */ + virtual void low_forget_process (pid_t pid) diff --git a/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-2of7.patch b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-2of7.patch new file mode 100644 index 0000000..232ce94 --- /dev/null +++ b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-2of7.patch @@ -0,0 +1,311 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Alan Hayward +Date: Wed, 7 Jul 2021 18:13:44 -0400 +Subject: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-2of7.patch + +;; Backport "Add linux_get_hwcap" +;; (Alan Hayward, RH BZ 1854784) + +Tidy up calls to read HWCAP (and HWCAP2) by adding common functions, +removing the PPC and AArch64 specific versions. + +The only function difference is in aarch64_linux_core_read_description - if +the hwcap read fails it now return a valid description instead of nullptr. + +gdb/ChangeLog: + +2019-03-25 Alan Hayward + + * aarch64-linux-nat.c (aarch64_linux_nat_target::read_description): + Call linux_get_hwcap. + * aarch64-linux-tdep.c (aarch64_linux_core_read_description): + Likewise. + (aarch64_linux_get_hwcap): Remove function. + * aarch64-linux-tdep.h (aarch64_linux_get_hwcap): Remove + declaration. + * arm-linux-nat.c (arm_linux_nat_target::read_description):Call + linux_get_hwcap. + * arm-linux-tdep.c (arm_linux_core_read_description): Likewise. + * linux-tdep.c (linux_get_hwcap): Add function. + (linux_get_hwcap2): Likewise. + * linux-tdep.h (linux_get_hwcap): Add declaration. + (linux_get_hwcap2): Likewise. + * ppc-linux-nat.c (ppc_linux_get_hwcap): Remove function. + (ppc_linux_get_hwcap2): Likewise. + (ppc_linux_nat_target::region_ok_for_hw_watchpoint): Call + linux_get_hwcap. + (ppc_linux_nat_target::insert_watchpoint): Likewise. + (ppc_linux_nat_target::watchpoint_addr_within_range): Likewise. + (ppc_linux_nat_target::read_description): Likewise. + * ppc-linux-tdep.c (ppc_linux_core_read_description): Likewise. + * s390-linux-nat.c: Likewise. + * s390-linux-tdep.c (s390_core_read_description): Likewise. + +diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c +--- a/gdb/aarch64-linux-nat.c ++++ b/gdb/aarch64-linux-nat.c +@@ -42,6 +42,7 @@ + #include + + #include "gregset.h" ++#include "linux-tdep.h" + + /* Defines ps_err_e, struct ps_prochandle. */ + #include "gdb_proc_service.h" +@@ -605,8 +606,7 @@ aarch64_linux_nat_target::read_description () + ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_VFP, &iovec); + if (ret == 0) + return tdesc_arm_with_neon; +- else +- return aarch64_read_description (aarch64_sve_get_vq (tid)); ++ else return aarch64_read_description (aarch64_sve_get_vq (tid)); + } + + /* Convert a native/host siginfo object, into/from the siginfo in the +diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c +--- a/gdb/aarch64-linux-tdep.c ++++ b/gdb/aarch64-linux-tdep.c +@@ -442,10 +442,7 @@ static const struct target_desc * + aarch64_linux_core_read_description (struct gdbarch *gdbarch, + struct target_ops *target, bfd *abfd) + { +- CORE_ADDR aarch64_hwcap = 0; +- +- if (target_auxv_search (target, AT_HWCAP, &aarch64_hwcap) != 1) +- return NULL; ++ CORE_ADDR hwcap = linux_get_hwcap (target); + + return aarch64_read_description (aarch64_linux_core_read_vq (gdbarch, abfd)); + } +diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c +--- a/gdb/arm-linux-nat.c ++++ b/gdb/arm-linux-nat.c +@@ -533,7 +533,7 @@ ps_get_thread_area (struct ps_prochandle *ph, + const struct target_desc * + arm_linux_nat_target::read_description () + { +- CORE_ADDR arm_hwcap = 0; ++ CORE_ADDR arm_hwcap = linux_get_hwcap (this); + + if (have_ptrace_getregset == TRIBOOL_UNKNOWN) + { +@@ -551,11 +551,6 @@ arm_linux_nat_target::read_description () + have_ptrace_getregset = TRIBOOL_TRUE; + } + +- if (target_auxv_search (this, AT_HWCAP, &arm_hwcap) != 1) +- { +- return this->beneath ()->read_description (); +- } +- + if (arm_hwcap & HWCAP_IWMMXT) + return tdesc_arm_with_iwmmxt; + +diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c +--- a/gdb/arm-linux-tdep.c ++++ b/gdb/arm-linux-tdep.c +@@ -730,10 +730,7 @@ arm_linux_core_read_description (struct gdbarch *gdbarch, + struct target_ops *target, + bfd *abfd) + { +- CORE_ADDR arm_hwcap = 0; +- +- if (target_auxv_search (target, AT_HWCAP, &arm_hwcap) != 1) +- return NULL; ++ CORE_ADDR arm_hwcap = linux_get_hwcap (target); + + if (arm_hwcap & HWCAP_VFP) + { +diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c +--- a/gdb/linux-tdep.c ++++ b/gdb/linux-tdep.c +@@ -2567,6 +2567,28 @@ linux_displaced_step_location (struct gdbarch *gdbarch) + return addr; + } + ++/* See linux-tdep.h. */ ++ ++CORE_ADDR ++linux_get_hwcap (struct target_ops *target) ++{ ++ CORE_ADDR field; ++ if (target_auxv_search (target, AT_HWCAP, &field) != 1) ++ return 0; ++ return field; ++} ++ ++/* See linux-tdep.h. */ ++ ++CORE_ADDR ++linux_get_hwcap2 (struct target_ops *target) ++{ ++ CORE_ADDR field; ++ if (target_auxv_search (target, AT_HWCAP2, &field) != 1) ++ return 0; ++ return field; ++} ++ + /* Display whether the gcore command is using the + /proc/PID/coredump_filter file. */ + +diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h +--- a/gdb/linux-tdep.h ++++ b/gdb/linux-tdep.h +@@ -61,4 +61,12 @@ extern void linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch); + + extern int linux_is_uclinux (void); + ++/* Fetch the AT_HWCAP entry from the auxv vector for the given TARGET. On ++ error, 0 is returned. */ ++extern CORE_ADDR linux_get_hwcap (struct target_ops *target); ++ ++/* Fetch the AT_HWCAP2 entry from the auxv vector for the given TARGET. On ++ error, 0 is returned. */ ++extern CORE_ADDR linux_get_hwcap2 (struct target_ops *target); ++ + #endif /* linux-tdep.h */ +diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c +--- a/gdb/ppc-linux-nat.c ++++ b/gdb/ppc-linux-nat.c +@@ -50,6 +50,7 @@ + #include "arch/ppc-linux-common.h" + #include "arch/ppc-linux-tdesc.h" + #include "nat/ppc-linux.h" ++#include "linux-tdep.h" + + /* Similarly for the hardware watchpoint support. These requests are used + when the PowerPC HWDEBUG ptrace interface is not available. */ +@@ -1560,31 +1561,6 @@ store_ppc_registers (const struct regcache *regcache, int tid) + function to fail most of the time, so we ignore them. */ + } + +-/* Fetch the AT_HWCAP entry from the aux vector. */ +-static CORE_ADDR +-ppc_linux_get_hwcap (void) +-{ +- CORE_ADDR field; +- +- if (target_auxv_search (current_top_target (), AT_HWCAP, &field) != 1) +- return 0; +- +- return field; +-} +- +-/* Fetch the AT_HWCAP2 entry from the aux vector. */ +- +-static CORE_ADDR +-ppc_linux_get_hwcap2 (void) +-{ +- CORE_ADDR field; +- +- if (target_auxv_search (current_top_target (), AT_HWCAP2, &field) != 1) +- return 0; +- +- return field; +-} +- + /* The cached DABR value, to install in new threads. + This variable is used when the PowerPC HWDEBUG ptrace + interface is not available. */ +@@ -1741,7 +1717,7 @@ ppc_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len) + takes two hardware watchpoints though. */ + if (len > 1 + && hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE +- && ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE) ++ && linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE) + return 2; + /* Check if the processor provides DAWR interface. */ + if (hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR) +@@ -1761,7 +1737,7 @@ ppc_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len) + ptrace interface, DAC-based processors (i.e., embedded processors) will + use addresses aligned to 4-bytes due to the way the read/write flags are + passed in the old ptrace interface. */ +- else if (((ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE) ++ else if (((linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE) + && (addr + len) > (addr & ~3) + 4) + || (addr + len) > (addr & ~7) + 8) + return 0; +@@ -2303,7 +2279,7 @@ ppc_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len, + long dabr_value; + long read_mode, write_mode; + +- if (ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE) ++ if (linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE) + { + /* PowerPC 440 requires only the read/write flags to be passed + to the kernel. */ +@@ -2506,9 +2482,9 @@ ppc_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr, + int mask; + + if (have_ptrace_hwdebug_interface () +- && ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE) ++ && linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE) + return start <= addr && start + length >= addr; +- else if (ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE) ++ else if (linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE) + mask = 3; + else + mask = 7; +@@ -2646,8 +2622,8 @@ ppc_linux_nat_target::read_description () + + features.wordsize = ppc_linux_target_wordsize (tid); + +- CORE_ADDR hwcap = ppc_linux_get_hwcap (); +- CORE_ADDR hwcap2 = ppc_linux_get_hwcap2 (); ++ CORE_ADDR hwcap = linux_get_hwcap (current_top_target ()); ++ CORE_ADDR hwcap2 = linux_get_hwcap2 (current_top_target ()); + + if (have_ptrace_getsetvsxregs + && (hwcap & PPC_FEATURE_HAS_VSX)) +diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c +--- a/gdb/ppc-linux-tdep.c ++++ b/gdb/ppc-linux-tdep.c +@@ -1602,10 +1602,7 @@ ppc_linux_core_read_description (struct gdbarch *gdbarch, + if (vsx) + features.vsx = true; + +- CORE_ADDR hwcap; +- +- if (target_auxv_search (target, AT_HWCAP, &hwcap) != 1) +- hwcap = 0; ++ CORE_ADDR hwcap = linux_get_hwcap (target); + + features.isa205 = ppc_linux_has_isa205 (hwcap); + +diff --git a/gdb/s390-linux-nat.c b/gdb/s390-linux-nat.c +--- a/gdb/s390-linux-nat.c ++++ b/gdb/s390-linux-nat.c +@@ -42,6 +42,7 @@ + #include + #include + #include "inf-ptrace.h" ++#include "linux-tdep.h" + + /* Per-thread arch-specific data. */ + +@@ -1018,9 +1019,8 @@ s390_linux_nat_target::read_description () + that mode, report s390 architecture with 64-bit GPRs. */ + #ifdef __s390x__ + { +- CORE_ADDR hwcap = 0; ++ CORE_ADDR hwcap = linux_get_hwcap (current_top_target ()); + +- target_auxv_search (current_top_target (), AT_HWCAP, &hwcap); + have_regset_tdb = (hwcap & HWCAP_S390_TE) + && check_regset (tid, NT_S390_TDB, s390_sizeof_tdbregset); + +diff --git a/gdb/s390-linux-tdep.c b/gdb/s390-linux-tdep.c +--- a/gdb/s390-linux-tdep.c ++++ b/gdb/s390-linux-tdep.c +@@ -332,10 +332,9 @@ s390_core_read_description (struct gdbarch *gdbarch, + struct target_ops *target, bfd *abfd) + { + asection *section = bfd_get_section_by_name (abfd, ".reg"); +- CORE_ADDR hwcap = 0; ++ CORE_ADDR hwcap = linux_get_hwcap (target); + bool high_gprs, v1, v2, te, vx, gs; + +- target_auxv_search (target, AT_HWCAP, &hwcap); + if (!section) + return NULL; + diff --git a/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-3of7.patch b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-3of7.patch new file mode 100644 index 0000000..31d60fe --- /dev/null +++ b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-3of7.patch @@ -0,0 +1,368 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Pedro Franco de Carvalho +Date: Wed, 7 Jul 2021 18:45:37 -0400 +Subject: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-3of7.patch + +;; Backport "[PowerPC] Move up some register access routines" +;; (Pedro Franco de Carvalho, RH BZ 1854784) + +Keep the routines related to register access grouped together. + +gdb/ChangeLog: +2020-03-30 Pedro Franco de Carvalho + + * ppc-linux-nat.c (ppc_linux_nat_target::store_registers) + (ppc_linux_nat_target::auxv_parse) + (ppc_linux_nat_target::read_description) + (supply_gregset, fill_gregset, supply_fpregset, fill_fpregset): + Move up. + +diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c +--- a/gdb/ppc-linux-nat.c ++++ b/gdb/ppc-linux-nat.c +@@ -1561,6 +1561,170 @@ store_ppc_registers (const struct regcache *regcache, int tid) + function to fail most of the time, so we ignore them. */ + } + ++void ++ppc_linux_nat_target::store_registers (struct regcache *regcache, int regno) ++{ ++ pid_t tid = get_ptrace_pid (regcache->ptid ()); ++ ++ if (regno >= 0) ++ store_register (regcache, tid, regno); ++ else ++ store_ppc_registers (regcache, tid); ++} ++ ++/* Functions for transferring registers between a gregset_t or fpregset_t ++ (see sys/ucontext.h) and gdb's regcache. The word size is that used ++ by the ptrace interface, not the current program's ABI. Eg. if a ++ powerpc64-linux gdb is being used to debug a powerpc32-linux app, we ++ read or write 64-bit gregsets. This is to suit the host libthread_db. */ ++ ++void ++supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp) ++{ ++ const struct regset *regset = ppc_linux_gregset (sizeof (long)); ++ ++ ppc_supply_gregset (regset, regcache, -1, gregsetp, sizeof (*gregsetp)); ++} ++ ++void ++fill_gregset (const struct regcache *regcache, ++ gdb_gregset_t *gregsetp, int regno) ++{ ++ const struct regset *regset = ppc_linux_gregset (sizeof (long)); ++ ++ if (regno == -1) ++ memset (gregsetp, 0, sizeof (*gregsetp)); ++ ppc_collect_gregset (regset, regcache, regno, gregsetp, sizeof (*gregsetp)); ++} ++ ++void ++supply_fpregset (struct regcache *regcache, const gdb_fpregset_t * fpregsetp) ++{ ++ const struct regset *regset = ppc_linux_fpregset (); ++ ++ ppc_supply_fpregset (regset, regcache, -1, ++ fpregsetp, sizeof (*fpregsetp)); ++} ++ ++void ++fill_fpregset (const struct regcache *regcache, ++ gdb_fpregset_t *fpregsetp, int regno) ++{ ++ const struct regset *regset = ppc_linux_fpregset (); ++ ++ ppc_collect_fpregset (regset, regcache, regno, ++ fpregsetp, sizeof (*fpregsetp)); ++} ++ ++int ++ppc_linux_nat_target::auxv_parse (gdb_byte **readptr, ++ gdb_byte *endptr, CORE_ADDR *typep, ++ CORE_ADDR *valp) ++{ ++ int tid = inferior_ptid.lwp (); ++ if (tid == 0) ++ tid = inferior_ptid.pid (); ++ ++ int sizeof_auxv_field = ppc_linux_target_wordsize (tid); ++ ++ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); ++ gdb_byte *ptr = *readptr; ++ ++ if (endptr == ptr) ++ return 0; ++ ++ if (endptr - ptr < sizeof_auxv_field * 2) ++ return -1; ++ ++ *typep = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order); ++ ptr += sizeof_auxv_field; ++ *valp = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order); ++ ptr += sizeof_auxv_field; ++ ++ *readptr = ptr; ++ return 1; ++} ++ ++const struct target_desc * ++ppc_linux_nat_target::read_description () ++{ ++ int tid = inferior_ptid.lwp (); ++ if (tid == 0) ++ tid = inferior_ptid.pid (); ++ ++ if (have_ptrace_getsetevrregs) ++ { ++ struct gdb_evrregset_t evrregset; ++ ++ if (ptrace (PTRACE_GETEVRREGS, tid, 0, &evrregset) >= 0) ++ return tdesc_powerpc_e500l; ++ ++ /* EIO means that the PTRACE_GETEVRREGS request isn't supported. ++ Anything else needs to be reported. */ ++ else if (errno != EIO) ++ perror_with_name (_("Unable to fetch SPE registers")); ++ } ++ ++ struct ppc_linux_features features = ppc_linux_no_features; ++ ++ features.wordsize = ppc_linux_target_wordsize (tid); ++ ++ CORE_ADDR hwcap = linux_get_hwcap (current_top_target ()); ++ CORE_ADDR hwcap2 = linux_get_hwcap2 (current_top_target ()); ++ ++ if (have_ptrace_getsetvsxregs ++ && (hwcap & PPC_FEATURE_HAS_VSX)) ++ { ++ gdb_vsxregset_t vsxregset; ++ ++ if (ptrace (PTRACE_GETVSXREGS, tid, 0, &vsxregset) >= 0) ++ features.vsx = true; ++ ++ /* EIO means that the PTRACE_GETVSXREGS request isn't supported. ++ Anything else needs to be reported. */ ++ else if (errno != EIO) ++ perror_with_name (_("Unable to fetch VSX registers")); ++ } ++ ++ if (have_ptrace_getvrregs ++ && (hwcap & PPC_FEATURE_HAS_ALTIVEC)) ++ { ++ gdb_vrregset_t vrregset; ++ ++ if (ptrace (PTRACE_GETVRREGS, tid, 0, &vrregset) >= 0) ++ features.altivec = true; ++ ++ /* EIO means that the PTRACE_GETVRREGS request isn't supported. ++ Anything else needs to be reported. */ ++ else if (errno != EIO) ++ perror_with_name (_("Unable to fetch AltiVec registers")); ++ } ++ ++ features.isa205 = ppc_linux_has_isa205 (hwcap); ++ ++ if ((hwcap2 & PPC_FEATURE2_DSCR) ++ && check_regset (tid, NT_PPC_PPR, PPC_LINUX_SIZEOF_PPRREGSET) ++ && check_regset (tid, NT_PPC_DSCR, PPC_LINUX_SIZEOF_DSCRREGSET)) ++ { ++ features.ppr_dscr = true; ++ if ((hwcap2 & PPC_FEATURE2_ARCH_2_07) ++ && (hwcap2 & PPC_FEATURE2_TAR) ++ && (hwcap2 & PPC_FEATURE2_EBB) ++ && check_regset (tid, NT_PPC_TAR, PPC_LINUX_SIZEOF_TARREGSET) ++ && check_regset (tid, NT_PPC_EBB, PPC_LINUX_SIZEOF_EBBREGSET) ++ && check_regset (tid, NT_PPC_PMU, PPC_LINUX_SIZEOF_PMUREGSET)) ++ { ++ features.isa207 = true; ++ if ((hwcap2 & PPC_FEATURE2_HTM) ++ && check_regset (tid, NT_PPC_TM_SPR, ++ PPC_LINUX_SIZEOF_TM_SPRREGSET)) ++ features.htm = true; ++ } ++ } ++ ++ return ppc_linux_match_description (features); ++} ++ + /* The cached DABR value, to install in new threads. + This variable is used when the PowerPC HWDEBUG ptrace + interface is not available. */ +@@ -2514,173 +2678,7 @@ ppc_linux_nat_target::masked_watch_num_registers (CORE_ADDR addr, CORE_ADDR mask + return 2; + } + +-void +-ppc_linux_nat_target::store_registers (struct regcache *regcache, int regno) +-{ +- pid_t tid = get_ptrace_pid (regcache->ptid ()); +- +- if (regno >= 0) +- store_register (regcache, tid, regno); +- else +- store_ppc_registers (regcache, tid); +-} +- +-/* Functions for transferring registers between a gregset_t or fpregset_t +- (see sys/ucontext.h) and gdb's regcache. The word size is that used +- by the ptrace interface, not the current program's ABI. Eg. if a +- powerpc64-linux gdb is being used to debug a powerpc32-linux app, we +- read or write 64-bit gregsets. This is to suit the host libthread_db. */ +- +-void +-supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp) +-{ +- const struct regset *regset = ppc_linux_gregset (sizeof (long)); +- +- ppc_supply_gregset (regset, regcache, -1, gregsetp, sizeof (*gregsetp)); +-} +- +-void +-fill_gregset (const struct regcache *regcache, +- gdb_gregset_t *gregsetp, int regno) +-{ +- const struct regset *regset = ppc_linux_gregset (sizeof (long)); +- +- if (regno == -1) +- memset (gregsetp, 0, sizeof (*gregsetp)); +- ppc_collect_gregset (regset, regcache, regno, gregsetp, sizeof (*gregsetp)); +-} +- +-void +-supply_fpregset (struct regcache *regcache, const gdb_fpregset_t * fpregsetp) +-{ +- const struct regset *regset = ppc_linux_fpregset (); +- +- ppc_supply_fpregset (regset, regcache, -1, +- fpregsetp, sizeof (*fpregsetp)); +-} +- +-void +-fill_fpregset (const struct regcache *regcache, +- gdb_fpregset_t *fpregsetp, int regno) +-{ +- const struct regset *regset = ppc_linux_fpregset (); +- +- ppc_collect_fpregset (regset, regcache, regno, +- fpregsetp, sizeof (*fpregsetp)); +-} +- +-int +-ppc_linux_nat_target::auxv_parse (gdb_byte **readptr, +- gdb_byte *endptr, CORE_ADDR *typep, +- CORE_ADDR *valp) +-{ +- int tid = inferior_ptid.lwp (); +- if (tid == 0) +- tid = inferior_ptid.pid (); +- +- int sizeof_auxv_field = ppc_linux_target_wordsize (tid); +- +- enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); +- gdb_byte *ptr = *readptr; +- +- if (endptr == ptr) +- return 0; +- +- if (endptr - ptr < sizeof_auxv_field * 2) +- return -1; +- +- *typep = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order); +- ptr += sizeof_auxv_field; +- *valp = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order); +- ptr += sizeof_auxv_field; +- +- *readptr = ptr; +- return 1; +-} +- +-const struct target_desc * +-ppc_linux_nat_target::read_description () +-{ +- int tid = inferior_ptid.lwp (); +- if (tid == 0) +- tid = inferior_ptid.pid (); +- +- if (have_ptrace_getsetevrregs) +- { +- struct gdb_evrregset_t evrregset; +- +- if (ptrace (PTRACE_GETEVRREGS, tid, 0, &evrregset) >= 0) +- return tdesc_powerpc_e500l; +- +- /* EIO means that the PTRACE_GETEVRREGS request isn't supported. +- Anything else needs to be reported. */ +- else if (errno != EIO) +- perror_with_name (_("Unable to fetch SPE registers")); +- } +- +- struct ppc_linux_features features = ppc_linux_no_features; +- +- features.wordsize = ppc_linux_target_wordsize (tid); +- +- CORE_ADDR hwcap = linux_get_hwcap (current_top_target ()); +- CORE_ADDR hwcap2 = linux_get_hwcap2 (current_top_target ()); +- +- if (have_ptrace_getsetvsxregs +- && (hwcap & PPC_FEATURE_HAS_VSX)) +- { +- gdb_vsxregset_t vsxregset; +- +- if (ptrace (PTRACE_GETVSXREGS, tid, 0, &vsxregset) >= 0) +- features.vsx = true; +- +- /* EIO means that the PTRACE_GETVSXREGS request isn't supported. +- Anything else needs to be reported. */ +- else if (errno != EIO) +- perror_with_name (_("Unable to fetch VSX registers")); +- } +- +- if (have_ptrace_getvrregs +- && (hwcap & PPC_FEATURE_HAS_ALTIVEC)) +- { +- gdb_vrregset_t vrregset; +- +- if (ptrace (PTRACE_GETVRREGS, tid, 0, &vrregset) >= 0) +- features.altivec = true; +- +- /* EIO means that the PTRACE_GETVRREGS request isn't supported. +- Anything else needs to be reported. */ +- else if (errno != EIO) +- perror_with_name (_("Unable to fetch AltiVec registers")); +- } +- +- if (hwcap & PPC_FEATURE_CELL) +- features.cell = true; +- +- features.isa205 = ppc_linux_has_isa205 (hwcap); +- +- if ((hwcap2 & PPC_FEATURE2_DSCR) +- && check_regset (tid, NT_PPC_PPR, PPC_LINUX_SIZEOF_PPRREGSET) +- && check_regset (tid, NT_PPC_DSCR, PPC_LINUX_SIZEOF_DSCRREGSET)) +- { +- features.ppr_dscr = true; +- if ((hwcap2 & PPC_FEATURE2_ARCH_2_07) +- && (hwcap2 & PPC_FEATURE2_TAR) +- && (hwcap2 & PPC_FEATURE2_EBB) +- && check_regset (tid, NT_PPC_TAR, PPC_LINUX_SIZEOF_TARREGSET) +- && check_regset (tid, NT_PPC_EBB, PPC_LINUX_SIZEOF_EBBREGSET) +- && check_regset (tid, NT_PPC_PMU, PPC_LINUX_SIZEOF_PMUREGSET)) +- { +- features.isa207 = true; +- if ((hwcap2 & PPC_FEATURE2_HTM) +- && check_regset (tid, NT_PPC_TM_SPR, +- PPC_LINUX_SIZEOF_TM_SPRREGSET)) +- features.htm = true; +- } +- } +- +- return ppc_linux_match_description (features); +-} +- ++void _initialize_ppc_linux_nat (); + void + _initialize_ppc_linux_nat (void) + { diff --git a/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-4of7.patch b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-4of7.patch new file mode 100644 index 0000000..b11bf4e --- /dev/null +++ b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-4of7.patch @@ -0,0 +1,2312 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Pedro Franco de Carvalho +Date: Wed, 7 Jul 2021 18:49:42 -0400 +Subject: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-4of7.patch + +;; Backport "[PowerPC] Fix debug register issues in ppc-linux-nat" +;; (Pedro Franco de Carvalho, RH BZ 1854784) + +This patch fixes some issues with debug register handling for the powerpc +linux native target. + +Currently, the target methods for installing and removing hardware +breakpoints and watchpoints in ppc-linux-nat.c affect all threads known to +linux-nat, including threads of different processes. + +This patch changes ppc-linux-nat.c so that only the process of +inferior_ptid is affected by these target methods, as GDB expects. + +This is done in the same way as various other architectures. The +install/remove target methods only register a hardware breakpoint or +watchpoint, and then send a stop signal to the threads. The debug +registers are only changed with ptrace right before each thread is next +resumed, using low_prepare_to_resume. + +There are two interfaces to modify debug registers for linux running on +powerpc, with different sets of ptrace requests: + +- PPC_PTRACE_GETHWDBGINFO, PPC_PTRACE_SETHWDEBUG, and + PPC_PTRACE_DELHWDEBUG. + + Or + +- PTRACE_SET_DEBUGREG and PTRACE_GET_DEBUGREG + +The first set (HWDEBUG) is the more flexible one and allows setting +watchpoints with a variable watched region length and, for certain +embedded processors, multiple types of debug registers (e.g. hardware +breakpoints and hardware-assisted conditions for watchpoints). +Currently, server processors only provide one watchpoint. The second one +(DEBUGREG) only allows setting one debug register, a watchpoint, so we +only use it if the first one is not available. + +The HWDEBUG interface handles debug registers with slot numbers. Once a +hardware watchpoint or breakpoint is installed (with +PPC_PTRACE_SETHWDEBUG), ptrace returns a slot number. This slot number +can then be used to remove the watchpoint or breakpoint from the inferior +(with PPC_PTRACE_DELHWDEBUG). The first interface also provides a +bitmask of available debug register features, which can be obtained with +PPC_PTRACE_GETHWDBGINFO. + +When GDB first tries to use debug registers, we try the first interface +with a ptrace call, and if it isn't available, we fall back to the second +one, if available. We use EIO as an indicator that an interface is not +available in the kernel. For simplicity, with any other error we +immediately assume no interface is available. Unfortunately this means +that if a process is killed by a signal right before we try to detect the +interface, we might get an ESRCH, which would prevent debug registers to +be used in the GDB session. However, it isn't clear that we can safely +raise an exception and try again in the future in all the contexts where +we try to detect the interface. + +If the HWDEBUG interface works but provides no feature bits, the target +falls back to the DEBUGREG interface. When the kernel is configured +without CONFIG_HW_BREAKPOINTS (selected by CONFIG_PERF_EVENTS), there is +a bug that causes watchpoints installed with the HWDEBUG interface not to +trigger. When this is the case, the feature bits will be zero, which is +used as the indicator to fall back to the DEBUGREG interface. This isn't +ideal, but has always been the behavior of GDB before this patch, so I +decided not to change it. + +A flag indicates for each thread if its debug registers need to be +updated the next time it is resumed. The flag is set whenever the upper +layers request or remove a hardware watchpoint or breakpoint, or when a +new thread is detected. Because some kernel configurations disable +watchpoints after they are hit, we also use the last stop reason of the +LWP to determine whether we should update the debug registers. It isn't +clear that this is also true of BookE hardware breakpoints, but we also +check their stop reason to be on the safe side, since it doesn't hurt. + +A map from process numbers to hardware watchpoint or breakpoint objects +keeps track of what has been requested by the upper layers of GDB, since +for GDB installing a hardware watchpoint or breakpoint means doing so for +the whole process. + +When using the HWDEBUG interface we also have to keep track of which +slots were last installed in each thread with a map from threads to the +slots, so that they can be removed when needed. When resuming a thread, +we remove all the slots using this map, then we install all the hardware +watchpoints and breakpoints from the per-process map of requests, and +then update the per-thread map accordingly. + +This per-thread state is also used for copying the debug register state +after a fork or a clone is detected. The kernel might do this depending +on the configuration. Recent kernels running on server processors that +were configured with CONFIG_PERF_EVENTS (and therefore +CONFIG_HW_BREAKPOINTS) don't copy debug registers across forks and +clones. Recent kernels without CONFIG_HW_BREAKPOINTS copy this state. I +believe that on embedded processors (e.g. a ppc440) the debug register +state is copied, but I haven't been able to test this. To handle both +cases, the per-thread state is always copied when forks and clones are +detected, and when we resume the thread and delete the debug register +slots before updating them, we ignore ENOENT errors. + +We don't need to handle this when using the DEBUGREG interface since it +only allows one hardware watchpoint and doesn't return slot numbers, we +just set or clear this watchpoint when needed. + +Since we signal running threads to stop after a request is processed, so +that we can update their debug registers when they are next resumed, +there will be a time between signalling the threads and their stop during +which the debug registers haven't been updated, even if the target +methods completed. + +The tests in gdb.threads/watchpoint-fork.exp no longer fail with this +patch. + +gdb/ChangeLog: +2020-03-30 Pedro Franco de Carvalho + + * ppc-linux-nat.c: Include , , and + . Remove inclusion of observable.h. + (PPC_DEBUG_CURRENT_VERSION): Move up define. + (struct arch_lwp_info): New struct. + (class ppc_linux_dreg_interface): New class. + (struct ppc_linux_process_info): New struct. + (struct ppc_linux_nat_target) + + + + + + + + + : Declare as + methods. + : New inner struct. + : Declare + members. + (saved_dabr_value, hwdebug_info, max_slots_number) + (struct hw_break_tuple, struct thread_points, ppc_threads) + (have_ptrace_hwdebug_interface) + (hwdebug_find_thread_points_by_tid) + (hwdebug_insert_point, hwdebug_remove_point): Remove. + (ppc_linux_nat_target::can_use_hw_breakpoint): Use + m_dreg_interface, remove call to PTRACE_SET_DEBUGREG. + (ppc_linux_nat_target::region_ok_for_hw_watchpoint): Add comment, + use m_dreg_interface. + (hwdebug_point_cmp): Change to... + (ppc_linux_nat_target::hwdebug_point_cmp): ...this method. Use + reference arguments instead of pointers. + (ppc_linux_nat_target::ranged_break_num_registers): Use + m_dreg_interface. + (ppc_linux_nat_target::insert_hw_breakpoint): Add comment, use + m_dreg_interface. Call register_hw_breakpoint. + (ppc_linux_nat_target::remove_hw_breakpoint): Add comment, use + m_dreg_interface. Call clear_hw_breakpoint. + (get_trigger_type): Change to... + (ppc_linux_nat_target::get_trigger_type): ...this method. Add + comment. + (ppc_linux_nat_target::insert_mask_watchpoint): Update comment, + use m_dreg_interface. Call register_hw_breakpoint. + (ppc_linux_nat_target::remove_mask_watchpoint): Update comment, + use m_dreg_interface. Call clear_hw_breakpoint. + (can_use_watchpoint_cond_accel): Change to... + (ppc_linux_nat_target::can_use_watchpoint_cond_accel): ...this + method. Update comment, use m_dreg_interface and + m_process_info. + (calculate_dvc): Change to... + (ppc_linux_nat_target::calculate_dvc): ...this method. Use + m_dreg_interface. + (num_memory_accesses): Change to... + (ppc_linux_nat_target::num_memory_accesses): ...this method. + (check_condition): Change to... + (ppc_linux_nat_target::check_condition): ...this method. + (ppc_linux_nat_target::can_accel_watchpoint_condition): Update + comment, use m_dreg_interface. + (create_watchpoint_request): Change to... + (ppc_linux_nat_target::create_watchpoint_request): ...this + method. Use m_dreg_interface. + (ppc_linux_nat_target::insert_watchpoint): Add comment, use + m_dreg_interface. Call register_hw_breakpoint or register_wp. + (ppc_linux_nat_target::remove_watchpoint): Add comment, use + m_dreg_interface. Call clear_hw_breakpoint or clear_wp. + (ppc_linux_nat_target::low_forget_process) + (ppc_linux_nat_target::low_new_fork) + (ppc_linux_nat_target::low_new_clone) + (ppc_linux_nat_target::low_delete_thread) + (ppc_linux_nat_target::low_prepare_to_resume): New methods. + (ppc_linux_nat_target::low_new_thread): Remove previous logic, + only call mark_thread_stale. + (ppc_linux_thread_exit): Remove. + (ppc_linux_nat_target::stopped_data_address): Change to... + (ppc_linux_nat_target::low_stopped_data_address): This. Add + comment, use m_dreg_interface and m_thread_hw_breakpoints. + (ppc_linux_nat_target::stopped_by_watchpoint): Change to... + (ppc_linux_nat_target::stopped_by_watchpoint): This. Add + comment. Call low_stopped_data_address. + (ppc_linux_nat_target::watchpoint_addr_within_range): Use + m_dreg_interface. + (ppc_linux_nat_target::masked_watch_num_registers): Use + m_dreg_interface. + (ppc_linux_nat_target::copy_thread_dreg_state) + (ppc_linux_nat_target::mark_thread_stale) + (ppc_linux_nat_target::mark_debug_registers_changed) + (ppc_linux_nat_target::register_hw_breakpoint) + (ppc_linux_nat_target::clear_hw_breakpoint) + (ppc_linux_nat_target::register_wp) + (ppc_linux_nat_target::clear_wp) + (ppc_linux_nat_target::init_arch_lwp_info) + (ppc_linux_nat_target::get_arch_lwp_info): New methods. + (_initialize_ppc_linux_nat): Remove observer callback. + +diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c +--- a/gdb/aarch64-linux-nat.c ++++ b/gdb/aarch64-linux-nat.c +@@ -64,14 +64,14 @@ public: + int can_use_hw_breakpoint (enum bptype, int, int) override; + int insert_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override; + int remove_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override; +- int region_ok_for_hw_watchpoint (CORE_ADDR, LONGEST) override; ++ int region_ok_for_hw_watchpoint (CORE_ADDR, int) override; + int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, + struct expression *) override; + int remove_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, + struct expression *) override; + bool stopped_by_watchpoint () override; + bool stopped_data_address (CORE_ADDR *) override; +- bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, LONGEST) override; ++ bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override; + + int can_do_single_step () override; + +@@ -808,7 +808,7 @@ aarch64_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len, + /* Implement the "region_ok_for_hw_watchpoint" target_ops method. */ + + int +-aarch64_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len) ++aarch64_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) + { + return aarch64_linux_region_ok_for_watchpoint (addr, len); + } +@@ -888,7 +888,7 @@ aarch64_linux_nat_target::stopped_by_watchpoint () + bool + aarch64_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr, + CORE_ADDR start, +- LONGEST length) ++ int length) + { + return start <= addr && start + length - 1 >= addr; + } +diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c +--- a/gdb/arm-linux-nat.c ++++ b/gdb/arm-linux-nat.c +@@ -80,7 +80,7 @@ public: + + int remove_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override; + +- int region_ok_for_hw_watchpoint (CORE_ADDR, LONGEST) override; ++ int region_ok_for_hw_watchpoint (CORE_ADDR, int) override; + + int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, + struct expression *) override; +@@ -91,7 +91,7 @@ public: + + bool stopped_data_address (CORE_ADDR *) override; + +- bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, LONGEST) override; ++ bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override; + + const struct target_desc *read_description () override; + +@@ -1093,7 +1093,7 @@ arm_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch, + /* Are we able to use a hardware watchpoint for the LEN bytes starting at + ADDR? */ + int +-arm_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len) ++arm_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) + { + const struct arm_linux_hwbp_cap *cap = arm_linux_get_hwbp_cap (); + CORE_ADDR max_wp_length, aligned_addr; +@@ -1202,7 +1202,7 @@ arm_linux_nat_target::stopped_by_watchpoint () + bool + arm_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr, + CORE_ADDR start, +- LONGEST length) ++ int length) + { + return start <= addr && start + length - 1 >= addr; + } +diff --git a/gdb/mips-linux-nat.c b/gdb/mips-linux-nat.c +--- a/gdb/mips-linux-nat.c ++++ b/gdb/mips-linux-nat.c +@@ -614,7 +614,7 @@ mips_linux_nat_target::stopped_data_address (CORE_ADDR *paddr) + the specified region can be covered by the watch registers. */ + + int +-mips_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len) ++mips_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) + { + struct pt_watch_regs dummy_regs; + int i; +diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c +--- a/gdb/ppc-linux-nat.c ++++ b/gdb/ppc-linux-nat.c +@@ -18,7 +18,6 @@ + along with this program. If not, see . */ + + #include "defs.h" +-#include "observable.h" + #include "frame.h" + #include "inferior.h" + #include "gdbthread.h" +@@ -37,6 +36,9 @@ + #include + #include "nat/gdb_ptrace.h" + #include "inf-ptrace.h" ++#include ++#include ++#include + + /* Prototypes for supply_gregset etc. */ + #include "gregset.h" +@@ -52,6 +54,13 @@ + #include "nat/ppc-linux.h" + #include "linux-tdep.h" + ++/* Function type for the CALLBACK argument of iterate_over_lwps_new. */ ++typedef int (iterate_over_lwps_new_ftype) (struct lwp_info *lwp); ++ ++static struct lwp_info *iterate_over_lwps_new ++ (ptid_t filter, ++ gdb::function_view callback); ++ + /* Similarly for the hardware watchpoint support. These requests are used + when the PowerPC HWDEBUG ptrace interface is not available. */ + #ifndef PTRACE_GET_DEBUGREG +@@ -135,6 +144,10 @@ struct ppc_hw_breakpoint + #define PPC_DEBUG_FEATURE_DATA_BP_DAWR 0x10 + #endif /* PPC_DEBUG_FEATURE_DATA_BP_DAWR */ + ++/* The version of the PowerPC HWDEBUG kernel interface that we will use, if ++ available. */ ++#define PPC_DEBUG_CURRENT_VERSION 1 ++ + /* Similarly for the general-purpose (gp0 -- gp31) + and floating-point registers (fp0 -- fp31). */ + #ifndef PTRACE_GETREGS +@@ -269,6 +282,214 @@ int have_ptrace_getsetregs = 1; + them and gotten an error. */ + int have_ptrace_getsetfpregs = 1; + ++/* Private arch info associated with each thread lwp_info object, used ++ for debug register handling. */ ++ ++struct arch_lwp_info ++{ ++ /* When true, indicates that the debug registers installed in the ++ thread no longer correspond to the watchpoints and breakpoints ++ requested by GDB. */ ++ bool debug_regs_stale; ++ ++ /* We need a back-reference to the PTID of the thread so that we can ++ cleanup the debug register state of the thread in ++ low_delete_thread. */ ++ ptid_t lwp_ptid; ++}; ++ ++/* Class used to detect which set of ptrace requests that ++ ppc_linux_nat_target will use to install and remove hardware ++ breakpoints and watchpoints. ++ ++ The interface is only detected once, testing the ptrace calls. The ++ result can indicate that no interface is available. ++ ++ The Linux kernel provides two different sets of ptrace requests to ++ handle hardware watchpoints and breakpoints for Power: ++ ++ - PPC_PTRACE_GETHWDBGINFO, PPC_PTRACE_SETHWDEBUG, and ++ PPC_PTRACE_DELHWDEBUG. ++ ++ Or ++ ++ - PTRACE_SET_DEBUGREG and PTRACE_GET_DEBUGREG ++ ++ The first set is the more flexible one and allows setting watchpoints ++ with a variable watched region length and, for BookE processors, ++ multiple types of debug registers (e.g. hardware breakpoints and ++ hardware-assisted conditions for watchpoints). The second one only ++ allows setting one debug register, a watchpoint, so we only use it if ++ the first one is not available. */ ++ ++class ppc_linux_dreg_interface ++{ ++public: ++ ++ ppc_linux_dreg_interface () ++ : m_interface (), m_hwdebug_info () ++ { ++ }; ++ ++ DISABLE_COPY_AND_ASSIGN (ppc_linux_dreg_interface); ++ ++ /* One and only one of these three functions returns true, indicating ++ whether the corresponding interface is the one we detected. The ++ interface must already have been detected as a precontidion. */ ++ ++ bool hwdebug_p () ++ { ++ gdb_assert (detected_p ()); ++ return *m_interface == HWDEBUG; ++ } ++ ++ bool debugreg_p () ++ { ++ gdb_assert (detected_p ()); ++ return *m_interface == DEBUGREG; ++ } ++ ++ bool unavailable_p () ++ { ++ gdb_assert (detected_p ()); ++ return *m_interface == UNAVAILABLE; ++ } ++ ++ /* Returns the debug register capabilities of the target. Should only ++ be called if the interface is HWDEBUG. */ ++ const struct ppc_debug_info &hwdebug_info () ++ { ++ gdb_assert (hwdebug_p ()); ++ ++ return m_hwdebug_info; ++ } ++ ++ /* Returns true if the interface has already been detected. This is ++ useful for cases when we know there is no work to be done if the ++ interface hasn't been detected yet. */ ++ bool detected_p () ++ { ++ return m_interface.has_value (); ++ } ++ ++ /* Detect the available interface, if any, if it hasn't been detected ++ before, using PTID for the necessary ptrace calls. */ ++ ++ void detect (const ptid_t &ptid) ++ { ++ if (m_interface.has_value ()) ++ return; ++ ++ gdb_assert (ptid.lwp_p ()); ++ ++ bool no_features = false; ++ ++ if (ptrace (PPC_PTRACE_GETHWDBGINFO, ptid.lwp (), 0, &m_hwdebug_info) ++ != -1) ++ { ++ /* If there are no advertised features, we don't use the ++ HWDEBUG interface and try the DEBUGREG interface instead. ++ It shouldn't be necessary to do this, however, when the ++ kernel is configured without CONFIG_HW_BREAKPOINTS (selected ++ by CONFIG_PERF_EVENTS), there is a bug that causes ++ watchpoints installed with the HWDEBUG interface not to ++ trigger. When this is the case, features will be zero, ++ which we use as an indicator to fall back to the DEBUGREG ++ interface. */ ++ if (m_hwdebug_info.features != 0) ++ { ++ m_interface.emplace (HWDEBUG); ++ return; ++ } ++ else ++ no_features = true; ++ } ++ ++ /* EIO indicates that the request is invalid, so we try DEBUGREG ++ next. Technically, it can also indicate other failures, but we ++ can't differentiate those. ++ ++ Other errors could happen for various reasons. We could get an ++ ESRCH if the traced thread was killed by a signal. Trying to ++ detect the interface with another thread in the future would be ++ complicated, as callers would have to handle an "unknown ++ interface" case. It's also unclear if raising an exception ++ here would be safe. ++ ++ Other errors, such as ENODEV, could be more permanent and cause ++ a failure for any thread. ++ ++ For simplicity, with all errors other than EIO, we set the ++ interface to UNAVAILABLE and don't try DEBUGREG. If DEBUGREG ++ fails too, we'll also set the interface to UNAVAILABLE. It's ++ unlikely that trying the DEBUGREG interface with this same thread ++ would work, for errors other than EIO. This means that these ++ errors will cause hardware watchpoints and breakpoints to become ++ unavailable throughout a GDB session. */ ++ ++ if (no_features || errno == EIO) ++ { ++ unsigned long wp; ++ ++ if (ptrace (PTRACE_GET_DEBUGREG, ptid.lwp (), 0, &wp) != -1) ++ { ++ m_interface.emplace (DEBUGREG); ++ return; ++ } ++ } ++ ++ if (errno != EIO) ++ warning (_("Error when detecting the debug register interface. " ++ "Debug registers will be unavailable.")); ++ ++ m_interface.emplace (UNAVAILABLE); ++ return; ++ } ++ ++private: ++ ++ /* HWDEBUG represents the set of calls PPC_PTRACE_GETHWDBGINFO, ++ PPC_PTRACE_SETHWDEBUG and PPC_PTRACE_DELHWDEBUG. ++ ++ DEBUGREG represents the set of calls PTRACE_SET_DEBUGREG and ++ PTRACE_GET_DEBUGREG. ++ ++ UNAVAILABLE can indicate that the kernel doesn't support any of the ++ two sets of requests or that there was an error when we tried to ++ detect wich interface is available. */ ++ ++ enum debug_reg_interface ++ { ++ UNAVAILABLE, ++ HWDEBUG, ++ DEBUGREG ++ }; ++ ++ /* The interface option. Initialized if has_value () returns true. */ ++ gdb::optional m_interface; ++ ++ /* The info returned by the kernel with PPC_PTRACE_GETHWDBGINFO. Only ++ valid if we determined that the interface is HWDEBUG. */ ++ struct ppc_debug_info m_hwdebug_info; ++}; ++ ++/* Per-process information. This includes the hardware watchpoints and ++ breakpoints that GDB requested to this target. */ ++ ++struct ppc_linux_process_info ++{ ++ /* The list of hardware watchpoints and breakpoints that GDB requested ++ for this process. ++ ++ Only used when the interface is HWDEBUG. */ ++ std::list requested_hw_bps; ++ ++ /* The watchpoint value that GDB requested for this process. ++ ++ Only used when the interface is DEBUGREG. */ ++ gdb::optional requested_wp_val; ++}; ++ + struct ppc_linux_nat_target final : public linux_nat_target + { + /* Add our register access methods. */ +@@ -284,7 +505,7 @@ struct ppc_linux_nat_target final : public linux_nat_target + int remove_hw_breakpoint (struct gdbarch *, struct bp_target_info *) + override; + +- int region_ok_for_hw_watchpoint (CORE_ADDR, LONGEST) override; ++ int region_ok_for_hw_watchpoint (CORE_ADDR, int) override; + + int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, + struct expression *) override; +@@ -298,13 +519,9 @@ struct ppc_linux_nat_target final : public linux_nat_target + int remove_mask_watchpoint (CORE_ADDR, CORE_ADDR, enum target_hw_bp_type) + override; + +- bool stopped_by_watchpoint () override; +- +- bool stopped_data_address (CORE_ADDR *) override; ++ bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override; + +- bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, LONGEST) override; +- +- bool can_accel_watchpoint_condition (CORE_ADDR, LONGEST, int, struct expression *) ++ bool can_accel_watchpoint_condition (CORE_ADDR, int, int, struct expression *) + override; + + int masked_watch_num_registers (CORE_ADDR, CORE_ADDR) override; +@@ -318,7 +535,95 @@ struct ppc_linux_nat_target final : public linux_nat_target + override; + + /* Override linux_nat_target low methods. */ ++ bool low_stopped_by_watchpoint () override; ++ ++ bool low_stopped_data_address (CORE_ADDR *) override; ++ + void low_new_thread (struct lwp_info *lp) override; ++ ++ void low_delete_thread (arch_lwp_info *) override; ++ ++ void low_new_fork (struct lwp_info *, pid_t) override; ++ ++ void low_new_clone (struct lwp_info *, pid_t) override; ++ ++ void low_forget_process (pid_t pid) override; ++ ++ void low_prepare_to_resume (struct lwp_info *) override; ++ ++private: ++ ++ void copy_thread_dreg_state (const ptid_t &parent_ptid, ++ const ptid_t &child_ptid); ++ ++ void mark_thread_stale (struct lwp_info *lp); ++ ++ void mark_debug_registers_changed (pid_t pid); ++ ++ void register_hw_breakpoint (pid_t pid, ++ const struct ppc_hw_breakpoint &bp); ++ ++ void clear_hw_breakpoint (pid_t pid, ++ const struct ppc_hw_breakpoint &a); ++ ++ void register_wp (pid_t pid, long wp_value); ++ ++ void clear_wp (pid_t pid); ++ ++ bool can_use_watchpoint_cond_accel (void); ++ ++ void calculate_dvc (CORE_ADDR addr, int len, ++ CORE_ADDR data_value, ++ uint32_t *condition_mode, ++ uint64_t *condition_value); ++ ++ int check_condition (CORE_ADDR watch_addr, ++ struct expression *cond, ++ CORE_ADDR *data_value, int *len); ++ ++ int num_memory_accesses (const std::vector &chain); ++ ++ int get_trigger_type (enum target_hw_bp_type type); ++ ++ void create_watchpoint_request (struct ppc_hw_breakpoint *p, ++ CORE_ADDR addr, ++ int len, ++ enum target_hw_bp_type type, ++ struct expression *cond, ++ int insert); ++ ++ bool hwdebug_point_cmp (const struct ppc_hw_breakpoint &a, ++ const struct ppc_hw_breakpoint &b); ++ ++ void init_arch_lwp_info (struct lwp_info *lp); ++ ++ arch_lwp_info *get_arch_lwp_info (struct lwp_info *lp); ++ ++ /* The ptrace interface we'll use to install hardware watchpoints and ++ breakpoints (debug registers). */ ++ ppc_linux_dreg_interface m_dreg_interface; ++ ++ /* A map from pids to structs containing info specific to each ++ process. */ ++ std::unordered_map m_process_info; ++ ++ /* Callable object to hash ptids by their lwp number. */ ++ struct ptid_hash ++ { ++ std::size_t operator() (const ptid_t &ptid) const ++ { ++ return std::hash{} (ptid.lwp ()); ++ } ++ }; ++ ++ /* A map from ptid_t objects to a list of pairs of slots and hardware ++ breakpoint objects. This keeps track of which hardware breakpoints ++ and watchpoints were last installed in each slot of each thread. ++ ++ Only used when the interface is HWDEBUG. */ ++ std::unordered_map >, ++ ptid_hash> m_installed_hw_bps; + }; + + static ppc_linux_nat_target the_ppc_linux_nat_target; +@@ -1725,102 +2030,50 @@ ppc_linux_nat_target::read_description () + return ppc_linux_match_description (features); + } + +-/* The cached DABR value, to install in new threads. +- This variable is used when the PowerPC HWDEBUG ptrace +- interface is not available. */ +-static long saved_dabr_value; +- +-/* Global structure that will store information about the available +- features provided by the PowerPC HWDEBUG ptrace interface. */ +-static struct ppc_debug_info hwdebug_info; +- +-/* Global variable that holds the maximum number of slots that the +- kernel will use. This is only used when PowerPC HWDEBUG ptrace interface +- is available. */ +-static size_t max_slots_number = 0; +- +-struct hw_break_tuple +-{ +- long slot; +- struct ppc_hw_breakpoint *hw_break; +-}; +- +-/* This is an internal VEC created to store information about *points inserted +- for each thread. This is used when PowerPC HWDEBUG ptrace interface is +- available. */ +-typedef struct thread_points +- { +- /* The TID to which this *point relates. */ +- int tid; +- /* Information about the *point, such as its address, type, etc. +- +- Each element inside this vector corresponds to a hardware +- breakpoint or watchpoint in the thread represented by TID. The maximum +- size of these vector is MAX_SLOTS_NUMBER. If the hw_break element of +- the tuple is NULL, then the position in the vector is free. */ +- struct hw_break_tuple *hw_breaks; +- } *thread_points_p; +-DEF_VEC_P (thread_points_p); +- +-VEC(thread_points_p) *ppc_threads = NULL; +- +-/* The version of the PowerPC HWDEBUG kernel interface that we will use, if +- available. */ +-#define PPC_DEBUG_CURRENT_VERSION 1 +- +-/* Returns non-zero if we support the PowerPC HWDEBUG ptrace interface. */ +-static int +-have_ptrace_hwdebug_interface (void) +-{ +- static int have_ptrace_hwdebug_interface = -1; +- +- if (have_ptrace_hwdebug_interface == -1) +- { +- int tid; +- +- tid = inferior_ptid.lwp (); +- if (tid == 0) +- tid = inferior_ptid.pid (); +- +- /* Check for kernel support for PowerPC HWDEBUG ptrace interface. */ +- if (ptrace (PPC_PTRACE_GETHWDBGINFO, tid, 0, &hwdebug_info) >= 0) +- { +- /* Check whether PowerPC HWDEBUG ptrace interface is functional and +- provides any supported feature. */ +- if (hwdebug_info.features != 0) +- { +- have_ptrace_hwdebug_interface = 1; +- max_slots_number = hwdebug_info.num_instruction_bps +- + hwdebug_info.num_data_bps +- + hwdebug_info.num_condition_regs; +- return have_ptrace_hwdebug_interface; +- } +- } +- /* Old school interface and no PowerPC HWDEBUG ptrace support. */ +- have_ptrace_hwdebug_interface = 0; +- memset (&hwdebug_info, 0, sizeof (struct ppc_debug_info)); +- } +- +- return have_ptrace_hwdebug_interface; +-} ++/* Routines for installing hardware watchpoints and breakpoints. When ++ GDB requests a hardware watchpoint or breakpoint to be installed, we ++ register the request for the pid of inferior_ptid in a map with one ++ entry per process. We then issue a stop request to all the threads of ++ this process, and mark a per-thread flag indicating that their debug ++ registers should be updated. Right before they are next resumed, we ++ remove all previously installed debug registers and install all the ++ ones GDB requested. We then update a map with one entry per thread ++ that keeps track of what debug registers were last installed in each ++ thread. ++ ++ We use this second map to remove installed registers before installing ++ the ones requested by GDB, and to copy the debug register state after ++ a thread clones or forks, since depending on the kernel configuration, ++ debug registers can be inherited. */ ++ ++/* Check if we support and have enough resources to install a hardware ++ watchpoint or breakpoint. See the description in target.h. */ + + int +-ppc_linux_nat_target::can_use_hw_breakpoint (enum bptype type, int cnt, int ot) ++ppc_linux_nat_target::can_use_hw_breakpoint (enum bptype type, int cnt, ++ int ot) + { + int total_hw_wp, total_hw_bp; + +- if (have_ptrace_hwdebug_interface ()) ++ m_dreg_interface.detect (inferior_ptid); ++ ++ if (m_dreg_interface.unavailable_p ()) ++ return 0; ++ ++ if (m_dreg_interface.hwdebug_p ()) + { + /* When PowerPC HWDEBUG ptrace interface is available, the number of + available hardware watchpoints and breakpoints is stored at the + hwdebug_info struct. */ +- total_hw_bp = hwdebug_info.num_instruction_bps; +- total_hw_wp = hwdebug_info.num_data_bps; ++ total_hw_bp = m_dreg_interface.hwdebug_info ().num_instruction_bps; ++ total_hw_wp = m_dreg_interface.hwdebug_info ().num_data_bps; + } + else + { +- /* When we do not have PowerPC HWDEBUG ptrace interface, we should +- consider having 1 hardware watchpoint and no hardware breakpoints. */ ++ gdb_assert (m_dreg_interface.debugreg_p ()); ++ ++ /* With the DEBUGREG ptrace interface, we should consider having 1 ++ hardware watchpoint and no hardware breakpoints. */ + total_hw_bp = 0; + total_hw_wp = 1; + } +@@ -1828,53 +2081,50 @@ ppc_linux_nat_target::can_use_hw_breakpoint (enum bptype type, int cnt, int ot) + if (type == bp_hardware_watchpoint || type == bp_read_watchpoint + || type == bp_access_watchpoint || type == bp_watchpoint) + { +- if (cnt + ot > total_hw_wp) ++ if (total_hw_wp == 0) ++ return 0; ++ else if (cnt + ot > total_hw_wp) + return -1; ++ else ++ return 1; + } + else if (type == bp_hardware_breakpoint) + { + if (total_hw_bp == 0) +- { +- /* No hardware breakpoint support. */ +- return 0; +- } +- if (cnt > total_hw_bp) +- return -1; +- } +- +- if (!have_ptrace_hwdebug_interface ()) +- { +- int tid; +- ptid_t ptid = inferior_ptid; +- +- /* We need to know whether ptrace supports PTRACE_SET_DEBUGREG +- and whether the target has DABR. If either answer is no, the +- ptrace call will return -1. Fail in that case. */ +- tid = ptid.lwp (); +- if (tid == 0) +- tid = ptid.pid (); +- +- if (ptrace (PTRACE_SET_DEBUGREG, tid, 0, 0) == -1) + return 0; ++ else if (cnt > total_hw_bp) ++ return -1; ++ else ++ return 1; + } + +- return 1; ++ return 0; + } + ++/* Returns 1 if we can watch LEN bytes at address ADDR, 0 otherwise. */ ++ + int +-ppc_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len) ++ppc_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) + { + /* Handle sub-8-byte quantities. */ + if (len <= 0) + return 0; + ++ m_dreg_interface.detect (inferior_ptid); ++ ++ if (m_dreg_interface.unavailable_p ()) ++ return 0; ++ + /* The PowerPC HWDEBUG ptrace interface tells if there are alignment + restrictions for watchpoints in the processors. In that case, we use that + information to determine the hardcoded watchable region for + watchpoints. */ +- if (have_ptrace_hwdebug_interface ()) ++ if (m_dreg_interface.hwdebug_p ()) + { + int region_size; ++ const struct ppc_debug_info &hwdebug_info = (m_dreg_interface ++ .hwdebug_info ()); ++ + /* Embedded DAC-based processors, like the PowerPC 440 have ranged + watchpoints and can watch any access within an arbitrary memory + region. This is useful to watch arrays and structs, for instance. It +@@ -1901,121 +2151,32 @@ ppc_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len) + ptrace interface, DAC-based processors (i.e., embedded processors) will + use addresses aligned to 4-bytes due to the way the read/write flags are + passed in the old ptrace interface. */ +- else if (((linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE) +- && (addr + len) > (addr & ~3) + 4) +- || (addr + len) > (addr & ~7) + 8) +- return 0; +- +- return 1; +-} +- +-/* This function compares two ppc_hw_breakpoint structs field-by-field. */ +-static int +-hwdebug_point_cmp (struct ppc_hw_breakpoint *a, struct ppc_hw_breakpoint *b) +-{ +- return (a->trigger_type == b->trigger_type +- && a->addr_mode == b->addr_mode +- && a->condition_mode == b->condition_mode +- && a->addr == b->addr +- && a->addr2 == b->addr2 +- && a->condition_value == b->condition_value); +-} +- +-/* This function can be used to retrieve a thread_points by the TID of the +- related process/thread. If nothing has been found, and ALLOC_NEW is 0, +- it returns NULL. If ALLOC_NEW is non-zero, a new thread_points for the +- provided TID will be created and returned. */ +-static struct thread_points * +-hwdebug_find_thread_points_by_tid (int tid, int alloc_new) +-{ +- int i; +- struct thread_points *t; +- +- for (i = 0; VEC_iterate (thread_points_p, ppc_threads, i, t); i++) +- if (t->tid == tid) +- return t; +- +- t = NULL; +- +- /* Do we need to allocate a new point_item +- if the wanted one does not exist? */ +- if (alloc_new) ++ else + { +- t = XNEW (struct thread_points); +- t->hw_breaks = XCNEWVEC (struct hw_break_tuple, max_slots_number); +- t->tid = tid; +- VEC_safe_push (thread_points_p, ppc_threads, t); +- } ++ gdb_assert (m_dreg_interface.debugreg_p ()); + +- return t; +-} +- +-/* This function is a generic wrapper that is responsible for inserting a +- *point (i.e., calling `ptrace' in order to issue the request to the +- kernel) and registering it internally in GDB. */ +-static void +-hwdebug_insert_point (struct ppc_hw_breakpoint *b, int tid) +-{ +- int i; +- long slot; +- gdb::unique_xmalloc_ptr p (XDUP (ppc_hw_breakpoint, b)); +- struct hw_break_tuple *hw_breaks; +- struct thread_points *t; +- struct hw_break_tuple *tuple; +- +- errno = 0; +- slot = ptrace (PPC_PTRACE_SETHWDEBUG, tid, 0, p.get ()); +- if (slot < 0) +- perror_with_name (_("Unexpected error setting breakpoint or watchpoint")); +- +- /* Everything went fine, so we have to register this *point. */ +- t = hwdebug_find_thread_points_by_tid (tid, 1); +- gdb_assert (t != NULL); +- hw_breaks = t->hw_breaks; +- +- /* Find a free element in the hw_breaks vector. */ +- for (i = 0; i < max_slots_number; i++) +- if (hw_breaks[i].hw_break == NULL) +- { +- hw_breaks[i].slot = slot; +- hw_breaks[i].hw_break = p.release (); +- break; +- } ++ if (((linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE) ++ && (addr + len) > (addr & ~3) + 4) ++ || (addr + len) > (addr & ~7) + 8) ++ return 0; ++ } + +- gdb_assert (i != max_slots_number); ++ return 1; + } + +-/* This function is a generic wrapper that is responsible for removing a +- *point (i.e., calling `ptrace' in order to issue the request to the +- kernel), and unregistering it internally at GDB. */ +-static void +-hwdebug_remove_point (struct ppc_hw_breakpoint *b, int tid) +-{ +- int i; +- struct hw_break_tuple *hw_breaks; +- struct thread_points *t; +- +- t = hwdebug_find_thread_points_by_tid (tid, 0); +- gdb_assert (t != NULL); +- hw_breaks = t->hw_breaks; +- +- for (i = 0; i < max_slots_number; i++) +- if (hw_breaks[i].hw_break && hwdebug_point_cmp (hw_breaks[i].hw_break, b)) +- break; ++/* This function compares two ppc_hw_breakpoint structs ++ field-by-field. */ + +- gdb_assert (i != max_slots_number); +- +- /* We have to ignore ENOENT errors because the kernel implements hardware +- breakpoints/watchpoints as "one-shot", that is, they are automatically +- deleted when hit. */ +- errno = 0; +- if (ptrace (PPC_PTRACE_DELHWDEBUG, tid, 0, hw_breaks[i].slot) < 0) +- if (errno != ENOENT) +- perror_with_name (_("Unexpected error deleting " +- "breakpoint or watchpoint")); +- +- xfree (hw_breaks[i].hw_break); +- hw_breaks[i].hw_break = NULL; ++bool ++ppc_linux_nat_target::hwdebug_point_cmp (const struct ppc_hw_breakpoint &a, ++ const struct ppc_hw_breakpoint &b) ++{ ++ return (a.trigger_type == b.trigger_type ++ && a.addr_mode == b.addr_mode ++ && a.condition_mode == b.condition_mode ++ && a.addr == b.addr ++ && a.addr2 == b.addr2 ++ && a.condition_value == b.condition_value); + } + + /* Return the number of registers needed for a ranged breakpoint. */ +@@ -2023,22 +2184,28 @@ hwdebug_remove_point (struct ppc_hw_breakpoint *b, int tid) + int + ppc_linux_nat_target::ranged_break_num_registers () + { +- return ((have_ptrace_hwdebug_interface () +- && hwdebug_info.features & PPC_DEBUG_FEATURE_INSN_BP_RANGE)? ++ m_dreg_interface.detect (inferior_ptid); ++ ++ return ((m_dreg_interface.hwdebug_p () ++ && (m_dreg_interface.hwdebug_info ().features ++ & PPC_DEBUG_FEATURE_INSN_BP_RANGE))? + 2 : -1); + } + +-/* Insert the hardware breakpoint described by BP_TGT. Returns 0 for +- success, 1 if hardware breakpoints are not supported or -1 for failure. */ ++/* Register the hardware breakpoint described by BP_TGT, to be inserted ++ when the threads of inferior_ptid are resumed. Returns 0 for success, ++ or -1 if the HWDEBUG interface that we need for hardware breakpoints ++ is not available. */ + + int + ppc_linux_nat_target::insert_hw_breakpoint (struct gdbarch *gdbarch, + struct bp_target_info *bp_tgt) + { +- struct lwp_info *lp; + struct ppc_hw_breakpoint p; + +- if (!have_ptrace_hwdebug_interface ()) ++ m_dreg_interface.detect (inferior_ptid); ++ ++ if (!m_dreg_interface.hwdebug_p ()) + return -1; + + p.version = PPC_DEBUG_CURRENT_VERSION; +@@ -2061,20 +2228,25 @@ ppc_linux_nat_target::insert_hw_breakpoint (struct gdbarch *gdbarch, + p.addr2 = 0; + } + +- ALL_LWPS (lp) +- hwdebug_insert_point (&p, lp->ptid.lwp ()); ++ register_hw_breakpoint (inferior_ptid.pid (), p); + + return 0; + } + ++/* Clear a registration for the hardware breakpoint given by type BP_TGT. ++ It will be removed from the threads of inferior_ptid when they are ++ next resumed. Returns 0 for success, or -1 if the HWDEBUG interface ++ that we need for hardware breakpoints is not available. */ ++ + int + ppc_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch, + struct bp_target_info *bp_tgt) + { +- struct lwp_info *lp; + struct ppc_hw_breakpoint p; + +- if (!have_ptrace_hwdebug_interface ()) ++ m_dreg_interface.detect (inferior_ptid); ++ ++ if (!m_dreg_interface.hwdebug_p ()) + return -1; + + p.version = PPC_DEBUG_CURRENT_VERSION; +@@ -2097,14 +2269,16 @@ ppc_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch, + p.addr2 = 0; + } + +- ALL_LWPS (lp) +- hwdebug_remove_point (&p, lp->ptid.lwp ()); ++ clear_hw_breakpoint (inferior_ptid.pid (), p); + + return 0; + } + +-static int +-get_trigger_type (enum target_hw_bp_type type) ++/* Return the trigger value to set in a ppc_hw_breakpoint object for a ++ given hardware watchpoint TYPE. We assume type is not hw_execute. */ ++ ++int ++ppc_linux_nat_target::get_trigger_type (enum target_hw_bp_type type) + { + int t; + +@@ -2118,19 +2292,18 @@ get_trigger_type (enum target_hw_bp_type type) + return t; + } + +-/* Insert a new masked watchpoint at ADDR using the mask MASK. +- RW may be hw_read for a read watchpoint, hw_write for a write watchpoint +- or hw_access for an access watchpoint. Returns 0 on success and throws +- an error on failure. */ ++/* Register a new masked watchpoint at ADDR using the mask MASK, to be ++ inserted when the threads of inferior_ptid are resumed. RW may be ++ hw_read for a read watchpoint, hw_write for a write watchpoint or ++ hw_access for an access watchpoint. */ + + int + ppc_linux_nat_target::insert_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask, + target_hw_bp_type rw) + { +- struct lwp_info *lp; + struct ppc_hw_breakpoint p; + +- gdb_assert (have_ptrace_hwdebug_interface ()); ++ gdb_assert (m_dreg_interface.hwdebug_p ()); + + p.version = PPC_DEBUG_CURRENT_VERSION; + p.trigger_type = get_trigger_type (rw); +@@ -2140,25 +2313,23 @@ ppc_linux_nat_target::insert_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask, + p.addr2 = mask; + p.condition_value = 0; + +- ALL_LWPS (lp) +- hwdebug_insert_point (&p, lp->ptid.lwp ()); ++ register_hw_breakpoint (inferior_ptid.pid (), p); + + return 0; + } + +-/* Remove a masked watchpoint at ADDR with the mask MASK. +- RW may be hw_read for a read watchpoint, hw_write for a write watchpoint +- or hw_access for an access watchpoint. Returns 0 on success and throws +- an error on failure. */ ++/* Clear a registration for a masked watchpoint at ADDR with the mask ++ MASK. It will be removed from the threads of inferior_ptid when they ++ are next resumed. RW may be hw_read for a read watchpoint, hw_write ++ for a write watchpoint or hw_access for an access watchpoint. */ + + int + ppc_linux_nat_target::remove_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask, + target_hw_bp_type rw) + { +- struct lwp_info *lp; + struct ppc_hw_breakpoint p; + +- gdb_assert (have_ptrace_hwdebug_interface ()); ++ gdb_assert (m_dreg_interface.hwdebug_p ()); + + p.version = PPC_DEBUG_CURRENT_VERSION; + p.trigger_type = get_trigger_type (rw); +@@ -2168,40 +2339,42 @@ ppc_linux_nat_target::remove_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask, + p.addr2 = mask; + p.condition_value = 0; + +- ALL_LWPS (lp) +- hwdebug_remove_point (&p, lp->ptid.lwp ()); ++ clear_hw_breakpoint (inferior_ptid.pid (), p); + + return 0; + } + +-/* Check whether we have at least one free DVC register. */ +-static int +-can_use_watchpoint_cond_accel (void) ++/* Check whether we have at least one free DVC register for the threads ++ of the pid of inferior_ptid. */ ++ ++bool ++ppc_linux_nat_target::can_use_watchpoint_cond_accel (void) + { +- struct thread_points *p; +- int tid = inferior_ptid.lwp (); +- int cnt = hwdebug_info.num_condition_regs, i; +- CORE_ADDR tmp_value; ++ m_dreg_interface.detect (inferior_ptid); + +- if (!have_ptrace_hwdebug_interface () || cnt == 0) +- return 0; ++ if (!m_dreg_interface.hwdebug_p ()) ++ return false; + +- p = hwdebug_find_thread_points_by_tid (tid, 0); ++ int cnt = m_dreg_interface.hwdebug_info ().num_condition_regs; + +- if (p) +- { +- for (i = 0; i < max_slots_number; i++) +- if (p->hw_breaks[i].hw_break != NULL +- && (p->hw_breaks[i].hw_break->condition_mode +- != PPC_BREAKPOINT_CONDITION_NONE)) +- cnt--; ++ if (cnt == 0) ++ return false; + +- /* There are no available slots now. */ +- if (cnt <= 0) +- return 0; +- } ++ auto process_it = m_process_info.find (inferior_ptid.pid ()); + +- return 1; ++ /* No breakpoints or watchpoints have been requested for this process, ++ we have at least one free DVC register. */ ++ if (process_it == m_process_info.end ()) ++ return true; ++ ++ for (const ppc_hw_breakpoint &bp : process_it->second.requested_hw_bps) ++ if (bp.condition_mode != PPC_BREAKPOINT_CONDITION_NONE) ++ cnt--; ++ ++ if (cnt <= 0) ++ return false; ++ ++ return true; + } + + /* Calculate the enable bits and the contents of the Data Value Compare +@@ -2212,10 +2385,16 @@ can_use_watchpoint_cond_accel (void) + On exit, CONDITION_MODE will hold the enable bits for the DVC, and + CONDITION_VALUE will hold the value which should be put in the + DVC register. */ +-static void +-calculate_dvc (CORE_ADDR addr, LONGEST len, CORE_ADDR data_value, +- uint32_t *condition_mode, uint64_t *condition_value) ++ ++void ++ppc_linux_nat_target::calculate_dvc (CORE_ADDR addr, int len, ++ CORE_ADDR data_value, ++ uint32_t *condition_mode, ++ uint64_t *condition_value) + { ++ const struct ppc_debug_info &hwdebug_info = (m_dreg_interface. ++ hwdebug_info ()); ++ + LONGEST i, num_byte_enable; + int align_offset, num_bytes_off_dvc, rightmost_enabled_byte; + CORE_ADDR addr_end_data, addr_end_dvc; +@@ -2254,8 +2433,10 @@ calculate_dvc (CORE_ADDR addr, LONGEST len, CORE_ADDR data_value, + Returns -1 if there's any register access involved, or if there are + other kinds of values which are not acceptable in a condition + expression (e.g., lval_computed or lval_internalvar). */ +-static int +-num_memory_accesses (const std::vector &chain) ++ ++int ++ppc_linux_nat_target::num_memory_accesses (const std::vector ++ &chain) + { + int found_memory_cnt = 0; + +@@ -2303,9 +2484,11 @@ num_memory_accesses (const std::vector &chain) + If the function returns 1, DATA_VALUE will contain the constant against + which the watch value should be compared and LEN will contain the size + of the constant. */ +-static int +-check_condition (CORE_ADDR watch_addr, struct expression *cond, +- CORE_ADDR *data_value, LONGEST *len) ++ ++int ++ppc_linux_nat_target::check_condition (CORE_ADDR watch_addr, ++ struct expression *cond, ++ CORE_ADDR *data_value, int *len) + { + int pc = 1, num_accesses_left, num_accesses_right; + struct value *left_val, *right_val; +@@ -2352,19 +2535,21 @@ check_condition (CORE_ADDR watch_addr, struct expression *cond, + return 1; + } + +-/* Return non-zero if the target is capable of using hardware to evaluate +- the condition expression, thus only triggering the watchpoint when it is ++/* Return true if the target is capable of using hardware to evaluate the ++ condition expression, thus only triggering the watchpoint when it is + true. */ ++ + bool + ppc_linux_nat_target::can_accel_watchpoint_condition (CORE_ADDR addr, +- LONGEST len, +- int rw, ++ int len, int rw, + struct expression *cond) + { + CORE_ADDR data_value; + +- return (have_ptrace_hwdebug_interface () +- && hwdebug_info.num_condition_regs > 0 ++ m_dreg_interface.detect (inferior_ptid); ++ ++ return (m_dreg_interface.hwdebug_p () ++ && (m_dreg_interface.hwdebug_info ().num_condition_regs > 0) + && check_condition (addr, cond, &data_value, &len)); + } + +@@ -2373,11 +2558,16 @@ ppc_linux_nat_target::can_accel_watchpoint_condition (CORE_ADDR addr, + evaluated by hardware. INSERT tells if we are creating a request for + inserting or removing the watchpoint. */ + +-static void +-create_watchpoint_request (struct ppc_hw_breakpoint *p, CORE_ADDR addr, +- LONGEST len, enum target_hw_bp_type type, +- struct expression *cond, int insert) ++void ++ppc_linux_nat_target::create_watchpoint_request (struct ppc_hw_breakpoint *p, ++ CORE_ADDR addr, int len, ++ enum target_hw_bp_type type, ++ struct expression *cond, ++ int insert) + { ++ const struct ppc_debug_info &hwdebug_info = (m_dreg_interface ++ .hwdebug_info ()); ++ + if (len == 1 + || !(hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE)) + { +@@ -2419,28 +2609,33 @@ create_watchpoint_request (struct ppc_hw_breakpoint *p, CORE_ADDR addr, + p->addr = (uint64_t) addr; + } + ++/* Register a watchpoint, to be inserted when the threads of the group of ++ inferior_ptid are next resumed. Returns 0 on success, and -1 if there ++ is no ptrace interface available to install the watchpoint. */ ++ + int + ppc_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len, + enum target_hw_bp_type type, + struct expression *cond) + { +- struct lwp_info *lp; +- int ret = -1; ++ m_dreg_interface.detect (inferior_ptid); ++ ++ if (m_dreg_interface.unavailable_p ()) ++ return -1; + +- if (have_ptrace_hwdebug_interface ()) ++ if (m_dreg_interface.hwdebug_p ()) + { + struct ppc_hw_breakpoint p; + + create_watchpoint_request (&p, addr, len, type, cond, 1); + +- ALL_LWPS (lp) +- hwdebug_insert_point (&p, lp->ptid.lwp ()); +- +- ret = 0; ++ register_hw_breakpoint (inferior_ptid.pid (), p); + } + else + { +- long dabr_value; ++ gdb_assert (m_dreg_interface.debugreg_p ()); ++ ++ long wp_value; + long read_mode, write_mode; + + if (linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE) +@@ -2458,142 +2653,300 @@ ppc_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len, + write_mode = 6; + } + +- dabr_value = addr & ~(read_mode | write_mode); ++ wp_value = addr & ~(read_mode | write_mode); + switch (type) + { + case hw_read: + /* Set read and translate bits. */ +- dabr_value |= read_mode; ++ wp_value |= read_mode; + break; + case hw_write: + /* Set write and translate bits. */ +- dabr_value |= write_mode; ++ wp_value |= write_mode; + break; + case hw_access: + /* Set read, write and translate bits. */ +- dabr_value |= read_mode | write_mode; ++ wp_value |= read_mode | write_mode; + break; + } + +- saved_dabr_value = dabr_value; +- +- ALL_LWPS (lp) +- if (ptrace (PTRACE_SET_DEBUGREG, lp->ptid.lwp (), 0, +- saved_dabr_value) < 0) +- return -1; +- +- ret = 0; ++ register_wp (inferior_ptid.pid (), wp_value); + } + +- return ret; ++ return 0; + } + ++/* Clear a registration for a hardware watchpoint. It will be removed ++ from the threads of the group of inferior_ptid when they are next ++ resumed. */ ++ + int + ppc_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len, + enum target_hw_bp_type type, + struct expression *cond) + { +- struct lwp_info *lp; +- int ret = -1; ++ gdb_assert (!m_dreg_interface.unavailable_p ()); + +- if (have_ptrace_hwdebug_interface ()) ++ if (m_dreg_interface.hwdebug_p ()) + { + struct ppc_hw_breakpoint p; + + create_watchpoint_request (&p, addr, len, type, cond, 0); + +- ALL_LWPS (lp) +- hwdebug_remove_point (&p, lp->ptid.lwp ()); +- +- ret = 0; ++ clear_hw_breakpoint (inferior_ptid.pid (), p); + } + else + { +- saved_dabr_value = 0; +- ALL_LWPS (lp) +- if (ptrace (PTRACE_SET_DEBUGREG, lp->ptid.lwp (), 0, +- saved_dabr_value) < 0) +- return -1; ++ gdb_assert (m_dreg_interface.debugreg_p ()); + +- ret = 0; ++ clear_wp (inferior_ptid.pid ()); + } + +- return ret; ++ return 0; + } + ++/* Clean up the per-process info associated with PID. When using the ++ HWDEBUG interface, we also erase the per-thread state of installed ++ debug registers for all the threads that belong to the group of PID. ++ ++ Usually the thread state is cleaned up by low_delete_thread. We also ++ do it here because low_new_thread is not called for the initial LWP, ++ so low_delete_thread won't be able to clean up this state. */ ++ + void +-ppc_linux_nat_target::low_new_thread (struct lwp_info *lp) ++ppc_linux_nat_target::low_forget_process (pid_t pid) + { +- int tid = lp->ptid.lwp (); ++ if ((!m_dreg_interface.detected_p ()) ++ || (m_dreg_interface.unavailable_p ())) ++ return; ++ ++ ptid_t pid_ptid (pid, 0, 0); + +- if (have_ptrace_hwdebug_interface ()) ++ m_process_info.erase (pid); ++ ++ if (m_dreg_interface.hwdebug_p ()) + { +- int i; +- struct thread_points *p; +- struct hw_break_tuple *hw_breaks; ++ for (auto it = m_installed_hw_bps.begin (); ++ it != m_installed_hw_bps.end ();) ++ { ++ if (it->first.matches (pid_ptid)) ++ it = m_installed_hw_bps.erase (it); ++ else ++ it++; ++ } ++ } ++} + +- if (VEC_empty (thread_points_p, ppc_threads)) +- return; ++/* Copy the per-process state associated with the pid of PARENT to the ++ sate of CHILD_PID. GDB expects that a forked process will have the ++ same hardware breakpoints and watchpoints as the parent. + +- /* Get a list of breakpoints from any thread. */ +- p = VEC_last (thread_points_p, ppc_threads); +- hw_breaks = p->hw_breaks; ++ If we're using the HWDEBUG interface, also copy the thread debug ++ register state for the ptid of PARENT to the state for CHILD_PID. + +- /* Copy that thread's breakpoints and watchpoints to the new thread. */ +- for (i = 0; i < max_slots_number; i++) +- if (hw_breaks[i].hw_break) +- { +- /* Older kernels did not make new threads inherit their parent +- thread's debug state, so we always clear the slot and replicate +- the debug state ourselves, ensuring compatibility with all +- kernels. */ ++ Like for clone events, we assume the kernel will copy the debug ++ registers from the parent thread to the child. The ++ low_prepare_to_resume function is made to work even if it doesn't. + +- /* The ppc debug resource accounting is done through "slots". +- Ask the kernel the deallocate this specific *point's slot. */ +- ptrace (PPC_PTRACE_DELHWDEBUG, tid, 0, hw_breaks[i].slot); ++ We copy the thread state here and not in low_new_thread since we don't ++ have the pid of the parent in low_new_thread. Even if we did, ++ low_new_thread might not be called immediately after the fork event is ++ detected. For instance, with the checkpointing system (see ++ linux-fork.c), the thread won't be added until GDB decides to switch ++ to a new checkpointed process. At that point, the debug register ++ state of the parent thread is unlikely to correspond to the state it ++ had at the point when it forked. */ + +- hwdebug_insert_point (hw_breaks[i].hw_break, tid); +- } ++void ++ppc_linux_nat_target::low_new_fork (struct lwp_info *parent, ++ pid_t child_pid) ++{ ++ if ((!m_dreg_interface.detected_p ()) ++ || (m_dreg_interface.unavailable_p ())) ++ return; ++ ++ auto process_it = m_process_info.find (parent->ptid.pid ()); ++ ++ if (process_it != m_process_info.end ()) ++ m_process_info[child_pid] = m_process_info[parent->ptid.pid ()]; ++ ++ if (m_dreg_interface.hwdebug_p ()) ++ { ++ ptid_t child_ptid (child_pid, child_pid, 0); ++ ++ copy_thread_dreg_state (parent->ptid, child_ptid); + } +- else +- ptrace (PTRACE_SET_DEBUGREG, tid, 0, saved_dabr_value); + } + +-static void +-ppc_linux_thread_exit (struct thread_info *tp, int silent) ++/* Copy the thread debug register state from the PARENT thread to the the ++ state for CHILD_LWP, if we're using the HWDEBUG interface. We assume ++ the kernel copies the debug registers from one thread to another after ++ a clone event. The low_prepare_to_resume function is made to work ++ even if it doesn't. */ ++ ++void ++ppc_linux_nat_target::low_new_clone (struct lwp_info *parent, ++ pid_t child_lwp) + { +- int i; +- int tid = tp->ptid.lwp (); +- struct hw_break_tuple *hw_breaks; +- struct thread_points *t = NULL, *p; ++ if ((!m_dreg_interface.detected_p ()) ++ || (m_dreg_interface.unavailable_p ())) ++ return; ++ ++ if (m_dreg_interface.hwdebug_p ()) ++ { ++ ptid_t child_ptid (parent->ptid.pid (), child_lwp, 0); ++ ++ copy_thread_dreg_state (parent->ptid, child_ptid); ++ } ++} ++ ++/* Initialize the arch-specific thread state for LP so that it contains ++ the ptid for lp, so that we can use it in low_delete_thread. Mark the ++ new thread LP as stale so that we update its debug registers before ++ resuming it. This is not called for the initial thread. */ ++ ++void ++ppc_linux_nat_target::low_new_thread (struct lwp_info *lp) ++{ ++ init_arch_lwp_info (lp); ++ ++ mark_thread_stale (lp); ++} ++ ++/* Delete the per-thread debug register stale flag. */ + +- if (!have_ptrace_hwdebug_interface ()) ++void ++ppc_linux_nat_target::low_delete_thread (struct arch_lwp_info ++ *lp_arch_info) ++{ ++ if (lp_arch_info != NULL) ++ { ++ if (m_dreg_interface.detected_p () ++ && m_dreg_interface.hwdebug_p ()) ++ m_installed_hw_bps.erase (lp_arch_info->lwp_ptid); ++ ++ xfree (lp_arch_info); ++ } ++} ++ ++/* Install or delete debug registers in thread LP so that it matches what ++ GDB requested before it is resumed. */ ++ ++void ++ppc_linux_nat_target::low_prepare_to_resume (struct lwp_info *lp) ++{ ++ if ((!m_dreg_interface.detected_p ()) ++ || (m_dreg_interface.unavailable_p ())) + return; + +- for (i = 0; VEC_iterate (thread_points_p, ppc_threads, i, p); i++) +- if (p->tid == tid) +- { +- t = p; +- break; +- } ++ /* We have to re-install or clear the debug registers if we set the ++ stale flag. ++ ++ In addition, some kernels configurations can disable a hardware ++ watchpoint after it is hit. Usually, GDB will remove and re-install ++ a hardware watchpoint when the thread stops if "breakpoint ++ always-inserted" is off, or to single-step a watchpoint. But so ++ that we don't rely on this behavior, if we stop due to a hardware ++ breakpoint or watchpoint, we also refresh our debug registers. */ + +- if (t == NULL) ++ arch_lwp_info *lp_arch_info = get_arch_lwp_info (lp); ++ ++ bool stale_dregs = (lp->stop_reason == TARGET_STOPPED_BY_WATCHPOINT ++ || lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT ++ || lp_arch_info->debug_regs_stale); ++ ++ if (!stale_dregs) + return; + +- VEC_unordered_remove (thread_points_p, ppc_threads, i); ++ gdb_assert (lp->ptid.lwp_p ()); ++ ++ auto process_it = m_process_info.find (lp->ptid.pid ()); ++ ++ if (m_dreg_interface.hwdebug_p ()) ++ { ++ /* First, delete any hardware watchpoint or breakpoint installed in ++ the inferior and update the thread state. */ ++ auto installed_it = m_installed_hw_bps.find (lp->ptid); ++ ++ if (installed_it != m_installed_hw_bps.end ()) ++ { ++ auto &bp_list = installed_it->second; ++ ++ for (auto bp_it = bp_list.begin (); bp_it != bp_list.end ();) ++ { ++ /* We ignore ENOENT to account for various possible kernel ++ behaviors, e.g. the kernel might or might not copy debug ++ registers across forks and clones, and we always copy ++ the debug register state when fork and clone events are ++ detected. */ ++ if (ptrace (PPC_PTRACE_DELHWDEBUG, lp->ptid.lwp (), 0, ++ bp_it->first) == -1) ++ if (errno != ENOENT) ++ perror_with_name (_("Error deleting hardware " ++ "breakpoint or watchpoint")); ++ ++ /* We erase the entries one at a time after successfuly ++ removing the corresponding slot form the thread so that ++ if we throw an exception above in a future iteration the ++ map remains consistent. */ ++ bp_it = bp_list.erase (bp_it); ++ } + +- hw_breaks = t->hw_breaks; ++ gdb_assert (bp_list.empty ()); ++ } + +- for (i = 0; i < max_slots_number; i++) +- if (hw_breaks[i].hw_break) +- xfree (hw_breaks[i].hw_break); ++ /* Now we install all the requested hardware breakpoints and ++ watchpoints and update the thread state. */ + +- xfree (t->hw_breaks); +- xfree (t); ++ if (process_it != m_process_info.end ()) ++ { ++ auto &bp_list = m_installed_hw_bps[lp->ptid]; ++ ++ for (ppc_hw_breakpoint bp ++ : process_it->second.requested_hw_bps) ++ { ++ long slot = ptrace (PPC_PTRACE_SETHWDEBUG, lp->ptid.lwp (), ++ 0, &bp); ++ ++ if (slot < 0) ++ perror_with_name (_("Error setting hardware " ++ "breakpoint or watchpoint")); ++ ++ /* Keep track of which slots we installed in this ++ thread. */ ++ bp_list.emplace (bp_list.begin (), slot, bp); ++ } ++ } ++ } ++ else ++ { ++ gdb_assert (m_dreg_interface.debugreg_p ()); ++ ++ /* Passing 0 to PTRACE_SET_DEBUGREG will clear the ++ watchpoint. */ ++ long wp = 0; ++ ++ /* GDB requested a watchpoint to be installed. */ ++ if (process_it != m_process_info.end () ++ && process_it->second.requested_wp_val.has_value ()) ++ wp = *(process_it->second.requested_wp_val); ++ ++ long ret = ptrace (PTRACE_SET_DEBUGREG, lp->ptid.lwp (), ++ 0, wp); ++ ++ if (ret == -1) ++ perror_with_name (_("Error setting hardware watchpoint")); ++ } ++ ++ lp_arch_info->debug_regs_stale = false; + } + ++/* Return true if INFERIOR_PTID is known to have been stopped by a ++ hardware watchpoint, false otherwise. If true is returned, write the ++ address that the kernel reported as causing the SIGTRAP in ADDR_P. */ ++ + bool +-ppc_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p) ++ppc_linux_nat_target::low_stopped_data_address (CORE_ADDR *addr_p) + { + siginfo_t siginfo; + +@@ -2604,48 +2957,57 @@ ppc_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p) + || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */) + return false; + +- if (have_ptrace_hwdebug_interface ()) ++ gdb_assert (!m_dreg_interface.unavailable_p ()); ++ ++ /* Check if this signal corresponds to a hardware breakpoint. We only ++ need to check this if we're using the HWDEBUG interface, since the ++ DEBUGREG interface only allows setting one hardware watchpoint. */ ++ if (m_dreg_interface.hwdebug_p ()) + { +- int i; +- struct thread_points *t; +- struct hw_break_tuple *hw_breaks; +- /* The index (or slot) of the *point is passed in the si_errno field. */ ++ /* The index (or slot) of the *point is passed in the si_errno ++ field. Currently, this is only the case if the kernel was ++ configured with CONFIG_PPC_ADV_DEBUG_REGS. If not, we assume ++ the kernel will set si_errno to a value that doesn't correspond ++ to any real slot. */ + int slot = siginfo.si_errno; + +- t = hwdebug_find_thread_points_by_tid (inferior_ptid.lwp (), 0); ++ auto installed_it = m_installed_hw_bps.find (inferior_ptid); + +- /* Find out if this *point is a hardware breakpoint. +- If so, we should return 0. */ +- if (t) +- { +- hw_breaks = t->hw_breaks; +- for (i = 0; i < max_slots_number; i++) +- if (hw_breaks[i].hw_break && hw_breaks[i].slot == slot +- && hw_breaks[i].hw_break->trigger_type +- == PPC_BREAKPOINT_TRIGGER_EXECUTE) +- return false; +- } ++ /* We must have installed slots for the thread if it got a ++ TRAP_HWBKPT signal. */ ++ gdb_assert (installed_it != m_installed_hw_bps.end ()); ++ ++ for (const auto & slot_bp_pair : installed_it->second) ++ if (slot_bp_pair.first == slot ++ && (slot_bp_pair.second.trigger_type ++ == PPC_BREAKPOINT_TRIGGER_EXECUTE)) ++ return false; + } + + *addr_p = (CORE_ADDR) (uintptr_t) siginfo.si_addr; + return true; + } + ++/* Return true if INFERIOR_PTID is known to have been stopped by a ++ hardware watchpoint, false otherwise. */ ++ + bool +-ppc_linux_nat_target::stopped_by_watchpoint () ++ppc_linux_nat_target::low_stopped_by_watchpoint () + { + CORE_ADDR addr; +- return stopped_data_address (&addr); ++ return low_stopped_data_address (&addr); + } + + bool + ppc_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr, + CORE_ADDR start, +- LONGEST length) ++ int length) + { ++ gdb_assert (!m_dreg_interface.unavailable_p ()); ++ + int mask; + +- if (have_ptrace_hwdebug_interface () ++ if (m_dreg_interface.hwdebug_p () + && linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE) + return start <= addr && start + length >= addr; + else if (linux_get_hwcap (current_top_target ()) & PPC_FEATURE_BOOKE) +@@ -2662,10 +3024,14 @@ ppc_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr, + /* Return the number of registers needed for a masked hardware watchpoint. */ + + int +-ppc_linux_nat_target::masked_watch_num_registers (CORE_ADDR addr, CORE_ADDR mask) ++ppc_linux_nat_target::masked_watch_num_registers (CORE_ADDR addr, ++ CORE_ADDR mask) + { +- if (!have_ptrace_hwdebug_interface () +- || (hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_MASK) == 0) ++ m_dreg_interface.detect (inferior_ptid); ++ ++ if (!m_dreg_interface.hwdebug_p () ++ || (m_dreg_interface.hwdebug_info ().features ++ & PPC_DEBUG_FEATURE_DATA_BP_MASK) == 0) + return -1; + else if ((mask & 0xC0000000) != 0xC0000000) + { +@@ -2678,14 +3044,204 @@ ppc_linux_nat_target::masked_watch_num_registers (CORE_ADDR addr, CORE_ADDR mask + return 2; + } + ++/* Copy the per-thread debug register state, if any, from thread ++ PARENT_PTID to thread CHILD_PTID, if the debug register being used is ++ HWDEBUG. */ ++ ++void ++ppc_linux_nat_target::copy_thread_dreg_state (const ptid_t &parent_ptid, ++ const ptid_t &child_ptid) ++{ ++ gdb_assert (m_dreg_interface.hwdebug_p ()); ++ ++ auto installed_it = m_installed_hw_bps.find (parent_ptid); ++ ++ if (installed_it != m_installed_hw_bps.end ()) ++ m_installed_hw_bps[child_ptid] = m_installed_hw_bps[parent_ptid]; ++} ++ ++/* Mark the debug register stale flag for the new thread, if we have ++ already detected which debug register interface we use. */ ++ ++void ++ppc_linux_nat_target::mark_thread_stale (struct lwp_info *lp) ++{ ++ if ((!m_dreg_interface.detected_p ()) ++ || (m_dreg_interface.unavailable_p ())) ++ return; ++ ++ arch_lwp_info *lp_arch_info = get_arch_lwp_info (lp); ++ ++ lp_arch_info->debug_regs_stale = true; ++} ++ ++/* Mark all the threads of the group of PID as stale with respect to ++ debug registers and issue a stop request to each such thread that ++ isn't already stopped. */ ++ ++void ++ppc_linux_nat_target::mark_debug_registers_changed (pid_t pid) ++{ ++ /* We do this in two passes to make sure all threads are marked even if ++ we get an exception when stopping one of them. */ ++ ++ iterate_over_lwps_new (ptid_t (pid), ++ [this] (struct lwp_info *lp) -> int { ++ this->mark_thread_stale (lp); ++ return 0; ++ }); ++ ++ iterate_over_lwps_new (ptid_t (pid), ++ [] (struct lwp_info *lp) -> int { ++ if (!lwp_is_stopped (lp)) ++ linux_stop_lwp (lp); ++ return 0; ++ }); ++} ++ ++/* Register a hardware breakpoint or watchpoint BP for the pid PID, then ++ mark the stale flag for all threads of the group of PID, and issue a ++ stop request for them. The breakpoint or watchpoint will be installed ++ the next time each thread is resumed. Should only be used if the ++ debug register interface is HWDEBUG. */ ++ ++void ++ppc_linux_nat_target::register_hw_breakpoint (pid_t pid, ++ const struct ++ ppc_hw_breakpoint &bp) ++{ ++ gdb_assert (m_dreg_interface.hwdebug_p ()); ++ ++ m_process_info[pid].requested_hw_bps.push_back (bp); ++ ++ mark_debug_registers_changed (pid); ++} ++ ++/* Clear a registration for a hardware breakpoint or watchpoint BP for ++ the pid PID, then mark the stale flag for all threads of the group of ++ PID, and issue a stop request for them. The breakpoint or watchpoint ++ will be removed the next time each thread is resumed. Should only be ++ used if the debug register interface is HWDEBUG. */ ++ ++void ++ppc_linux_nat_target::clear_hw_breakpoint (pid_t pid, ++ const struct ppc_hw_breakpoint &bp) ++{ ++ gdb_assert (m_dreg_interface.hwdebug_p ()); ++ ++ auto process_it = m_process_info.find (pid); ++ ++ gdb_assert (process_it != m_process_info.end ()); ++ ++ auto bp_it = std::find_if (process_it->second.requested_hw_bps.begin (), ++ process_it->second.requested_hw_bps.end (), ++ [&bp, this] ++ (const struct ppc_hw_breakpoint &curr) ++ { return hwdebug_point_cmp (bp, curr); } ++ ); ++ ++ /* If GDB is removing a watchpoint, it must have been inserted. */ ++ gdb_assert (bp_it != process_it->second.requested_hw_bps.end ()); ++ ++ process_it->second.requested_hw_bps.erase (bp_it); ++ ++ mark_debug_registers_changed (pid); ++} ++ ++/* Register the hardware watchpoint value WP_VALUE for the pid PID, ++ then mark the stale flag for all threads of the group of PID, and ++ issue a stop request for them. The breakpoint or watchpoint will be ++ installed the next time each thread is resumed. Should only be used ++ if the debug register interface is DEBUGREG. */ ++ ++void ++ppc_linux_nat_target::register_wp (pid_t pid, long wp_value) ++{ ++ gdb_assert (m_dreg_interface.debugreg_p ()); ++ ++ /* Our other functions should have told GDB that we only have one ++ hardware watchpoint with this interface. */ ++ gdb_assert (!m_process_info[pid].requested_wp_val.has_value ()); ++ ++ m_process_info[pid].requested_wp_val.emplace (wp_value); ++ ++ mark_debug_registers_changed (pid); ++} ++ ++/* Clear the hardware watchpoint registration for the pid PID, then mark ++ the stale flag for all threads of the group of PID, and issue a stop ++ request for them. The breakpoint or watchpoint will be installed the ++ next time each thread is resumed. Should only be used if the debug ++ register interface is DEBUGREG. */ ++ ++void ++ppc_linux_nat_target::clear_wp (pid_t pid) ++{ ++ gdb_assert (m_dreg_interface.debugreg_p ()); ++ ++ auto process_it = m_process_info.find (pid); ++ ++ gdb_assert (process_it != m_process_info.end ()); ++ gdb_assert (process_it->second.requested_wp_val.has_value ()); ++ ++ process_it->second.requested_wp_val.reset (); ++ ++ mark_debug_registers_changed (pid); ++} ++ ++/* Initialize the arch-specific thread state for LWP, if it not already ++ created. */ ++ ++void ++ppc_linux_nat_target::init_arch_lwp_info (struct lwp_info *lp) ++{ ++ if (lwp_arch_private_info (lp) == NULL) ++ { ++ lwp_set_arch_private_info (lp, XCNEW (struct arch_lwp_info)); ++ lwp_arch_private_info (lp)->debug_regs_stale = false; ++ lwp_arch_private_info (lp)->lwp_ptid = lp->ptid; ++ } ++} ++ ++/* Get the arch-specific thread state for LWP, creating it if ++ necessary. */ ++ ++arch_lwp_info * ++ppc_linux_nat_target::get_arch_lwp_info (struct lwp_info *lp) ++{ ++ init_arch_lwp_info (lp); ++ ++ return lwp_arch_private_info (lp); ++} ++ ++/* The post-gdb-8 version of iterate_over_lwps. */ ++ ++static struct lwp_info * ++iterate_over_lwps_new (ptid_t filter, ++ gdb::function_view callback) ++{ ++ struct lwp_info *lp, *lpnext; ++ ++ for (lp = lwp_list; lp; lp = lpnext) ++ { ++ lpnext = lp->next; ++ ++ if (lp->ptid.matches (filter)) ++ { ++ if (callback (lp) != 0) ++ return lp; ++ } ++ } ++ ++ return NULL; ++} ++ + void _initialize_ppc_linux_nat (); + void + _initialize_ppc_linux_nat (void) + { + linux_target = &the_ppc_linux_nat_target; + +- gdb::observers::thread_exit.attach (ppc_linux_thread_exit); +- + /* Register the target. */ + add_inf_child_target (linux_target); + } +diff --git a/gdb/procfs.c b/gdb/procfs.c +--- a/gdb/procfs.c ++++ b/gdb/procfs.c +@@ -3358,7 +3358,7 @@ procfs_target::remove_watchpoint (CORE_ADDR addr, int len, + } + + int +-procfs_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len) ++procfs_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) + { + /* The man page for proc(4) on Solaris 2.6 and up says that the + system can support "thousands" of hardware watchpoints, but gives +diff --git a/gdb/remote.c b/gdb/remote.c +--- a/gdb/remote.c ++++ b/gdb/remote.c +@@ -454,7 +454,7 @@ public: + + bool stopped_data_address (CORE_ADDR *) override; + +- bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, LONGEST) override; ++ bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override; + + int can_use_hw_breakpoint (enum bptype, int, int) override; + +@@ -462,7 +462,7 @@ public: + + int remove_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override; + +- int region_ok_for_hw_watchpoint (CORE_ADDR, LONGEST) override; ++ int region_ok_for_hw_watchpoint (CORE_ADDR, int) override; + + int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, + struct expression *) override; +@@ -10362,7 +10362,7 @@ remote_target::insert_watchpoint (CORE_ADDR addr, int len, + + bool + remote_target::watchpoint_addr_within_range (CORE_ADDR addr, +- CORE_ADDR start, LONGEST length) ++ CORE_ADDR start, int length) + { + CORE_ADDR diff = remote_address_masked (addr - start); + +@@ -10413,7 +10413,7 @@ int remote_hw_watchpoint_length_limit = -1; + int remote_hw_breakpoint_limit = -1; + + int +-remote_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len) ++remote_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) + { + if (remote_hw_watchpoint_length_limit == 0) + return 0; +diff --git a/gdb/s390-linux-nat.c b/gdb/s390-linux-nat.c +--- a/gdb/s390-linux-nat.c ++++ b/gdb/s390-linux-nat.c +@@ -122,7 +122,7 @@ public: + override; + int remove_hw_breakpoint (struct gdbarch *, struct bp_target_info *) + override; +- int region_ok_for_hw_watchpoint (CORE_ADDR, LONGEST) override; ++ int region_ok_for_hw_watchpoint (CORE_ADDR, int) override; + bool have_continuable_watchpoint () override { return true; } + bool stopped_by_watchpoint () override; + int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type, +@@ -954,7 +954,7 @@ s390_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch, + + int + s390_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, +- LONGEST cnt) ++ int cnt) + { + return 1; + } +diff --git a/gdb/spu-multiarch.c b/gdb/spu-multiarch.c +--- a/gdb/spu-multiarch.c ++++ b/gdb/spu-multiarch.c +@@ -66,7 +66,7 @@ struct spu_multiarch_target final : public target_ops + const gdb_byte *pattern, ULONGEST pattern_len, + CORE_ADDR *found_addrp) override; + +- int region_ok_for_hw_watchpoint (CORE_ADDR, LONGEST) override; ++ int region_ok_for_hw_watchpoint (CORE_ADDR, int) override; + + struct gdbarch *thread_architecture (ptid_t) override; + }; +@@ -163,7 +163,7 @@ spu_multiarch_target::thread_architecture (ptid_t ptid) + /* Override the to_region_ok_for_hw_watchpoint routine. */ + + int +-spu_multiarch_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len) ++spu_multiarch_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) + { + /* We cannot watch SPU local store. */ + if (SPUADDR_SPU (addr) != -1) +diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c +--- a/gdb/target-delegates.c ++++ b/gdb/target-delegates.c +@@ -38,9 +38,9 @@ struct dummy_target : public target_ops + int have_steppable_watchpoint () override; + bool have_continuable_watchpoint () override; + bool stopped_data_address (CORE_ADDR *arg0) override; +- bool watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, LONGEST arg2) override; +- int region_ok_for_hw_watchpoint (CORE_ADDR arg0, LONGEST arg1) override; +- bool can_accel_watchpoint_condition (CORE_ADDR arg0, LONGEST arg1, int arg2, struct expression *arg3) override; ++ bool watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, int arg2) override; ++ int region_ok_for_hw_watchpoint (CORE_ADDR arg0, int arg1) override; ++ bool can_accel_watchpoint_condition (CORE_ADDR arg0, int arg1, int arg2, struct expression *arg3) override; + int masked_watch_num_registers (CORE_ADDR arg0, CORE_ADDR arg1) override; + int can_do_single_step () override; + bool supports_terminal_ours () override; +@@ -206,9 +206,9 @@ struct debug_target : public target_ops + int have_steppable_watchpoint () override; + bool have_continuable_watchpoint () override; + bool stopped_data_address (CORE_ADDR *arg0) override; +- bool watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, LONGEST arg2) override; +- int region_ok_for_hw_watchpoint (CORE_ADDR arg0, LONGEST arg1) override; +- bool can_accel_watchpoint_condition (CORE_ADDR arg0, LONGEST arg1, int arg2, struct expression *arg3) override; ++ bool watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, int arg2) override; ++ int region_ok_for_hw_watchpoint (CORE_ADDR arg0, int arg1) override; ++ bool can_accel_watchpoint_condition (CORE_ADDR arg0, int arg1, int arg2, struct expression *arg3) override; + int masked_watch_num_registers (CORE_ADDR arg0, CORE_ADDR arg1) override; + int can_do_single_step () override; + bool supports_terminal_ours () override; +@@ -1068,19 +1068,19 @@ debug_target::stopped_data_address (CORE_ADDR *arg0) + } + + bool +-target_ops::watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, LONGEST arg2) ++target_ops::watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, int arg2) + { + return this->beneath ()->watchpoint_addr_within_range (arg0, arg1, arg2); + } + + bool +-dummy_target::watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, LONGEST arg2) ++dummy_target::watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, int arg2) + { + return default_watchpoint_addr_within_range (this, arg0, arg1, arg2); + } + + bool +-debug_target::watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, LONGEST arg2) ++debug_target::watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, int arg2) + { + bool result; + fprintf_unfiltered (gdb_stdlog, "-> %s->watchpoint_addr_within_range (...)\n", this->beneath ()->shortname ()); +@@ -1098,19 +1098,19 @@ debug_target::watchpoint_addr_within_range (CORE_ADDR arg0, CORE_ADDR arg1, LONG + } + + int +-target_ops::region_ok_for_hw_watchpoint (CORE_ADDR arg0, LONGEST arg1) ++target_ops::region_ok_for_hw_watchpoint (CORE_ADDR arg0, int arg1) + { + return this->beneath ()->region_ok_for_hw_watchpoint (arg0, arg1); + } + + int +-dummy_target::region_ok_for_hw_watchpoint (CORE_ADDR arg0, LONGEST arg1) ++dummy_target::region_ok_for_hw_watchpoint (CORE_ADDR arg0, int arg1) + { + return default_region_ok_for_hw_watchpoint (this, arg0, arg1); + } + + int +-debug_target::region_ok_for_hw_watchpoint (CORE_ADDR arg0, LONGEST arg1) ++debug_target::region_ok_for_hw_watchpoint (CORE_ADDR arg0, int arg1) + { + int result; + fprintf_unfiltered (gdb_stdlog, "-> %s->region_ok_for_hw_watchpoint (...)\n", this->beneath ()->shortname ()); +@@ -1126,19 +1126,19 @@ debug_target::region_ok_for_hw_watchpoint (CORE_ADDR arg0, LONGEST arg1) + } + + bool +-target_ops::can_accel_watchpoint_condition (CORE_ADDR arg0, LONGEST arg1, int arg2, struct expression *arg3) ++target_ops::can_accel_watchpoint_condition (CORE_ADDR arg0, int arg1, int arg2, struct expression *arg3) + { + return this->beneath ()->can_accel_watchpoint_condition (arg0, arg1, arg2, arg3); + } + + bool +-dummy_target::can_accel_watchpoint_condition (CORE_ADDR arg0, LONGEST arg1, int arg2, struct expression *arg3) ++dummy_target::can_accel_watchpoint_condition (CORE_ADDR arg0, int arg1, int arg2, struct expression *arg3) + { + return false; + } + + bool +-debug_target::can_accel_watchpoint_condition (CORE_ADDR arg0, LONGEST arg1, int arg2, struct expression *arg3) ++debug_target::can_accel_watchpoint_condition (CORE_ADDR arg0, int arg1, int arg2, struct expression *arg3) + { + bool result; + fprintf_unfiltered (gdb_stdlog, "-> %s->can_accel_watchpoint_condition (...)\n", this->beneath ()->shortname ()); +diff --git a/gdb/target.h b/gdb/target.h +--- a/gdb/target.h ++++ b/gdb/target.h +@@ -557,15 +557,15 @@ struct target_ops + TARGET_DEFAULT_RETURN (false); + virtual bool stopped_data_address (CORE_ADDR *) + TARGET_DEFAULT_RETURN (false); +- virtual bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, LONGEST) ++ virtual bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) + TARGET_DEFAULT_FUNC (default_watchpoint_addr_within_range); + + /* Documentation of this routine is provided with the corresponding + target_* macro. */ +- virtual int region_ok_for_hw_watchpoint (CORE_ADDR, LONGEST) ++ virtual int region_ok_for_hw_watchpoint (CORE_ADDR, int) + TARGET_DEFAULT_FUNC (default_region_ok_for_hw_watchpoint); + +- virtual bool can_accel_watchpoint_condition (CORE_ADDR, LONGEST, int, ++ virtual bool can_accel_watchpoint_condition (CORE_ADDR, int, int, + struct expression *) + TARGET_DEFAULT_RETURN (false); + virtual int masked_watch_num_registers (CORE_ADDR, CORE_ADDR) +diff --git a/gdb/x86-nat.c b/gdb/x86-nat.c +--- a/gdb/x86-nat.c ++++ b/gdb/x86-nat.c +@@ -173,7 +173,7 @@ x86_remove_watchpoint (CORE_ADDR addr, int len, + address ADDR and whose length is LEN bytes. */ + + int +-x86_region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len) ++x86_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) + { + struct x86_debug_reg_state *state + = x86_debug_reg_state (inferior_ptid.pid ()); +diff --git a/gdb/x86-nat.h b/gdb/x86-nat.h +--- a/gdb/x86-nat.h ++++ b/gdb/x86-nat.h +@@ -49,7 +49,7 @@ extern void x86_forget_process (pid_t pid); + definitions. */ + + extern int x86_can_use_hw_breakpoint (enum bptype type, int cnt, int othertype); +-extern int x86_region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len); ++extern int x86_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len); + extern int x86_stopped_by_watchpoint (); + extern int x86_stopped_data_address (CORE_ADDR *addr_p); + extern int x86_insert_watchpoint (CORE_ADDR addr, int len, +@@ -82,7 +82,7 @@ struct x86_nat_target : public BaseTarget + int can_use_hw_breakpoint (enum bptype type, int cnt, int othertype) override + { return x86_can_use_hw_breakpoint (type, cnt, othertype); } + +- int region_ok_for_hw_watchpoint (CORE_ADDR addr, LONGEST len) override ++ int region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) override + { return x86_region_ok_for_hw_watchpoint (addr, len); } + + int insert_watchpoint (CORE_ADDR addr, int len, diff --git a/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-5of7.patch b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-5of7.patch new file mode 100644 index 0000000..c050015 --- /dev/null +++ b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-5of7.patch @@ -0,0 +1,58 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Pedro Franco de Carvalho +Date: Wed, 7 Jul 2021 19:03:16 -0400 +Subject: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-5of7.patch + +;; Backport "[PowerPC] Use < 0 and >= 0 for watchpoint ptrace calls" +;; (Pedro Franco de Carvalho, RH BZ 1854784) + +In commit 227c0bf4b3dd0cf65dceb58e729e9da81b38b5a7, which fixed some +watchpoint bugs, I compared the return value of some ptrace calls with == +-1 and != -1. Althought this should be correct, since the rest of the +file uses < 0 and >= 0, I have modified this for consistency. + +gdb/ChangeLog: + + * ppc-linux-nat.c (ppc_linux_dreg_interface::detect) + (ppc_linux_nat_target::low_prepare_to_resume): Use ptrace () < 0 + and >= to check return value instead of == -1 and != -1. + +diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c +--- a/gdb/ppc-linux-nat.c ++++ b/gdb/ppc-linux-nat.c +@@ -385,7 +385,7 @@ public: + bool no_features = false; + + if (ptrace (PPC_PTRACE_GETHWDBGINFO, ptid.lwp (), 0, &m_hwdebug_info) +- != -1) ++ >= 0) + { + /* If there are no advertised features, we don't use the + HWDEBUG interface and try the DEBUGREG interface instead. +@@ -431,7 +431,7 @@ public: + { + unsigned long wp; + +- if (ptrace (PTRACE_GET_DEBUGREG, ptid.lwp (), 0, &wp) != -1) ++ if (ptrace (PTRACE_GET_DEBUGREG, ptid.lwp (), 0, &wp) >= 0) + { + m_interface.emplace (DEBUGREG); + return; +@@ -2880,7 +2880,7 @@ ppc_linux_nat_target::low_prepare_to_resume (struct lwp_info *lp) + the debug register state when fork and clone events are + detected. */ + if (ptrace (PPC_PTRACE_DELHWDEBUG, lp->ptid.lwp (), 0, +- bp_it->first) == -1) ++ bp_it->first) < 0) + if (errno != ENOENT) + perror_with_name (_("Error deleting hardware " + "breakpoint or watchpoint")); +@@ -2934,7 +2934,7 @@ ppc_linux_nat_target::low_prepare_to_resume (struct lwp_info *lp) + long ret = ptrace (PTRACE_SET_DEBUGREG, lp->ptid.lwp (), + 0, wp); + +- if (ret == -1) ++ if (ret < 0) + perror_with_name (_("Error setting hardware watchpoint")); + } + diff --git a/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-6of7.patch b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-6of7.patch new file mode 100644 index 0000000..621ad80 --- /dev/null +++ b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-6of7.patch @@ -0,0 +1,86 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Pedro Franco de Carvalho +Date: Wed, 7 Jul 2021 19:05:04 -0400 +Subject: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-6of7.patch + +;; Backport "[PowerPC] Always clear watchpoint with PTRACE_SET_DEBUGREG" +;; (Pedro Franco de Carvalho, RH BZ 1854784) + +This patches changes low_prepare_to_resume in the ppc linux native target +to always clear the watchpoint when the old PTRACE_SET_DEBUGREG interface +is used, even if another watchpoint GDB requested to the target is +written right after using the same call. + +The reason for this is that there were some older kernel versions for +which overwriting a watchpoint with PTRACE_SET_DEBUGREG would not +re-activate the watchpoint if it was previouly disabled following a hit. +This happened when the kernel was configured with CONFIG_HW_BREAKPOINT on +and uses perf events to install watchpoints. + +Previously, the ppc linux native target would immediately remove or +insert watchpoints following a request from the upper layers. This was +changed in commit 227c0bf4b3dd0cf65dceb58e729e9da81b38b5a7 to fix other +issues, which caused watchpoint requests to be applied to the inferior +only in low_prepare_to_resume, right before the inferior is resumed. + +Usually, but maybe not always, after a hit, GDB will remove the +watchpoint, resume the inferior for a single-step, possibly report the +watchpoint hit to the user, and then re-insert the watchpoint before the +inferior is next resumed. In this case there would be no problems, but +since I can't guarantee that there aren't other paths in GDB that allow +the user to set a new watchpoint after the first one hit, and after its +deletion by GDB, but before the inferior is resumed, there is a chance +that PTRACE_SET_DEBUGREG could be called directly without the watchpoint +first having been cleared, which could cause a false negative with the +older kernel versions. + +This issue would affect kernel versions starting from this commit: + +5aae8a53708025d4e718f0d2e7c2f766779ddc71 + +Up to the fix in this commit: + +a53fd61ac2f411745471c1c877d5e072fbbf0e5c + +gdb/ChangeLog: + + PR breakpoints/26385 + * ppc-linux-nat.c (ppc_linux_nat_target::low_prepare_to_resume): + Always clear watchpoint with PTRACE_SET_DEBUGREG. + +diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c +--- a/gdb/ppc-linux-nat.c ++++ b/gdb/ppc-linux-nat.c +@@ -2922,20 +2922,23 @@ ppc_linux_nat_target::low_prepare_to_resume (struct lwp_info *lp) + { + gdb_assert (m_dreg_interface.debugreg_p ()); + +- /* Passing 0 to PTRACE_SET_DEBUGREG will clear the +- watchpoint. */ +- long wp = 0; ++ /* Passing 0 to PTRACE_SET_DEBUGREG will clear the watchpoint. We ++ always clear the watchpoint instead of just overwriting it, in ++ case there is a request for a new watchpoint, because on some ++ older kernel versions and configurations simply overwriting the ++ watchpoint after it was hit would not re-enable it. */ ++ if (ptrace (PTRACE_SET_DEBUGREG, lp->ptid.lwp (), 0, 0) < 0) ++ perror_with_name (_("Error clearing hardware watchpoint")); + + /* GDB requested a watchpoint to be installed. */ + if (process_it != m_process_info.end () + && process_it->second.requested_wp_val.has_value ()) +- wp = *(process_it->second.requested_wp_val); +- +- long ret = ptrace (PTRACE_SET_DEBUGREG, lp->ptid.lwp (), +- 0, wp); ++ { ++ long wp = *(process_it->second.requested_wp_val); + +- if (ret < 0) +- perror_with_name (_("Error setting hardware watchpoint")); ++ if (ptrace (PTRACE_SET_DEBUGREG, lp->ptid.lwp (), 0, wp) < 0) ++ perror_with_name (_("Error setting hardware watchpoint")); ++ } + } + + lp_arch_info->debug_regs_stale = false; diff --git a/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-7of7.patch b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-7of7.patch new file mode 100644 index 0000000..1a2aea3 --- /dev/null +++ b/SOURCES/gdb-rhbz1854784-powerpc-remove-region-limit-dawr-7of7.patch @@ -0,0 +1,73 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Rogerio Alves +Date: Wed, 7 Jul 2021 19:06:52 -0400 +Subject: gdb-rhbz1854784-powerpc-remove-region-limit-dawr-7of7.patch + +;; Backport "PowerPC remove 512 bytes region limit if 2nd DAWR is available." +;; (Rogerio Alves, RH BZ 1854784) + +Power 10 introduces the 2nd DAWR (second watchpoint) and also removed +a restriction that limit the watch region to 512 bytes. + +2020-11-08 Rogerio A. Cardoso + +/gdb + + * ppc-linux-nat.c: (PPC_DEBUG_FEATURE_DATA_BP_ARCH_31): New define. + (region_ok_for_hw_watchpoint): Check if 2nd DAWR is avaliable before + set region. + +diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c +--- a/gdb/ppc-linux-nat.c ++++ b/gdb/ppc-linux-nat.c +@@ -144,6 +144,11 @@ struct ppc_hw_breakpoint + #define PPC_DEBUG_FEATURE_DATA_BP_DAWR 0x10 + #endif /* PPC_DEBUG_FEATURE_DATA_BP_DAWR */ + ++/* Feature defined on Linux kernel v5.1: Second watchpoint support. */ ++#ifndef PPC_DEBUG_FEATURE_DATA_BP_ARCH_31 ++#define PPC_DEBUG_FEATURE_DATA_BP_ARCH_31 0x20 ++#endif /* PPC_DEBUG_FEATURE_DATA_BP_ARCH_31 */ ++ + /* The version of the PowerPC HWDEBUG kernel interface that we will use, if + available. */ + #define PPC_DEBUG_CURRENT_VERSION 1 +@@ -2121,9 +2126,10 @@ ppc_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) + watchpoints. */ + if (m_dreg_interface.hwdebug_p ()) + { +- int region_size; + const struct ppc_debug_info &hwdebug_info = (m_dreg_interface + .hwdebug_info ()); ++ int region_size = hwdebug_info.data_bp_alignment; ++ int region_align = region_size; + + /* Embedded DAC-based processors, like the PowerPC 440 have ranged + watchpoints and can watch any access within an arbitrary memory +@@ -2135,15 +2141,19 @@ ppc_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) + return 2; + /* Check if the processor provides DAWR interface. */ + if (hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR) +- /* DAWR interface allows to watch up to 512 byte wide ranges which +- can't cross a 512 byte boundary. */ +- region_size = 512; +- else +- region_size = hwdebug_info.data_bp_alignment; ++ { ++ /* DAWR interface allows to watch up to 512 byte wide ranges. */ ++ region_size = 512; ++ /* DAWR interface allows to watch up to 512 byte wide ranges which ++ can't cross a 512 byte bondary on machines that doesn't have a ++ second DAWR (P9 or less). */ ++ if (!(hwdebug_info.features & PPC_DEBUG_FEATURE_DATA_BP_ARCH_31)) ++ region_align = 512; ++ } + /* Server processors provide one hardware watchpoint and addr+len should + fall in the watchable region provided by the ptrace interface. */ +- if (region_size +- && (addr + len > (addr & ~(region_size - 1)) + region_size)) ++ if (region_align ++ && (addr + len > (addr & ~(region_align - 1)) + region_size)) + return 0; + } + /* addr+len must fall in the 8 byte watchable region for DABR-based diff --git a/SOURCES/gdb-rhbz1934673-fortran-nameless-modules.patch b/SOURCES/gdb-rhbz1934673-fortran-nameless-modules.patch new file mode 100644 index 0000000..1e2257e --- /dev/null +++ b/SOURCES/gdb-rhbz1934673-fortran-nameless-modules.patch @@ -0,0 +1,206 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Keith Seitz +Date: Wed, 5 May 2021 13:57:13 -0700 +Subject: gdb-rhbz1934673-fortran-nameless-modules.patch + +;; Fix segfault with nameless fortran modules. +;; Bernhard Heckel, RH BZ 1943673 + + Dwarf: Don't add nameless modules to partial symbol table + + A name for BLOCK DATA in Fortran is optional. If no name has been + assigned, GDB crashes during read-in of DWARF when BLOCK DATA is + represented via DW_TAG_module. BLOCK DATA is used for one-time + initialization of non-pointer variables in named common blocks. + + As of now there is no issue when gfortran is used as DW_TAG_module is + not emitted. However, with Intel ifort the nameless DW_TAG_module is + present and has the following form: + + ... + <1>
: Abbrev Number: 7 (DW_TAG_module) + DW_AT_decl_line : 46 + DW_AT_decl_file : 1 + DW_AT_description : (indirect string, offset: 0x110): block + data + DW_AT_high_pc : 0x402bb7 + DW_AT_low_pc : 0x402bb7 + ... + + The missing name leads to a crash in add_partial_symbol, during length + calculation. + + gdb/ChangeLog: + 2019-06-11 Bernhard Heckel + + * dwarf2read.c (add_partial_symbol): Skip nameless modules. + + gdb/testsuite/Changelog: + 2019-06-11 Bernhard Heckel + + * gdb.fortran/block-data.f: New. + * gdb.fortran/block-data.exp: New. + +diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c +--- a/gdb/dwarf2read.c ++++ b/gdb/dwarf2read.c +@@ -8936,11 +8936,15 @@ add_partial_symbol (struct partial_die_info *pdi, struct dwarf2_cu *cu) + 0, cu->language, objfile); + break; + case DW_TAG_module: +- add_psymbol_to_list (actual_name, strlen (actual_name), +- built_actual_name != NULL, +- MODULE_DOMAIN, LOC_TYPEDEF, +- &objfile->global_psymbols, +- 0, cu->language, objfile); ++ /* With Fortran 77 there might be a "BLOCK DATA" module ++ available without any name. If so, we skip the module as it ++ doesn't bring any value. */ ++ if (actual_name != nullptr) ++ add_psymbol_to_list (actual_name, strlen (actual_name), ++ built_actual_name != NULL, ++ MODULE_DOMAIN, LOC_TYPEDEF, ++ &objfile->global_psymbols, ++ 0, cu->language, objfile); + break; + case DW_TAG_class_type: + case DW_TAG_interface_type: +@@ -16777,9 +16781,6 @@ read_module_type (struct die_info *die, struct dwarf2_cu *cu) + struct type *type; + + module_name = dwarf2_name (die, cu); +- if (!module_name) +- complaint (_("DW_TAG_module has no name, offset %s"), +- sect_offset_str (die->sect_off)); + type = init_type (objfile, TYPE_CODE_MODULE, 0, module_name); + + return set_die_type (die, type, cu); +diff --git a/gdb/testsuite/gdb.fortran/block-data.exp b/gdb/testsuite/gdb.fortran/block-data.exp +new file mode 100644 +--- /dev/null ++++ b/gdb/testsuite/gdb.fortran/block-data.exp +@@ -0,0 +1,63 @@ ++# Copyright 2016-2019 Free Software Foundation, Inc. ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see . ++ ++# Test anonymous block-data statements. ++ ++# A name for BLOCK DATA in Fortran is optional. BLOCK DATA is used ++# for one-time initialization of non-pointer variables in named common ++# blocks. GDB used to crash with 'Intel ifort'-generated code, which ++# outputs nameless DW_TAG_module, unlike with gfortran which just ++# doesn't emit DW_TAG_module in this case. ++ ++if { [skip_fortran_tests] } { return -1 } ++ ++standard_testfile .f ++load_lib "fortran.exp" ++ ++if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug f90}]} { ++ return -1 ++} ++ ++if ![runto MAIN__] then { ++ untested "couldn't run to breakpoint MAIN__" ++ return -1 ++} ++ ++with_test_prefix "default values" { ++ gdb_test "print doub1" "= 1\.11\\d+" ++ gdb_test "print doub2" "= 2\.22\\d+" ++ gdb_test "print char1" "= 'abcdef'" ++ gdb_test "print char2" "= 'ghijkl'" ++} ++ ++gdb_breakpoint [gdb_get_line_number "! BP_BEFORE_SUB"] ++gdb_continue_to_breakpoint "! BP_BEFORE_SUB" ".*! BP_BEFORE_SUB.*" ++ ++with_test_prefix "before sub" { ++ gdb_test "print doub1" "= 11\.11\\d+" ++ gdb_test "print doub2" "= 22\.22\\d+" ++ gdb_test "print char1" "= 'ABCDEF'" ++ gdb_test "print char2" "= 'GHIJKL'" ++} ++ ++gdb_breakpoint [gdb_get_line_number "! BP_SUB"] ++gdb_continue_to_breakpoint "! BP_SUB" ".*! BP_SUB.*" ++ ++with_test_prefix "in sub" { ++ gdb_test "print doub1" "= 11\.11\\d+" ++ gdb_test "print doub2" "= 22\.22\\d+" ++ gdb_test "print char1" "= 'ABCDEF'" ++ gdb_test "print char2" "= 'GHIJKL'" ++} +diff --git a/gdb/testsuite/gdb.fortran/block-data.f b/gdb/testsuite/gdb.fortran/block-data.f +new file mode 100644 +--- /dev/null ++++ b/gdb/testsuite/gdb.fortran/block-data.f +@@ -0,0 +1,56 @@ ++! Copyright 2016-2019 Free Software Foundation, Inc. ++! ++! This program is free software; you can redistribute it and/or modify ++! it under the terms of the GNU General Public License as published by ++! the Free Software Foundation; either version 3 of the License, or ++! (at your option) any later version. ++! ++! This program is distributed in the hope that it will be useful, ++! but WITHOUT ANY WARRANTY; without even the implied warranty of ++! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++! GNU General Public License for more details. ++! ++! You should have received a copy of the GNU General Public License ++! along with this program. If not, see . ++! ++! Check that GDB can handle block data with no global name. ++! ++! MAIN ++ PROGRAM bdata ++ DOUBLE PRECISION doub1, doub2 ++ CHARACTER*6 char1, char2 ++ ++ COMMON /BLK1/ doub1, char1 ++ COMMON /BLK2/ doub2, char2 ++ ++ doub1 = 11.111 ++ doub2 = 22.222 ++ char1 = 'ABCDEF' ++ char2 = 'GHIJKL' ++ CALL sub_block_data ! BP_BEFORE_SUB ++ STOP ++ END ++ ++! BLOCK DATA ++ BLOCK DATA ++ ++ DOUBLE PRECISION doub1, doub2 ++ CHARACTER*6 char1, char2 ++ ++ COMMON /BLK1/ doub1, char1 ++ COMMON /BLK2/ doub2, char2 ++ DATA doub1, doub2 /1.111, 2.222/ ++ DATA char1, char2 /'abcdef', 'ghijkl'/ ++ END ++ ++! SUBROUTINE ++ SUBROUTINE sub_block_data ++ ++ DOUBLE PRECISION doub1, doub2 ++ CHARACTER*6 char1, char2 ++ ++ COMMON /BLK1/ doub1, char1 ++ COMMON /BLK2/ doub2, char2 ++ ++ char1 = char2; ! BP_SUB ++ END diff --git a/SPECS/gdb.spec b/SPECS/gdb.spec index ed26b1e..cb61032 100644 --- a/SPECS/gdb.spec +++ b/SPECS/gdb.spec @@ -26,7 +26,7 @@ Version: 8.2 # The release always contains a leading reserved number, start it at 1. # `upstream' is not a part of `name' to stay fully rpm dependencies compatible for the testing. -Release: 15%{?dist} +Release: 16%{?dist} License: GPLv3+ and GPLv3+ with exceptions and GPLv2+ and GPLv2+ with exceptions and GPL+ and LGPLv2+ and LGPLv3+ and BSD and Public Domain and GFDL Group: Development/Debuggers @@ -1062,6 +1062,13 @@ fi %endif %changelog +* Wed Jul 14 2021 Keith Seitz - 8.2-16.el8 +- Backport "Fix segfault with nameless fortran modules." + (Bernhard Heckel, RH BZ 1934673) +- Backport "PowerPC remove 512 bytes region limit if 2nd DAWR is available" + and related patches. + (Rogerio Alves et al RH BZ 1854784) + * Fri Dec 11 2020 Keith Seitz - 8.2-15.el8 - Backport "Correct recording of 'store on condition' insns" (Andreas Arnaz, RH BZ 1903374)