[PATCH 3/3] Dynamic core regset sections support

Andreas Arnez
2013-06-07T13:53:31Z

Rework the handling of core file register note sections. So far the
target-dependent code provided a static list of supported register
note sections via set_gdbarch_core_regset_sections. This patch
replaces the static list by a target-dependent iterator function which
can be set with set_gdbarch_iterate_over_regset_sections. Only those
architectures that have been using set_gdbarch_core_regset_sections
are affected by this change.

The patch is needed on newer z/Architecture systems for correct gcore
handling. With the static approach, gcore always writes the TDB
regset into the core file, even if the TDB registers are currently
invalid. The new iterator omits the TDB in this case.

2013-06-07 Andreas Arnez <arnez@linux.vnet.ibm.com>

* regset.h (struct core_regset_section): Remove structure
declaration.

* gdbarch.sh: Add typedef for iterate_over_regset_sections_cb.
(function_list): Remove core_regset_sections. Add
iterate_over_regset_sections.

* gdbarch.h: Regenerate.

* gdbarch.c: Regenerate.

* linux-tdep.c (linux_collect_regset_section_cb_data): New
structure.
(linux_collect_regset_section_cb): New function.
(linux_collect_thread_registers): Replace loop by iteration
through gdbarch_iterate_over_regset_sections.
(linux_make_corefile_notes_1): Adjust check.

* amd64-linux-tdep.c (amd64_linux_regset_sections): Remove array.
(amd64_linux_iterate_over_regset_sections): New function.
(amd64_linux_init_abi_common): Remove invocation of
set_gdbarch_core_regset_sections; instead, call
set_gdbarch_iterate_over_regset_sections.

* arm-linux-tdep.c (arm_linux_fpa_regset_sections): Remove array.
(arm_linux_vfp_regset_sections): Likewise.
(arm_linux_iterate_over_regset_sections): New function.
(arm_linux_init_abi): Remove invocations of
set_gdbarch_core_regset_sections; instead, call
set_gdbarch_iterate_over_regset_sections.

* i386-linux-tdep.c (i386_linux_regset_sections): Remove array.
(i386_linux_sse_regset_sections): Likewise.
(i386_linux_avx_regset_sections): Likewise.
(i386_linux_iterate_over_regset_sections): New function.
(i386_linux_init_abi): Remove invocations of
set_gdbarch_core_regset_sections; instead, call
set_gdbarch_iterate_over_regset_sections.

* ppc-linux-tdep.c (ppc_linux_vsx_regset_sections): Remove array.
(ppc_linux_vmx_regset_sections): Likewise.
(ppc64_linux_vsx_regset_sections): Likewise.
(ppc64_linux_vmx_regset_sections): Likewise.
(ppc64_linux_fp_regset_sections): Likewise.
(ppc_linux_iterate_over_regset_sections): New function.
(ppc_linux_init_abi): Remove invocations of
set_gdbarch_core_regset_sections; instead, call
set_gdbarch_iterate_over_regset_sections.

* s390-tdep.c (gdbarch_tdep): New fields "have_linux_v1" and
"have_linux_v2".
(s390_linux32_regset_sections): Remove array.
(s390_linux32v1_regset_sections): Likewise.
(s390_linux32v2_regset_sections): Likewise.
(s390_linux64_regset_sections): Likewise.
(s390_linux64v1_regset_sections): Likewise.
(s390_linux64v2_regset_sections): Likewise.
(s390x_linux64_regset_sections): Likewise.
(s390x_linux64v1_regset_sections): Likewise.
(s390x_linux64v2_regset_sections): Likewise.
(s390_iterate_over_regset_sections ): New function.
(s390_gdbarch_init): Initialize new tdep fields "have_linux_v1"
and "have_linux_v2". Remove all invocations of
set_gdbarch_core_regset_sections; instead, call
set_gdbarch_iterate_over_regset_sections.

* corelow.c (get_core_registers_cb): New function.
(get_core_registers): Instead of looping through the list returned
by gdbarch_core_regset_sections, iterate get_core_registers_cb via
gdbarch_iterate_over_regset_sections.

Index: gdb/gdb/gdbarch.sh
===================================================================
--- gdb.orig/gdb/gdbarch.sh
+++ gdb/gdb/gdbarch.sh
@@ -635,8 +635,13 @@ F:CORE_ADDR:fetch_pointer_argument:struc
# name SECT_NAME and size SECT_SIZE.
M:const struct regset *:regset_from_core_section:const char *sect_name, size_t sect_size:sect_name, sect_size

-# Supported register notes in a core file.
-v:struct core_regset_section *:core_regset_sections:const char *name, int len::::::host_address_to_string (gdbarch->core_regset_sections)
+# Iterate over all supported register notes in a core file. For each
+# supported register note section, the iterator must call CB and pass
+# CB_DATA unchanged. If CB returns non-zero, the iterator must stop.
+# If REGCACHE is not NULL, the iterator can limit the supported
+# register note sections based on the current register values.
+# Otherwise it should enumerate all supported register note sections.
+M:void:iterate_over_regset_sections:iterate_over_regset_sections_cb *cb, void *cb_data, const struct regcache *regcache:cb, cb_data, regcache

