/* Target-dependent code for GDB, the GNU debugger.
- Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
- Free Software Foundation, Inc.
+ Copyright (C) 2001-2012 Free Software Foundation, Inc.
Contributed by D.J. Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
for IBM Deutschland Entwicklung GmbH, IBM Corporation.
#include "dis-asm.h"
#include "solib-svr4.h"
#include "prologue-value.h"
-
+#include "linux-tdep.h"
#include "s390-tdep.h"
+#include "stap-probe.h"
+#include "ax.h"
+#include "ax-gdb.h"
+#include "user-regs.h"
+#include "cli/cli-utils.h"
+#include <ctype.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. */
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. */
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,
+
+ /* GNU/Linux-specific registers (not mapped). */
+ -1, -1, -1,
};
/* Convert DWARF register number REG to the appropriate register
internal_error (__FILE__, __LINE__, _("invalid regnum"));
}
-static void
+static enum register_status
s390_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
int regnum, gdb_byte *buf)
{
if (regnum == tdep->pc_regnum)
{
- regcache_raw_read_unsigned (regcache, S390_PSWA_REGNUM, &val);
- if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
- val &= 0x7fffffff;
- store_unsigned_integer (buf, regsize, byte_order, val);
- return;
+ enum register_status status;
+
+ status = regcache_raw_read_unsigned (regcache, S390_PSWA_REGNUM, &val);
+ if (status == REG_VALID)
+ {
+ if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+ val &= 0x7fffffff;
+ store_unsigned_integer (buf, regsize, byte_order, val);
+ }
+ return status;
}
if (regnum == tdep->cc_regnum)
{
- regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &val);
- if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
- val = (val >> 12) & 3;
- else
- val = (val >> 44) & 3;
- store_unsigned_integer (buf, regsize, byte_order, val);
- return;
+ enum register_status status;
+
+ status = regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &val);
+ if (status == REG_VALID)
+ {
+ if (register_size (gdbarch, S390_PSWA_REGNUM) == 4)
+ val = (val >> 12) & 3;
+ else
+ val = (val >> 44) & 3;
+ store_unsigned_integer (buf, regsize, byte_order, val);
+ }
+ return status;
}
if (tdep->gpr_full_regnum != -1
&& regnum >= tdep->gpr_full_regnum
&& regnum < tdep->gpr_full_regnum + 16)
{
+ enum register_status status;
ULONGEST val_upper;
+
regnum -= tdep->gpr_full_regnum;
- regcache_raw_read_unsigned (regcache, S390_R0_REGNUM + regnum, &val);
- regcache_raw_read_unsigned (regcache, S390_R0_UPPER_REGNUM + regnum,
- &val_upper);
- val |= val_upper << 32;
- store_unsigned_integer (buf, regsize, byte_order, val);
- return;
+ status = regcache_raw_read_unsigned (regcache, S390_R0_REGNUM + regnum, &val);
+ if (status == REG_VALID)
+ status = regcache_raw_read_unsigned (regcache, S390_R0_UPPER_REGNUM + regnum,
+ &val_upper);
+ if (status == REG_VALID)
+ {
+ val |= val_upper << 32;
+ store_unsigned_integer (buf, regsize, byte_order, val);
+ }
+ return status;
}
internal_error (__FILE__, __LINE__, _("invalid regnum"));
struct frame_info *frame)
{
struct value *value = default_value_from_register (type, regnum, frame);
- int len = TYPE_LENGTH (type);
+ int len = TYPE_LENGTH (check_typedef (type));
if (regnum >= S390_F0_REGNUM && regnum <= S390_F15_REGNUM && len < 8)
set_value_offset (value, 0);
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- /* PC and CC pseudo registers need to be saved/restored in order to
- push or pop frames. */
+ /* We usually save/restore the whole PSW, which includes PC and CC.
+ However, some older gdbservers may not support saving/restoring
+ the whole PSW yet, and will return an XML register description
+ excluding those from the save/restore register groups. In those
+ cases, we still need to explicitly save/restore PC and CC in order
+ to push or pop frames. Since this doesn't hurt anything if we
+ already save/restore the whole PSW (it's just redundant), we add
+ PC and CC at this point unconditionally. */
if (group == save_reggroup || group == restore_reggroup)
return regnum == tdep->pc_regnum || regnum == tdep->cc_regnum;
/* GPR Uppper Halves. */
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
+ /* GNU/Linux-specific optional "registers". */
+ 0x88, -1, -1,
};
int s390x_regmap_gregset[S390_NUM_REGS] =
0x30, 0x38, 0x40, 0x48,
0x50, 0x58, 0x60, 0x68,
0x70, 0x78, 0x80, 0x88,
+ /* GNU/Linux-specific optional "registers". */
+ 0xd0, -1, -1,
};
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,
+ /* GNU/Linux-specific optional "registers". */
+ -1, -1, -1,
};
int s390_regmap_upper[S390_NUM_REGS] =
0x10, 0x14, 0x18, 0x1c,
0x20, 0x24, 0x28, 0x2c,
0x30, 0x34, 0x38, 0x3c,
+ /* GNU/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,
+ /* GNU/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,
+ /* GNU/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,
+ /* GNU/Linux-specific optional "registers". */
+ -1, -1, 0,
};
/* Supply register REGNUM from the register set REGSET to register cache
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" },
{ 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 *
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;
}
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;
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;
{
pv_t addr = s390_addr (data, d2, x2, b2);
- pv_t offset;
/* If it's a load from an in-line constant pool, then we can
simulate that, under the assumption that the code isn't
register was saved, record its offset in the reg_offset table in
PROLOGUE_UNTYPED. */
static void
-s390_check_for_saved (void *data_untyped, pv_t addr, CORE_ADDR size, pv_t value)
+s390_check_for_saved (void *data_untyped, pv_t addr,
+ CORE_ADDR size, pv_t value)
{
struct s390_prologue_data *data = data_untyped;
int i, offset;
s390_store (data, d2, x2, b2, data->fpr_size, data->fpr[r1]);
/* STM r1, r3, d2(b2) --- store multiple. */
- /* STMY r1, r3, d2(b2) --- store multiple (long-displacement version). */
+ /* STMY r1, r3, d2(b2) --- store multiple (long-displacement
+ version). */
/* STMG r1, r3, d2(b2) --- store multiple (64-bit version). */
else if (is_rs (insn32, op_stm, &r1, &r3, &d2, &b2)
|| is_rsy (insn32, op1_stmy, op2_stmy, &r1, &r3, &d2, &b2)
exactly one case: when pc points to that branch instruction.
Thus we try to disassemble the one instructions immediately
- preceeding pc and check whether it is an LM-type instruction
+ preceding pc and check whether it is an LM-type instruction
modifying the stack pointer.
Note that disassembling backwards is not reliable, so there
if (debug_displaced)
fprintf_unfiltered (gdb_stdlog,
- "displaced: (s390) fixup (%s, %s) pc %s amode 0x%x\n",
+ "displaced: (s390) fixup (%s, %s) pc %s len %d amode 0x%x\n",
paddress (gdbarch, from), paddress (gdbarch, to),
- paddress (gdbarch, pc), (int) amode);
+ paddress (gdbarch, pc), insnlen, (int) amode);
/* Handle absolute branch and save instructions. */
if (is_rr (insn, op_basr, &r1, &r2)
/* Handle LOAD ADDRESS RELATIVE LONG. */
else if (is_ril (insn, op1_larl, op2_larl, &r1, &i2))
{
+ /* Update PC. */
+ regcache_write_pc (regs, from + insnlen);
/* Recompute output address in R1. */
regcache_cooked_write_unsigned (regs, S390_R0_REGNUM + r1,
- amode | (from + insnlen + i2*2));
+ amode | (from + i2 * 2));
}
/* If we executed a breakpoint instruction, point PC right back at it. */
/* For any other insn, PC points right after the original instruction. */
else
regcache_write_pc (regs, from + insnlen);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: (s390) pc is now %s\n",
+ paddress (gdbarch, regcache_read_pc (regs)));
+}
+
+
+/* Helper routine to unwind pseudo registers. */
+
+static struct value *
+s390_unwind_pseudo_register (struct frame_info *this_frame, int regnum)
+{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ struct type *type = register_type (gdbarch, regnum);
+
+ /* Unwind PC via PSW address. */
+ if (regnum == tdep->pc_regnum)
+ {
+ struct value *val;
+
+ val = frame_unwind_register_value (this_frame, S390_PSWA_REGNUM);
+ if (!value_optimized_out (val))
+ {
+ LONGEST pswa = value_as_long (val);
+
+ if (TYPE_LENGTH (type) == 4)
+ return value_from_pointer (type, pswa & 0x7fffffff);
+ else
+ return value_from_pointer (type, pswa);
+ }
+ }
+
+ /* Unwind CC via PSW mask. */
+ if (regnum == tdep->cc_regnum)
+ {
+ struct value *val;
+
+ val = frame_unwind_register_value (this_frame, S390_PSWM_REGNUM);
+ if (!value_optimized_out (val))
+ {
+ LONGEST pswm = value_as_long (val);
+
+ if (TYPE_LENGTH (type) == 4)
+ return value_from_longest (type, (pswm >> 12) & 3);
+ else
+ return value_from_longest (type, (pswm >> 44) & 3);
+ }
+ }
+
+ /* Unwind full GPRs to show at least the lower halves (as the
+ upper halves are undefined). */
+ if (tdep->gpr_full_regnum != -1
+ && regnum >= tdep->gpr_full_regnum
+ && regnum < tdep->gpr_full_regnum + 16)
+ {
+ int reg = regnum - tdep->gpr_full_regnum;
+ struct value *val;
+
+ val = frame_unwind_register_value (this_frame, S390_R0_REGNUM + reg);
+ if (!value_optimized_out (val))
+ return value_cast (type, val);
+ }
+
+ return allocate_optimized_out_value (type);
}
+static struct value *
+s390_trad_frame_prev_register (struct frame_info *this_frame,
+ struct trad_frame_saved_reg saved_regs[],
+ int regnum)
+{
+ if (regnum < S390_NUM_REGS)
+ return trad_frame_get_prev_register (this_frame, saved_regs, regnum);
+ else
+ return s390_unwind_pseudo_register (this_frame, regnum);
+}
+
+
/* Normal stack frames. */
struct s390_unwind_cache {
struct s390_unwind_cache *info)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
struct s390_prologue_data data;
pv_t *fp = &data.gpr[S390_FRAME_REGNUM - S390_R0_REGNUM];
/* If we've detected a function with stack frame, we'll still have to
treat it as frameless if we're currently within the function epilog
- code at a point where the frame pointer has already been restored.
+ code at a point where the frame pointer has already been restored.
This can only happen in an innermost frame. */
/* FIXME: cagney/2004-05-01: This sanity check shouldn't be needed,
instead the code should simpliy rely on its analysis. */
trad_frame_set_unknown (info->saved_regs, i);
/* CC is always call-clobbered. */
- trad_frame_set_unknown (info->saved_regs, tdep->cc_regnum);
+ trad_frame_set_unknown (info->saved_regs, S390_PSWM_REGNUM);
/* Record the addresses of all register spill slots the prologue parser
has recognized. Consider only registers defined as call-saved by the
info->saved_regs[S390_F0_REGNUM + i].addr = cfa - data.fpr_slot[i];
/* Function return will set PC to %r14. */
- info->saved_regs[tdep->pc_regnum] = info->saved_regs[S390_RETADDR_REGNUM];
+ info->saved_regs[S390_PSWA_REGNUM] = info->saved_regs[S390_RETADDR_REGNUM];
/* In frameless functions, we unwind simply by moving the return
address to the PC. However, if we actually stored to the
save area, use that -- we might only think the function frameless
because we're in the middle of the prologue ... */
if (size == 0
- && !trad_frame_addr_p (info->saved_regs, tdep->pc_regnum))
+ && !trad_frame_addr_p (info->saved_regs, S390_PSWA_REGNUM))
{
- info->saved_regs[tdep->pc_regnum].realreg = S390_RETADDR_REGNUM;
+ info->saved_regs[S390_PSWA_REGNUM].realreg = S390_RETADDR_REGNUM;
}
/* Another sanity check: unless this is a frameless function,
if (size > 0)
{
if (!trad_frame_addr_p (info->saved_regs, S390_SP_REGNUM)
- || !trad_frame_addr_p (info->saved_regs, tdep->pc_regnum))
+ || !trad_frame_addr_p (info->saved_regs, S390_PSWA_REGNUM))
prev_sp = -1;
}
struct s390_unwind_cache *info)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
CORE_ADDR backchain;
trad_frame_set_unknown (info->saved_regs, i);
/* CC is always call-clobbered. */
- trad_frame_set_unknown (info->saved_regs, tdep->cc_regnum);
+ trad_frame_set_unknown (info->saved_regs, S390_PSWM_REGNUM);
/* Get the backchain. */
reg = get_frame_register_unsigned (this_frame, S390_SP_REGNUM);
info->saved_regs[S390_RETADDR_REGNUM].addr = backchain + 14*word_size;
/* Function return will set PC to %r14. */
- info->saved_regs[tdep->pc_regnum]
+ info->saved_regs[S390_PSWA_REGNUM]
= info->saved_regs[S390_RETADDR_REGNUM];
/* We use the current value of the frame register as local_base,
void **this_prologue_cache, int regnum)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
struct s390_unwind_cache *info
= s390_frame_unwind_cache (this_frame, this_prologue_cache);
- /* Unwind full GPRs to show at least the lower halves (as the
- upper halves are undefined). */
- if (tdep->gpr_full_regnum != -1
- && regnum >= tdep->gpr_full_regnum
- && regnum < tdep->gpr_full_regnum + 16)
- {
- int reg = regnum - tdep->gpr_full_regnum + S390_R0_REGNUM;
- struct value *val, *newval;
-
- val = trad_frame_get_prev_register (this_frame, info->saved_regs, reg);
- newval = value_cast (register_type (gdbarch, regnum), val);
- if (value_optimized_out (val))
- set_value_optimized_out (newval, 1);
-
- return newval;
- }
-
- return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
+ return s390_trad_frame_prev_register (this_frame, info->saved_regs, regnum);
}
static const struct frame_unwind s390_frame_unwind = {
NORMAL_FRAME,
+ default_frame_unwind_stop_reason,
s390_frame_this_id,
s390_frame_prev_register,
NULL,
void **this_prologue_cache)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
struct s390_stub_unwind_cache *info;
ULONGEST reg;
info->saved_regs = trad_frame_alloc_saved_regs (this_frame);
/* The return address is in register %r14. */
- info->saved_regs[tdep->pc_regnum].realreg = S390_RETADDR_REGNUM;
+ info->saved_regs[S390_PSWA_REGNUM].realreg = S390_RETADDR_REGNUM;
/* Retrieve stack pointer and determine our frame base. */
reg = get_frame_register_unsigned (this_frame, S390_SP_REGNUM);
{
struct s390_stub_unwind_cache *info
= s390_stub_frame_unwind_cache (this_frame, this_prologue_cache);
- return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
+ return s390_trad_frame_prev_register (this_frame, info->saved_regs, regnum);
}
static int
static const struct frame_unwind s390_stub_frame_unwind = {
NORMAL_FRAME,
+ default_frame_unwind_stop_reason,
s390_stub_frame_this_id,
s390_stub_frame_prev_register,
NULL,
struct s390_sigtramp_unwind_cache *info;
ULONGEST this_sp, prev_sp;
CORE_ADDR next_ra, next_cfa, sigreg_ptr, sigreg_high_off;
- ULONGEST pswm;
int i;
if (*this_prologue_cache)
/* New-style RT frame:
retcode + alignment (8 bytes)
siginfo (128 bytes)
- ucontext (contains sigregs at offset 5 words) */
+ ucontext (contains sigregs at offset 5 words). */
if (next_ra == next_cfa)
{
sigreg_ptr = next_cfa + 8 + 128 + align_up (5*word_size, 8);
/* Old-style RT frame and all non-RT frames:
old signal mask (8 bytes)
- pointer to sigregs */
+ pointer to sigregs. */
else
{
sigreg_ptr = read_memory_unsigned_integer (next_cfa + 8,
info->saved_regs[S390_PSWA_REGNUM].addr = sigreg_ptr;
sigreg_ptr += word_size;
- /* Point PC to PSWA as well. */
- info->saved_regs[tdep->pc_regnum] = info->saved_regs[S390_PSWA_REGNUM];
-
- /* Extract CC from PSWM. */
- pswm = read_memory_unsigned_integer (
- info->saved_regs[S390_PSWM_REGNUM].addr,
- word_size, byte_order);
- trad_frame_set_value (info->saved_regs, tdep->cc_regnum,
- (pswm >> (8 * word_size - 20)) & 3);
-
/* Then the GPRs. */
for (i = 0; i < 16; i++)
{
sigreg_ptr += 4;
}
- /* Provide read-only copies of the full registers. */
- if (tdep->gpr_full_regnum != -1)
- for (i = 0; i < 16; i++)
- {
- ULONGEST low, high;
- low = read_memory_unsigned_integer (
- info->saved_regs[S390_R0_REGNUM + i].addr,
- 4, byte_order);
- high = read_memory_unsigned_integer (
- info->saved_regs[S390_R0_UPPER_REGNUM + i].addr,
- 4, byte_order);
-
- trad_frame_set_value (info->saved_regs, tdep->gpr_full_regnum + i,
- (high << 32) | low);
- }
-
/* Restore the previous frame's SP. */
prev_sp = read_memory_unsigned_integer (
info->saved_regs[S390_SP_REGNUM].addr,
{
struct s390_sigtramp_unwind_cache *info
= s390_sigtramp_frame_unwind_cache (this_frame, this_prologue_cache);
- return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum);
+ return s390_trad_frame_prev_register (this_frame, info->saved_regs, regnum);
}
static int
static const struct frame_unwind s390_sigtramp_frame_unwind = {
SIGTRAMP_FRAME,
+ default_frame_unwind_stop_reason,
s390_sigtramp_frame_this_id,
s390_sigtramp_frame_prev_register,
NULL,
s390_dwarf2_prev_register (struct frame_info *this_frame, void **this_cache,
int regnum)
{
- struct gdbarch *gdbarch = get_frame_arch (this_frame);
- struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
- int reg = regnum - tdep->gpr_full_regnum;
- struct value *val, *newval;
-
- val = frame_unwind_register_value (this_frame, S390_R0_REGNUM + reg);
- newval = value_cast (register_type (gdbarch, regnum), val);
- if (value_optimized_out (val))
- set_value_optimized_out (newval, 1);
-
- return newval;
+ return s390_unwind_pseudo_register (this_frame, regnum);
}
static void
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ /* The condition code (and thus PSW mask) is call-clobbered. */
+ if (regnum == S390_PSWM_REGNUM)
+ reg->how = DWARF2_FRAME_REG_UNDEFINED;
+
+ /* The PSW address unwinds to the return address. */
+ else if (regnum == S390_PSWA_REGNUM)
+ reg->how = DWARF2_FRAME_REG_RA;
+
/* Fixed registers are call-saved or call-clobbered
depending on the ABI in use. */
- if (regnum >= 0 && regnum < S390_NUM_REGS)
+ else if (regnum < S390_NUM_REGS)
{
if (s390_register_call_saved (gdbarch, regnum))
reg->how = DWARF2_FRAME_REG_SAME_VALUE;
reg->how = DWARF2_FRAME_REG_UNDEFINED;
}
- /* The CC pseudo register is call-clobbered. */
- else if (regnum == tdep->cc_regnum)
- reg->how = DWARF2_FRAME_REG_UNDEFINED;
-
- /* The PC register unwinds to the return address. */
- else if (regnum == tdep->pc_regnum)
- reg->how = DWARF2_FRAME_REG_RA;
-
- /* We install a special function to unwind full GPRs to show at
- least the lower halves (as the upper halves are undefined). */
- else if (tdep->gpr_full_regnum != -1
- && regnum >= tdep->gpr_full_regnum
- && regnum < tdep->gpr_full_regnum + 16)
+ /* We install a special function to unwind pseudos. */
+ else
{
reg->how = DWARF2_FRAME_REG_FN;
reg->loc.fn = s390_dwarf2_prev_register;
if (length > 8)
return 1;
- /* FIXME: All complex and vector types are also returned by reference. */
- return is_struct_like (type) && !is_power_of_two (length);
+ return (is_struct_like (type) && !is_power_of_two (TYPE_LENGTH (type)))
+ || TYPE_CODE (type) == TYPE_CODE_COMPLEX
+ || (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type));
}
/* Return non-zero if TYPE should be passed in a float register
extend_simple_arg (struct gdbarch *gdbarch, struct value *arg)
{
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- struct type *type = value_type (arg);
+ struct type *type = check_typedef (value_type (arg));
/* Even structs get passed in the least significant bits of the
register / memory word. It's not really right to extract them as
alignment = 1;
for (i = 0; i < TYPE_NFIELDS (type); i++)
{
- int field_alignment = alignment_of (TYPE_FIELD_TYPE (type, i));
+ int field_alignment
+ = alignment_of (check_typedef (TYPE_FIELD_TYPE (type, i)));
if (field_alignment > alignment)
alignment = field_alignment;
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
- ULONGEST orig_sp;
int i;
/* If the i'th argument is passed as a reference to a copy, then
copy_addr[i] is the address of the copy we made. */
CORE_ADDR *copy_addr = alloca (nargs * sizeof (CORE_ADDR));
- /* Build the reference-to-copy area. */
+ /* Reserve space for the reference-to-copy area. */
for (i = 0; i < nargs; i++)
{
struct value *arg = args[i];
- struct type *type = value_type (arg);
+ struct type *type = check_typedef (value_type (arg));
unsigned length = TYPE_LENGTH (type);
if (s390_function_arg_pass_by_reference (type))
{
sp -= length;
sp = align_down (sp, alignment_of (type));
- write_memory (sp, value_contents (arg), length);
copy_addr[i] = sp;
}
}
boundary. */
sp = align_down (sp, 8);
+ /* Allocate the standard frame areas: the register save area, the
+ word reserved for the compiler (which seems kind of meaningless),
+ and the back chain pointer. */
+ sp -= 16*word_size + 32;
+
+ /* Now we have the final SP value. Make sure we didn't underflow;
+ on 31-bit, this would result in addresses with the high bit set,
+ which causes confusion elsewhere. Note that if we error out
+ here, stack and registers remain untouched. */
+ if (gdbarch_addr_bits_remove (gdbarch, sp) != sp)
+ error (_("Stack overflow"));
+
+
/* Finally, place the actual parameters, working from SP towards
higher addresses. The code above is supposed to reserve enough
space for this. */
{
int fr = 0;
int gr = 2;
- CORE_ADDR starg = sp;
+ CORE_ADDR starg = sp + 16*word_size + 32;
/* A struct is returned using general register 2. */
if (struct_return)
for (i = 0; i < nargs; i++)
{
struct value *arg = args[i];
- struct type *type = value_type (arg);
+ struct type *type = check_typedef (value_type (arg));
unsigned length = TYPE_LENGTH (type);
if (s390_function_arg_pass_by_reference (type))
{
+ /* Actually copy the argument contents to the stack slot
+ that was reserved above. */
+ write_memory (copy_addr[i], value_contents (arg), length);
+
if (gr <= 6)
{
regcache_cooked_write_unsigned (regcache, S390_R0_REGNUM + gr,
{
/* Integer arguments are always extended to word size. */
regcache_cooked_write_signed (regcache, S390_R0_REGNUM + gr,
- extend_simple_arg (gdbarch, arg));
+ extend_simple_arg (gdbarch,
+ arg));
gr++;
}
else
}
}
- /* Allocate the standard frame areas: the register save area, the
- word reserved for the compiler (which seems kind of meaningless),
- and the back chain pointer. */
- sp -= 16*word_size + 32;
-
- /* Store return address. */
+ /* Store return PSWA. In 31-bit mode, keep addressing mode bit. */
+ if (word_size == 4)
+ {
+ ULONGEST pswa;
+ regcache_cooked_read_unsigned (regcache, S390_PSWA_REGNUM, &pswa);
+ bp_addr = (bp_addr & 0x7fffffff) | (pswa & 0x80000000);
+ }
regcache_cooked_write_unsigned (regcache, S390_RETADDR_REGNUM, bp_addr);
-
+
/* Store updated stack pointer. */
regcache_cooked_write_unsigned (regcache, S390_SP_REGNUM, sp);
case TYPE_CODE_STRUCT:
case TYPE_CODE_UNION:
case TYPE_CODE_ARRAY:
+ case TYPE_CODE_COMPLEX:
return RETURN_VALUE_STRUCT_CONVENTION;
default:
}
static enum return_value_convention
-s390_return_value (struct gdbarch *gdbarch, struct type *func_type,
+s390_return_value (struct gdbarch *gdbarch, struct value *function,
struct type *type, struct regcache *regcache,
gdb_byte *out, const gdb_byte *in)
{
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
- int length = TYPE_LENGTH (type);
- enum return_value_convention rvc =
- s390_return_value_convention (gdbarch, type);
+ enum return_value_convention rvc;
+ int length;
+
+ type = check_typedef (type);
+ rvc = s390_return_value_convention (gdbarch, type);
+ length = TYPE_LENGTH (type);
+
if (in)
{
switch (rvc)
/* Breakpoints. */
static const gdb_byte *
-s390_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr)
+s390_breakpoint_from_pc (struct gdbarch *gdbarch,
+ CORE_ADDR *pcptr, int *lenptr)
{
static const gdb_byte breakpoint[] = { 0x0, 0x1 };
}
static int
-s390_address_class_name_to_type_flags (struct gdbarch *gdbarch, const char *name,
+s390_address_class_name_to_type_flags (struct gdbarch *gdbarch,
+ const char *name,
int *type_flags_ptr)
{
if (strcmp (name, "mode32") == 0)
return 0;
}
+/* Implementation of `gdbarch_stap_is_single_operand', as defined in
+ gdbarch.h. */
+
+static int
+s390_stap_is_single_operand (struct gdbarch *gdbarch, const char *s)
+{
+ return ((isdigit (*s) && s[1] == '(' && s[2] == '%') /* Displacement
+ or indirection. */
+ || *s == '%' /* Register access. */
+ || isdigit (*s)); /* Literal number. */
+}
+
/* Set up gdbarch struct. */
static struct gdbarch *
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. */
valid_p &= tdesc_numbered_register (feature, tdesc_data,
S390_A0_REGNUM + i, acrs[i]);
+ /* Optional GNU/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);
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);
displaced_step_at_entry_point);
set_gdbarch_max_insn_length (gdbarch, S390_MAX_INSTR_SIZE);
+ /* Note that GNU/Linux is the only OS supported on this
+ platform. */
+ linux_init_abi (info, gdbarch);
+
switch (tdep->abi)
{
case ABI_LINUX_S390:
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:
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;
}
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
+ set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
+
+ /* SystemTap functions. */
+ set_gdbarch_stap_register_prefix (gdbarch, "%");
+ set_gdbarch_stap_register_indirection_prefix (gdbarch, "(");
+ set_gdbarch_stap_register_indirection_suffix (gdbarch, ")");
+ set_gdbarch_stap_is_single_operand (gdbarch, s390_stap_is_single_operand);
+
return gdbarch;
}
/* Hook us into the gdbarch mechanism. */
register_gdbarch_init (bfd_arch_s390, s390_gdbarch_init);
- /* Initialize the Linux target descriptions. */
+ /* Initialize the GNU/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 ();
}