summaryrefslogtreecommitdiff
path: root/gdb/s390-tdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/s390-tdep.c')
-rw-r--r--gdb/s390-tdep.c282
1 files changed, 277 insertions, 5 deletions
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 1bc6fde422a..d5bc1b1f428 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -47,8 +47,14 @@
#include "s390-tdep.h"
#include "features/s390-linux32.c"
+#include "features/s390-linux32v1.c"
+#include "features/s390-linux32v2.c"
#include "features/s390-linux64.c"
+#include "features/s390-linux64v1.c"
+#include "features/s390-linux64v2.c"
#include "features/s390x-linux64.c"
+#include "features/s390x-linux64v1.c"
+#include "features/s390x-linux64v2.c"
/* The tdep structure. */
@@ -101,6 +107,32 @@ s390_register_call_saved (struct gdbarch *gdbarch, int regnum)
return 0;
}
+static int
+s390_cannot_store_register (struct gdbarch *gdbarch, int regnum)
+{
+ /* The last-break address is read-only. */
+ return regnum == S390_LAST_BREAK_REGNUM;
+}
+
+static void
+s390_write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ regcache_cooked_write_unsigned (regcache, tdep->pc_regnum, pc);
+
+ /* Set special SYSTEM_CALL register to 0 to prevent the kernel from
+ messing with the PC we just installed, if we happen to be within
+ an interrupted system call that the kernel wants to restart.
+
+ Note that after we return from the dummy call, the SYSTEM_CALL and
+ ORIG_R2 registers will be automatically restored, and the kernel
+ continues to restart the system call at this point. */
+ if (register_size (gdbarch, S390_SYSTEM_CALL_REGNUM) > 0)
+ regcache_cooked_write_unsigned (regcache, S390_SYSTEM_CALL_REGNUM, 0);
+}
+
/* DWARF Register Mapping. */
@@ -137,6 +169,9 @@ static int s390_dwarf_regmap[] =
S390_R4_REGNUM, S390_R5_REGNUM, S390_R6_REGNUM, S390_R7_REGNUM,
S390_R8_REGNUM, S390_R9_REGNUM, S390_R10_REGNUM, S390_R11_REGNUM,
S390_R12_REGNUM, S390_R13_REGNUM, S390_R14_REGNUM, S390_R15_REGNUM,
+
+ /* Linux-specific registers (not mapped). */
+ -1, -1, -1,
};
/* Convert DWARF register number REG to the appropriate register
@@ -391,6 +426,8 @@ int s390_regmap_gregset[S390_NUM_REGS] =
/* GPR Uppper Halves. */
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
+ /* Linux-specific optional "registers". */
+ 0x88, -1, -1,
};
int s390x_regmap_gregset[S390_NUM_REGS] =
@@ -417,6 +454,8 @@ int s390x_regmap_gregset[S390_NUM_REGS] =
0x30, 0x38, 0x40, 0x48,
0x50, 0x58, 0x60, 0x68,
0x70, 0x78, 0x80, 0x88,
+ /* Linux-specific optional "registers". */
+ 0xd0, -1, -1,
};
int s390_regmap_fpregset[S390_NUM_REGS] =
@@ -439,6 +478,8 @@ int s390_regmap_fpregset[S390_NUM_REGS] =
/* GPR Uppper Halves. */
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
+ /* Linux-specific optional "registers". */
+ -1, -1, -1,
};
int s390_regmap_upper[S390_NUM_REGS] =
@@ -461,6 +502,74 @@ int s390_regmap_upper[S390_NUM_REGS] =
0x10, 0x14, 0x18, 0x1c,
0x20, 0x24, 0x28, 0x2c,
0x30, 0x34, 0x38, 0x3c,
+ /* Linux-specific optional "registers". */
+ -1, -1, -1,
+};
+
+int s390_regmap_last_break[S390_NUM_REGS] =
+{
+ /* Program Status Word. */
+ -1, -1,
+ /* General Purpose Registers. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* Access Registers. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* Floating Point Control Word. */
+ -1,
+ /* Floating Point Registers. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* GPR Uppper Halves. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* Linux-specific optional "registers". */
+ -1, 4, -1,
+};
+
+int s390x_regmap_last_break[S390_NUM_REGS] =
+{
+ /* Program Status Word. */
+ -1, -1,
+ /* General Purpose Registers. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* Access Registers. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* Floating Point Control Word. */
+ -1,
+ /* Floating Point Registers. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* GPR Uppper Halves. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* Linux-specific optional "registers". */
+ -1, 0, -1,
+};
+
+int s390_regmap_system_call[S390_NUM_REGS] =
+{
+ /* Program Status Word. */
+ -1, -1,
+ /* General Purpose Registers. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* Access Registers. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* Floating Point Control Word. */
+ -1,
+ /* Floating Point Registers. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* GPR Uppper Halves. */
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ /* Linux-specific optional "registers". */
+ -1, -1, 0,
};
/* Supply register REGNUM from the register set REGSET to register cache
@@ -522,7 +631,49 @@ static const struct regset s390_upper_regset = {
s390_collect_regset
};
-static struct core_regset_section s390_upper_regset_sections[] =
+static const struct regset s390_last_break_regset = {
+ s390_regmap_last_break,
+ s390_supply_regset,
+ s390_collect_regset
+};
+
+static const struct regset s390x_last_break_regset = {
+ s390x_regmap_last_break,
+ s390_supply_regset,
+ s390_collect_regset
+};
+
+static const struct regset s390_system_call_regset = {
+ s390_regmap_system_call,
+ s390_supply_regset,
+ 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" },
@@ -530,6 +681,50 @@ static struct core_regset_section s390_upper_regset_sections[] =
{ 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" },
+ { 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[] =
+{
+ { ".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" },
+ { NULL, 0}
+};
+
+
/* Return the appropriate register set for the core section identified
by SECT_NAME and SECT_SIZE. */
static const struct regset *
@@ -547,6 +742,13 @@ s390_regset_from_core_section (struct gdbarch *gdbarch,
if (strcmp (sect_name, ".reg-s390-high-gprs") == 0 && sect_size >= 16*4)
return &s390_upper_regset;
+ if (strcmp (sect_name, ".reg-s390-last-break") == 0 && sect_size >= 8)
+ return (gdbarch_ptr_bit (gdbarch) == 32
+ ? &s390_last_break_regset : &s390x_last_break_regset);
+
+ if (strcmp (sect_name, ".reg-s390-system-call") == 0 && sect_size >= 4)
+ return &s390_system_call_regset;
+
return NULL;
}
@@ -555,6 +757,8 @@ s390_core_read_description (struct gdbarch *gdbarch,
struct target_ops *target, bfd *abfd)
{
asection *high_gprs = bfd_get_section_by_name (abfd, ".reg-s390-high-gprs");
+ asection *v1 = bfd_get_section_by_name (abfd, ".reg-s390-last-break");
+ asection *v2 = bfd_get_section_by_name (abfd, ".reg-s390-system-call");
asection *section = bfd_get_section_by_name (abfd, ".reg");
if (!section)
return NULL;
@@ -562,10 +766,16 @@ s390_core_read_description (struct gdbarch *gdbarch,
switch (bfd_section_size (abfd, section))
{
case s390_sizeof_gregset:
- return high_gprs? tdesc_s390_linux64 : tdesc_s390_linux32;
+ if (high_gprs)
+ return (v2? tdesc_s390_linux64v2 :
+ v1? tdesc_s390_linux64v1 : tdesc_s390_linux64);
+ else
+ return (v2? tdesc_s390_linux32v2 :
+ v1? tdesc_s390_linux32v1 : tdesc_s390_linux32);
case s390x_sizeof_gregset:
- return tdesc_s390x_linux64;
+ return (v2? tdesc_s390x_linux64v2 :
+ v1? tdesc_s390x_linux64v1 : tdesc_s390x_linux64);
default:
return NULL;
@@ -2749,6 +2959,8 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
struct gdbarch_tdep *tdep;
int tdep_abi;
int have_upper = 0;
+ int have_linux_v1 = 0;
+ int have_linux_v2 = 0;
int first_pseudo_reg, last_pseudo_reg;
/* Default ABI and register size. */
@@ -2856,6 +3068,25 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
valid_p &= tdesc_numbered_register (feature, tdesc_data,
S390_A0_REGNUM + i, acrs[i]);
+ /* Optional Linux-specific "registers". */
+ feature = tdesc_find_feature (tdesc, "org.gnu.gdb.s390.linux");
+ if (feature)
+ {
+ tdesc_numbered_register (feature, tdesc_data,
+ S390_ORIG_R2_REGNUM, "orig_r2");
+
+ if (tdesc_numbered_register (feature, tdesc_data,
+ S390_LAST_BREAK_REGNUM, "last_break"))
+ have_linux_v1 = 1;
+
+ if (tdesc_numbered_register (feature, tdesc_data,
+ S390_SYSTEM_CALL_REGNUM, "system_call"))
+ have_linux_v2 = 1;
+
+ if (have_linux_v2 > have_linux_v1)
+ valid_p = 0;
+ }
+
if (!valid_p)
{
tdesc_data_cleanup (tdesc_data);
@@ -2913,8 +3144,8 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
set_gdbarch_regset_from_core_section (gdbarch,
s390_regset_from_core_section);
set_gdbarch_core_read_description (gdbarch, s390_core_read_description);
- if (have_upper)
- set_gdbarch_core_regset_sections (gdbarch, s390_upper_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);
set_gdbarch_pseudo_register_write (gdbarch, s390_pseudo_register_write);
set_tdesc_pseudo_register_name (gdbarch, s390_pseudo_register_name);
@@ -2980,6 +3211,31 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
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:
@@ -2999,6 +3255,16 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
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;
}
@@ -3026,6 +3292,12 @@ _initialize_s390_tdep (void)
/* Initialize the Linux target descriptions. */
initialize_tdesc_s390_linux32 ();
+ initialize_tdesc_s390_linux32v1 ();
+ initialize_tdesc_s390_linux32v2 ();
initialize_tdesc_s390_linux64 ();
+ initialize_tdesc_s390_linux64v1 ();
+ initialize_tdesc_s390_linux64v2 ();
initialize_tdesc_s390x_linux64 ();
+ initialize_tdesc_s390x_linux64v1 ();
+ initialize_tdesc_s390x_linux64v2 ();
}