#include "cli/cli-utils.h"
#include <ctype.h>
#include "elf/common.h"
+#include "elf/s390.h"
+#include "elf-bfd.h"
#include "features/s390-linux32.c"
#include "features/s390-linux32v1.c"
ABI_LINUX_ZSERIES
};
+enum s390_vector_abi_kind
+{
+ S390_VECTOR_ABI_NONE,
+ S390_VECTOR_ABI_128
+};
+
/* The tdep structure. */
struct gdbarch_tdep
/* ABI version. */
enum s390_abi_kind abi;
+ /* Vector ABI. */
+ enum s390_vector_abi_kind vector_abi;
+
/* Pseudo register numbers. */
int gpr_full_regnum;
int pc_regnum;
return skip_pc ? skip_pc : pc;
}
-/* Return true if we are in the functin's epilogue, i.e. after the
- instruction that destroyed the function's stack frame. */
+/* Implmement the stack_frame_destroyed_p gdbarch method. */
static int
-s390_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc)
+s390_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc)
{
int word_size = gdbarch_ptr_bit (gdbarch) / 8;
/* Displaced stepping. */
+/* Return true if INSN is a non-branch RIL-b or RIL-c format
+ instruction. */
+
+static int
+is_non_branch_ril (gdb_byte *insn)
+{
+ gdb_byte op1 = insn[0];
+
+ if (op1 == 0xc4)
+ {
+ gdb_byte op2 = insn[1] & 0x0f;
+
+ switch (op2)
+ {
+ case 0x02: /* llhrl */
+ case 0x04: /* lghrl */
+ case 0x05: /* lhrl */
+ case 0x06: /* llghrl */
+ case 0x07: /* sthrl */
+ case 0x08: /* lgrl */
+ case 0x0b: /* stgrl */
+ case 0x0c: /* lgfrl */
+ case 0x0d: /* lrl */
+ case 0x0e: /* llgfrl */
+ case 0x0f: /* strl */
+ return 1;
+ }
+ }
+ else if (op1 == 0xc6)
+ {
+ gdb_byte op2 = insn[1] & 0x0f;
+
+ switch (op2)
+ {
+ case 0x00: /* exrl */
+ case 0x02: /* pfdrl */
+ case 0x04: /* cghrl */
+ case 0x05: /* chrl */
+ case 0x06: /* clghrl */
+ case 0x07: /* clhrl */
+ case 0x08: /* cgrl */
+ case 0x0a: /* clgrl */
+ case 0x0c: /* cgfrl */
+ case 0x0d: /* crl */
+ case 0x0e: /* clgfrl */
+ case 0x0f: /* clrl */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Implementation of gdbarch_displaced_step_copy_insn. */
+
+static struct displaced_step_closure *
+s390_displaced_step_copy_insn (struct gdbarch *gdbarch,
+ CORE_ADDR from, CORE_ADDR to,
+ struct regcache *regs)
+{
+ size_t len = gdbarch_max_insn_length (gdbarch);
+ gdb_byte *buf = xmalloc (len);
+ struct cleanup *old_chain = make_cleanup (xfree, buf);
+
+ read_memory (from, buf, len);
+
+ /* Adjust the displacement field of PC-relative RIL instructions,
+ except branches. The latter are handled in the fixup hook. */
+ if (is_non_branch_ril (buf))
+ {
+ LONGEST offset;
+
+ offset = extract_signed_integer (buf + 2, 4, BFD_ENDIAN_BIG);
+ offset = (from - to + offset * 2) / 2;
+
+ /* If the instruction is too far from the jump pad, punt. This
+ will usually happen with instructions in shared libraries.
+ We could probably support these by rewriting them to be
+ absolute or fully emulating them. */
+ if (offset < INT32_MIN || offset > INT32_MAX)
+ {
+ /* Let the core fall back to stepping over the breakpoint
+ in-line. */
+ if (debug_displaced)
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: can't displaced step "
+ "RIL instruction: offset %s out of range\n",
+ plongest (offset));
+ }
+ do_cleanups (old_chain);
+ return NULL;
+ }
+
+ store_signed_integer (buf + 2, 4, BFD_ENDIAN_BIG, offset);
+ }
+
+ write_memory (to, buf, len);
+
+ if (debug_displaced)
+ {
+ fprintf_unfiltered (gdb_stdlog, "displaced: copy %s->%s: ",
+ paddress (gdbarch, from), paddress (gdbarch, to));
+ displaced_step_dump_bytes (gdb_stdlog, buf, len);
+ }
+
+ discard_cleanups (old_chain);
+ return (struct displaced_step_closure *) buf;
+}
+
/* Fix up the state of registers and memory after having single-stepped
a displaced instruction. */
static void
CORE_ADDR from, CORE_ADDR to,
struct regcache *regs)
{
- /* Since we use simple_displaced_step_copy_insn, our closure is a
- copy of the instruction. */
+ /* Our closure is a copy of the instruction. */
gdb_byte *insn = (gdb_byte *) closure;
static int s390_instrlen[] = { 2, 4, 4, 6 };
int insnlen = s390_instrlen[insn[0] >> 6];
&& (next_frame == NULL
|| get_frame_type (get_next_frame (this_frame)) != NORMAL_FRAME))
{
- /* See the comment in s390_in_function_epilogue_p on why this is
+ /* See the comment in s390_stack_frame_destroyed_p on why this is
not completely reliable ... */
- if (s390_in_function_epilogue_p (gdbarch, get_frame_pc (this_frame)))
+ if (s390_stack_frame_destroyed_p (gdbarch, get_frame_pc (this_frame)))
{
memset (&data, 0, sizeof (data));
size = 0;
float x;
struct { float x };
struct { struct { float x; } x; };
- struct { struct { struct { float x; } x; } x; }; */
+ struct { struct { struct { float x; } x; } x; };
+
+ However, if an inner type is smaller than MIN_SIZE, abort the
+ unwrapping. */
static struct type *
-s390_effective_inner_type (struct type *type)
+s390_effective_inner_type (struct type *type, unsigned int min_size)
{
while (TYPE_CODE (type) == TYPE_CODE_STRUCT
&& TYPE_NFIELDS (type) == 1)
- type = check_typedef (TYPE_FIELD_TYPE (type, 0));
+ {
+ struct type *inner = check_typedef (TYPE_FIELD_TYPE (type, 0));
+
+ if (TYPE_LENGTH (inner) < min_size)
+ break;
+ type = inner;
+ }
+
return type;
}
/* A struct containing just a float or double is passed like a float
or double. */
- type = s390_effective_inner_type (type);
+ type = s390_effective_inner_type (type, 0);
return (TYPE_CODE (type) == TYPE_CODE_FLT
|| TYPE_CODE (type) == TYPE_CODE_DECFLOAT);
}
+/* Return non-zero if TYPE should be passed like a vector. */
+
+static int
+s390_function_arg_vector (struct type *type)
+{
+ if (TYPE_LENGTH (type) > 16)
+ return 0;
+
+ /* Structs containing just a vector are passed like a vector. */
+ type = s390_effective_inner_type (type, TYPE_LENGTH (type));
+
+ return TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type);
+}
+
/* Determine whether N is a power of two. */
static int
}
/* For an argument whose type is TYPE and which is not passed like a
- float, return non-zero if it should be passed like "int" or "long
- long". */
+ float or vector, return non-zero if it should be passed like "int"
+ or "long long". */
static int
s390_function_arg_integer (struct type *type)
{
/* Register cache, or NULL, if we are in "preparation mode". */
struct regcache *regcache;
- /* Next available general/floating-point register for argument
- passing. */
- int gr, fr;
+ /* Next available general/floating-point/vector register for
+ argument passing. */
+ int gr, fr, vr;
/* Current pointer to copy area (grows downwards). */
CORE_ADDR copy;
/* Current pointer to parameter area (grows upwards). */
static void
s390_handle_arg (struct s390_arg_state *as, struct value *arg,
struct gdbarch_tdep *tdep, int word_size,
- enum bfd_endian byte_order)
+ enum bfd_endian byte_order, int is_unnamed)
{
struct type *type = check_typedef (value_type (arg));
unsigned int length = TYPE_LENGTH (type);
length);
}
}
+ else if (tdep->vector_abi == S390_VECTOR_ABI_128
+ && s390_function_arg_vector (type))
+ {
+ static const char use_vr[] = {24, 26, 28, 30, 25, 27, 29, 31};
+
+ if (!is_unnamed && as->vr < ARRAY_SIZE (use_vr))
+ {
+ int regnum = S390_V24_REGNUM + use_vr[as->vr] - 24;
+
+ if (write_mode)
+ regcache_cooked_write_part (as->regcache, regnum,
+ 0, length,
+ value_contents (arg));
+ as->vr++;
+ }
+ else
+ {
+ if (write_mode)
+ write_memory (as->argp, value_contents (arg), length);
+ as->argp = align_up (as->argp + length, word_size);
+ }
+ }
else if (s390_function_arg_integer (type) && length <= word_size)
{
- ULONGEST val;
+ /* Initialize it just to avoid a GCC false warning. */
+ ULONGEST val = 0;
if (write_mode)
{
int i;
struct s390_arg_state arg_state, arg_prep;
CORE_ADDR param_area_start, new_sp;
+ struct type *ftype = check_typedef (value_type (function));
+
+ if (TYPE_CODE (ftype) == TYPE_CODE_PTR)
+ ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
arg_prep.copy = sp;
arg_prep.gr = struct_return ? 3 : 2;
arg_prep.fr = 0;
+ arg_prep.vr = 0;
arg_prep.argp = 0;
arg_prep.regcache = NULL;
/* Update arg_state.copy with the start of the reference-to-copy area
and arg_state.argp with the size of the parameter area. */
for (i = 0; i < nargs; i++)
- s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order);
+ s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order,
+ TYPE_VARARGS (ftype) && i >= TYPE_NFIELDS (ftype));
param_area_start = align_down (arg_state.copy - arg_state.argp, 8);
/* Write all parameters. */
for (i = 0; i < nargs; i++)
- s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order);
+ s390_handle_arg (&arg_state, args[i], tdep, word_size, byte_order,
+ TYPE_VARARGS (ftype) && i >= TYPE_NFIELDS (ftype));
/* Store return PSWA. In 31-bit mode, keep addressing mode bit. */
if (word_size == 4)
}
-/* Function return value access. */
+/* Helper for s390_return_value: Set or retrieve a function return
+ value if it resides in a register. */
-static enum return_value_convention
-s390_return_value_convention (struct gdbarch *gdbarch, struct type *type)
+static void
+s390_register_return_value (struct gdbarch *gdbarch, struct type *type,
+ struct regcache *regcache,
+ gdb_byte *out, const gdb_byte *in)
{
- if (TYPE_LENGTH (type) > 8)
- return RETURN_VALUE_STRUCT_CONVENTION;
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ int word_size = gdbarch_ptr_bit (gdbarch) / 8;
+ int length = TYPE_LENGTH (type);
+ int code = TYPE_CODE (type);
- switch (TYPE_CODE (type))
+ if (code == TYPE_CODE_FLT || code == TYPE_CODE_DECFLOAT)
{
- case TYPE_CODE_STRUCT:
- case TYPE_CODE_UNION:
- case TYPE_CODE_ARRAY:
- case TYPE_CODE_COMPLEX:
- return RETURN_VALUE_STRUCT_CONVENTION;
-
- default:
- return RETURN_VALUE_REGISTER_CONVENTION;
+ /* Float-like value: left-aligned in f0. */
+ if (in != NULL)
+ regcache_cooked_write_part (regcache, S390_F0_REGNUM,
+ 0, length, in);
+ else
+ regcache_cooked_read_part (regcache, S390_F0_REGNUM,
+ 0, length, out);
}
+ else if (code == TYPE_CODE_ARRAY)
+ {
+ /* Vector: left-aligned in v24. */
+ if (in != NULL)
+ regcache_cooked_write_part (regcache, S390_V24_REGNUM,
+ 0, length, in);
+ else
+ regcache_cooked_read_part (regcache, S390_V24_REGNUM,
+ 0, length, out);
+ }
+ else if (length <= word_size)
+ {
+ /* Integer: zero- or sign-extended in r2. */
+ if (out != NULL)
+ regcache_cooked_read_part (regcache, S390_R2_REGNUM,
+ word_size - length, length, out);
+ else if (TYPE_UNSIGNED (type))
+ regcache_cooked_write_unsigned
+ (regcache, S390_R2_REGNUM,
+ extract_unsigned_integer (in, length, byte_order));
+ else
+ regcache_cooked_write_signed
+ (regcache, S390_R2_REGNUM,
+ extract_signed_integer (in, length, byte_order));
+ }
+ else if (length == 2 * word_size)
+ {
+ /* Double word: in r2 and r3. */
+ if (in != NULL)
+ {
+ regcache_cooked_write (regcache, S390_R2_REGNUM, in);
+ regcache_cooked_write (regcache, S390_R3_REGNUM,
+ in + word_size);
+ }
+ else
+ {
+ 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"));
}
+
+/* Implement the 'return_value' gdbarch method. */
+
static enum return_value_convention
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;
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 (TYPE_CODE (type))
{
- switch (rvc)
- {
- case RETURN_VALUE_REGISTER_CONVENTION:
- if (TYPE_CODE (type) == TYPE_CODE_FLT
- || TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
- {
- /* When we store a single-precision value in an FP register,
- it occupies the leftmost bits. */
- regcache_cooked_write_part (regcache, S390_F0_REGNUM,
- 0, length, in);
- }
- else if (length <= word_size)
- {
- /* Integer arguments are always extended to word size. */
- if (TYPE_UNSIGNED (type))
- regcache_cooked_write_unsigned (regcache, S390_R2_REGNUM,
- extract_unsigned_integer (in, length, byte_order));
- else
- regcache_cooked_write_signed (regcache, S390_R2_REGNUM,
- extract_signed_integer (in, length, byte_order));
- }
- 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;
- }
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ case TYPE_CODE_COMPLEX:
+ rvc = RETURN_VALUE_STRUCT_CONVENTION;
+ break;
+ case TYPE_CODE_ARRAY:
+ rvc = (gdbarch_tdep (gdbarch)->vector_abi == S390_VECTOR_ABI_128
+ && TYPE_LENGTH (type) <= 16 && TYPE_VECTOR (type))
+ ? RETURN_VALUE_REGISTER_CONVENTION
+ : RETURN_VALUE_STRUCT_CONVENTION;
+ break;
+ default:
+ rvc = TYPE_LENGTH (type) <= 8
+ ? RETURN_VALUE_REGISTER_CONVENTION
+ : RETURN_VALUE_STRUCT_CONVENTION;
}
- else if (out)
- {
- switch (rvc)
- {
- case RETURN_VALUE_REGISTER_CONVENTION:
- if (TYPE_CODE (type) == TYPE_CODE_FLT
- || TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
- {
- /* When we store a single-precision value in an FP register,
- it occupies the leftmost bits. */
- regcache_cooked_read_part (regcache, S390_F0_REGNUM,
- 0, length, out);
- }
- else if (length <= word_size)
- {
- /* Integer arguments occupy the rightmost bits. */
- 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;
- }
+ if (in != NULL || out != NULL)
+ {
+ if (rvc == RETURN_VALUE_REGISTER_CONVENTION)
+ s390_register_return_value (gdbarch, type, regcache, out, in);
+ else if (in != NULL)
+ error (_("Cannot set function return value."));
+ else
+ error (_("Function return value unknown."));
}
return rvc;
struct tdesc_arch_data *tdesc_data = NULL;
struct gdbarch *gdbarch;
struct gdbarch_tdep *tdep;
- int tdep_abi;
+ enum s390_abi_kind tdep_abi;
+ enum s390_vector_abi_kind vector_abi;
int have_upper = 0;
int have_linux_v1 = 0;
int have_linux_v2 = 0;
}
}
+ /* Determine vector ABI. */
+ vector_abi = S390_VECTOR_ABI_NONE;
+#ifdef HAVE_ELF
+ if (have_vx
+ && info.abfd != NULL
+ && info.abfd->format == bfd_object
+ && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour
+ && bfd_elf_get_obj_attr_int (info.abfd, OBJ_ATTR_GNU,
+ Tag_GNU_S390_ABI_Vector) == 2)
+ vector_abi = S390_VECTOR_ABI_128;
+#endif
+
/* Find a candidate among extant architectures. */
for (arches = gdbarch_list_lookup_by_info (arches, &info);
arches != NULL;
continue;
if (tdep->abi != tdep_abi)
continue;
+ if (tdep->vector_abi != vector_abi)
+ continue;
if ((tdep->gpr_full_regnum != -1) != have_upper)
continue;
if (tdesc_data != NULL)
/* Otherwise create a new gdbarch for the specified machine type. */
tdep = XCNEW (struct gdbarch_tdep);
tdep->abi = tdep_abi;
+ tdep->vector_abi = vector_abi;
tdep->have_linux_v1 = have_linux_v1;
tdep->have_linux_v2 = have_linux_v2;
tdep->have_tdb = have_tdb;
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_stack_frame_destroyed_p (gdbarch, s390_stack_frame_destroyed_p);
set_gdbarch_num_regs (gdbarch, S390_NUM_REGS);
set_gdbarch_sp_regnum (gdbarch, S390_SP_REGNUM);
/* Displaced stepping. */
set_gdbarch_displaced_step_copy_insn (gdbarch,
- simple_displaced_step_copy_insn);
+ s390_displaced_step_copy_insn);
set_gdbarch_displaced_step_fixup (gdbarch, s390_displaced_step_fixup);
set_gdbarch_displaced_step_free_closure (gdbarch,
simple_displaced_step_free_closure);
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_prefixes (gdbarch, stap_register_prefixes);
set_gdbarch_stap_register_indirection_prefixes (gdbarch,