# Create core file notes
M:char *:make_corefile_notes:bfd *obfd, int *note_size:obfd, note_size
@@ -1116,6 +1121,9 @@ extern struct gdbarch startup_gdbarch;

typedef int (iterate_over_objfiles_in_search_order_cb_ftype)
(struct objfile *objfile, void *cb_data);
+
+typedef int (iterate_over_regset_sections_cb)
+ (const char *sect_name, int size, const char *human_name, void *cb_data);
EOF

# function typedef's
Index: gdb/gdb/linux-tdep.c
===================================================================
--- gdb.orig/gdb/linux-tdep.c
+++ gdb/gdb/linux-tdep.c
@@ -1010,6 +1010,54 @@ linux_make_mappings_corefile_notes (stru
return note_data;
}

+/* Structure for passing information from
+ linux_collect_thread_registers via an iterator to
+ linux_collect_regset_section_cb. */
+
+struct linux_collect_regset_section_cb_data
+{
+ struct gdbarch *gdbarch;
+ const struct regcache *regcache;
+ bfd *obfd;
+ char *note_data;
+ int *note_size;
+ unsigned long lwp;
+ enum gdb_signal stop_signal;
+};
+
+/* Callback for iterate_over_regset_sections that records a single
+ regset in the corefile note section. */
+
+static int
+linux_collect_regset_section_cb (const char *sect_name, int size,
+ const char *human_name, void *cb_data)
+{
+ const struct regset *regset;
+ char *buf;
+ struct linux_collect_regset_section_cb_data *data = cb_data;
+
+ regset = gdbarch_regset_from_core_section (data->gdbarch, sect_name, size);
+ gdb_assert (regset && regset->collect_regset);
+
+ buf = xmalloc (size);
+ regset->collect_regset (regset, data->regcache, -1, buf, size);
+
+ /* PRSTATUS still needs to be treated specially. */
+ if (strcmp (sect_name, ".reg") == 0)
+ data->note_data = (char *) elfcore_write_prstatus
+ (data->obfd, data->note_data, data->note_size, data->lwp,
+ gdb_signal_to_host (data->stop_signal), buf);
+ else
+ data->note_data = (char *) elfcore_write_register_note
+ (data->obfd, data->note_data, data->note_size,
+ sect_name, buf, size);
+ xfree (buf);
+
+ if (!data->note_data)
+ return 1; /* Abort iteration. */
+ return 0;
+}
+
/* Records the thread's register state for the corefile note
section. */

@@ -1020,47 +1068,24 @@ linux_collect_thread_registers (const st
enum gdb_signal stop_signal)
{
struct gdbarch *gdbarch = get_regcache_arch (regcache);
- struct core_regset_section *sect_list;
- unsigned long lwp;
+ struct linux_collect_regset_section_cb_data data;

- sect_list = gdbarch_core_regset_sections (gdbarch);
- gdb_assert (sect_list);
+ data.gdbarch = gdbarch;
+ data.regcache = regcache;
+ data.obfd = obfd;
+ data.note_data = note_data;
+ data.note_size = note_size;
+ data.stop_signal = stop_signal;

/* For remote targets the LWP may not be available, so use the TID. */
- lwp = ptid_get_lwp (ptid);
- if (!lwp)
- lwp = ptid_get_tid (ptid);
-
- while (sect_list->sect_name != NULL)
- {
- const struct regset *regset;
- char *buf;
-
- regset = gdbarch_regset_from_core_section (gdbarch,
- sect_list->sect_name,
- sect_list->size);
- gdb_assert (regset && regset->collect_regset);
-
- buf = xmalloc (sect_list->size);
- regset->collect_regset (regset, regcache, -1, buf, sect_list->size);
-
- /* PRSTATUS still needs to be treated specially. */
- if (strcmp (sect_list->sect_name, ".reg") == 0)
- note_data = (char *) elfcore_write_prstatus
- (obfd, note_data, note_size, lwp,
- gdb_signal_to_host (stop_signal), buf);
- else
- note_data = (char *) elfcore_write_register_note
- (obfd, note_data, note_size,
- sect_list->sect_name, buf, sect_list->size);
- xfree (buf);
- sect_list++;
-
- if (!note_data)
- return NULL;
- }
-
- return note_data;
+ data.lwp = ptid_get_lwp (ptid);
+ if (!data.lwp)
+ data.lwp = ptid_get_tid (ptid);
+
+ gdbarch_iterate_over_regset_sections (gdbarch,
+ linux_collect_regset_section_cb,
+ &data, regcache);
+ return data.note_data;
}

/* Fetch the siginfo data for the current thread, if it exists. If
@@ -1440,7 +1465,7 @@ linux_make_corefile_notes_1 (struct gdba
converted to gdbarch_core_regset_sections, we no longer need to fall back
to the target method at this point. */

