#include "defs.h"
#include "arch-utils.h"
#include "frame.h"
#include "inferior.h"
#include "symtab.h"
#include "target.h"
#include "gdbcore.h"
#include "gdbcmd.h"
#include "objfiles.h"
#include "tm.h"
#include "../bfd/bfd.h"
#include "floatformat.h"
#include "regcache.h"
#include "trad-frame.h"
#include "frame-base.h"
#include "frame-unwind.h"
#include "dwarf2-frame.h"
#include "reggroups.h"
#include "regset.h"
#include "value.h"
#include "gdb_assert.h"
#include "dis-asm.h"
#include "solib-svr4.h"
#include "s390-tdep.h"
struct gdbarch_tdep
{
enum { ABI_LINUX_S390, ABI_LINUX_ZSERIES } abi;
const struct regset *gregset;
int sizeof_gregset;
const struct regset *fpregset;
int sizeof_fpregset;
};
struct s390_register_info
{
char *name;
struct type **type;
};
static struct s390_register_info s390_register_info[S390_NUM_TOTAL_REGS] =
{
{ "pswm", &builtin_type_long },
{ "pswa", &builtin_type_long },
{ "r0", &builtin_type_long },
{ "r1", &builtin_type_long },
{ "r2", &builtin_type_long },
{ "r3", &builtin_type_long },
{ "r4", &builtin_type_long },
{ "r5", &builtin_type_long },
{ "r6", &builtin_type_long },
{ "r7", &builtin_type_long },
{ "r8", &builtin_type_long },
{ "r9", &builtin_type_long },
{ "r10", &builtin_type_long },
{ "r11", &builtin_type_long },
{ "r12", &builtin_type_long },
{ "r13", &builtin_type_long },
{ "r14", &builtin_type_long },
{ "r15", &builtin_type_long },
{ "acr0", &builtin_type_int },
{ "acr1", &builtin_type_int },
{ "acr2", &builtin_type_int },
{ "acr3", &builtin_type_int },
{ "acr4", &builtin_type_int },
{ "acr5", &builtin_type_int },
{ "acr6", &builtin_type_int },
{ "acr7", &builtin_type_int },
{ "acr8", &builtin_type_int },
{ "acr9", &builtin_type_int },
{ "acr10", &builtin_type_int },
{ "acr11", &builtin_type_int },
{ "acr12", &builtin_type_int },
{ "acr13", &builtin_type_int },
{ "acr14", &builtin_type_int },
{ "acr15", &builtin_type_int },
{ "fpc", &builtin_type_int },
{ "f0", &builtin_type_double },
{ "f1", &builtin_type_double },
{ "f2", &builtin_type_double },
{ "f3", &builtin_type_double },
{ "f4", &builtin_type_double },
{ "f5", &builtin_type_double },
{ "f6", &builtin_type_double },
{ "f7", &builtin_type_double },
{ "f8", &builtin_type_double },
{ "f9", &builtin_type_double },
{ "f10", &builtin_type_double },
{ "f11", &builtin_type_double },
{ "f12", &builtin_type_double },
{ "f13", &builtin_type_double },
{ "f14", &builtin_type_double },
{ "f15", &builtin_type_double },
{ "pc", &builtin_type_void_func_ptr },
{ "cc", &builtin_type_int },
};
static const char *
s390_register_name (int regnum)
{
gdb_assert (regnum >= 0 && regnum < S390_NUM_TOTAL_REGS);
return s390_register_info[regnum].name;
}
static struct type *
s390_register_type (struct gdbarch *gdbarch, int regnum)
{
gdb_assert (regnum >= 0 && regnum < S390_NUM_TOTAL_REGS);
return *s390_register_info[regnum].type;
}
static int s390_dwarf_regmap[] =
{
S390_R0_REGNUM, S390_R1_REGNUM, S390_R2_REGNUM, S390_R3_REGNUM,
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,
S390_F0_REGNUM, S390_F2_REGNUM, S390_F4_REGNUM, S390_F6_REGNUM,
S390_F1_REGNUM, S390_F3_REGNUM, S390_F5_REGNUM, S390_F7_REGNUM,
S390_F8_REGNUM, S390_F10_REGNUM, S390_F12_REGNUM, S390_F14_REGNUM,
S390_F9_REGNUM, S390_F11_REGNUM, S390_F13_REGNUM, S390_F15_REGNUM,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
S390_A0_REGNUM, S390_A1_REGNUM, S390_A2_REGNUM, S390_A3_REGNUM,
S390_A4_REGNUM, S390_A5_REGNUM, S390_A6_REGNUM, S390_A7_REGNUM,
S390_A8_REGNUM, S390_A9_REGNUM, S390_A10_REGNUM, S390_A11_REGNUM,
S390_A12_REGNUM, S390_A13_REGNUM, S390_A14_REGNUM, S390_A15_REGNUM,
S390_PSWM_REGNUM,
S390_PSWA_REGNUM
};
static int
s390_dwarf_reg_to_regnum (int reg)
{
int regnum = -1;
if (reg >= 0 && reg < ARRAY_SIZE (s390_dwarf_regmap))
regnum = s390_dwarf_regmap[reg];
if (regnum == -1)
warning (_("Unmapped DWARF Register #%d encountered."), reg);
return regnum;
}
static void
s390_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
int regnum, gdb_byte *buf)
{
ULONGEST val;
switch (regnum)
{
case S390_PC_REGNUM:
regcache_raw_read_unsigned (regcache, S390_PSWA_REGNUM, &val);
store_unsigned_integer (buf, 4, val & 0x7fffffff);
break;
case S390_CC_REGNUM:
regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &val);
store_unsigned_integer (buf, 4, (val >> 12) & 3);
break;
default:
internal_error (__FILE__, __LINE__, _("invalid regnum"));
}
}
static void
s390_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
int regnum, const gdb_byte *buf)
{
ULONGEST val, psw;
switch (regnum)
{
case S390_PC_REGNUM:
val = extract_unsigned_integer (buf, 4);
regcache_raw_read_unsigned (regcache, S390_PSWA_REGNUM, &psw);
psw = (psw & 0x80000000) | (val & 0x7fffffff);
regcache_raw_write_unsigned (regcache, S390_PSWA_REGNUM, psw);
break;
case S390_CC_REGNUM:
val = extract_unsigned_integer (buf, 4);
regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &psw);
psw = (psw & ~((ULONGEST)3 << 12)) | ((val & 3) << 12);
regcache_raw_write_unsigned (regcache, S390_PSWM_REGNUM, psw);
break;
default:
internal_error (__FILE__, __LINE__, _("invalid regnum"));
}
}
static void
s390x_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache,
int regnum, gdb_byte *buf)
{
ULONGEST val;
switch (regnum)
{
case S390_PC_REGNUM:
regcache_raw_read (regcache, S390_PSWA_REGNUM, buf);
break;
case S390_CC_REGNUM:
regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &val);
store_unsigned_integer (buf, 4, (val >> 44) & 3);
break;
default:
internal_error (__FILE__, __LINE__, _("invalid regnum"));
}
}
static void
s390x_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache,
int regnum, const gdb_byte *buf)
{
ULONGEST val, psw;
switch (regnum)
{
case S390_PC_REGNUM:
regcache_raw_write (regcache, S390_PSWA_REGNUM, buf);
break;
case S390_CC_REGNUM:
val = extract_unsigned_integer (buf, 4);
regcache_raw_read_unsigned (regcache, S390_PSWM_REGNUM, &psw);
psw = (psw & ~((ULONGEST)3 << 44)) | ((val & 3) << 44);
regcache_raw_write_unsigned (regcache, S390_PSWM_REGNUM, psw);
break;
default:
internal_error (__FILE__, __LINE__, _("invalid regnum"));
}
}
static int
s390_convert_register_p (int regno, struct type *type)
{
return (regno >= S390_F0_REGNUM && regno <= S390_F15_REGNUM)
&& TYPE_LENGTH (type) < 8;
}
static void
s390_register_to_value (struct frame_info *frame, int regnum,
struct type *valtype, gdb_byte *out)
{
gdb_byte in[8];
int len = TYPE_LENGTH (valtype);
gdb_assert (len < 8);
get_frame_register (frame, regnum, in);
memcpy (out, in, len);
}
static void
s390_value_to_register (struct frame_info *frame, int regnum,
struct type *valtype, const gdb_byte *in)
{
gdb_byte out[8];
int len = TYPE_LENGTH (valtype);
gdb_assert (len < 8);
memset (out, 0, 8);
memcpy (out, in, len);
put_frame_register (frame, regnum, out);
}
static int
s390_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
struct reggroup *group)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
if (group == general_reggroup)
return (regnum >= S390_R0_REGNUM && regnum <= S390_R15_REGNUM)
|| regnum == S390_PC_REGNUM
|| regnum == S390_CC_REGNUM;
if (group == float_reggroup)
return (regnum >= S390_F0_REGNUM && regnum <= S390_F15_REGNUM)
|| regnum == S390_FPC_REGNUM;
if (group == save_reggroup || group == restore_reggroup)
return regnum != S390_PSWM_REGNUM && regnum != S390_PSWA_REGNUM;
return default_register_reggroup_p (gdbarch, regnum, group);
}
int s390_regmap_gregset[S390_NUM_REGS] =
{
0x00, 0x04,
0x08, 0x0c, 0x10, 0x14,
0x18, 0x1c, 0x20, 0x24,
0x28, 0x2c, 0x30, 0x34,
0x38, 0x3c, 0x40, 0x44,
0x48, 0x4c, 0x50, 0x54,
0x58, 0x5c, 0x60, 0x64,
0x68, 0x6c, 0x70, 0x74,
0x78, 0x7c, 0x80, 0x84,
-1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
};
int s390x_regmap_gregset[S390_NUM_REGS] =
{
0x00, 0x08,
0x10, 0x18, 0x20, 0x28,
0x30, 0x38, 0x40, 0x48,
0x50, 0x58, 0x60, 0x68,
0x70, 0x78, 0x80, 0x88,
0x90, 0x94, 0x98, 0x9c,
0xa0, 0xa4, 0xa8, 0xac,
0xb0, 0xb4, 0xb8, 0xbc,
0xc0, 0xc4, 0xc8, 0xcc,
-1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
};
int s390_regmap_fpregset[S390_NUM_REGS] =
{
-1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
0x00,
0x08, 0x10, 0x18, 0x20,
0x28, 0x30, 0x38, 0x40,
0x48, 0x50, 0x58, 0x60,
0x68, 0x70, 0x78, 0x80,
};
static void
s390_supply_regset (const struct regset *regset, struct regcache *regcache,
int regnum, const void *regs, size_t len)
{
const int *offset = regset->descr;
int i;
for (i = 0; i < S390_NUM_REGS; i++)
{
if ((regnum == i || regnum == -1) && offset[i] != -1)
regcache_raw_supply (regcache, i, (const char *)regs + offset[i]);
}
}
static const struct regset s390_gregset = {
s390_regmap_gregset,
s390_supply_regset
};
static const struct regset s390x_gregset = {
s390x_regmap_gregset,
s390_supply_regset
};
static const struct regset s390_fpregset = {
s390_regmap_fpregset,
s390_supply_regset
};
const struct regset *
s390_regset_from_core_section (struct gdbarch *gdbarch,
const char *sect_name, size_t sect_size)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
if (strcmp (sect_name, ".reg") == 0 && sect_size == tdep->sizeof_gregset)
return tdep->gregset;
if (strcmp (sect_name, ".reg2") == 0 && sect_size == tdep->sizeof_fpregset)
return tdep->fpregset;
return NULL;
}
struct prologue_value {
enum {
pv_unknown,
pv_constant,
pv_register,
} kind;
int reg;
CORE_ADDR k;
};
static void
pv_set_to_unknown (struct prologue_value *v)
{
v->kind = pv_unknown;
}
static void
pv_set_to_constant (struct prologue_value *v, CORE_ADDR k)
{
v->kind = pv_constant;
v->k = k;
}
static void
pv_set_to_register (struct prologue_value *v, int reg, CORE_ADDR k)
{
v->kind = pv_register;
v->reg = reg;
v->k = k;
}
static void
pv_constant_last (struct prologue_value **a,
struct prologue_value **b)
{
if ((*a)->kind == pv_constant
&& (*b)->kind != pv_constant)
{
struct prologue_value *temp = *a;
*a = *b;
*b = temp;
}
}
static void
pv_add (struct prologue_value *sum,
struct prologue_value *a,
struct prologue_value *b)
{
pv_constant_last (&a, &b);
if (b->kind == pv_constant
&& (a->kind == pv_register
|| a->kind == pv_constant))
{
sum->kind = a->kind;
sum->reg = a->reg;
sum->k = a->k + b->k;
}
else
sum->kind = pv_unknown;
}
static void
pv_add_constant (struct prologue_value *v, CORE_ADDR k)
{
struct prologue_value pv_k;
pv_set_to_constant (&pv_k, k);
pv_add (v, v, &pv_k);
}
static void
pv_subtract (struct prologue_value *diff,
struct prologue_value *a,
struct prologue_value *b)
{
pv_constant_last (&a, &b);
if (b->kind == pv_constant
&& (a->kind == pv_register
|| a->kind == pv_constant))
{
diff->kind = a->kind;
diff->reg = a->reg;
diff->k = a->k - b->k;
}
else if (a->kind == pv_register
&& b->kind == pv_register
&& a->reg == b->reg)
{
diff->kind = pv_constant;
diff->k = a->k - b->k;
}
else
diff->kind = pv_unknown;
}
static void
pv_logical_and (struct prologue_value *and,
struct prologue_value *a,
struct prologue_value *b)
{
pv_constant_last (&a, &b);
if (a->kind == pv_constant
&& b->kind == pv_constant)
{
and->kind = pv_constant;
and->k = a->k & b->k;
}
else if (b->kind == pv_constant
&& b->k == 0)
{
and->kind = pv_constant;
and->k = 0;
}
else if (b->kind == pv_constant
&& b->k == ~ (CORE_ADDR) 0)
*and = *a;
else if (a->kind == pv_register
&& b->kind == pv_register
&& a->reg == b->reg
&& a->k == b->k)
*and = *a;
else
pv_set_to_unknown (and);
}
static int
pv_is_identical (struct prologue_value *a,
struct prologue_value *b)
{
if (a->kind != b->kind)
return 0;
switch (a->kind)
{
case pv_unknown:
return 1;
case pv_constant:
return (a->k == b->k);
case pv_register:
return (a->reg == b->reg && a->k == b->k);
default:
gdb_assert (0);
}
}
static int
pv_is_register (struct prologue_value *a, int r, CORE_ADDR k)
{
return (a->kind == pv_register
&& a->reg == r
&& a->k == k);
}
enum
{
op1_lhi = 0xa7, op2_lhi = 0x08,
op1_lghi = 0xa7, op2_lghi = 0x09,
op_lr = 0x18,
op_lgr = 0xb904,
op_l = 0x58,
op1_ly = 0xe3, op2_ly = 0x58,
op1_lg = 0xe3, op2_lg = 0x04,
op_lm = 0x98,
op1_lmy = 0xeb, op2_lmy = 0x98,
op1_lmg = 0xeb, op2_lmg = 0x04,
op_st = 0x50,
op1_sty = 0xe3, op2_sty = 0x50,
op1_stg = 0xe3, op2_stg = 0x24,
op_std = 0x60,
op_stm = 0x90,
op1_stmy = 0xeb, op2_stmy = 0x90,
op1_stmg = 0xeb, op2_stmg = 0x24,
op1_aghi = 0xa7, op2_aghi = 0x0b,
op1_ahi = 0xa7, op2_ahi = 0x0a,
op_ar = 0x1a,
op_agr = 0xb908,
op_a = 0x5a,
op1_ay = 0xe3, op2_ay = 0x5a,
op1_ag = 0xe3, op2_ag = 0x08,
op_sr = 0x1b,
op_sgr = 0xb909,
op_s = 0x5b,
op1_sy = 0xe3, op2_sy = 0x5b,
op1_sg = 0xe3, op2_sg = 0x09,
op_nr = 0x14,
op_ngr = 0xb980,
op_la = 0x41,
op1_lay = 0xe3, op2_lay = 0x71,
op1_larl = 0xc0, op2_larl = 0x00,
op_basr = 0x0d,
op_bas = 0x4d,
op_bcr = 0x07,
op_bc = 0x0d,
op1_bras = 0xa7, op2_bras = 0x05,
op1_brasl= 0xc0, op2_brasl= 0x05,
op1_brc = 0xa7, op2_brc = 0x04,
op1_brcl = 0xc0, op2_brcl = 0x04,
};
#define S390_MAX_INSTR_SIZE 6
static int
s390_readinstruction (bfd_byte instr[], CORE_ADDR at)
{
static int s390_instrlen[] = { 2, 4, 4, 6 };
int instrlen;
if (deprecated_read_memory_nobpt (at, &instr[0], 2))
return -1;
instrlen = s390_instrlen[instr[0] >> 6];
if (instrlen > 2)
{
if (deprecated_read_memory_nobpt (at + 2, &instr[2], instrlen - 2))
return -1;
}
return instrlen;
}
static int
is_ri (bfd_byte *insn, int op1, int op2, unsigned int *r1, int *i2)
{
if (insn[0] == op1 && (insn[1] & 0xf) == op2)
{
*r1 = (insn[1] >> 4) & 0xf;
*i2 = (((insn[2] << 8) | insn[3]) ^ 0x8000) - 0x8000;
return 1;
}
else
return 0;
}
static int
is_ril (bfd_byte *insn, int op1, int op2,
unsigned int *r1, int *i2)
{
if (insn[0] == op1 && (insn[1] & 0xf) == op2)
{
*r1 = (insn[1] >> 4) & 0xf;
*i2 = (((insn[2] << 24)
| (insn[3] << 16)
| (insn[4] << 8)
| (insn[5])) ^ 0x80000000) - 0x80000000;
return 1;
}
else
return 0;
}
static int
is_rr (bfd_byte *insn, int op, unsigned int *r1, unsigned int *r2)
{
if (insn[0] == op)
{
*r1 = (insn[1] >> 4) & 0xf;
*r2 = insn[1] & 0xf;
return 1;
}
else
return 0;
}
static int
is_rre (bfd_byte *insn, int op, unsigned int *r1, unsigned int *r2)
{
if (((insn[0] << 8) | insn[1]) == op)
{
*r1 = (insn[3] >> 4) & 0xf;
*r2 = insn[3] & 0xf;
return 1;
}
else
return 0;
}
static int
is_rs (bfd_byte *insn, int op,
unsigned int *r1, unsigned int *r3, unsigned int *d2, unsigned int *b2)
{
if (insn[0] == op)
{
*r1 = (insn[1] >> 4) & 0xf;
*r3 = insn[1] & 0xf;
*b2 = (insn[2] >> 4) & 0xf;
*d2 = ((insn[2] & 0xf) << 8) | insn[3];
return 1;
}
else
return 0;
}
static int
is_rsy (bfd_byte *insn, int op1, int op2,
unsigned int *r1, unsigned int *r3, unsigned int *d2, unsigned int *b2)
{
if (insn[0] == op1
&& insn[5] == op2)
{
*r1 = (insn[1] >> 4) & 0xf;
*r3 = insn[1] & 0xf;
*b2 = (insn[2] >> 4) & 0xf;
*d2 = ((((insn[2] & 0xf) << 8) | insn[3] | (insn[4] << 12))
^ 0x80000) - 0x80000;
return 1;
}
else
return 0;
}
static int
is_rx (bfd_byte *insn, int op,
unsigned int *r1, unsigned int *d2, unsigned int *x2, unsigned int *b2)
{
if (insn[0] == op)
{
*r1 = (insn[1] >> 4) & 0xf;
*x2 = insn[1] & 0xf;
*b2 = (insn[2] >> 4) & 0xf;
*d2 = ((insn[2] & 0xf) << 8) | insn[3];
return 1;
}
else
return 0;
}
static int
is_rxy (bfd_byte *insn, int op1, int op2,
unsigned int *r1, unsigned int *d2, unsigned int *x2, unsigned int *b2)
{
if (insn[0] == op1
&& insn[5] == op2)
{
*r1 = (insn[1] >> 4) & 0xf;
*x2 = insn[1] & 0xf;
*b2 = (insn[2] >> 4) & 0xf;
*d2 = ((((insn[2] & 0xf) << 8) | insn[3] | (insn[4] << 12))
^ 0x80000) - 0x80000;
return 1;
}
else
return 0;
}
static void
compute_x_addr (struct prologue_value *addr,
struct prologue_value *gpr,
int d2, unsigned int x2, unsigned int b2)
{
struct prologue_value result;
pv_set_to_constant (&result, d2);
if (x2)
pv_add (&result, &result, &gpr[x2]);
if (b2)
pv_add (&result, &result, &gpr[b2]);
*addr = result;
}
#define S390_NUM_GPRS 16
#define S390_NUM_FPRS 16
struct s390_prologue_data {
int gpr_size;
int fpr_size;
struct prologue_value gpr[S390_NUM_GPRS];
struct prologue_value fpr[S390_NUM_FPRS];
int gpr_slot[S390_NUM_GPRS];
int fpr_slot[S390_NUM_FPRS];
int back_chain_saved_p;
};
static void
s390_store (struct prologue_value *addr,
CORE_ADDR size,
struct prologue_value *value,
struct s390_prologue_data *data)
{
struct prologue_value cfa, offset;
int i;
pv_subtract (&offset, &data->gpr[S390_SP_REGNUM - S390_R0_REGNUM], addr);
if (offset.kind == pv_constant && offset.k == 0)
if (size == data->gpr_size
&& pv_is_register (value, S390_SP_REGNUM, 0))
{
data->back_chain_saved_p = 1;
return;
}
pv_set_to_register (&cfa, S390_SP_REGNUM, 16 * data->gpr_size + 32);
pv_subtract (&offset, &cfa, addr);
if (offset.kind == pv_constant
&& offset.k < INT_MAX && offset.k > 0
&& offset.k % data->gpr_size == 0)
{
for (i = 0; i < S390_NUM_GPRS; i++)
if (size == data->gpr_size
&& pv_is_register (value, S390_R0_REGNUM + i, 0))
if (data->gpr_slot[i] == 0
|| data->gpr_slot[i] > offset.k)
{
data->gpr_slot[i] = offset.k;
return;
}
for (i = 0; i < S390_NUM_FPRS; i++)
if (size == data->fpr_size
&& pv_is_register (value, S390_F0_REGNUM + i, 0))
if (data->fpr_slot[i] == 0
|| data->fpr_slot[i] > offset.k)
{
data->fpr_slot[i] = offset.k;
return;
}
}
}
static void
s390_load (struct prologue_value *addr,
CORE_ADDR size,
struct prologue_value *value,
struct s390_prologue_data *data)
{
struct prologue_value cfa, offset;
int i;
if (addr->kind == pv_constant)
{
struct section_table *secp;
secp = target_section_by_addr (¤t_target, addr->k);
if (secp != NULL
&& (bfd_get_section_flags (secp->bfd, secp->the_bfd_section)
& SEC_READONLY))
{
pv_set_to_constant (value, read_memory_integer (addr->k, size));
return;
}
}
pv_set_to_register (&cfa, S390_SP_REGNUM, 16 * data->gpr_size + 32);
pv_subtract (&offset, &cfa, addr);
if (offset.kind == pv_constant
&& offset.k < INT_MAX && offset.k > 0)
{
for (i = 0; i < S390_NUM_GPRS; i++)
if (offset.k == data->gpr_slot[i])
{
pv_set_to_register (value, S390_R0_REGNUM + i, 0);
return;
}
for (i = 0; i < S390_NUM_FPRS; i++)
if (offset.k == data->fpr_slot[i])
{
pv_set_to_register (value, S390_F0_REGNUM + i, 0);
return;
}
}
pv_set_to_unknown (value);
}
static CORE_ADDR
s390_analyze_prologue (struct gdbarch *gdbarch,
CORE_ADDR start_pc,
CORE_ADDR current_pc,
struct s390_prologue_data *data)
{
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
CORE_ADDR result = start_pc;
CORE_ADDR pc;
CORE_ADDR next_pc;
{
int i;
data->gpr_size = word_size;
data->fpr_size = 8;
for (i = 0; i < S390_NUM_GPRS; i++)
pv_set_to_register (&data->gpr[i], S390_R0_REGNUM + i, 0);
for (i = 0; i < S390_NUM_FPRS; i++)
pv_set_to_register (&data->fpr[i], S390_F0_REGNUM + i, 0);
for (i = 0; i < S390_NUM_GPRS; i++)
data->gpr_slot[i] = 0;
for (i = 0; i < S390_NUM_FPRS; i++)
data->fpr_slot[i] = 0;
data->back_chain_saved_p = 0;
}
for (pc = start_pc; pc > 0 && pc < current_pc; pc = next_pc)
{
bfd_byte insn[S390_MAX_INSTR_SIZE];
int insn_len = s390_readinstruction (insn, pc);
unsigned int b2, r1, r2, x2, r3;
int i2, d2;
struct prologue_value pre_insn_sp, pre_insn_fp;
int pre_insn_back_chain_saved_p;
if (insn_len < 0)
{
result = 0;
break;
}
next_pc = pc + insn_len;
pre_insn_sp = data->gpr[S390_SP_REGNUM - S390_R0_REGNUM];
pre_insn_fp = data->gpr[S390_FRAME_REGNUM - S390_R0_REGNUM];
pre_insn_back_chain_saved_p = data->back_chain_saved_p;
if (word_size == 4
&& is_ri (insn, op1_lhi, op2_lhi, &r1, &i2))
pv_set_to_constant (&data->gpr[r1], i2);
else if (word_size == 8
&& is_ri (insn, op1_lghi, op2_lghi, &r1, &i2))
pv_set_to_constant (&data->gpr[r1], i2);
else if (word_size == 4
&& is_rr (insn, op_lr, &r1, &r2))
data->gpr[r1] = data->gpr[r2];
else if (word_size == 8
&& is_rre (insn, op_lgr, &r1, &r2))
data->gpr[r1] = data->gpr[r2];
else if (word_size == 4
&& is_rx (insn, op_l, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_load (&addr, 4, &data->gpr[r1], data);
}
else if (word_size == 4
&& is_rxy (insn, op1_ly, op2_ly, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_load (&addr, 4, &data->gpr[r1], data);
}
else if (word_size == 8
&& is_rxy (insn, op1_lg, op2_lg, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_load (&addr, 8, &data->gpr[r1], data);
}
else if (word_size == 4
&& is_rx (insn, op_st, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_store (&addr, 4, &data->gpr[r1], data);
}
else if (word_size == 4
&& is_rxy (insn, op1_sty, op2_sty, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_store (&addr, 4, &data->gpr[r1], data);
}
else if (word_size == 8
&& is_rxy (insn, op1_stg, op2_stg, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_store (&addr, 8, &data->gpr[r1], data);
}
else if (is_rx (insn, op_std, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_store (&addr, 8, &data->fpr[r1], data);
}
else if (word_size == 4
&& is_rs (insn, op_stm, &r1, &r3, &d2, &b2))
{
int regnum;
int offset;
struct prologue_value addr;
for (regnum = r1, offset = 0;
regnum <= r3;
regnum++, offset += 4)
{
compute_x_addr (&addr, data->gpr, d2 + offset, 0, b2);
s390_store (&addr, 4, &data->gpr[regnum], data);
}
}
else if (word_size == 4
&& is_rsy (insn, op1_stmy, op2_stmy, &r1, &r3, &d2, &b2))
{
int regnum;
int offset;
struct prologue_value addr;
for (regnum = r1, offset = 0;
regnum <= r3;
regnum++, offset += 4)
{
compute_x_addr (&addr, data->gpr, d2 + offset, 0, b2);
s390_store (&addr, 4, &data->gpr[regnum], data);
}
}
else if (word_size == 8
&& is_rsy (insn, op1_stmg, op2_stmg, &r1, &r3, &d2, &b2))
{
int regnum;
int offset;
struct prologue_value addr;
for (regnum = r1, offset = 0;
regnum <= r3;
regnum++, offset += 8)
{
compute_x_addr (&addr, data->gpr, d2 + offset, 0, b2);
s390_store (&addr, 8, &data->gpr[regnum], data);
}
}
else if (word_size == 4
&& is_ri (insn, op1_ahi, op2_ahi, &r1, &i2))
pv_add_constant (&data->gpr[r1], i2);
else if (word_size == 8
&& is_ri (insn, op1_aghi, op2_aghi, &r1, &i2))
pv_add_constant (&data->gpr[r1], i2);
else if (word_size == 4
&& is_rr (insn, op_ar, &r1, &r2))
pv_add (&data->gpr[r1], &data->gpr[r1], &data->gpr[r2]);
else if (word_size == 8
&& is_rre (insn, op_agr, &r1, &r2))
pv_add (&data->gpr[r1], &data->gpr[r1], &data->gpr[r2]);
else if (word_size == 4
&& is_rx (insn, op_a, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
struct prologue_value value;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_load (&addr, 4, &value, data);
pv_add (&data->gpr[r1], &data->gpr[r1], &value);
}
else if (word_size == 4
&& is_rxy (insn, op1_ay, op2_ay, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
struct prologue_value value;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_load (&addr, 4, &value, data);
pv_add (&data->gpr[r1], &data->gpr[r1], &value);
}
else if (word_size == 8
&& is_rxy (insn, op1_ag, op2_ag, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
struct prologue_value value;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_load (&addr, 8, &value, data);
pv_add (&data->gpr[r1], &data->gpr[r1], &value);
}
else if (word_size == 4
&& is_rr (insn, op_sr, &r1, &r2))
pv_subtract (&data->gpr[r1], &data->gpr[r1], &data->gpr[r2]);
else if (word_size == 8
&& is_rre (insn, op_sgr, &r1, &r2))
pv_subtract (&data->gpr[r1], &data->gpr[r1], &data->gpr[r2]);
else if (word_size == 4
&& is_rx (insn, op_s, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
struct prologue_value value;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_load (&addr, 4, &value, data);
pv_subtract (&data->gpr[r1], &data->gpr[r1], &value);
}
else if (word_size == 4
&& is_rxy (insn, op1_sy, op2_sy, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
struct prologue_value value;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_load (&addr, 4, &value, data);
pv_subtract (&data->gpr[r1], &data->gpr[r1], &value);
}
else if (word_size == 8
&& is_rxy (insn, op1_sg, op2_sg, &r1, &d2, &x2, &b2))
{
struct prologue_value addr;
struct prologue_value value;
compute_x_addr (&addr, data->gpr, d2, x2, b2);
s390_load (&addr, 8, &value, data);
pv_subtract (&data->gpr[r1], &data->gpr[r1], &value);
}
else if (word_size == 4
&& is_rr (insn, op_nr, &r1, &r2))
pv_logical_and (&data->gpr[r1], &data->gpr[r1], &data->gpr[r2]);
else if (word_size == 8
&& is_rre (insn, op_ngr, &r1, &r2))
pv_logical_and (&data->gpr[r1], &data->gpr[r1], &data->gpr[r2]);
else if (is_rx (insn, op_la, &r1, &d2, &x2, &b2))
compute_x_addr (&data->gpr[r1], data->gpr, d2, x2, b2);
else if (is_rxy (insn, op1_lay, op2_lay, &r1, &d2, &x2, &b2))
compute_x_addr (&data->gpr[r1], data->gpr, d2, x2, b2);
else if (is_ril (insn, op1_larl, op2_larl, &r1, &i2))
pv_set_to_constant (&data->gpr[r1], pc + i2 * 2);
else if (is_rr (insn, op_basr, &r1, &r2)
&& r2 == 0)
pv_set_to_constant (&data->gpr[r1], next_pc);
else if (is_ri (insn, op1_bras, op2_bras, &r1, &i2))
{
pv_set_to_constant (&data->gpr[r1], next_pc);
next_pc = pc + i2 * 2;
if (next_pc <= pc)
break;
}
else if (is_rr (insn, op_basr, &r1, &r2)
|| is_rx (insn, op_bas, &r1, &d2, &x2, &b2)
|| is_rr (insn, op_bcr, &r1, &r2)
|| is_rx (insn, op_bc, &r1, &d2, &x2, &b2)
|| is_ri (insn, op1_brc, op2_brc, &r1, &i2)
|| is_ril (insn, op1_brcl, op2_brcl, &r1, &i2)
|| is_ril (insn, op1_brasl, op2_brasl, &r2, &i2))
break;
else
;
{
struct prologue_value *sp = &data->gpr[S390_SP_REGNUM - S390_R0_REGNUM];
struct prologue_value *fp = &data->gpr[S390_FRAME_REGNUM - S390_R0_REGNUM];
if ((! pv_is_identical (&pre_insn_sp, sp)
&& ! pv_is_register (sp, S390_SP_REGNUM, 0))
|| (! pv_is_identical (&pre_insn_fp, fp)
&& ! pv_is_register (fp, S390_FRAME_REGNUM, 0))
|| pre_insn_back_chain_saved_p != data->back_chain_saved_p)
result = next_pc;
}
}
return result;
}
static CORE_ADDR
s390_skip_prologue (CORE_ADDR pc)
{
struct s390_prologue_data data;
CORE_ADDR skip_pc;
skip_pc = s390_analyze_prologue (current_gdbarch, pc, (CORE_ADDR)-1, &data);
return skip_pc ? skip_pc : pc;
}
static int
s390_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
{
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
bfd_byte insn[6];
unsigned int r1, r3, b2;
int d2;
if (word_size == 4
&& !deprecated_read_memory_nobpt (pc - 4, insn, 4)
&& is_rs (insn, op_lm, &r1, &r3, &d2, &b2)
&& r3 == S390_SP_REGNUM - S390_R0_REGNUM)
return 1;
if (word_size == 4
&& !deprecated_read_memory_nobpt (pc - 6, insn, 6)
&& is_rsy (insn, op1_lmy, op2_lmy, &r1, &r3, &d2, &b2)
&& r3 == S390_SP_REGNUM - S390_R0_REGNUM)
return 1;
if (word_size == 8
&& !deprecated_read_memory_nobpt (pc - 6, insn, 6)
&& is_rsy (insn, op1_lmg, op2_lmg, &r1, &r3, &d2, &b2)
&& r3 == S390_SP_REGNUM - S390_R0_REGNUM)
return 1;
return 0;
}
struct s390_unwind_cache {
CORE_ADDR func;
CORE_ADDR frame_base;
CORE_ADDR local_base;
struct trad_frame_saved_reg *saved_regs;
};
static int
s390_prologue_frame_unwind_cache (struct frame_info *next_frame,
struct s390_unwind_cache *info)
{
struct gdbarch *gdbarch = get_frame_arch (next_frame);
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
struct s390_prologue_data data;
struct prologue_value *fp = &data.gpr[S390_FRAME_REGNUM - S390_R0_REGNUM];
struct prologue_value *sp = &data.gpr[S390_SP_REGNUM - S390_R0_REGNUM];
int i;
CORE_ADDR cfa;
CORE_ADDR func;
CORE_ADDR result;
ULONGEST reg;
CORE_ADDR prev_sp;
int frame_pointer;
int size;
func = frame_func_unwind (next_frame);
if (!func)
return 0;
result = s390_analyze_prologue (gdbarch, func,
frame_pc_unwind (next_frame), &data);
if (!result)
return 0;
if (sp->kind != pv_register || sp->reg != S390_SP_REGNUM)
return 0;
if (sp->k == 0)
{
if (get_frame_type (next_frame) == NORMAL_FRAME)
return 0;
reg = frame_unwind_register_unsigned (next_frame, S390_RETADDR_REGNUM);
reg = gdbarch_addr_bits_remove (gdbarch, reg) - 1;
if (get_pc_function_start (reg) == func)
{
struct s390_prologue_data data2;
struct prologue_value *sp = &data2.gpr[S390_SP_REGNUM - S390_R0_REGNUM];
if (!(s390_analyze_prologue (gdbarch, func, (CORE_ADDR)-1, &data2)
&& sp->kind == pv_register
&& sp->reg == S390_SP_REGNUM
&& sp->k != 0))
return 0;
}
}
size = -sp->k;
if (pv_is_identical (sp, fp))
frame_pointer = S390_FRAME_REGNUM;
else
frame_pointer = S390_SP_REGNUM;
if (size > 0 && get_frame_type (next_frame) != NORMAL_FRAME)
{
if (s390_in_function_epilogue_p (gdbarch, frame_pc_unwind (next_frame)))
{
memset (&data, 0, sizeof (data));
size = 0;
frame_pointer = S390_SP_REGNUM;
}
}
prev_sp = frame_unwind_register_unsigned (next_frame, frame_pointer) + size;
cfa = prev_sp + 16*word_size + 32;
for (i = 6; i <= 15; i++)
if (data.gpr_slot[i] != 0)
info->saved_regs[S390_R0_REGNUM + i].addr = cfa - data.gpr_slot[i];
switch (tdep->abi)
{
case ABI_LINUX_S390:
if (data.fpr_slot[4] != 0)
info->saved_regs[S390_F4_REGNUM].addr = cfa - data.fpr_slot[4];
if (data.fpr_slot[6] != 0)
info->saved_regs[S390_F6_REGNUM].addr = cfa - data.fpr_slot[6];
break;
case ABI_LINUX_ZSERIES:
for (i = 8; i <= 15; i++)
if (data.fpr_slot[i] != 0)
info->saved_regs[S390_F0_REGNUM + i].addr = cfa - data.fpr_slot[i];
break;
}
info->saved_regs[S390_PC_REGNUM] = info->saved_regs[S390_RETADDR_REGNUM];
if (size == 0
&& !trad_frame_addr_p (info->saved_regs, S390_PC_REGNUM))
{
info->saved_regs[S390_PC_REGNUM].realreg = S390_RETADDR_REGNUM;
}
if (size > 0)
{
if (!trad_frame_addr_p (info->saved_regs, S390_SP_REGNUM)
|| !trad_frame_addr_p (info->saved_regs, S390_PC_REGNUM))
prev_sp = -1;
}
if (prev_sp != -1)
{
info->frame_base = prev_sp + 16*word_size + 32;
info->local_base = prev_sp - size;
}
info->func = func;
return 1;
}
static void
s390_backchain_frame_unwind_cache (struct frame_info *next_frame,
struct s390_unwind_cache *info)
{
struct gdbarch *gdbarch = get_frame_arch (next_frame);
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
CORE_ADDR backchain;
ULONGEST reg;
LONGEST sp;
reg = frame_unwind_register_unsigned (next_frame, S390_SP_REGNUM);
backchain = read_memory_unsigned_integer (reg, word_size);
if (backchain != 0
&& safe_read_memory_integer (backchain + 15*word_size, word_size, &sp)
&& (CORE_ADDR)sp == backchain)
{
info->saved_regs[S390_SP_REGNUM].addr = backchain + 15*word_size;
info->saved_regs[S390_RETADDR_REGNUM].addr = backchain + 14*word_size;
info->saved_regs[S390_PC_REGNUM] = info->saved_regs[S390_RETADDR_REGNUM];
info->frame_base = backchain + 16*word_size + 32;
info->local_base = reg;
}
info->func = frame_pc_unwind (next_frame);
}
static struct s390_unwind_cache *
s390_frame_unwind_cache (struct frame_info *next_frame,
void **this_prologue_cache)
{
struct s390_unwind_cache *info;
if (*this_prologue_cache)
return *this_prologue_cache;
info = FRAME_OBSTACK_ZALLOC (struct s390_unwind_cache);
*this_prologue_cache = info;
info->saved_regs = trad_frame_alloc_saved_regs (next_frame);
info->func = -1;
info->frame_base = -1;
info->local_base = -1;
if (!s390_prologue_frame_unwind_cache (next_frame, info))
s390_backchain_frame_unwind_cache (next_frame, info);
return info;
}
static void
s390_frame_this_id (struct frame_info *next_frame,
void **this_prologue_cache,
struct frame_id *this_id)
{
struct s390_unwind_cache *info
= s390_frame_unwind_cache (next_frame, this_prologue_cache);
if (info->frame_base == -1)
return;
*this_id = frame_id_build (info->frame_base, info->func);
}
static void
s390_frame_prev_register (struct frame_info *next_frame,
void **this_prologue_cache,
int regnum, enum opt_state *optimizedp,
enum lval_type *lvalp, CORE_ADDR *addrp,
int *realnump, gdb_byte *bufferp)
{
struct s390_unwind_cache *info
= s390_frame_unwind_cache (next_frame, this_prologue_cache);
trad_frame_get_prev_register (next_frame, info->saved_regs, regnum,
optimizedp, lvalp, addrp, realnump, bufferp);
}
static const struct frame_unwind s390_frame_unwind = {
NORMAL_FRAME,
s390_frame_this_id,
s390_frame_prev_register
};
static const struct frame_unwind *
s390_frame_sniffer (struct frame_info *next_frame)
{
return &s390_frame_unwind;
}
struct s390_stub_unwind_cache
{
CORE_ADDR frame_base;
struct trad_frame_saved_reg *saved_regs;
};
static struct s390_stub_unwind_cache *
s390_stub_frame_unwind_cache (struct frame_info *next_frame,
void **this_prologue_cache)
{
struct gdbarch *gdbarch = get_frame_arch (next_frame);
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
struct s390_stub_unwind_cache *info;
ULONGEST reg;
if (*this_prologue_cache)
return *this_prologue_cache;
info = FRAME_OBSTACK_ZALLOC (struct s390_stub_unwind_cache);
*this_prologue_cache = info;
info->saved_regs = trad_frame_alloc_saved_regs (next_frame);
info->saved_regs[S390_PC_REGNUM].realreg = S390_RETADDR_REGNUM;
reg = frame_unwind_register_unsigned (next_frame, S390_SP_REGNUM);
info->frame_base = reg + 16*word_size + 32;
return info;
}
static void
s390_stub_frame_this_id (struct frame_info *next_frame,
void **this_prologue_cache,
struct frame_id *this_id)
{
struct s390_stub_unwind_cache *info
= s390_stub_frame_unwind_cache (next_frame, this_prologue_cache);
*this_id = frame_id_build (info->frame_base, frame_pc_unwind (next_frame));
}
static void
s390_stub_frame_prev_register (struct frame_info *next_frame,
void **this_prologue_cache,
int regnum, enum opt_state *optimizedp,
enum lval_type *lvalp, CORE_ADDR *addrp,
int *realnump, gdb_byte *bufferp)
{
struct s390_stub_unwind_cache *info
= s390_stub_frame_unwind_cache (next_frame, this_prologue_cache);
trad_frame_get_prev_register (next_frame, info->saved_regs, regnum,
optimizedp, lvalp, addrp, realnump, bufferp);
}
static const struct frame_unwind s390_stub_frame_unwind = {
NORMAL_FRAME,
s390_stub_frame_this_id,
s390_stub_frame_prev_register
};
static const struct frame_unwind *
s390_stub_frame_sniffer (struct frame_info *next_frame)
{
CORE_ADDR pc = frame_pc_unwind (next_frame);
bfd_byte insn[S390_MAX_INSTR_SIZE];
if (in_plt_section (pc, NULL)
|| s390_readinstruction (insn, pc) < 0)
return &s390_stub_frame_unwind;
return NULL;
}
struct s390_sigtramp_unwind_cache {
CORE_ADDR frame_base;
struct trad_frame_saved_reg *saved_regs;
};
static struct s390_sigtramp_unwind_cache *
s390_sigtramp_frame_unwind_cache (struct frame_info *next_frame,
void **this_prologue_cache)
{
struct gdbarch *gdbarch = get_frame_arch (next_frame);
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
struct s390_sigtramp_unwind_cache *info;
ULONGEST this_sp, prev_sp;
CORE_ADDR next_ra, next_cfa, sigreg_ptr;
int i;
if (*this_prologue_cache)
return *this_prologue_cache;
info = FRAME_OBSTACK_ZALLOC (struct s390_sigtramp_unwind_cache);
*this_prologue_cache = info;
info->saved_regs = trad_frame_alloc_saved_regs (next_frame);
this_sp = frame_unwind_register_unsigned (next_frame, S390_SP_REGNUM);
next_ra = frame_pc_unwind (next_frame);
next_cfa = this_sp + 16*word_size + 32;
if (next_ra == next_cfa)
{
sigreg_ptr = next_cfa + 8 + 128 + align_up (5*word_size, 8);
}
else
{
sigreg_ptr = read_memory_unsigned_integer (next_cfa + 8, word_size);
}
sigreg_ptr += word_size;
info->saved_regs[S390_PC_REGNUM].addr = sigreg_ptr;
sigreg_ptr += word_size;
for (i = 0; i < 16; i++)
{
info->saved_regs[S390_R0_REGNUM + i].addr = sigreg_ptr;
sigreg_ptr += word_size;
}
for (i = 0; i < 16; i++)
{
info->saved_regs[S390_A0_REGNUM + i].addr = sigreg_ptr;
sigreg_ptr += 4;
}
info->saved_regs[S390_FPC_REGNUM].addr = sigreg_ptr;
sigreg_ptr += 8;
for (i = 0; i < 16; i++)
{
info->saved_regs[S390_F0_REGNUM + i].addr = sigreg_ptr;
sigreg_ptr += 8;
}
prev_sp = read_memory_unsigned_integer (
info->saved_regs[S390_SP_REGNUM].addr,
word_size);
info->frame_base = prev_sp + 16*word_size + 32;
return info;
}
static void
s390_sigtramp_frame_this_id (struct frame_info *next_frame,
void **this_prologue_cache,
struct frame_id *this_id)
{
struct s390_sigtramp_unwind_cache *info
= s390_sigtramp_frame_unwind_cache (next_frame, this_prologue_cache);
*this_id = frame_id_build (info->frame_base, frame_pc_unwind (next_frame));
}
static void
s390_sigtramp_frame_prev_register (struct frame_info *next_frame,
void **this_prologue_cache,
int regnum, enum opt_state *optimizedp,
enum lval_type *lvalp, CORE_ADDR *addrp,
int *realnump, gdb_byte *bufferp)
{
struct s390_sigtramp_unwind_cache *info
= s390_sigtramp_frame_unwind_cache (next_frame, this_prologue_cache);
trad_frame_get_prev_register (next_frame, info->saved_regs, regnum,
optimizedp, lvalp, addrp, realnump, bufferp);
}
static const struct frame_unwind s390_sigtramp_frame_unwind = {
SIGTRAMP_FRAME,
s390_sigtramp_frame_this_id,
s390_sigtramp_frame_prev_register
};
static const struct frame_unwind *
s390_sigtramp_frame_sniffer (struct frame_info *next_frame)
{
CORE_ADDR pc = frame_pc_unwind (next_frame);
bfd_byte sigreturn[2];
if (deprecated_read_memory_nobpt (pc, sigreturn, 2))
return NULL;
if (sigreturn[0] != 0x0a )
return NULL;
if (sigreturn[1] != 119
&& sigreturn[1] != 173 )
return NULL;
return &s390_sigtramp_frame_unwind;
}
static CORE_ADDR
s390_frame_base_address (struct frame_info *next_frame, void **this_cache)
{
struct s390_unwind_cache *info
= s390_frame_unwind_cache (next_frame, this_cache);
return info->frame_base;
}
static CORE_ADDR
s390_local_base_address (struct frame_info *next_frame, void **this_cache)
{
struct s390_unwind_cache *info
= s390_frame_unwind_cache (next_frame, this_cache);
return info->local_base;
}
static const struct frame_base s390_frame_base = {
&s390_frame_unwind,
s390_frame_base_address,
s390_local_base_address,
s390_local_base_address
};
static CORE_ADDR
s390_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
{
ULONGEST pc;
pc = frame_unwind_register_unsigned (next_frame, S390_PC_REGNUM);
return gdbarch_addr_bits_remove (gdbarch, pc);
}
static CORE_ADDR
s390_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
{
ULONGEST sp;
sp = frame_unwind_register_unsigned (next_frame, S390_SP_REGNUM);
return gdbarch_addr_bits_remove (gdbarch, sp);
}
static void
s390_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
struct dwarf2_frame_state_reg *reg)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
switch (tdep->abi)
{
case ABI_LINUX_S390:
if ((regnum >= S390_R6_REGNUM && regnum <= S390_R15_REGNUM)
|| regnum == S390_F4_REGNUM
|| regnum == S390_F6_REGNUM)
reg->how = DWARF2_FRAME_REG_SAME_VALUE;
else if ((regnum >= S390_R0_REGNUM && regnum <= S390_R5_REGNUM)
|| (regnum >= S390_F0_REGNUM && regnum <= S390_F15_REGNUM
&& regnum != S390_F4_REGNUM && regnum != S390_F6_REGNUM))
reg->how = DWARF2_FRAME_REG_UNDEFINED;
else if (regnum == S390_PC_REGNUM)
reg->how = DWARF2_FRAME_REG_RA;
break;
case ABI_LINUX_ZSERIES:
if ((regnum >= S390_R6_REGNUM && regnum <= S390_R15_REGNUM)
|| (regnum >= S390_F8_REGNUM && regnum <= S390_F15_REGNUM))
reg->how = DWARF2_FRAME_REG_SAME_VALUE;
else if ((regnum >= S390_R0_REGNUM && regnum <= S390_R5_REGNUM)
|| (regnum >= S390_F0_REGNUM && regnum <= S390_F7_REGNUM))
reg->how = DWARF2_FRAME_REG_UNDEFINED;
else if (regnum == S390_PC_REGNUM)
reg->how = DWARF2_FRAME_REG_RA;
break;
}
}
static int
is_integer_like (struct type *type)
{
enum type_code code = TYPE_CODE (type);
return (code == TYPE_CODE_INT
|| code == TYPE_CODE_ENUM
|| code == TYPE_CODE_RANGE
|| code == TYPE_CODE_CHAR
|| code == TYPE_CODE_BOOL);
}
static int
is_pointer_like (struct type *type)
{
enum type_code code = TYPE_CODE (type);
return (code == TYPE_CODE_PTR
|| code == TYPE_CODE_REF);
}
static int
is_float_singleton (struct type *type)
{
if (TYPE_CODE (type) == TYPE_CODE_STRUCT && TYPE_NFIELDS (type) == 1)
{
struct type *singleton_type = TYPE_FIELD_TYPE (type, 0);
CHECK_TYPEDEF (singleton_type);
return (TYPE_CODE (singleton_type) == TYPE_CODE_FLT
|| is_float_singleton (singleton_type));
}
return 0;
}
static int
is_struct_like (struct type *type)
{
enum type_code code = TYPE_CODE (type);
return (code == TYPE_CODE_UNION
|| (code == TYPE_CODE_STRUCT && ! is_float_singleton (type)));
}
static int
is_float_like (struct type *type)
{
return (TYPE_CODE (type) == TYPE_CODE_FLT
|| is_float_singleton (type));
}
static int
is_power_of_two (unsigned int n)
{
return ((n & (n - 1)) == 0);
}
static int
s390_function_arg_pass_by_reference (struct type *type)
{
unsigned length = TYPE_LENGTH (type);
if (length > 8)
return 1;
return is_struct_like (type) && !is_power_of_two (length);
}
static int
s390_function_arg_float (struct type *type)
{
unsigned length = TYPE_LENGTH (type);
if (length > 8)
return 0;
return is_float_like (type);
}
static int
s390_function_arg_integer (struct type *type)
{
unsigned length = TYPE_LENGTH (type);
if (length > 8)
return 0;
return is_integer_like (type)
|| is_pointer_like (type)
|| (is_struct_like (type) && is_power_of_two (length));
}
static LONGEST
extend_simple_arg (struct value *arg)
{
struct type *type = value_type (arg);
if (TYPE_UNSIGNED (type))
return extract_unsigned_integer (value_contents (arg),
TYPE_LENGTH (type));
else
return extract_signed_integer (value_contents (arg),
TYPE_LENGTH (type));
}
static int
alignment_of (struct type *type)
{
int alignment;
if (is_integer_like (type)
|| is_pointer_like (type)
|| TYPE_CODE (type) == TYPE_CODE_FLT)
alignment = TYPE_LENGTH (type);
else if (TYPE_CODE (type) == TYPE_CODE_STRUCT
|| TYPE_CODE (type) == TYPE_CODE_UNION)
{
int i;
alignment = 1;
for (i = 0; i < TYPE_NFIELDS (type); i++)
{
int field_alignment = alignment_of (TYPE_FIELD_TYPE (type, i));
if (field_alignment > alignment)
alignment = field_alignment;
}
}
else
alignment = 1;
gdb_assert ((alignment & (alignment - 1)) == 0);
return alignment;
}
static CORE_ADDR
s390_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
struct regcache *regcache, CORE_ADDR bp_addr,
int nargs, struct value **args, CORE_ADDR sp,
int struct_return, CORE_ADDR struct_addr)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
ULONGEST orig_sp;
int i;
CORE_ADDR *copy_addr = alloca (nargs * sizeof (CORE_ADDR));
for (i = 0; i < nargs; i++)
{
struct value *arg = args[i];
struct type *type = 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;
}
}
sp -= nargs * 8;
sp = align_down (sp, 8);
{
int fr = 0;
int gr = 2;
CORE_ADDR starg = sp;
if (struct_return)
{
regcache_cooked_write_unsigned (regcache, S390_R0_REGNUM + gr,
struct_addr);
gr++;
}
for (i = 0; i < nargs; i++)
{
struct value *arg = args[i];
struct type *type = value_type (arg);
unsigned length = TYPE_LENGTH (type);
if (s390_function_arg_pass_by_reference (type))
{
if (gr <= 6)
{
regcache_cooked_write_unsigned (regcache, S390_R0_REGNUM + gr,
copy_addr[i]);
gr++;
}
else
{
write_memory_unsigned_integer (starg, word_size, copy_addr[i]);
starg += word_size;
}
}
else if (s390_function_arg_float (type))
{
if (fr <= (tdep->abi == ABI_LINUX_S390 ? 2 : 6))
{
regcache_cooked_write_part (regcache, S390_F0_REGNUM + fr,
0, length, value_contents (arg));
fr += 2;
}
else
{
starg = align_up (starg + length, word_size);
write_memory (starg - length, value_contents (arg), length);
}
}
else if (s390_function_arg_integer (type) && length <= word_size)
{
if (gr <= 6)
{
regcache_cooked_write_signed (regcache, S390_R0_REGNUM + gr,
extend_simple_arg (arg));
gr++;
}
else
{
write_memory_signed_integer (starg, word_size,
extend_simple_arg (arg));
starg += word_size;
}
}
else if (s390_function_arg_integer (type) && length == 2*word_size)
{
if (gr <= 5)
{
regcache_cooked_write (regcache, S390_R0_REGNUM + gr,
value_contents (arg));
regcache_cooked_write (regcache, S390_R0_REGNUM + gr + 1,
value_contents (arg) + word_size);
gr += 2;
}
else
{
gr = 7;
write_memory (starg, value_contents (arg), length);
starg += length;
}
}
else
internal_error (__FILE__, __LINE__, _("unknown argument type"));
}
}
sp -= 16*word_size + 32;
regcache_cooked_write_unsigned (regcache, S390_RETADDR_REGNUM, bp_addr);
regcache_cooked_write_unsigned (regcache, S390_SP_REGNUM, sp);
return sp + 16*word_size + 32;
}
static struct frame_id
s390_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
{
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
CORE_ADDR sp = s390_unwind_sp (gdbarch, next_frame);
return frame_id_build (sp + 16*word_size + 32,
frame_pc_unwind (next_frame));
}
static CORE_ADDR
s390_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr)
{
return (addr & -8);
}
static enum return_value_convention
s390_return_value_convention (struct gdbarch *gdbarch, struct type *type)
{
int length = TYPE_LENGTH (type);
if (length > 8)
return RETURN_VALUE_STRUCT_CONVENTION;
switch (TYPE_CODE (type))
{
case TYPE_CODE_STRUCT:
case TYPE_CODE_UNION:
case TYPE_CODE_ARRAY:
return RETURN_VALUE_STRUCT_CONVENTION;
default:
return RETURN_VALUE_REGISTER_CONVENTION;
}
}
static enum return_value_convention
s390_return_value (struct gdbarch *gdbarch, struct type *type,
struct regcache *regcache, gdb_byte *out,
const gdb_byte *in)
{
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
int length = TYPE_LENGTH (type);
enum return_value_convention rvc =
s390_return_value_convention (gdbarch, type);
if (in)
{
switch (rvc)
{
case RETURN_VALUE_REGISTER_CONVENTION:
if (TYPE_CODE (type) == TYPE_CODE_FLT)
{
regcache_cooked_write_part (regcache, S390_F0_REGNUM,
0, length, in);
}
else if (length <= word_size)
{
if (TYPE_UNSIGNED (type))
regcache_cooked_write_unsigned (regcache, S390_R2_REGNUM,
extract_unsigned_integer (in, length));
else
regcache_cooked_write_signed (regcache, S390_R2_REGNUM,
extract_signed_integer (in, length));
}
else if (length == 2*word_size)
{
regcache_cooked_write (regcache, S390_R2_REGNUM, in);
regcache_cooked_write (regcache, S390_R3_REGNUM, in + word_size);
}
else
internal_error (__FILE__, __LINE__, _("invalid return type"));
break;
case RETURN_VALUE_STRUCT_CONVENTION:
error (_("Cannot set function return value."));
break;
}
}
else if (out)
{
switch (rvc)
{
case RETURN_VALUE_REGISTER_CONVENTION:
if (TYPE_CODE (type) == TYPE_CODE_FLT)
{
regcache_cooked_read_part (regcache, S390_F0_REGNUM,
0, length, out);
}
else if (length <= word_size)
{
regcache_cooked_read_part (regcache, S390_R2_REGNUM,
word_size - length, length, out);
}
else if (length == 2*word_size)
{
regcache_cooked_read (regcache, S390_R2_REGNUM, out);
regcache_cooked_read (regcache, S390_R3_REGNUM, out + word_size);
}
else
internal_error (__FILE__, __LINE__, _("invalid return type"));
break;
case RETURN_VALUE_STRUCT_CONVENTION:
error (_("Function return value unknown."));
break;
}
}
return rvc;
}
static const gdb_byte *
s390_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
{
static const gdb_byte breakpoint[] = { 0x0, 0x1 };
*lenptr = sizeof (breakpoint);
return breakpoint;
}
static CORE_ADDR
s390_addr_bits_remove (CORE_ADDR addr)
{
return addr & 0x7fffffff;
}
static int
s390_address_class_type_flags (int byte_size, int dwarf2_addr_class)
{
if (byte_size == 4)
return TYPE_FLAG_ADDRESS_CLASS_1;
else
return 0;
}
static const char *
s390_address_class_type_flags_to_name (struct gdbarch *gdbarch, int type_flags)
{
if (type_flags & TYPE_FLAG_ADDRESS_CLASS_1)
return "mode32";
else
return NULL;
}
static int
s390_address_class_name_to_type_flags (struct gdbarch *gdbarch, const char *name,
int *type_flags_ptr)
{
if (strcmp (name, "mode32") == 0)
{
*type_flags_ptr = TYPE_FLAG_ADDRESS_CLASS_1;
return 1;
}
else
return 0;
}
static struct link_map_offsets *
s390_svr4_fetch_link_map_offsets (void)
{
static struct link_map_offsets lmo;
static struct link_map_offsets *lmp = NULL;
if (lmp == NULL)
{
lmp = &lmo;
lmo.r_debug_size = 8;
lmo.r_map_offset = 4;
lmo.r_map_size = 4;
lmo.link_map_size = 20;
lmo.l_addr_offset = 0;
lmo.l_addr_size = 4;
lmo.l_name_offset = 4;
lmo.l_name_size = 4;
lmo.l_next_offset = 12;
lmo.l_next_size = 4;
lmo.l_prev_offset = 16;
lmo.l_prev_size = 4;
}
return lmp;
}
static struct link_map_offsets *
s390x_svr4_fetch_link_map_offsets (void)
{
static struct link_map_offsets lmo;
static struct link_map_offsets *lmp = NULL;
if (lmp == NULL)
{
lmp = &lmo;
lmo.r_debug_size = 16;
lmo.r_map_offset = 8;
lmo.r_map_size = 8;
lmo.link_map_size = 40;
lmo.l_addr_offset = 0;
lmo.l_addr_size = 8;
lmo.l_name_offset = 8;
lmo.l_name_size = 8;
lmo.l_next_offset = 24;
lmo.l_next_size = 8;
lmo.l_prev_offset = 32;
lmo.l_prev_size = 8;
}
return lmp;
}
static struct gdbarch *
s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
{
struct gdbarch *gdbarch;
struct gdbarch_tdep *tdep;
arches = gdbarch_list_lookup_by_info (arches, &info);
if (arches != NULL)
return arches->gdbarch;
if (info.bfd_arch_info->arch != bfd_arch_s390)
return NULL;
tdep = XCALLOC (1, struct gdbarch_tdep);
gdbarch = gdbarch_alloc (&info, tdep);
set_gdbarch_believe_pcc_promotion (gdbarch, 0);
set_gdbarch_char_signed (gdbarch, 0);
set_gdbarch_decr_pc_after_break (gdbarch, 2);
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
set_gdbarch_breakpoint_from_pc (gdbarch, s390_breakpoint_from_pc);
set_gdbarch_skip_prologue (gdbarch, s390_skip_prologue);
set_gdbarch_in_function_epilogue_p (gdbarch, s390_in_function_epilogue_p);
set_gdbarch_pc_regnum (gdbarch, S390_PC_REGNUM);
set_gdbarch_sp_regnum (gdbarch, S390_SP_REGNUM);
set_gdbarch_fp0_regnum (gdbarch, S390_F0_REGNUM);
set_gdbarch_num_regs (gdbarch, S390_NUM_REGS);
set_gdbarch_num_pseudo_regs (gdbarch, S390_NUM_PSEUDO_REGS);
set_gdbarch_register_name (gdbarch, s390_register_name);
set_gdbarch_register_type (gdbarch, s390_register_type);
set_gdbarch_stab_reg_to_regnum (gdbarch, s390_dwarf_reg_to_regnum);
set_gdbarch_dwarf_reg_to_regnum (gdbarch, s390_dwarf_reg_to_regnum);
set_gdbarch_dwarf2_reg_to_regnum (gdbarch, s390_dwarf_reg_to_regnum);
set_gdbarch_convert_register_p (gdbarch, s390_convert_register_p);
set_gdbarch_register_to_value (gdbarch, s390_register_to_value);
set_gdbarch_value_to_register (gdbarch, s390_value_to_register);
set_gdbarch_register_reggroup_p (gdbarch, s390_register_reggroup_p);
set_gdbarch_regset_from_core_section (gdbarch,
s390_regset_from_core_section);
set_gdbarch_push_dummy_call (gdbarch, s390_push_dummy_call);
set_gdbarch_unwind_dummy_id (gdbarch, s390_unwind_dummy_id);
set_gdbarch_frame_align (gdbarch, s390_frame_align);
set_gdbarch_return_value (gdbarch, s390_return_value);
dwarf2_frame_set_init_reg (gdbarch, s390_dwarf2_frame_init_reg);
frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
frame_base_append_sniffer (gdbarch, dwarf2_frame_base_sniffer);
frame_unwind_append_sniffer (gdbarch, s390_stub_frame_sniffer);
frame_unwind_append_sniffer (gdbarch, s390_sigtramp_frame_sniffer);
frame_unwind_append_sniffer (gdbarch, s390_frame_sniffer);
frame_base_set_default (gdbarch, &s390_frame_base);
set_gdbarch_unwind_pc (gdbarch, s390_unwind_pc);
set_gdbarch_unwind_sp (gdbarch, s390_unwind_sp);
switch (info.bfd_arch_info->mach)
{
case bfd_mach_s390_31:
tdep->abi = ABI_LINUX_S390;
tdep->gregset = &s390_gregset;
tdep->sizeof_gregset = s390_sizeof_gregset;
tdep->fpregset = &s390_fpregset;
tdep->sizeof_fpregset = s390_sizeof_fpregset;
set_gdbarch_addr_bits_remove (gdbarch, s390_addr_bits_remove);
set_gdbarch_pseudo_register_read (gdbarch, s390_pseudo_register_read);
set_gdbarch_pseudo_register_write (gdbarch, s390_pseudo_register_write);
set_solib_svr4_fetch_link_map_offsets (gdbarch,
s390_svr4_fetch_link_map_offsets);
break;
case bfd_mach_s390_64:
tdep->abi = ABI_LINUX_ZSERIES;
tdep->gregset = &s390x_gregset;
tdep->sizeof_gregset = s390x_sizeof_gregset;
tdep->fpregset = &s390_fpregset;
tdep->sizeof_fpregset = s390_sizeof_fpregset;
set_gdbarch_long_bit (gdbarch, 64);
set_gdbarch_long_long_bit (gdbarch, 64);
set_gdbarch_ptr_bit (gdbarch, 64);
set_gdbarch_pseudo_register_read (gdbarch, s390x_pseudo_register_read);
set_gdbarch_pseudo_register_write (gdbarch, s390x_pseudo_register_write);
set_solib_svr4_fetch_link_map_offsets (gdbarch,
s390x_svr4_fetch_link_map_offsets);
set_gdbarch_address_class_type_flags (gdbarch,
s390_address_class_type_flags);
set_gdbarch_address_class_type_flags_to_name (gdbarch,
s390_address_class_type_flags_to_name);
set_gdbarch_address_class_name_to_type_flags (gdbarch,
s390_address_class_name_to_type_flags);
break;
}
set_gdbarch_print_insn (gdbarch, print_insn_s390);
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
return gdbarch;
}
extern initialize_file_ftype _initialize_s390_tdep;
void
_initialize_s390_tdep (void)
{
register_gdbarch_init (bfd_arch_s390, s390_gdbarch_init);
}