aboutsummaryrefslogtreecommitdiff
path: root/gdb/s390-tdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/s390-tdep.c')
-rw-r--r--gdb/s390-tdep.c306
1 files changed, 290 insertions, 16 deletions
diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c
index 6687127..8a2b405 100644
--- a/gdb/s390-tdep.c
+++ b/gdb/s390-tdep.c
@@ -1,6 +1,6 @@
/* Target-dependent code for s390.
- Copyright (C) 2001-2024 Free Software Foundation, Inc.
+ Copyright (C) 2001-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -40,6 +40,9 @@
#include "trad-frame.h"
#include "value.h"
#include "inferior.h"
+#include "dwarf2/loc.h"
+#include "gdbsupport/selftest.h"
+#include "gdb/disasm-selftests.h"
#include "features/s390-linux32.c"
#include "features/s390x-linux64.c"
@@ -854,6 +857,11 @@ s390_analyze_prologue (struct gdbarch *gdbarch,
|| is_rre (insn64, op_lgr, &r1, &r2))
data->gpr[r1] = data->gpr[r2];
+ /* LDGR r1, r2 --- load from register to floating-point register
+ (64-bit version). */
+ else if (is_rre (insn64, op_ldgr, &r1, &r2))
+ data->fpr[r1] = data->gpr[r2];
+
/* L r1, d2(x2, b2) --- load. */
/* LY r1, d2(x2, b2) --- load (long-displacement version). */
/* LG r1, d2(x2, b2) --- load (64-bit version). */
@@ -1254,6 +1262,28 @@ s390_value_from_register (gdbarch *gdbarch, type *type, int regnum,
return value;
}
+/* Implementation of the gdbarch_dwarf2_reg_piece_offset hook. */
+
+static ULONGEST
+s390_dwarf2_reg_piece_offset (gdbarch *gdbarch, int gdb_regnum, ULONGEST size)
+{
+ s390_gdbarch_tdep *tdep = gdbarch_tdep<s390_gdbarch_tdep> (gdbarch);
+
+ /* Floating point register. */
+ if (gdb_regnum >= S390_F0_REGNUM && gdb_regnum <= S390_F15_REGNUM)
+ return 0;
+
+ /* Vector register, v0 - v15. */
+ if (regnum_is_vxr_full (tdep, gdb_regnum))
+ return 0;
+
+ /* Vector register, v16 - v31. */
+ if (gdb_regnum >= S390_V16_REGNUM && gdb_regnum <= S390_V31_REGNUM)
+ return 0;
+
+ return default_dwarf2_reg_piece_offset (gdbarch, gdb_regnum, size);
+}
+
/* Implement pseudo_register_name tdesc method. */
static const char *
@@ -1897,7 +1927,7 @@ s390_handle_arg (struct s390_arg_state *as, struct value *arg,
for S/390 ELF Application Binary Interface Supplement".
SP is the current stack pointer. We must put arguments, links,
- padding, etc. whereever they belong, and return the new stack
+ padding, etc. wherever they belong, and return the new stack
pointer value.
If STRUCT_RETURN is non-zero, then the function we're calling is
@@ -2119,6 +2149,62 @@ s390_return_value (struct gdbarch *gdbarch, struct value *function,
return rvc;
}
+/* Try to get the value of DWARF_REG in FRAME at function entry. If successful,
+ return it as value of type VAL_TYPE. */
+
+static struct value *
+dwarf_reg_on_entry (int dwarf_reg, struct type *val_type,
+ const frame_info_ptr &frame)
+{
+ enum call_site_parameter_kind kind = CALL_SITE_PARAMETER_DWARF_REG;
+ union call_site_parameter_u kind_u = { .dwarf_reg = dwarf_reg };
+
+ try
+ {
+ return value_of_dwarf_reg_entry (val_type, frame, kind, kind_u);
+ }
+ catch (const gdb_exception_error &e)
+ {
+ if (e.error == NO_ENTRY_VALUE_ERROR)
+ return nullptr;
+
+ throw;
+ }
+}
+
+/* Both the 32-bit and 64-bit ABIs specify that values of some types are
+ returned in a storage buffer provided by the caller. Return the address of
+ that storage buffer, if possible. Implements the
+ gdbarch_get_return_buf_addr hook. */
+
+static CORE_ADDR
+s390_get_return_buf_addr (struct type *val_type,
+ const frame_info_ptr &cur_frame)
+{
+ /* The address of the storage buffer is provided as a hidden argument in
+ register r2. */
+ int dwarf_reg = 2;
+
+ /* The ABI does not guarantee that the register will not be changed while
+ executing the function. Hence, it cannot be assumed that it will still
+ contain the address of the storage buffer when execution reaches the end
+ of the function.
+
+ Attempt to determine the value on entry using the DW_OP_entry_value DWARF
+ entries. This requires compiling the user program with -fvar-tracking. */
+ struct value *val_on_entry
+ = dwarf_reg_on_entry (dwarf_reg, lookup_pointer_type (val_type), cur_frame);
+
+ if (val_on_entry == nullptr)
+ {
+ warning ("Cannot determine the function return value.\n"
+ "Try compiling with -fvar-tracking.");
+ return 0;
+ }
+
+ return value_as_address (val_on_entry);
+}
+
/* Frame unwinding. */
/* Implement the stack_frame_destroyed_p gdbarch method. */
@@ -2248,6 +2334,22 @@ s390_unwind_pseudo_register (const frame_info_ptr &this_frame, int regnum)
return value_cast (type, val);
}
+ if (regnum_is_vxr_full (tdep, regnum))
+ {
+ struct value *val = value::allocate_register (this_frame, regnum);
+
+ int reg = regnum - tdep->v0_full_regnum;
+ struct value *val1
+ = frame_unwind_register_value (this_frame, S390_F0_REGNUM + reg);
+ struct value *val2
+ = frame_unwind_register_value (this_frame, S390_V0_LOWER_REGNUM + reg);
+
+ val1->contents_copy (val, 0, 0, 8);
+ val2->contents_copy (val, 8, 0, 8);
+
+ return value_cast (type, val);
+ }
+
return value::allocate_optimized_out (type);
}
@@ -2388,7 +2490,7 @@ s390_prologue_frame_unwind_cache (const frame_info_ptr &this_frame,
size zero. This is only possible if the next frame is a sentinel
frame, a dummy frame, or a signal trampoline frame. */
/* FIXME: cagney/2004-05-01: This sanity check shouldn't be
- needed, instead the code should simpliy rely on its
+ needed, instead the code should simply rely on its
analysis. */
next_frame = get_next_frame (this_frame);
while (next_frame && get_frame_type (next_frame) == INLINE_FRAME)
@@ -2437,7 +2539,7 @@ s390_prologue_frame_unwind_cache (const frame_info_ptr &this_frame,
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. */
+ instead the code should simply rely on its analysis. */
next_frame = get_next_frame (this_frame);
while (next_frame && get_frame_type (next_frame) == INLINE_FRAME)
next_frame = get_next_frame (next_frame);
@@ -2475,16 +2577,50 @@ s390_prologue_frame_unwind_cache (const frame_info_ptr &this_frame,
ABI; for call-clobbered registers the parser may have recognized
spurious stores. */
- for (i = 0; i < 16; i++)
+ for (i = 0; i < S390_NUM_GPRS; i++)
if (s390_register_call_saved (gdbarch, S390_R0_REGNUM + i)
&& data.gpr_slot[i] != 0)
info->saved_regs[S390_R0_REGNUM + i].set_addr (cfa - data.gpr_slot[i]);
- for (i = 0; i < 16; i++)
+ for (i = 0; i < S390_NUM_FPRS; i++)
if (s390_register_call_saved (gdbarch, S390_F0_REGNUM + i)
&& data.fpr_slot[i] != 0)
info->saved_regs[S390_F0_REGNUM + i].set_addr (cfa - data.fpr_slot[i]);
+ /* Handle this type of prologue:
+ ldgr %f2,%r11
+ ldgr %f0,%r15
+ where call-clobbered floating point registers are used as register save
+ slots. */
+ for (i = 0; i < S390_NUM_FPRS; i++)
+ {
+ int fpr = S390_F0_REGNUM + i;
+
+ /* Check that fpr is a call-clobbered register. */
+ if (s390_register_call_saved (gdbarch, fpr))
+ continue;
+
+ /* Check that fpr contains the value of a register at function
+ entry. */
+ if (data.fpr[i].kind != pvk_register)
+ continue;
+
+ int entry_val_reg = data.fpr[i].reg;
+
+ /* Check that entry_val_reg is a call-saved register. */
+ if (!s390_register_call_saved (gdbarch, entry_val_reg))
+ continue;
+
+ /* In the prologue, we've copied:
+ - the value of a call-saved register (entry_val_reg) at function
+ entry, to
+ - a call-clobbered floating point register (fpr).
+
+ Heuristic: assume that makes the floating point register a register
+ save slot, leaving the value constant throughout the function. */
+ info->saved_regs[entry_val_reg].set_realreg (fpr);
+ }
+
/* Function return will set PC to %r14. */
info->saved_regs[S390_PSWA_REGNUM] = info->saved_regs[S390_RETADDR_REGNUM];
@@ -2646,15 +2782,16 @@ s390_frame_prev_register (const frame_info_ptr &this_frame,
/* Default S390 frame unwinder. */
-static const struct frame_unwind s390_frame_unwind = {
+static const struct frame_unwind_legacy s390_frame_unwind (
"s390 prologue",
NORMAL_FRAME,
+ FRAME_UNWIND_ARCH,
default_frame_unwind_stop_reason,
s390_frame_this_id,
s390_frame_prev_register,
NULL,
default_frame_sniffer
-};
+);
/* Code stubs and their stack frames. For things like PLTs and NULL
function calls (where there is no true frame and the return address
@@ -2740,15 +2877,16 @@ s390_stub_frame_sniffer (const struct frame_unwind *self,
/* S390 stub frame unwinder. */
-static const struct frame_unwind s390_stub_frame_unwind = {
+static const struct frame_unwind_legacy s390_stub_frame_unwind (
"s390 stub",
NORMAL_FRAME,
+ FRAME_UNWIND_ARCH,
default_frame_unwind_stop_reason,
s390_stub_frame_this_id,
s390_stub_frame_prev_register,
NULL,
s390_stub_frame_sniffer
-};
+);
/* Frame base handling. */
@@ -4014,6 +4152,13 @@ ex:
/* 0xb330-0xb335 undefined */
case 0xb33a: /* MAYR - multiply and add unnormalized */
+ /* float pair destination [RRD]; R1 may designate lower- or
+ higher-numbered register of pair */
+ if (record_full_arch_list_add_reg (regcache, S390_F0_REGNUM + (inib[4] & 13)))
+ return -1;
+ if (record_full_arch_list_add_reg (regcache, S390_F0_REGNUM + (inib[4] | 2)))
+ return -1;
+ break;
case 0xb33b: /* MYR - multiply unnormalized */
/* float pair destination [RRD] */
if (record_full_arch_list_add_reg (regcache, S390_F0_REGNUM + inib[4]))
@@ -4245,6 +4390,10 @@ ex:
case 0xb917: /* LLGTR - load logical thirty one bits */
case 0xb91c: /* MSGFR - multiply single 64<32 */
case 0xb946: /* BCTGR - branch on count */
+ case 0xb968: /* CLZG - count leading zeros */
+ case 0xb969: /* CTZG - count trailing zeros */
+ case 0xb96c: /* BEXTG - bit extract */
+ case 0xb96d: /* BDEPG - bit deposit */
case 0xb984: /* LLGCR - load logical character */
case 0xb985: /* LLGHR - load logical halfword */
case 0xb9e2: /* LOCGR - load on condition */
@@ -5125,7 +5274,14 @@ ex:
return -1;
break;
- /* 0xc86-0xc8f undefined */
+ case 0xc86: /* CAL - compare and load 32 */
+ case 0xc87: /* CALG - compare and load 64 */
+ case 0xc8f: /* CALGF - compare and load 64<32 */
+ if (s390_record_gpr_g (gdbarch, regcache, inib[2]))
+ return -1;
+ if (record_full_arch_list_add_reg (regcache, S390_PSWM_REGNUM))
+ return -1;
+ break;
default:
goto UNKNOWN_OP;
@@ -5336,6 +5492,16 @@ ex:
case 0xe33b: /* LZRF - load and zero rightmost byte */
case 0xe351: /* MSY - multiply single */
case 0xe358: /* LY - load */
+ case 0xe360: /* LXAB - load indexed address (shift 0) */
+ case 0xe361: /* LLXAB - load logical indexed address (shift 0) */
+ case 0xe362: /* LXAH - load indexed address (shift 1) */
+ case 0xe363: /* LLXAH - load logical indexed address (shift 1) */
+ case 0xe364: /* LXAF - load indexed address (shift 2) */
+ case 0xe365: /* LLXAF - load logical indexed address (shift 2) */
+ case 0xe366: /* LXAG - load indexed address (shift 3) */
+ case 0xe367: /* LLXAG - load logical indexed address (shift 3) */
+ case 0xe368: /* LXAQ - load indexed address (shift 4) */
+ case 0xe369: /* LLXAQ - load logical indexed address (shift 4) */
case 0xe371: /* LAY - load address */
case 0xe373: /* ICY - insert character */
case 0xe376: /* LB - load byte */
@@ -5448,7 +5614,7 @@ ex:
break;
/* 0xe35d undefined */
- /* 0xe360-0xe36f undefined */
+ /* 0xe36a-0xe36f undefined */
case 0xe372: /* STCY - store character */
case 0xe3c3: /* STCH - store character high */
@@ -5569,6 +5735,7 @@ ex:
case 0xe750: /* VPOPCT - vector population count */
case 0xe752: /* VCTZ - vector count trailing zeros */
case 0xe753: /* VCLZ - vector count leading zeros */
+ case 0xe754: /* VGEM - vector generate element masks */
case 0xe756: /* VLR - vector load */
case 0xe75f: /* VSEG -vector sign extend to doubleword */
case 0xe760: /* VMRL - vector merge low */
@@ -5602,6 +5769,8 @@ ex:
case 0xe785: /* VBPERM - vector bit permute */
case 0xe786: /* VSLD - vector shift left double by bit */
case 0xe787: /* VSRD - vector shift right double by bit */
+ case 0xe788: /* VEVAL - vector evaluate */
+ case 0xe789: /* VBLEND - vector blend */
case 0xe78b: /* VSTRS - vector string search */
case 0xe78c: /* VPERM - vector permute */
case 0xe78d: /* VSEL - vector select */
@@ -5624,6 +5793,10 @@ ex:
case 0xe7ad: /* VMALO - vector multiply and add logical odd */
case 0xe7ae: /* VMAE - vector multiply and add even */
case 0xe7af: /* VMAO - vector multiply and add odd */
+ case 0xe7b0: /* VDL - vector divide logical */
+ case 0xe7b1: /* VRL - vector remainder logical */
+ case 0xe7b2: /* VD - vector divide */
+ case 0xe7b3: /* VR - vector remainder */
case 0xe7b4: /* VGFM - vector Galois field multiply sum */
case 0xe7b8: /* VMSL - vector multiply sum logical */
case 0xe7b9: /* VACCC - vector add with carry compute carry */
@@ -5799,6 +5972,8 @@ ex:
/* 0xe747-0xe749 undefined */
+ case 0xe64a: /* VCVDQ - vector convert to decimal 128 bits */
+ case 0xe64e: /* VCVBQ - vector convert to binary 128 bits */
case 0xe651: /* VCLZDP - vector count leading zero digits */
case 0xe654: /* VUPKZH - vector unpack zoned high */
case 0xe658: /* VCVD - vector convert to decimal 32 bit */
@@ -5839,6 +6014,7 @@ ex:
break;
case 0xe65f: /* VTP - vector test decimal */
+ case 0xe67f: /* VTZ - vector test zoned */
/* flags + FPC */
if (record_full_arch_list_add_reg (regcache, S390_PSWM_REGNUM))
return -1;
@@ -5932,7 +6108,48 @@ ex:
return -1;
break;
- /* 0xeb15-0xeb1b undefined */
+ case 0xeb16: /* PFCR - perform functions with concurrent results */
+ if (record_full_arch_list_add_reg (regcache, S390_PSWM_REGNUM))
+ return -1;
+ regcache_raw_read_unsigned (regcache, S390_R0_REGNUM, &tmp);
+ oaddr = s390_record_calc_disp (gdbarch, regcache, 0, insn[1],
+ ibyte[4]);
+ {
+ uint8_t fc = tmp & 0xff;
+ if (fc == 0) /* PFCR-QAF */
+ {
+ if (record_full_arch_list_add_mem (oaddr, 16))
+ return -1;
+ }
+ else if (fc >= 1 && fc <= 4)
+ {
+ /* Compare and swap and double/triple store. */
+ int bytesize = fc & 1 ? 4 : 8;
+ int startbit = fc >= 3 ? 16 : 32;
+ if (record_full_arch_list_add_reg (regcache,
+ S390_R0_REGNUM + inib[2]))
+ return -1;
+ regcache_raw_read_unsigned (regcache,
+ S390_R0_REGNUM + inib[3], &tmp);
+ for (i = startbit; i < 64; i += 16)
+ {
+ oaddr = s390_record_calc_disp (gdbarch, regcache, 0,
+ (tmp >> i) & 0xffff, 0);
+ if (record_full_arch_list_add_mem (oaddr, bytesize))
+ return -1;
+ }
+ }
+ else
+ {
+ gdb_printf (gdb_stdlog,
+ "Warning: Unknown PFCR FC %02x at %s.\n",
+ fc, paddress (gdbarch, addr));
+ return -1;
+ }
+ }
+ break;
+
+ /* 0xeb17-0xeb1b undefined */
/* 0xeb1e-0xeb1f undefined */
/* 0xeb22 undefined */
@@ -6261,6 +6478,13 @@ ex:
/* 0xed36 undefined */
case 0xed3a: /* MAY - multiply and add unnormalized */
+ /* float pair destination [RXF]; R1 may designate lower- or
+ higher-numbered register of pair */
+ if (record_full_arch_list_add_reg (regcache, S390_F0_REGNUM + (inib[8] & 13)))
+ return -1;
+ if (record_full_arch_list_add_reg (regcache, S390_F0_REGNUM + (inib[8] | 2)))
+ return -1;
+ break;
case 0xed3b: /* MY - multiply unnormalized */
/* float pair destination [RXF] */
if (record_full_arch_list_add_reg (regcache, S390_F0_REGNUM + inib[8]))
@@ -7091,6 +7315,7 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
set_gdbarch_stab_reg_to_regnum (gdbarch, s390_dwarf_reg_to_regnum);
set_gdbarch_dwarf2_reg_to_regnum (gdbarch, s390_dwarf_reg_to_regnum);
set_gdbarch_value_from_register (gdbarch, s390_value_from_register);
+ set_gdbarch_dwarf2_reg_piece_offset (gdbarch, s390_dwarf2_reg_piece_offset);
/* Pseudo registers. */
set_gdbarch_pseudo_register_read (gdbarch, s390_pseudo_register_read);
@@ -7111,6 +7336,7 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
set_gdbarch_dummy_id (gdbarch, s390_dummy_id);
set_gdbarch_frame_align (gdbarch, s390_frame_align);
set_gdbarch_return_value (gdbarch, s390_return_value);
+ set_gdbarch_get_return_buf_addr (gdbarch, s390_get_return_buf_addr);
/* Frame handling. */
/* Stack grows downward. */
@@ -7244,13 +7470,61 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
return gdbarch;
}
-void _initialize_s390_tdep ();
-void
-_initialize_s390_tdep ()
+#if GDB_SELF_TEST
+namespace selftests {
+
+/* Return bfd_arch_info representing s390x. */
+
+static const bfd_arch_info *
+bfd_arch_info_s390x ()
+{
+ return bfd_lookup_arch (bfd_arch_s390, bfd_mach_s390_64);
+}
+
+/* Return gdbarch representing s390x. */
+
+static gdbarch *
+gdbarch_s390x ()
+{
+ struct gdbarch_info info;
+ info.bfd_arch_info = bfd_arch_info_s390x ();
+ if (info.bfd_arch_info == nullptr)
+ return nullptr;
+
+ info.osabi = GDB_OSABI_NONE;
+ return gdbarch_find_by_info (info);
+}
+
+/* Check disassembly of s390x instructions. */
+
+static void
+disassemble_s390x ()
+{
+ gdbarch *gdbarch = gdbarch_s390x ();
+ if (gdbarch == nullptr)
+ return;
+
+ scoped_restore disassembler_options_restore
+ = make_scoped_restore (&s390_disassembler_options, "zarch");
+
+ gdb::byte_vector insn = { 0xb9, 0x68, 0x00, 0x03 };
+ disassemble_insn (gdbarch, insn, "clzg\t%r0,%r3");
+}
+
+} /* namespace selftests */
+
+#endif /* GDB_SELF_TEST */
+
+INIT_GDB_FILE (s390_tdep)
{
/* Hook us into the gdbarch mechanism. */
gdbarch_register (bfd_arch_s390, s390_gdbarch_init);
initialize_tdesc_s390_linux32 ();
initialize_tdesc_s390x_linux64 ();
+
+#if GDB_SELF_TEST
+ selftests::register_test ("disassemble-s390x",
+ selftests::disassemble_s390x);
+#endif /* GDB_SELF_TEST */
}