- if (!gdbarch_core_regset_sections (gdbarch))
+ if (!gdbarch_iterate_over_regset_sections_p (gdbarch))
return target_make_corefile_notes (obfd, note_size);
else
return linux_make_corefile_notes (gdbarch, obfd, note_size,
Index: gdb/gdb/s390-tdep.c
===================================================================
--- gdb.orig/gdb/s390-tdep.c
+++ gdb/gdb/s390-tdep.c
@@ -80,6 +80,9 @@ struct gdbarch_tdep

const struct regset *fpregset;
int sizeof_fpregset;
+
+ int have_linux_v1;
+ int have_linux_v2;
};

@@ -669,83 +672,37 @@ static const struct regset s390_tdb_regs
s390_collect_regset
};

-static struct core_regset_section s390_linux32_regset_sections[] =
-{
- { ".reg", s390_sizeof_gregset, "general-purpose" },
- { ".reg2", s390_sizeof_fpregset, "floating-point" },
- { NULL, 0}
-};
-
-static struct core_regset_section s390_linux32v1_regset_sections[] =
-{
- { ".reg", s390_sizeof_gregset, "general-purpose" },
- { ".reg2", s390_sizeof_fpregset, "floating-point" },
- { ".reg-s390-last-break", 8, "s390 last-break address" },
- { NULL, 0}
-};
-
-static struct core_regset_section s390_linux32v2_regset_sections[] =
-{
- { ".reg", s390_sizeof_gregset, "general-purpose" },
- { ".reg2", s390_sizeof_fpregset, "floating-point" },
- { ".reg-s390-last-break", 8, "s390 last-break address" },
- { ".reg-s390-system-call", 4, "s390 system-call" },
- { NULL, 0}
-};
-
-static struct core_regset_section s390_linux64_regset_sections[] =
-{
- { ".reg", s390_sizeof_gregset, "general-purpose" },
- { ".reg2", s390_sizeof_fpregset, "floating-point" },
- { ".reg-s390-high-gprs", 16*4, "s390 GPR upper halves" },
- { NULL, 0}
-};
-
-static struct core_regset_section s390_linux64v1_regset_sections[] =
-{
- { ".reg", s390_sizeof_gregset, "general-purpose" },
- { ".reg2", s390_sizeof_fpregset, "floating-point" },
- { ".reg-s390-high-gprs", 16*4, "s390 GPR upper halves" },
- { ".reg-s390-last-break", 8, "s930 last-break address" },
- { NULL, 0}
-};
-
-static struct core_regset_section s390_linux64v2_regset_sections[] =
-{
- { ".reg", s390_sizeof_gregset, "general-purpose" },
- { ".reg2", s390_sizeof_fpregset, "floating-point" },
- { ".reg-s390-high-gprs", 16*4, "s390 GPR upper halves" },
- { ".reg-s390-last-break", 8, "s930 last-break address" },
- { ".reg-s390-system-call", 4, "s390 system-call" },
- { ".reg-s390-tdb", s390_sizeof_tdbregset, "s390 TDB" },
- { NULL, 0}
-};
-
-static struct core_regset_section s390x_linux64_regset_sections[] =
-{
- { ".reg", s390x_sizeof_gregset, "general-purpose" },
- { ".reg2", s390_sizeof_fpregset, "floating-point" },
- { NULL, 0}
-};
-
-static struct core_regset_section s390x_linux64v1_regset_sections[] =
-{
- { ".reg", s390x_sizeof_gregset, "general-purpose" },
- { ".reg2", s390_sizeof_fpregset, "floating-point" },
- { ".reg-s390-last-break", 8, "s930 last-break address" },
- { NULL, 0}
-};
-
-static struct core_regset_section s390x_linux64v2_regset_sections[] =
+/* Iterate over supported core file register note sections. */
+static void
+s390_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
{
- { ".reg", s390x_sizeof_gregset, "general-purpose" },
- { ".reg2", s390_sizeof_fpregset, "floating-point" },
- { ".reg-s390-last-break", 8, "s930 last-break address" },
- { ".reg-s390-system-call", 4, "s390 system-call" },
- { ".reg-s390-tdb", s390_sizeof_tdbregset, "s390 TDB" },
- { NULL, 0}
-};
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int is_32_bit = tdep->abi == ABI_LINUX_S390 && tdep->gpr_full_regnum == -1;

+ if ((*cb) (".reg", is_32_bit ? s390_sizeof_gregset : s390x_sizeof_gregset,
+ "general-purpose", cb_data))
+ return;
+ if ((*cb) (".reg2", s390_sizeof_fpregset, "floating-point", cb_data))
+ return;
+ if (tdep->abi == ABI_LINUX_S390 && tdep->gpr_full_regnum != -1)
+ if ((*cb) (".reg-s390-high-gprs", 16*4, "s390 GPR upper halves", cb_data))
+ return;
+ if (tdep->have_linux_v1)
+ if ((*cb) (".reg-s390-last-break", 8, "s930 last-break address", cb_data))
+ return;
+ if (tdep->have_linux_v2)
+ {
+ if ((*cb) (".reg-s390-system-call", 4, "s390 system-call", cb_data))
+ return;
+ if (regcache == NULL
+ || REG_VALID == regcache_register_status (regcache,
+ S390_TDB_DWORD0_REGNUM))
+ (*cb) (".reg-s390-tdb", s390_sizeof_tdbregset, "s390 TDB", cb_data);
+ }
+}

