X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Faarch64-linux-tdep.c;h=0d46acde5fad5c4804424602a407de9d8b8fc60d;hb=7bc02706c3e23b58e1a74ca47fada84fc68699c7;hp=7b63cddbe66ae0e22d89fb04a7365fb12f797e6a;hpb=a616bb94509c22c96ae8bf1432ec28a7f3e31778;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index 7b63cddbe6..0d46acde5f 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -176,12 +176,12 @@ static const struct tramp_frame aarch64_linux_rt_sigframe = { /* movz x8, 0x8b (S=1,o=10,h=0,i=0x8b,r=8) Soo1 0010 1hhi iiii iiii iiii iiir rrrr */ - {0xd2801168, -1}, + {0xd2801168, ULONGEST_MAX}, /* svc 0x0 (o=0, l=1) 1101 0100 oooi iiii iiii iiii iii0 00ll */ - {0xd4000001, -1}, - {TRAMP_SENTINEL_INSN, -1} + {0xd4000001, ULONGEST_MAX}, + {TRAMP_SENTINEL_INSN, ULONGEST_MAX} }, aarch64_linux_sigframe_init }; @@ -219,6 +219,180 @@ const struct regset aarch64_linux_fpregset = regcache_supply_regset, regcache_collect_regset }; +/* The fields in an SVE header at the start of a SVE regset. */ + +#define SVE_HEADER_SIZE_LENGTH 4 +#define SVE_HEADER_MAX_SIZE_LENGTH 4 +#define SVE_HEADER_VL_LENGTH 2 +#define SVE_HEADER_MAX_VL_LENGTH 2 +#define SVE_HEADER_FLAGS_LENGTH 2 +#define SVE_HEADER_RESERVED_LENGTH 2 + +#define SVE_HEADER_SIZE_OFFSET 0 +#define SVE_HEADER_MAX_SIZE_OFFSET \ + (SVE_HEADER_SIZE_OFFSET + SVE_HEADER_SIZE_LENGTH) +#define SVE_HEADER_VL_OFFSET \ + (SVE_HEADER_MAX_SIZE_OFFSET + SVE_HEADER_MAX_SIZE_LENGTH) +#define SVE_HEADER_MAX_VL_OFFSET \ + (SVE_HEADER_VL_OFFSET + SVE_HEADER_VL_LENGTH) +#define SVE_HEADER_FLAGS_OFFSET \ + (SVE_HEADER_MAX_VL_OFFSET + SVE_HEADER_MAX_VL_LENGTH) +#define SVE_HEADER_RESERVED_OFFSET \ + (SVE_HEADER_FLAGS_OFFSET + SVE_HEADER_FLAGS_LENGTH) +#define SVE_HEADER_SIZE \ + (SVE_HEADER_RESERVED_OFFSET + SVE_HEADER_RESERVED_LENGTH) + +#define SVE_HEADER_FLAG_SVE 1 + +/* Get VQ value from SVE section in the core dump. */ + +static uint64_t +aarch64_linux_core_read_vq (struct gdbarch *gdbarch, bfd *abfd) +{ + gdb_byte header[SVE_HEADER_SIZE]; + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + asection *sve_section = bfd_get_section_by_name (abfd, ".reg-aarch-sve"); + + if (sve_section == nullptr) + { + /* No SVE state. */ + return 0; + } + + size_t size = bfd_section_size (abfd, sve_section); + + /* Check extended state size. */ + if (size < SVE_HEADER_SIZE) + { + warning (_("'.reg-aarch-sve' section in core file too small.")); + return 0; + } + + if (!bfd_get_section_contents (abfd, sve_section, header, 0, SVE_HEADER_SIZE)) + { + warning (_("Couldn't read sve header from " + "'.reg-aarch-sve' section in core file.")); + return 0; + } + + uint64_t vl = extract_unsigned_integer (header + SVE_HEADER_VL_OFFSET, + SVE_HEADER_VL_LENGTH, byte_order); + uint64_t vq = sve_vq_from_vl (vl); + + if (vq > AARCH64_MAX_SVE_VQ) + { + warning (_("SVE Vector length in core file not supported by this version" + " of GDB. (VQ=%s)"), pulongest (vq)); + return 0; + } + else if (vq == 0) + { + warning (_("SVE Vector length in core file is invalid. (VQ=%s"), + pulongest (vq)); + return 0; + } + + return vq; +} + +/* Supply register REGNUM from BUF to REGCACHE, using the register map + in REGSET. If REGNUM is -1, do this for all registers in REGSET. + If BUF is NULL, set the registers to "unavailable" status. */ + +static void +aarch64_linux_supply_sve_regset (const struct regset *regset, + struct regcache *regcache, + int regnum, const void *buf, size_t size) +{ + gdb_byte *header = (gdb_byte *) buf; + struct gdbarch *gdbarch = regcache->arch (); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + + if (buf == nullptr) + return regcache->supply_regset (regset, regnum, nullptr, size); + gdb_assert (size > SVE_HEADER_SIZE); + + /* BUF contains an SVE header followed by a register dump of either the + passed in SVE regset or a NEON fpregset. */ + + /* Extract required fields from the header. */ + uint64_t vl = extract_unsigned_integer (header + SVE_HEADER_VL_OFFSET, + SVE_HEADER_VL_LENGTH, byte_order); + uint16_t flags = extract_unsigned_integer (header + SVE_HEADER_FLAGS_OFFSET, + SVE_HEADER_FLAGS_LENGTH, + byte_order); + + if (regnum == -1 || regnum == AARCH64_SVE_VG_REGNUM) + { + gdb_byte vg_target[8]; + store_integer ((gdb_byte *)&vg_target, sizeof (uint64_t), byte_order, + sve_vg_from_vl (vl)); + regcache->raw_supply (AARCH64_SVE_VG_REGNUM, &vg_target); + } + + if (flags & SVE_HEADER_FLAG_SVE) + { + /* Register dump is a SVE structure. */ + regcache->supply_regset (regset, regnum, + (gdb_byte *) buf + SVE_HEADER_SIZE, + size - SVE_HEADER_SIZE); + } + else + { + /* Register dump is a fpsimd structure. First clear the SVE + registers. */ + for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) + regcache->raw_supply_zeroed (AARCH64_SVE_Z0_REGNUM + i); + for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) + regcache->raw_supply_zeroed (AARCH64_SVE_P0_REGNUM + i); + regcache->raw_supply_zeroed (AARCH64_SVE_FFR_REGNUM); + + /* Then supply the fpsimd registers. */ + regcache->supply_regset (&aarch64_linux_fpregset, regnum, + (gdb_byte *) buf + SVE_HEADER_SIZE, + size - SVE_HEADER_SIZE); + } +} + +/* Collect register REGNUM from REGCACHE to BUF, using the register + map in REGSET. If REGNUM is -1, do this for all registers in + REGSET. */ + +static void +aarch64_linux_collect_sve_regset (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *buf, size_t size) +{ + gdb_byte *header = (gdb_byte *) buf; + struct gdbarch *gdbarch = regcache->arch (); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + uint64_t vq = gdbarch_tdep (gdbarch)->vq; + + gdb_assert (buf != NULL); + gdb_assert (size > SVE_HEADER_SIZE); + + /* BUF starts with a SVE header prior to the register dump. */ + + store_unsigned_integer (header + SVE_HEADER_SIZE_OFFSET, + SVE_HEADER_SIZE_LENGTH, byte_order, size); + store_unsigned_integer (header + SVE_HEADER_MAX_SIZE_OFFSET, + SVE_HEADER_MAX_SIZE_LENGTH, byte_order, size); + store_unsigned_integer (header + SVE_HEADER_VL_OFFSET, SVE_HEADER_VL_LENGTH, + byte_order, sve_vl_from_vq (vq)); + store_unsigned_integer (header + SVE_HEADER_MAX_VL_OFFSET, + SVE_HEADER_MAX_VL_LENGTH, byte_order, + sve_vl_from_vq (vq)); + store_unsigned_integer (header + SVE_HEADER_FLAGS_OFFSET, + SVE_HEADER_FLAGS_LENGTH, byte_order, + SVE_HEADER_FLAG_SVE); + store_unsigned_integer (header + SVE_HEADER_RESERVED_OFFSET, + SVE_HEADER_RESERVED_LENGTH, byte_order, 0); + + /* The SVE register dump follows. */ + regcache->collect_regset (regset, regnum, (gdb_byte *) buf + SVE_HEADER_SIZE, + size - SVE_HEADER_SIZE); +} + /* Implement the "regset_from_core_section" gdbarch method. */ static void @@ -227,14 +401,42 @@ aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch, void *cb_data, const struct regcache *regcache) { + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + cb (".reg", AARCH64_LINUX_SIZEOF_GREGSET, AARCH64_LINUX_SIZEOF_GREGSET, &aarch64_linux_gregset, NULL, cb_data); - cb (".reg2", AARCH64_LINUX_SIZEOF_FPREGSET, AARCH64_LINUX_SIZEOF_FPREGSET, - &aarch64_linux_fpregset, NULL, cb_data); + + if (tdep->has_sve ()) + { + /* Create this on the fly in order to handle vector register sizes. */ + const struct regcache_map_entry sve_regmap[] = + { + { 32, AARCH64_SVE_Z0_REGNUM, tdep->vq * 16 }, + { 16, AARCH64_SVE_P0_REGNUM, tdep->vq * 16 / 8 }, + { 1, AARCH64_SVE_FFR_REGNUM, 4 }, + { 1, AARCH64_FPSR_REGNUM, 4 }, + { 1, AARCH64_FPCR_REGNUM, 4 }, + { 0 } + }; + + const struct regset aarch64_linux_sve_regset = + { + sve_regmap, + aarch64_linux_supply_sve_regset, aarch64_linux_collect_sve_regset, + REGSET_VARIABLE_SIZE + }; + + cb (".reg-aarch-sve", + SVE_HEADER_SIZE + regcache_map_entry_size (aarch64_linux_fpregmap), + SVE_HEADER_SIZE + regcache_map_entry_size (sve_regmap), + &aarch64_linux_sve_regset, "SVE registers", cb_data); + } + else + cb (".reg2", AARCH64_LINUX_SIZEOF_FPREGSET, AARCH64_LINUX_SIZEOF_FPREGSET, + &aarch64_linux_fpregset, NULL, cb_data); } -/* Implement the "core_read_description" gdbarch method. SVE not yet - supported. */ +/* Implement the "core_read_description" gdbarch method. */ static const struct target_desc * aarch64_linux_core_read_description (struct gdbarch *gdbarch, @@ -245,7 +447,7 @@ aarch64_linux_core_read_description (struct gdbarch *gdbarch, if (target_auxv_search (target, AT_HWCAP, &aarch64_hwcap) != 1) return NULL; - return aarch64_read_description (0); + return aarch64_read_description (aarch64_linux_core_read_vq (gdbarch, abfd)); } /* Implementation of `gdbarch_stap_is_single_operand', as defined in