/* Target-dependent code for GNU/Linux AArch64.
- Copyright (C) 2009-2019 Free Software Foundation, Inc.
+ Copyright (C) 2009-2020 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of GDB.
#include "defs.h"
+#include "gdbcmd.h"
#include "gdbarch.h"
-#include "arch-utils.h"
#include "glibc-tdep.h"
#include "linux-tdep.h"
#include "aarch64-tdep.h"
#include "aarch64-linux-tdep.h"
+#include "arch/aarch64-cap-linux.h"
#include "osabi.h"
#include "solib-svr4.h"
#include "symtab.h"
#include "tramp-frame.h"
#include "trad-frame.h"
+#include "target/target.h"
+#include "target.h"
-#include "inferior.h"
#include "regcache.h"
#include "regset.h"
-#include "cli/cli-utils.h"
#include "stap-probe.h"
#include "parser-defs.h"
#include "user-regs.h"
#include "record-full.h"
#include "linux-record.h"
-#include "auxv.h"
+
+#include "value.h"
+
+/* For aarch64_debug. */
+#include "arch/aarch64-insn.h"
+
#include "elf/common.h"
+#include "gdbsupport/capability.h"
+
/* Signal frame handling.
+------------+ ^
#define AARCH64_EXTRA_MAGIC 0x45585401
#define AARCH64_FPSIMD_MAGIC 0x46508001
#define AARCH64_SVE_MAGIC 0x53564501
+#define AARCH64_MORELLO_MAGIC 0x4d524c01
/* Defines for the extra_context that follows an AARCH64_EXTRA_MAGIC. */
#define AARCH64_EXTRA_DATAP_OFFSET 8
#define AARCH64_SVE_CONTEXT_SIZE(vq) \
(AARCH64_SVE_CONTEXT_FFR_OFFSET (vq) + (vq * 2))
+/* Defines for the Morello sigcontext data, which is define in the kernel like
+ so:
+
+ struct morello_context
+ {
+ struct _aarch64_ctx head;
+ __u64 __pad;
+ __kernel_uintcap_t cregs[31];
+ __kernel_uintcap_t csp;
+ __kernel_uintcap_t rcsp;
+ __kernel_uintcap_t pcc;
+ };
+
+*/
+
+#define AARCH64_MORELLO_SIGCONTEXT_SIZE (8 + 8 + 34 * 16)
+#define AARCH64_MORELLO_SIGCONTEXT_C0_OFFSET 16
/* Read an aarch64_ctx, returning the magic value, and setting *SIZE to the
size, or return 0 on error. */
bool extra_found = false;
int num_regs = gdbarch_num_regs (gdbarch);
+ if (aarch64_debug)
+ {
+ debug_printf ("\naarch64: Entering aarch64_linux_sigframe_init\n");
+ }
+
/* Read in the integer registers. */
for (int i = 0; i < 31; i++)
break;
}
+ case AARCH64_MORELLO_MAGIC:
+ {
+ if (aarch64_debug)
+ debug_printf ("aarch64: Found Morello section at %s.\n",
+ paddress (gdbarch, section));
+
+ /* Handle Morello sigcontext. */
+ if (!tdep->has_capability ())
+ break;
+
+ int regno = tdep->cap_reg_base;
+ CORE_ADDR offset = section + AARCH64_MORELLO_SIGCONTEXT_C0_OFFSET;
+ int reg_size = C_REGISTER_SIZE;
+
+ if (aarch64_debug)
+ {
+ debug_printf ("aarch64: Reading C registers from sigreturn "
+ "frame.\n");
+ }
+
+ for (int i = 0; i < AARCH64_C_REGS_NUM; i++)
+ {
+ trad_frame_set_reg_addr (this_cache, regno + i,
+ offset + i * reg_size);
+ }
+
+ if (aarch64_debug)
+ {
+ debug_printf ("aarch64: Reading PCC, CSP and RCSP registers "
+ "from sigreturn frame at %s.\n",
+ paddress (gdbarch, offset + 31 * reg_size));
+ }
+
+ trad_frame_set_reg_addr (this_cache, tdep->cap_reg_csp,
+ offset + 31 * reg_size);
+ trad_frame_set_reg_addr (this_cache, tdep->cap_reg_rcsp,
+ offset + 32 * reg_size);
+ trad_frame_set_reg_addr (this_cache, tdep->cap_reg_pcc,
+ offset + 33 * reg_size);
+
+ section += size;
+ break;
+ }
+
case AARCH64_EXTRA_MAGIC:
{
/* Extra is always the last valid section in reserved and points to
}
trad_frame_set_id (this_cache, frame_id_build (sp, func));
+
+ if (aarch64_debug)
+ debug_printf ("aarch64: Exitting aarch64_linux_sigframe_init\n");
}
static const struct tramp_frame aarch64_linux_rt_sigframe =
{ 0 }
};
+/* Since the C register numbers are determined dynamically, we leave
+ placeholders so we can update the numbers later. */
+static struct regcache_map_entry aarch64_linux_cregmap[] =
+ {
+ { 31, -1, 16 }, /* c0 ... c30 */
+ { 1, -1, 16 }, /* pcc */
+ { 1, -1, 16 }, /* csp */
+ { 1, -1, 16 }, /* ddc */
+ { 1, -1, 16 }, /* ctpidr */
+ { 1, -1, 16 }, /* rcsp */
+ { 1, -1, 16 }, /* rddc */
+ { 1, -1, 16 }, /* rctpidr */
+ { 1, -1, 16 }, /* cid */
+ { 1, -1, 8 }, /* tag_map */
+ { 1, -1, 8 }, /* cctlr */
+ { 0 }
+ };
+
/* Register set definitions. */
const struct regset aarch64_linux_gregset =
regcache_supply_regset, regcache_collect_regset
};
+/* The capability register set. */
+const struct regset aarch64_linux_cregset =
+ {
+ aarch64_linux_cregmap,
+ 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
return 0;
}
- size_t size = bfd_section_size (abfd, sve_section);
+ size_t size = bfd_section_size (sve_section);
/* Check extended state size. */
if (size < SVE_HEADER_SIZE)
size - SVE_HEADER_SIZE);
}
-/* Implement the "regset_from_core_section" gdbarch method. */
+/* Implement the "iterate_over_regset_sections" gdbarch method. */
static void
aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
AARCH64_LINUX_SIZEOF_PAUTH, &aarch64_linux_pauth_regset,
"pauth registers", cb_data);
}
+
+ /* Morello capability registers. */
+ if (tdep->has_capability ())
+ {
+ cb (".reg-aarch-morello", AARCH64_LINUX_CREGS_SIZE,
+ AARCH64_LINUX_CREGS_SIZE, &aarch64_linux_cregset,
+ NULL, cb_data);
+ }
}
/* Implement the "core_read_description" gdbarch method. */
struct target_ops *target, bfd *abfd)
{
CORE_ADDR hwcap = linux_get_hwcap (target);
+ CORE_ADDR hwcap2 = linux_get_hwcap2 (target);
+ bool pauth_p = hwcap & AARCH64_HWCAP_PACA;
+ bool capability_p = hwcap2 & HWCAP2_MORELLO;
return aarch64_read_description (aarch64_linux_core_read_vq (gdbarch, abfd),
- hwcap & AARCH64_HWCAP_PACA);
+ pauth_p, capability_p);
}
/* Implementation of `gdbarch_stap_is_single_operand', as defined in
This function will only ever get called when stopped at the entry or exit
of a syscall, so by checking for 0 in x0 (arg0/retval), x1 (arg1), x8
(syscall), x29 (FP) and x30 (LR) we can infer:
- 1) Either inferior is at exit from sucessful execve.
+ 1) Either inferior is at exit from successful execve.
2) Or inferior is at entry to a call to io_setup with invalid arguments and
a corrupted FP and LR.
It should be safe enough to assume case 1. */
/* Implement the "gcc_target_options" gdbarch method. */
-static char *
+static std::string
aarch64_linux_gcc_target_options (struct gdbarch *gdbarch)
{
/* GCC doesn't know "-m64". */
- return NULL;
+ return {};
+}
+
+/* AArch64 Linux implementation of the report_signal_info gdbarch
+ hook. Displays information about possible memory tag violations or
+ capability violations. */
+
+static void
+aarch64_linux_report_signal_info (struct gdbarch *gdbarch,
+ struct ui_out *uiout,
+ enum gdb_signal siggnal)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if (!tdep->has_capability () || siggnal != GDB_SIGNAL_SEGV)
+ return;
+
+ CORE_ADDR fault_addr = 0;
+ long si_code = 0;
+
+ try
+ {
+ /* Sigcode tells us if the segfault is actually a capability
+ violation. */
+ si_code = parse_and_eval_long ("$_siginfo.si_code\n");
+
+ fault_addr
+ = parse_and_eval_long ("$_siginfo._sifields._sigfault.si_addr");
+ }
+ catch (const gdb_exception &exception)
+ {
+ return;
+ }
+
+ /* If this is not a capability violation, just return. */
+ if (si_code != SEGV_CAPTAGERR && si_code != SEGV_CAPSEALEDERR
+ && si_code != SEGV_CAPBOUNDSERR && si_code != SEGV_CAPPERMERR
+ && si_code != SEGV_CAPSTORETAGERR)
+ return;
+
+ uiout->text ("\n");
+
+ std::string str_si_code;
+
+ switch (si_code)
+ {
+ case SEGV_CAPTAGERR:
+ str_si_code = "tag";
+ break;
+ case SEGV_CAPSEALEDERR:
+ str_si_code = "sealed";
+ break;
+ case SEGV_CAPBOUNDSERR:
+ str_si_code = "bounds";
+ break;
+ case SEGV_CAPPERMERR:
+ str_si_code = "permission";
+ break;
+ case SEGV_CAPSTORETAGERR:
+ str_si_code = "access";
+ break;
+ default:
+ str_si_code = "unknown";
+ break;
+ }
+
+ std::string str_meaning = "Capability " + str_si_code + " fault";
+ uiout->field_string ("sigcode-meaning", str_meaning);
+
+ /* FIXME-Morello: Show more information about the faults. */
+ uiout->text (_(" while accessing address "));
+ uiout->field_core_addr ("fault-addr", gdbarch, fault_addr);
+}
+
+/* AArch64 Linux implementation of the get_cap_tag_from_address gdbarch
+ hook. Returns the tag from the capability located at ADDR. */
+
+static bool
+aarch64_linux_get_cap_tag_from_address (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+ gdb::byte_vector cap;
+
+ cap = target_read_capability (addr);
+
+ if (cap.size () == 0)
+ return false;
+
+ return cap[0] != 0;
+}
+
+/* Return the number of Morello memory tag granules contained in the memory
+ range [addr, addr + len). */
+
+static size_t
+morello_get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size)
+{
+ /* An empty range has 0 tag granules. */
+ if (len == 0)
+ return 0;
+
+ /* Start address */
+ CORE_ADDR s_addr = align_down (addr, granule_size);
+ /* End address */
+ CORE_ADDR e_addr = align_down (addr + len - 1, granule_size);
+
+ /* We always have at least 1 granule because len is non-zero at this
+ point. */
+ return 1 + (e_addr - s_addr) / granule_size;
+}
+
+/* Maximum number of tags to request. */
+#define MAX_TAGS_TO_TRANSFER 1024
+
+/* AArch64 Linux implementation of the aarch64_create_memtag_notes_from_range
+ gdbarch hook. Create core file notes for memory tags. */
+
+static std::vector<gdb::byte_vector>
+aarch64_linux_create_memtag_notes_from_range (struct gdbarch *gdbarch,
+ CORE_ADDR start_address,
+ CORE_ADDR end_address)
+{
+ /* We only handle CHERI capability tags for now. */
+
+ /* Figure out how many tags we need to store in this memory range. */
+ int granules = morello_get_tag_granules (start_address,
+ end_address - start_address,
+ MORELLO_TAG_GRANULE_SIZE);
+
+ /* A vector to store multiple notes. */
+ std::vector<gdb::byte_vector> notes;
+
+ /* Add the CHERI note. */
+ notes.resize (1);
+ /* Resize to the number of bytes the tag granules will take. */
+ notes[0].resize (sizeof (struct tag_dump_header) + granules);
+
+ /* Retrieve the tags and store them in the vector. */
+ gdb::byte_vector tags;
+ CORE_ADDR address = start_address;
+ while (granules > 0)
+ {
+ /* Transfer tags in chunks. */
+ gdb::byte_vector tags_read;
+ size_t xfer_len
+ = (granules >= MAX_TAGS_TO_TRANSFER)? MAX_TAGS_TO_TRANSFER : granules;
+
+ /* First clear the vector of tags. */
+ tags_read.resize (0);
+
+ /* Copy tags in chunks. */
+ while (tags_read.size () < xfer_len)
+ {
+ CORE_ADDR addr
+ = address + tags_read.size () * MORELLO_TAG_GRANULE_SIZE;
+
+ /* Always align the address to 16 bytes so we can read the
+ capability properly. When we have a request to read only the
+ capability tags, then we won't need to do this. */
+ CORE_ADDR aligned_addr = align_down (addr, MORELLO_TAG_GRANULE_SIZE);
+ bool tag
+ = aarch64_linux_get_cap_tag_from_address (gdbarch, aligned_addr);
+ gdb_byte tag_byte = (tag == false)? 0 : 1;
+ tags_read.push_back (tag_byte);
+ }
+
+ /* This process may take a while. Make sure it is interruptible. */
+ QUIT;
+
+ /* Transfer over the tags that have been read. */
+ tags.insert (tags.end (), tags_read.begin (), tags_read.end ());
+
+ /* Adjust the remaining granules and starting address. */
+ granules -= tags_read.size ();
+ address += tags_read.size () * MORELLO_TAG_GRANULE_SIZE;
+ }
+
+ /* Create the header. Please note we don't yet compress the tag data.
+ We may do so in the future to save space, since a capability tag is only
+ 1 bit in size. */
+ struct tag_dump_header header;
+ header.format = ELF_CORE_TAG_CHERI;
+ header.start_vma = start_address;
+ header.end_vma = end_address;
+ header.u.cheri.granule_byte_size = MORELLO_TAG_GRANULE_SIZE;
+ header.u.cheri.tag_bit_size = MORELLO_TAG_BIT_SIZE;
+ header.u.cheri.__unused = 0;
+
+ /* Copy the tags to the note. */
+ memcpy (notes[0].data (), &header, sizeof (header));
+ memcpy (notes[0].data () + sizeof (header), tags.data (), tags.size ());
+
+ return notes;
+}
+
+/* AArch64 Linux implementation of the aarch64_decode_memtag_note gdbarch
+ hook. Decode a memory tag note and return the tag that it contains for
+ a particular address. */
+
+static gdb_byte
+aarch64_linux_decode_memtag_note (struct gdbarch *gdbarch,
+ gdb::array_view <const gdb_byte> note,
+ CORE_ADDR address)
+{
+ /* Read the header. */
+ struct tag_dump_header header;
+ memcpy (&header, note.data (), sizeof (header));
+
+ /* Align the address to 16 bytes. We assume the start_vma is already
+ aligned to the proper boundary, otherwise we would have tags dumped
+ into two different memory mappings. */
+ address = align_down (address, MORELLO_TAG_GRANULE_SIZE);
+ CORE_ADDR offset = address - header.start_vma;
+ gdb_byte tag;
+
+ /* Read the tag. */
+ memcpy (&tag, note.data () + sizeof (header)
+ + (offset / header.u.cheri.granule_byte_size), 1);
+
+ return tag;
+}
+
+/* Implement the maintenance print capability tag command. */
+
+static void
+maint_print_cap_from_addr_cmd (const char *args, int from_tty)
+{
+ gdb::byte_vector cap;
+ CORE_ADDR addr = parse_and_eval_address (args);
+ cap = target_read_capability (addr);
+
+ if (cap.empty ())
+ {
+ fprintf_unfiltered (gdb_stdlog,
+ "Could not read capability from address %s.\n",
+ phex_nz (addr, 8));
+ return;
+ }
+
+ for (auto it : cap)
+ fprintf_unfiltered (gdb_stdlog, "%02x ", it);
+ fputs_unfiltered ("\n", gdb_stdlog);
+
+ bool tag = (cap[0] == 1);
+ uint128_t cap_128bits;
+ memcpy (&cap_128bits, &cap[1], 16);
+
+ capability capability (cap_128bits, tag);
+
+ fprintf_unfiltered (gdb_stdlog, "verbose: %s\n",
+ capability.to_str (false).c_str ());
+ fputs_unfiltered ("\n", gdb_stdlog);
+ fprintf_unfiltered (gdb_stdlog, "compact: %s\n",
+ capability.to_str (true).c_str ());
+}
+
+/* Implement the maintenance set capability in memory command. */
+
+static void
+maint_set_capability_in_memory_cmd (const char *args, int from_tty)
+{
+ std::string addr_str, tag_str, upper_str, lower_str;
+ const char *args_ptr = args;
+
+ addr_str = extract_string_maybe_quoted (&args_ptr);
+ tag_str = extract_string_maybe_quoted (&args_ptr);
+ upper_str = extract_string_maybe_quoted (&args_ptr);
+ lower_str = extract_string_maybe_quoted (&args_ptr);
+
+ CORE_ADDR addr = parse_and_eval_address (addr_str.c_str ());
+ CORE_ADDR tag_part = parse_and_eval_address (tag_str.c_str ());
+ CORE_ADDR half_a = parse_and_eval_address (upper_str.c_str ());
+ CORE_ADDR half_b = parse_and_eval_address (lower_str.c_str ());
+
+ unsigned __int128 a, b;
+
+ a = half_a;
+ b = half_b;
+
+ a = (a << 64) | b;
+ bool tag = (tag_part != 0)? true : false;
+
+ gdb::byte_vector cap;
+
+ cap.resize (17);
+ memcpy (cap.data (), &tag, 1);
+ memcpy (cap.data () + 1, &a, 16);
+
+ if (!target_write_capability (addr, {cap.data (), cap.size ()}))
+ perror_with_name (_("Failed to set capability in memory."));
}
static void
set_xml_syscall_file_name (gdbarch, "syscalls/aarch64-linux.xml");
set_gdbarch_get_syscall_number (gdbarch, aarch64_linux_get_syscall_number);
- /* Displaced stepping. */
- set_gdbarch_max_insn_length (gdbarch, 4 * DISPLACED_MODIFIED_INSNS);
- set_gdbarch_displaced_step_copy_insn (gdbarch,
- aarch64_displaced_step_copy_insn);
- set_gdbarch_displaced_step_fixup (gdbarch, aarch64_displaced_step_fixup);
- set_gdbarch_displaced_step_location (gdbarch, linux_displaced_step_location);
set_gdbarch_displaced_step_hw_singlestep (gdbarch,
aarch64_displaced_step_hw_singlestep);
set_gdbarch_gcc_target_options (gdbarch, aarch64_linux_gcc_target_options);
+
+ if (tdep->has_capability ())
+ {
+ /* Register CHERI-specific linkmap offsets for the AAPCS64_CAP ABI. */
+ if (tdep->abi == AARCH64_ABI_AAPCS64_CAP)
+ set_solib_svr4_fetch_link_map_offsets (gdbarch,
+ svr4_lp64_cheri_fetch_link_map_offsets);
+
+ /* Initialize the register numbers for the core file register set.
+ Please note the PCC/CSP position in GDB's target description is
+ the inverse of the position in the Linux Kernel's user_morello_state
+ data structure. This can cause some confusion. */
+ aarch64_linux_cregmap[0].regno = tdep->cap_reg_base;
+ aarch64_linux_cregmap[1].regno = tdep->cap_reg_pcc;
+ aarch64_linux_cregmap[2].regno = tdep->cap_reg_csp;
+
+ /* Set the rest of the registers. */
+ int next_regnum = tdep->cap_reg_base + 33;
+ for (int i = 3; i <= 10; i++)
+ {
+ aarch64_linux_cregmap[i].regno = next_regnum;
+ next_regnum++;
+ }
+
+ set_gdbarch_report_signal_info (gdbarch,
+ aarch64_linux_report_signal_info);
+ set_gdbarch_get_cap_tag_from_address (gdbarch,
+ aarch64_linux_get_cap_tag_from_address);
+
+ /* Core file helpers. */
+
+ /* Core file helper to create memory tag notes for a particular range of
+ addresses. */
+ set_gdbarch_create_memtag_notes_from_range (gdbarch,
+ aarch64_linux_create_memtag_notes_from_range);
+
+ /* Core file helper to decode a memory tag note. */
+ set_gdbarch_decode_memtag_note (gdbarch,
+ aarch64_linux_decode_memtag_note);
+
+ add_cmd ("cap_from_addr", class_maintenance, maint_print_cap_from_addr_cmd,
+ _("Print the capability from addr."), &maintenanceprintlist);
+
+ add_cmd ("cap_in_memory", class_maintenance,
+ maint_set_capability_in_memory_cmd,
+ _("Print the capability from addr."), &maintenancelist);
+ }
+ else
+ {
+ /* Displaced stepping. */
+ /* Note: Morello does not support displaced stepping yet because
+ adjustments to GPR's may not be correct. This is because GDB can't
+ make adjustments to the upper 65 bits of the C registers. */
+ set_gdbarch_max_insn_length (gdbarch,
+ 4 * AARCH64_DISPLACED_MODIFIED_INSNS);
+ set_gdbarch_displaced_step_copy_insn (gdbarch,
+ aarch64_displaced_step_copy_insn);
+ set_gdbarch_displaced_step_fixup (gdbarch, aarch64_displaced_step_fixup);
+ set_gdbarch_displaced_step_location (gdbarch,
+ linux_displaced_step_location);
+ }
}
+void _initialize_aarch64_linux_tdep ();
void
-_initialize_aarch64_linux_tdep (void)
+_initialize_aarch64_linux_tdep ()
{
gdbarch_register_osabi (bfd_arch_aarch64, 0, GDB_OSABI_LINUX,
aarch64_linux_init_abi);