/* Return the appropriate register set for the core section identified
by SECT_NAME and SECT_SIZE. */
@@ -3151,20 +3108,23 @@ s390_gdbarch_init (struct gdbarch_info i
arches = gdbarch_list_lookup_by_info (arches->next, &info))
{
tdep = gdbarch_tdep (arches->gdbarch);
- if (!tdep)
- continue;
- if (tdep->abi != tdep_abi)
- continue;
- if ((tdep->gpr_full_regnum != -1) != have_upper)
- continue;
- if (tdesc_data != NULL)
- tdesc_data_cleanup (tdesc_data);
- return arches->gdbarch;
+ if (tdep
+ && tdep->abi == tdep_abi
+ && (tdep->gpr_full_regnum != -1) == have_upper
+ && tdep->have_linux_v1 == have_linux_v1
+ && tdep->have_linux_v2 == have_linux_v2)
+ {
+ if (tdesc_data != NULL)
+ tdesc_data_cleanup (tdesc_data);
+ return arches->gdbarch;
+ }
}

/* Otherwise create a new gdbarch for the specified machine type. */
tdep = XCALLOC (1, struct gdbarch_tdep);
tdep->abi = tdep_abi;
+ tdep->have_linux_v1 = have_linux_v1;
+ tdep->have_linux_v2 = have_linux_v2;
gdbarch = gdbarch_alloc (&info, tdep);

set_gdbarch_believe_pcc_promotion (gdbarch, 0);
@@ -3195,6 +3155,8 @@ s390_gdbarch_init (struct gdbarch_info i
set_gdbarch_regset_from_core_section (gdbarch,
s390_regset_from_core_section);
set_gdbarch_core_read_description (gdbarch, s390_core_read_description);
+ set_gdbarch_iterate_over_regset_sections (gdbarch,
+ s390_iterate_over_regset_sections);
set_gdbarch_cannot_store_register (gdbarch, s390_cannot_store_register);
set_gdbarch_write_pc (gdbarch, s390_write_pc);
set_gdbarch_pseudo_register_read (gdbarch, s390_pseudo_register_read);
@@ -3262,31 +3224,6 @@ s390_gdbarch_init (struct gdbarch_info i
set_gdbarch_addr_bits_remove (gdbarch, s390_addr_bits_remove);
set_solib_svr4_fetch_link_map_offsets
(gdbarch, svr4_ilp32_fetch_link_map_offsets);
-
- if (have_upper)
- {
- if (have_linux_v2)
- set_gdbarch_core_regset_sections (gdbarch,
- s390_linux64v2_regset_sections);
- else if (have_linux_v1)
- set_gdbarch_core_regset_sections (gdbarch,
- s390_linux64v1_regset_sections);
- else
- set_gdbarch_core_regset_sections (gdbarch,
- s390_linux64_regset_sections);
- }
- else
- {
- if (have_linux_v2)
- set_gdbarch_core_regset_sections (gdbarch,
- s390_linux32v2_regset_sections);
- else if (have_linux_v1)
- set_gdbarch_core_regset_sections (gdbarch,
- s390_linux32v1_regset_sections);
- else
- set_gdbarch_core_regset_sections (gdbarch,
- s390_linux32_regset_sections);
- }
break;

case ABI_LINUX_ZSERIES:
@@ -3306,16 +3243,6 @@ s390_gdbarch_init (struct gdbarch_info i
s390_address_class_type_flags_to_name);
set_gdbarch_address_class_name_to_type_flags (gdbarch,
s390_address_class_name_to_type_flags);
-
- if (have_linux_v2)
- set_gdbarch_core_regset_sections (gdbarch,
- s390x_linux64v2_regset_sections);
- else if (have_linux_v1)
- set_gdbarch_core_regset_sections (gdbarch,
- s390x_linux64v1_regset_sections);
- else
- set_gdbarch_core_regset_sections (gdbarch,
- s390x_linux64_regset_sections);
break;
}

Index: gdb/gdb/gdbarch.c
===================================================================
--- gdb.orig/gdb/gdbarch.c
+++ gdb/gdb/gdbarch.c
@@ -236,7 +236,7 @@ struct gdbarch
gdbarch_register_reggroup_p_ftype *register_reggroup_p;
gdbarch_fetch_pointer_argument_ftype *fetch_pointer_argument;
gdbarch_regset_from_core_section_ftype *regset_from_core_section;
- struct core_regset_section * core_regset_sections;
+ gdbarch_iterate_over_regset_sections_ftype *iterate_over_regset_sections;
gdbarch_make_corefile_notes_ftype *make_corefile_notes;
gdbarch_elfcore_write_linux_prpsinfo_ftype *elfcore_write_linux_prpsinfo;
gdbarch_find_memory_regions_ftype *find_memory_regions;
@@ -407,7 +407,7 @@ struct gdbarch startup_gdbarch =
default_register_reggroup_p, /* register_reggroup_p */
0, /* fetch_pointer_argument */
0, /* regset_from_core_section */
- 0, /* core_regset_sections */
+ 0, /* iterate_over_regset_sections */
0, /* make_corefile_notes */
0, /* elfcore_write_linux_prpsinfo */
0, /* find_memory_regions */
@@ -710,6 +710,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
/* Skip verify of register_reggroup_p, invalid_p == 0 */
/* Skip verify of fetch_pointer_argument, has predicate. */
/* Skip verify of regset_from_core_section, has predicate. */
+ /* Skip verify of iterate_over_regset_sections, has predicate. */
/* Skip verify of make_corefile_notes, has predicate. */
/* Skip verify of elfcore_write_linux_prpsinfo, has predicate. */
/* Skip verify of find_memory_regions, has predicate. */
@@ -895,9 +896,6 @@ gdbarch_dump (struct gdbarch *gdbarch, s
"gdbarch_dump: core_read_description = <%s>\n",
host_address_to_string (gdbarch->core_read_description));
fprintf_unfiltered (file,
- "gdbarch_dump: core_regset_sections = %s\n",
- host_address_to_string (gdbarch->core_regset_sections));
- fprintf_unfiltered (file,
"gdbarch_dump: gdbarch_core_xfer_shared_libraries_p() = %d\n",
gdbarch_core_xfer_shared_libraries_p (gdbarch));
fprintf_unfiltered (file,
@@ -1093,6 +1091,12 @@ gdbarch_dump (struct gdbarch *gdbarch, s
"gdbarch_dump: iterate_over_objfiles_in_search_order = <%s>\n",
host_address_to_string (gdbarch->iterate_over_objfiles_in_search_order));
fprintf_unfiltered (file,
+ "gdbarch_dump: gdbarch_iterate_over_regset_sections_p() = %d\n",
+ gdbarch_iterate_over_regset_sections_p (gdbarch));
+ fprintf_unfiltered (file,
+ "gdbarch_dump: iterate_over_regset_sections = <%s>\n",
+ host_address_to_string (gdbarch->iterate_over_regset_sections));
+ fprintf_unfiltered (file,
"gdbarch_dump: long_bit = %s\n",
plongest (gdbarch->long_bit));
fprintf_unfiltered (file,
@@ -3336,20 +3340,28 @@ set_gdbarch_regset_from_core_section (st
gdbarch->regset_from_core_section = regset_from_core_section;
}

-struct core_regset_section *
-gdbarch_core_regset_sections (struct gdbarch *gdbarch)
+int
+gdbarch_iterate_over_regset_sections_p (struct gdbarch *gdbarch)
+{
+ gdb_assert (gdbarch != NULL);
+ return gdbarch->iterate_over_regset_sections != NULL;
+}
+
+void
+gdbarch_iterate_over_regset_sections (struct gdbarch *gdbarch, iterate_over_regset_sections_cb *cb, void *cb_data, const struct regcache *regcache)
{
gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->iterate_over_regset_sections != NULL);
if (gdbarch_debug >= 2)
- fprintf_unfiltered (gdb_stdlog, "gdbarch_core_regset_sections called\n");
- return gdbarch->core_regset_sections;
+ fprintf_unfiltered (gdb_stdlog, "gdbarch_iterate_over_regset_sections called\n");
+ gdbarch->iterate_over_regset_sections (gdbarch, cb, cb_data, regcache);
}

void
-set_gdbarch_core_regset_sections (struct gdbarch *gdbarch,
- struct core_regset_section * core_regset_sections)
+set_gdbarch_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ gdbarch_iterate_over_regset_sections_ftype iterate_over_regset_sections)
{
- gdbarch->core_regset_sections = core_regset_sections;
+ gdbarch->iterate_over_regset_sections = iterate_over_regset_sections;
}

int
Index: gdb/gdb/gdbarch.h
===================================================================
--- gdb.orig/gdb/gdbarch.h
+++ gdb/gdb/gdbarch.h
@@ -85,6 +85,9 @@ extern struct gdbarch startup_gdbarch;
typedef int (iterate_over_objfiles_in_search_order_cb_ftype)
(struct objfile *objfile, void *cb_data);

+typedef int (iterate_over_regset_sections_cb)
+ (const char *sect_name, int size, const char *human_name, void *cb_data);
+

/* The following are pre-initialized by GDBARCH. */

@@ -724,10 +727,18 @@ typedef const struct regset * (gdbarch_r
extern const struct regset * gdbarch_regset_from_core_section (struct gdbarch *gdbarch, const char *sect_name, size_t sect_size);
extern void set_gdbarch_regset_from_core_section (struct gdbarch *gdbarch, gdbarch_regset_from_core_section_ftype *regset_from_core_section);

-/* Supported register notes in a core file. */
-
-extern struct core_regset_section * gdbarch_core_regset_sections (struct gdbarch *gdbarch);
-extern void set_gdbarch_core_regset_sections (struct gdbarch *gdbarch, struct core_regset_section * core_regset_sections);
+/* Iterate over all supported register notes in a core file. For each
+ supported register note section, the iterator must call CB and pass
+ CB_DATA unchanged. If CB returns non-zero, the iterator must stop.
+ If REGCACHE is not NULL, the iterator can limit the supported
+ register note sections based on the current register values.
+ Otherwise it should enumerate all supported register note sections. */
+
+extern int gdbarch_iterate_over_regset_sections_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_iterate_over_regset_sections_ftype) (struct gdbarch *gdbarch, iterate_over_regset_sections_cb *cb, void *cb_data, const struct regcache *regcache);
+extern void gdbarch_iterate_over_regset_sections (struct gdbarch *gdbarch, iterate_over_regset_sections_cb *cb, void *cb_data, const struct regcache *regcache);
+extern void set_gdbarch_iterate_over_regset_sections (struct gdbarch *gdbarch, gdbarch_iterate_over_regset_sections_ftype *iterate_over_regset_sections);

/* Create core file notes */

Index: gdb/gdb/arm-linux-tdep.c
===================================================================
--- gdb.orig/gdb/arm-linux-tdep.c
+++ gdb/gdb/arm-linux-tdep.c
@@ -724,21 +724,25 @@ arm_linux_regset_from_core_section (stru
return NULL;
}

-/* Core file register set sections. */
+/* Iterate over core file register note sections. */

-static struct core_regset_section arm_linux_fpa_regset_sections[] =
+static void
+arm_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
{
- { ".reg", ARM_LINUX_SIZEOF_GREGSET, "general-purpose" },
- { ".reg2", ARM_LINUX_SIZEOF_NWFPE, "FPA floating-point" },
- { NULL, 0}
-};
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

-static struct core_regset_section arm_linux_vfp_regset_sections[] =
-{
- { ".reg", ARM_LINUX_SIZEOF_GREGSET, "general-purpose" },
- { ".reg-arm-vfp", ARM_LINUX_SIZEOF_VFP, "VFP floating-point" },
- { NULL, 0}
-};
+ if ((*cb) (".reg", ARM_LINUX_SIZEOF_GREGSET, "general-purpose", cb_data))
+ return;
+
+ if (tdep->have_vfp_registers)
+ (*cb) (".reg-arm-vfp", ARM_LINUX_SIZEOF_VFP, "VFP floating-point",
+ cb_data);
+ else if (tdep->have_fpa_registers)
+ (*cb) (".reg2", ARM_LINUX_SIZEOF_NWFPE, "FPA floating-point", cb_data);
+}

/* Determine target description from core file. */

@@ -1262,13 +1266,10 @@ arm_linux_init_abi (struct gdbarch_info
/* Core file support. */
set_gdbarch_regset_from_core_section (gdbarch,
arm_linux_regset_from_core_section);
+ set_gdbarch_iterate_over_regset_sections
+ (gdbarch, arm_linux_iterate_over_regset_sections);
set_gdbarch_core_read_description (gdbarch, arm_linux_core_read_description);

- if (tdep->have_vfp_registers)
- set_gdbarch_core_regset_sections (gdbarch, arm_linux_vfp_regset_sections);
- else if (tdep->have_fpa_registers)
- set_gdbarch_core_regset_sections (gdbarch, arm_linux_fpa_regset_sections);
-
set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);

/* Displaced stepping. */
Index: gdb/gdb/regset.h
===================================================================
--- gdb.orig/gdb/regset.h
+++ gdb/gdb/regset.h
@@ -23,14 +23,6 @@
struct gdbarch;
struct regcache;

-/* Data structure for the supported register notes in a core file. */
-struct core_regset_section
-{
- const char *sect_name;
- int size;
- const char *human_name;
-};
-
/* Data structure describing a register set. */

typedef void (supply_regset_ftype) (const struct regset *, struct regcache *,
Index: gdb/gdb/i386-linux-tdep.c
===================================================================
--- gdb.orig/gdb/i386-linux-tdep.c
+++ gdb/gdb/i386-linux-tdep.c
@@ -52,27 +52,27 @@
#include "features/i386/i386-mmx-linux.c"
#include "features/i386/i386-avx-linux.c"

-/* Supported register note sections. */
-static struct core_regset_section i386_linux_regset_sections[] =
-{
- { ".reg", 68, "general-purpose" },
- { ".reg2", 108, "floating-point" },
- { NULL, 0 }
-};
+/* Iterate over core file register note sections. */

-static struct core_regset_section i386_linux_sse_regset_sections[] =
+static void
+i386_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
{
- { ".reg", 68, "general-purpose" },
- { ".reg-xfp", 512, "extended floating-point" },
- { NULL, 0 }
-};
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);

-static struct core_regset_section i386_linux_avx_regset_sections[] =
-{
- { ".reg", 68, "general-purpose" },
- { ".reg-xstate", I386_XSTATE_MAX_SIZE, "XSAVE extended state" },
- { NULL, 0 }
-};
+ if ((*cb) (".reg", 68, "general-purpose", cb_data))
+ return;
+
+ if (tdep->xstateregset)
+ (*cb) (".reg-xstate", I386_XSTATE_MAX_SIZE, "XSAVE extended state",
+ cb_data);
+ else if (I387_NUM_XMM_REGS (tdep))
+ (*cb) (".reg-xfp", 512, "extended floating-point", cb_data);
+ else
+ (*cb) (".reg2", 108, "floating-point", cb_data);
+}

/* Return non-zero, when the register is in the corresponding register
group. Put the LINUX_ORIG_EAX register in the system group. */
@@ -939,14 +939,9 @@ i386_linux_init_abi (struct gdbarch_info
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);

- /* Install supported register note sections. */
- if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.avx"))
- set_gdbarch_core_regset_sections (gdbarch, i386_linux_avx_regset_sections);
- else if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.sse"))
- set_gdbarch_core_regset_sections (gdbarch, i386_linux_sse_regset_sections);
- else
- set_gdbarch_core_regset_sections (gdbarch, i386_linux_regset_sections);
-
+ /* Core file support. */
+ set_gdbarch_iterate_over_regset_sections
+ (gdbarch, i386_linux_iterate_over_regset_sections);
set_gdbarch_core_read_description (gdbarch,
i386_linux_core_read_description);

Index: gdb/gdb/ppc-linux-tdep.c
===================================================================
--- gdb.orig/gdb/ppc-linux-tdep.c
+++ gdb/gdb/ppc-linux-tdep.c
@@ -256,53 +256,26 @@ ppc_linux_return_value (struct gdbarch *
readbuf, writebuf);
}

-static struct core_regset_section ppc_linux_vsx_regset_sections[] =
-{
- { ".reg", 48 * 4, "general-purpose" },
- { ".reg2", 264, "floating-point" },
- { ".reg-ppc-vmx", 544, "ppc Altivec" },
- { ".reg-ppc-vsx", 256, "POWER7 VSX" },
- { NULL, 0}
-};
-
-static struct core_regset_section ppc_linux_vmx_regset_sections[] =
-{
- { ".reg", 48 * 4, "general-purpose" },
- { ".reg2", 264, "floating-point" },
- { ".reg-ppc-vmx", 544, "ppc Altivec" },
- { NULL, 0}
-};
-
-static struct core_regset_section ppc_linux_fp_regset_sections[] =
-{
- { ".reg", 48 * 4, "general-purpose" },
- { ".reg2", 264, "floating-point" },
- { NULL, 0}
-};
-
-static struct core_regset_section ppc64_linux_vsx_regset_sections[] =
-{
- { ".reg", 48 * 8, "general-purpose" },
- { ".reg2", 264, "floating-point" },
- { ".reg-ppc-vmx", 544, "ppc Altivec" },
- { ".reg-ppc-vsx", 256, "POWER7 VSX" },
- { NULL, 0}
-};
-
-static struct core_regset_section ppc64_linux_vmx_regset_sections[] =
+static void
+ppc_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
{
- { ".reg", 48 * 8, "general-purpose" },
- { ".reg2", 264, "floating-point" },
- { ".reg-ppc-vmx", 544, "ppc Altivec" },
- { NULL, 0}
-};
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int have_vsx = tdep->ppc_vr0_regnum != -1;
+ int have_altivec = tdep->ppc_vsr0_upper_regnum != -1;

-static struct core_regset_section ppc64_linux_fp_regset_sections[] =
-{
- { ".reg", 48 * 8, "general-purpose" },
- { ".reg2", 264, "floating-point" },
- { NULL, 0}
-};
+ if ((*cb) (".reg", 48 * 4, "general-purpose", cb_data))
+ return;
+ if ((*cb) (".reg2", 264, "floating-point", cb_data))
+ return;
+ if (have_altivec)
+ if ((*cb) (".reg-ppc-vmx", 544, "ppc Altivec", cb_data))
+ return;
+ if (have_vsx)
+ (*cb) (".reg-ppc-vsx", 256, "POWER7 VSX", cb_data);
+}

/* PLT stub in executable. */
static struct ppc_insn_pattern powerpc32_plt_stub[] =
@@ -1305,18 +1278,9 @@ ppc_linux_init_abi (struct gdbarch_info
else
set_gdbarch_gcore_bfd_target (gdbarch, "elf32-powerpc");

- /* Supported register sections. */
- if (tdesc_find_feature (info.target_desc,
- "org.gnu.gdb.power.vsx"))
- set_gdbarch_core_regset_sections (gdbarch,
- ppc_linux_vsx_regset_sections);
- else if (tdesc_find_feature (info.target_desc,
- "org.gnu.gdb.power.altivec"))
- set_gdbarch_core_regset_sections (gdbarch,
- ppc_linux_vmx_regset_sections);
- else
- set_gdbarch_core_regset_sections (gdbarch,
- ppc_linux_fp_regset_sections);
+ /* Iterator for supported register sections. */
+ set_gdbarch_iterate_over_regset_sections
+ (gdbarch, ppc_linux_iterate_over_regset_sections);

if (powerpc_so_ops.in_dynsym_resolve_code == NULL)
{
@@ -1360,18 +1324,9 @@ ppc_linux_init_abi (struct gdbarch_info
else
set_gdbarch_gcore_bfd_target (gdbarch, "elf64-powerpc");

- /* Supported register sections. */
- if (tdesc_find_feature (info.target_desc,
- "org.gnu.gdb.power.vsx"))
- set_gdbarch_core_regset_sections (gdbarch,
- ppc64_linux_vsx_regset_sections);
- else if (tdesc_find_feature (info.target_desc,
- "org.gnu.gdb.power.altivec"))
- set_gdbarch_core_regset_sections (gdbarch,
- ppc64_linux_vmx_regset_sections);
- else
- set_gdbarch_core_regset_sections (gdbarch,
- ppc64_linux_fp_regset_sections);
+ /* Iterator for supported register sections. */
+ set_gdbarch_iterate_over_regset_sections
+ (gdbarch, ppc_linux_iterate_over_regset_sections);
}

/* PPC32 uses a different prpsinfo32 compared to most other Linux
Index: gdb/gdb/amd64-linux-tdep.c
===================================================================
--- gdb.orig/gdb/amd64-linux-tdep.c
+++ gdb/gdb/amd64-linux-tdep.c
@@ -51,15 +51,6 @@
#include "record-full.h"
#include "linux-record.h"

-/* Supported register note sections. */
-static struct core_regset_section amd64_linux_regset_sections[] =
-{
- { ".reg", 27 * 8, "general-purpose" },
- { ".reg2", 512, "floating-point" },
- { ".reg-xstate", I386_XSTATE_MAX_SIZE, "XSAVE extended state" },
- { NULL, 0 }
-};
-
/* Mapping between the general-purpose registers in `struct user'
format and GDB's register cache layout. */

@@ -1278,6 +1269,23 @@ amd64_linux_record_signal (struct gdbarc
return 0;
}

+/* Iterate over supported core file register note sections. */
+
+static void
+amd64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if ((*cb) (".reg", 27 * 8, "general-purpose", cb_data))
+ return;
+ if ((*cb) (".reg2", 512, "floating-point", cb_data))
+ return;
+ (*cb) (".reg-xstate", I386_XSTATE_MAX_SIZE, "XSAVE extended state", cb_data);
+}
+
/* Get Linux/x86 target description from core dump. */

static const struct target_desc *
@@ -1336,9 +1344,9 @@ amd64_linux_init_abi_common(struct gdbar
/* GNU/Linux uses the dynamic linker included in the GNU C Library. */
set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);

- /* Install supported register note sections. */
- set_gdbarch_core_regset_sections (gdbarch, amd64_linux_regset_sections);
-
+ /* Core file support. */
+ set_gdbarch_iterate_over_regset_sections
+ (gdbarch, amd64_linux_iterate_over_regset_sections);
set_gdbarch_core_read_description (gdbarch,
amd64_linux_core_read_description);

Index: gdb/gdb/corelow.c
===================================================================
--- gdb.orig/gdb/corelow.c
+++ gdb/gdb/corelow.c
@@ -547,6 +547,24 @@ get_core_register_section (struct regcac
bfd_section_vma (core_bfd, section)));
}

+/* Callback for get_core_registers that handles a single core file
+ register note section. */
+
+static int
+get_core_registers_cb (const char *sect_name, int size,
+ const char *human_name, void *cb_data)
+{
+ struct regcache *regcache = (struct regcache *) cb_data;
+
+ if (strcmp (sect_name, ".reg") == 0)
+ get_core_register_section (regcache, sect_name, 0, human_name, 1);
+ else if (strcmp (sect_name, ".reg2") == 0)
+ get_core_register_section (regcache, sect_name, 2, human_name, 0);
+ else
+ get_core_register_section (regcache, sect_name, 3, human_name, 0);
+
+ return 0;
+}

/* Get the registers out of a core file. This is the machine-
independent part. Fetch_core_registers is the machine-dependent
@@ -559,8 +577,8 @@ static void
get_core_registers (struct target_ops *ops,
struct regcache *regcache, int regno)
{
- struct core_regset_section *sect_list;
int i;
+ struct gdbarch *gdbarch;

if (!(core_gdbarch && gdbarch_regset_from_core_section_p (core_gdbarch))
&& (core_vec == NULL || core_vec->core_read_registers == NULL))
@@ -570,23 +588,11 @@ get_core_registers (struct target_ops *o
return;
}

- sect_list = gdbarch_core_regset_sections (get_regcache_arch (regcache));
- if (sect_list)
- while (sect_list->sect_name != NULL)
- {
- if (strcmp (sect_list->sect_name, ".reg") == 0)
- get_core_register_section (regcache, sect_list->sect_name,
- 0, sect_list->human_name, 1);
- else if (strcmp (sect_list->sect_name, ".reg2") == 0)
- get_core_register_section (regcache, sect_list->sect_name,
- 2, sect_list->human_name, 0);
- else
- get_core_register_section (regcache, sect_list->sect_name,
- 3, sect_list->human_name, 0);
-
- sect_list++;
- }
-
+ gdbarch = get_regcache_arch (regcache);
+ if (gdbarch_iterate_over_regset_sections_p (gdbarch))
+ gdbarch_iterate_over_regset_sections (gdbarch,
+ get_core_registers_cb,
+ (void *) regcache, NULL);
else
{
get_core_register_section (regcache,