This patch enables capability reads from memory, including the tag bit.
It enables both native GDB and gdbserver to do so.
gdb/ChangeLog:
2021-01-15 Luis Machado <luis.machado@arm.com>
* aarch64-linux-nat.c (aarch64_linux_nat_target)
<read_capability>: New function override.
(aarch64_linux_nat_target::read_capability): New function.
(maint_print_cap_from_addr_cmd): New function.
(add_show_debug_regs_command): Register new maintenance command.
* aarch64-linux-tdep.c: Include target.h.
(aarch64_linux_get_cap_tag_from_address): New function.
(aarch64_linux_init_abi): Register hook for
gdbarch_get_cap_tag_from_address
* arch-utils.c (default_get_cap_tag_from_address): New function.
* arch-utils.h (default_get_cap_tag_from_address): New prototype.
* configure.nat: Add nat/aarch64-cap-linux.o to the list of object
files for AArch64-Linux.
* gdbarch.c: Regenerate.
* gdbarch.h: Regenerate.
* gdbarch.sh (get_cap_tag_from_address): New gdbarch hook.
* nat/aarch64-cap-linux.c: New file.
* nat/aarch64-cap-linux.h (aarch64_linux_read_capability): New
prototype.
* remote.c (remote_target) <read_capability>: New function override.
(PACKET_qXfer_capability): New enum.
(remote_target::xfer_partial): Handle TARGET_OBJECT_CAPABILITY.
(remote_target::read_capability): New member function.
(_initialize_remote): Add new packet configuration.
* target-delegates.c: Regenerate.
* target.c (target_read_capability): New function.
* target.h (target_object) <TARGET_OBJECT_CAPABILITY>: New enum field.
(struct target_ops) <read_capability>: New virtual member function.
(target_read_capability): New prototype.
* valprint.c (generic_value_print_capability): Handle tags from
capabilities stored in memory.
* value.c (value_fetch_lazy_memory): Fetch tag if reading a capability.
gdbserver/ChangeLog
2021-01-15 Luis Machado <luis.machado@arm.com>
* configure.srv: Add nat/aarch64-cap-linux.o to the list of object
files for AArch64-Linux
* linux-aarch64-low.cc (aarch64_target) <supports_qxfer_capability>
<qxfer_capability>: New member function overrides.
(aarch64_target::supports_qxfer_capability): New function.
(aarch64_target::qxfer_capability): New function.
* server.cc (handle_qxfer_capability): New function.
(qxfer_packets): Add "capa" packet.
* target.cc (process_stratum_target::supports_qxfer_capability)
(process_stratum_target::qxfer_capability): New functions.
* target.h (process_stratum_target) <supports_qxfer_capability>
<qxfer_capability>: New virtual member functions.
+2021-01-15 Luis Machado <luis.machado@arm.com>
+
+ * aarch64-linux-nat.c (aarch64_linux_nat_target)
+ <read_capability>: New function override.
+ (aarch64_linux_nat_target::read_capability): New function.
+ (maint_print_cap_from_addr_cmd): New function.
+ (add_show_debug_regs_command): Register new maintenance command.
+ * aarch64-linux-tdep.c: Include target.h.
+ (aarch64_linux_get_cap_tag_from_address): New function.
+ (aarch64_linux_init_abi): Register hook for
+ gdbarch_get_cap_tag_from_address
+ * arch-utils.c (default_get_cap_tag_from_address): New function.
+ * arch-utils.h (default_get_cap_tag_from_address): New prototype.
+ * configure.nat: Add nat/aarch64-cap-linux.o to the list of object
+ files for AArch64-Linux.
+ * gdbarch.c: Regenerate.
+ * gdbarch.h: Regenerate.
+ * gdbarch.sh (get_cap_tag_from_address): New gdbarch hook.
+ * nat/aarch64-cap-linux.c: New file.
+ * nat/aarch64-cap-linux.h (aarch64_linux_read_capability): New
+ prototype.
+ * remote.c (remote_target) <read_capability>: New function override.
+ (PACKET_qXfer_capability): New enum.
+ (remote_target::xfer_partial): Handle TARGET_OBJECT_CAPABILITY.
+ (remote_target::read_capability): New member function.
+ (_initialize_remote): Add new packet configuration.
+ * target-delegates.c: Regenerate.
+ * target.c (target_read_capability): New function.
+ * target.h (target_object) <TARGET_OBJECT_CAPABILITY>: New enum field.
+ (struct target_ops) <read_capability>: New virtual member function.
+ (target_read_capability): New prototype.
+ * valprint.c (generic_value_print_capability): Handle tags from
+ capabilities stored in memory.
+ * value.c (value_fetch_lazy_memory): Fetch tag if reading a capability.
+
2021-01-15 Luis Machado <luis.machado@arm.com>
* aarch64-tdep.c (aarch64_prologue_prev_register)
override;
struct gdbarch *thread_architecture (ptid_t) override;
+ gdb::byte_vector read_capability (CORE_ADDR addr) override;
};
static aarch64_linux_nat_target the_aarch64_linux_nat_target;
return gdbarch_find_by_info (info);
}
+/* Implement the "read_capability" target_ops method. */
+
+gdb::byte_vector
+aarch64_linux_nat_target::read_capability (CORE_ADDR addr)
+{
+ int tid = get_ptrace_pid (inferior_ptid);
+
+ struct user_cap cap;
+
+ if (!aarch64_linux_read_capability (tid, addr, cap))
+ perror_with_name (_("Unable to read capability from address."));
+
+ gdb::byte_vector cap_vec (17);
+ memcpy (cap_vec.data (), &cap.tag, 1);
+ memcpy (cap_vec.data () + 1, &cap.val, 16);
+
+ return cap_vec;
+}
+
+/* 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);
+
+ for (auto it : cap)
+ fprintf_unfiltered (gdb_stdlog, "%x ", it);
+
+ fputs_unfiltered ("\n", gdb_stdlog);
+}
+
/* Define AArch64 maintenance commands. */
static void
NULL,
&maintenance_set_cmdlist,
&maintenance_show_cmdlist);
+
+ add_cmd ("cap_from_addr", class_maintenance, maint_print_cap_from_addr_cmd, _("\
+Print the capability from addr."),
+ &maintenanceprintlist);
}
void _initialize_aarch64_linux_nat ();
#include "tramp-frame.h"
#include "trad-frame.h"
#include "target/target.h"
+#include "target.h"
#include "regcache.h"
#include "regset.h"
uiout->text ("\n");
}
+/* 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;
+}
+
static void
aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
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);
}
else
{
return false;
}
+/* See arch-utils.h. */
+bool default_get_cap_tag_from_address (struct gdbarch *gdbarch,
+ CORE_ADDR addr)
+{
+ return false;
+}
+
void _initialize_gdbarch_utils ();
void
_initialize_gdbarch_utils ()
extern bool default_register_tag (struct gdbarch *gdbarch,
readable_regcache *regcache,
int cookednum);
+
+/* Default implementation of gdbarch_cap_tag_from_address. */
+extern bool default_get_cap_tag_from_address (struct gdbarch *gdbarch,
+ CORE_ADDR addr);
#endif /* ARCH_UTILS_H */
# Host: AArch64 based machine running GNU/Linux
NATDEPFILES="${NATDEPFILES} aarch64-linux-nat.o \
aarch32-linux-nat.o nat/aarch64-linux-hw-point.o \
- nat/aarch64-linux.o \
+ nat/aarch64-cap-linux.o nat/aarch64-linux.o \
nat/aarch64-sve-linux-ptrace.o"
;;
arm)
CORE_ADDR decr_pc_after_break;
CORE_ADDR deprecated_function_start_offset;
gdbarch_remote_register_number_ftype *remote_register_number;
+ gdbarch_get_cap_tag_from_address_ftype *get_cap_tag_from_address;
gdbarch_fetch_tls_load_module_address_ftype *fetch_tls_load_module_address;
gdbarch_get_thread_local_address_ftype *get_thread_local_address;
CORE_ADDR frame_args_skip;
gdbarch->memory_insert_breakpoint = default_memory_insert_breakpoint;
gdbarch->memory_remove_breakpoint = default_memory_remove_breakpoint;
gdbarch->remote_register_number = default_remote_register_number;
+ gdbarch->get_cap_tag_from_address = default_get_cap_tag_from_address;
gdbarch->unwind_pc = default_unwind_pc;
gdbarch->unwind_sp = default_unwind_sp;
gdbarch->stabs_argument_has_addr = default_stabs_argument_has_addr;
/* Skip verify of decr_pc_after_break, invalid_p == 0 */
/* Skip verify of deprecated_function_start_offset, invalid_p == 0 */
/* Skip verify of remote_register_number, invalid_p == 0 */
+ /* Skip verify of get_cap_tag_from_address, invalid_p == 0 */
/* Skip verify of fetch_tls_load_module_address, has predicate. */
/* Skip verify of get_thread_local_address, has predicate. */
/* Skip verify of frame_args_skip, invalid_p == 0 */
fprintf_unfiltered (file,
"gdbarch_dump: gen_return_address = <%s>\n",
host_address_to_string (gdbarch->gen_return_address));
+ fprintf_unfiltered (file,
+ "gdbarch_dump: get_cap_tag_from_address = <%s>\n",
+ host_address_to_string (gdbarch->get_cap_tag_from_address));
fprintf_unfiltered (file,
"gdbarch_dump: gdbarch_get_longjmp_target_p() = %d\n",
gdbarch_get_longjmp_target_p (gdbarch));
gdbarch->remote_register_number = remote_register_number;
}
+bool
+gdbarch_get_cap_tag_from_address (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+ gdb_assert (gdbarch != NULL);
+ gdb_assert (gdbarch->get_cap_tag_from_address != NULL);
+ if (gdbarch_debug >= 2)
+ fprintf_unfiltered (gdb_stdlog, "gdbarch_get_cap_tag_from_address called\n");
+ return gdbarch->get_cap_tag_from_address (gdbarch, addr);
+}
+
+void
+set_gdbarch_get_cap_tag_from_address (struct gdbarch *gdbarch,
+ gdbarch_get_cap_tag_from_address_ftype get_cap_tag_from_address)
+{
+ gdbarch->get_cap_tag_from_address = get_cap_tag_from_address;
+}
+
int
gdbarch_fetch_tls_load_module_address_p (struct gdbarch *gdbarch)
{
extern int gdbarch_remote_register_number (struct gdbarch *gdbarch, int regno);
extern void set_gdbarch_remote_register_number (struct gdbarch *gdbarch, gdbarch_remote_register_number_ftype *remote_register_number);
+/* Return the tag from a capability stored at address ADDR. */
+
+typedef bool (gdbarch_get_cap_tag_from_address_ftype) (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern bool gdbarch_get_cap_tag_from_address (struct gdbarch *gdbarch, CORE_ADDR addr);
+extern void set_gdbarch_get_cap_tag_from_address (struct gdbarch *gdbarch, gdbarch_get_cap_tag_from_address_ftype *get_cap_tag_from_address);
+
/* Fetch the target specific address used to represent a load module. */
extern int gdbarch_fetch_tls_load_module_address_p (struct gdbarch *gdbarch);
# register. Normally the identity mapping.
m;int;remote_register_number;int regno;regno;;default_remote_register_number;;0
+# Return the tag from a capability stored at address ADDR.
+m;bool;get_cap_tag_from_address;CORE_ADDR addr;addr;0;default_get_cap_tag_from_address;;0
+
# Fetch the target specific address used to represent a load module.
F;CORE_ADDR;fetch_tls_load_module_address;struct objfile *objfile;objfile
--- /dev/null
+/* Copyright (C) 2020-2021 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "gdbsupport/common-defs.h"
+#include "aarch64-cap-linux.h"
+#include "gdb_ptrace.h"
+
+/* See aarch64-cap-linux.h */
+
+bool
+aarch64_linux_read_capability (int tid, CORE_ADDR address,
+ user_cap &cap)
+{
+ cap.val = 0;
+ cap.tag = 0;
+ memset (cap.__reserved, 0, 15);
+
+ /* Fetch the tag from ptrace. */
+ if (ptrace (PTRACE_PEEKCAP, tid, address, &cap) < 0)
+ return false;
+
+ return true;
+}
uint8_t __reserved[15];
};
+/* From thread TID, read a capability from memory at ADDRESS and store
+ it into CAP.
+
+ Return true if successful and false otherwise. */
+
+extern bool aarch64_linux_read_capability (int tid, CORE_ADDR address,
+ user_cap &cap);
#endif /* NAT_AARCH64_CAP_LINUX_H */
int remove_exec_catchpoint (int) override;
enum exec_direction_kind execution_direction () override;
+ gdb::byte_vector read_capability (CORE_ADDR addr) override;
+
public: /* Remote specific methods. */
void remote_download_command_source (int num, ULONGEST addr,
/* Support TARGET_WAITKIND_NO_RESUMED. */
PACKET_no_resumed,
+ /* Support for the qXfer:capa:read packet. */
+ PACKET_qXfer_capability,
+
PACKET_MAX
};
return TARGET_XFER_E_IO;
}
+
+ /* Read CHERI capabilities. */
+ if (object == TARGET_OBJECT_CAPABILITY)
+ {
+ if (readbuf)
+ return remote_read_qxfer ("capa", annex,
+ readbuf, offset, len, xfered_len,
+ &remote_protocol_packets
+ [PACKET_qXfer_capability]);
+ else
+ return TARGET_XFER_E_IO;
+ }
+
/* Only handle flash writes. */
if (writebuf != NULL)
{
}
}
+/* Implementation of the read_capability method. */
+
+gdb::byte_vector
+remote_target::read_capability (CORE_ADDR addr)
+{
+ gdb::optional<gdb::byte_vector> cap;
+ gdb::byte_vector cap_vec;
+
+ std::string addr_str = string_printf ("%s", phex_nz (addr, 0));
+
+ cap = target_read_alloc (current_top_target (), TARGET_OBJECT_CAPABILITY,
+ addr_str.c_str ());
+
+ if (cap.has_value ())
+ cap_vec = *cap;
+ else
+ perror_with_name (_("Unable to read capability from address."));
+
+ return cap_vec;
+}
+
void _initialize_remote ();
void
_initialize_remote ()
add_packet_config_cmd (&remote_protocol_packets[PACKET_no_resumed],
"N stop reply", "no-resumed-stop-reply", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_capability],
+ "qXfer:capa:read", "read-capability", 0);
+
/* Assert that we've registered "set remote foo-packet" commands
for all packet configs. */
{
const struct frame_unwind *get_tailcall_unwinder () override;
void prepare_to_generate_core () override;
void done_generating_core () override;
+ gdb::byte_vector read_capability (CORE_ADDR arg0) override;
};
struct debug_target : public target_ops
const struct frame_unwind *get_tailcall_unwinder () override;
void prepare_to_generate_core () override;
void done_generating_core () override;
+ gdb::byte_vector read_capability (CORE_ADDR arg0) override;
};
void
fputs_unfiltered (")\n", gdb_stdlog);
}
+gdb::byte_vector
+target_ops::read_capability (CORE_ADDR arg0)
+{
+ return this->beneath ()->read_capability (arg0);
+}
+
+gdb::byte_vector
+dummy_target::read_capability (CORE_ADDR arg0)
+{
+ tcomplain ();
+}
+
+gdb::byte_vector
+debug_target::read_capability (CORE_ADDR arg0)
+{
+ gdb::byte_vector result;
+ fprintf_unfiltered (gdb_stdlog, "-> %s->read_capability (...)\n", this->beneath ()->shortname ());
+ result = this->beneath ()->read_capability (arg0);
+ fprintf_unfiltered (gdb_stdlog, "<- %s->read_capability (", this->beneath ()->shortname ());
+ target_debug_print_CORE_ADDR (arg0);
+ fputs_unfiltered (") = ", gdb_stdlog);
+ target_debug_print_gdb_byte_vector (result);
+ fputs_unfiltered ("\n", gdb_stdlog);
+ return result;
+}
+
current_top_target ()->done_generating_core ();
}
+/* See target.h. */
+
+gdb::byte_vector
+target_read_capability (CORE_ADDR addr)
+{
+ return current_top_target ()->read_capability (addr);
+}
+
\f
static char targ_desc[] =
TARGET_OBJECT_FREEBSD_VMMAP,
/* FreeBSD process strings. */
TARGET_OBJECT_FREEBSD_PS_STRINGS,
+ /* CHERI capabilities. */
+ TARGET_OBJECT_CAPABILITY,
/* Possible future objects: TARGET_OBJECT_FILE, ... */
};
/* Cleanup after generating a core file. */
virtual void done_generating_core ()
TARGET_DEFAULT_IGNORE ();
+
+ /* Read a capability from ADDR. */
+ virtual gdb::byte_vector read_capability (CORE_ADDR addr)
+ TARGET_DEFAULT_NORETURN (tcomplain ());
};
/* Deleter for std::unique_ptr. See comments in
/* See to_done_generating_core. */
extern void target_done_generating_core (void);
+/* See read_capability. */
+extern gdb::byte_vector target_read_capability (CORE_ADDR addr);
+
#endif /* !defined (TARGET_H) */
tag = value_tag (val);
break;
case lval_memory:
- /* TODO-Morello: Add hook that reads capabilities from memory. We
- should use those here to fetch the tag from a memory location. */
- tag = true;
+ {
+ struct gdbarch *gdbarch = get_type_arch (type);
+ tag = gdbarch_get_cap_tag_from_address (gdbarch, value_address (val));
+ }
break;
default:
break;
read_value_memory (val, 0, value_stack (val),
addr, value_contents_all_raw (val),
type_length_units (type));
+
+ if (TYPE_CAPABILITY (type))
+ {
+ set_value_tagged (val, true);
+ bool tag = gdbarch_get_cap_tag_from_address (get_value_arch (val), addr);
+ set_value_tag (val, tag);
+ }
}
/* Helper for value_fetch_lazy when the value is in a register. */
+2021-01-15 Luis Machado <luis.machado@arm.com>
+
+ * configure.srv: Add nat/aarch64-cap-linux.o to the list of object
+ files for AArch64-Linux
+ * linux-aarch64-low.cc (aarch64_target) <supports_qxfer_capability>
+ <qxfer_capability>: New member function overrides.
+ (aarch64_target::supports_qxfer_capability): New function.
+ (aarch64_target::qxfer_capability): New function.
+ * server.cc (handle_qxfer_capability): New function.
+ (qxfer_packets): Add "capa" packet.
+ * target.cc (process_stratum_target::supports_qxfer_capability)
+ (process_stratum_target::qxfer_capability): New functions.
+ * target.h (process_stratum_target) <supports_qxfer_capability>
+ <qxfer_capability>: New virtual member functions.
+
2021-01-15 Luis Machado <luis.machado@arm.com>
* linux-aarch64-low.cc: Include nat/aarch64-cap-linux.h.
srv_tgtobj="$srv_tgtobj linux-aarch32-tdesc.o"
srv_tgtobj="${srv_tgtobj} arch/aarch32.o"
srv_tgtobj="${srv_tgtobj} arch/arm.o"
+ srv_tgtobj="$srv_tgtobj nat/aarch64-cap-linux.o"
srv_tgtobj="$srv_tgtobj nat/aarch64-linux.o"
srv_tgtobj="$srv_tgtobj arch/aarch64-insn.o"
srv_tgtobj="$srv_tgtobj arch/aarch64.o"
struct emit_ops *emit_ops () override;
+ bool supports_qxfer_capability () override;
+
+ int qxfer_capability (const CORE_ADDR address, unsigned char *readbuf,
+ unsigned const char *writebuf,
+ CORE_ADDR offset, int len) override;
+
protected:
void low_arch_setup () override;
return arm_breakpoint_kind_from_current_state (pcptr);
}
+/* Implementation of targets ops method "supports_qxfer_capability. */
+
+bool
+aarch64_target::supports_qxfer_capability ()
+{
+ unsigned long hwcap2 = linux_get_hwcap2 (8);
+
+ return (hwcap2 & HWCAP2_MORELLO) != 0;
+}
+
+/* Implementation of targets ops method "qxfer_capability. */
+
+int
+aarch64_target::qxfer_capability (const CORE_ADDR address,
+ unsigned char *readbuf,
+ unsigned const char *writebuf,
+ CORE_ADDR offset, int len)
+{
+ int tid = pid_of (current_thread);
+
+ struct user_cap cap;
+
+ if (!aarch64_linux_read_capability (tid, address, cap))
+ {
+ warning (_("Unable to read capability from address."));
+ return 0;
+ }
+
+ memcpy (readbuf, &cap.tag, 1);
+ memcpy (readbuf + 1, &cap.val, 16);
+
+ return sizeof (cap.val) + 1;
+}
+
/* The linux target ops object. */
linux_process_target *the_linux_target = &the_aarch64_target;
return len;
}
+/* Handle qXfer:capa:read. */
+
+static int
+handle_qxfer_capability (const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset,
+ LONGEST len)
+{
+ if (!the_target->supports_qxfer_capability () || writebuf != NULL)
+ return -2;
+
+ CORE_ADDR addr;
+ unpack_varlen_hex (annex, &addr);
+
+ /* Read a capability and its tag. */
+ return the_target->qxfer_capability (addr, readbuf, NULL, offset, len);
+}
+
static const struct qxfer qxfer_packets[] =
{
{ "auxv", handle_qxfer_auxv },
{ "btrace", handle_qxfer_btrace },
{ "btrace-conf", handle_qxfer_btrace_conf },
+ { "capa", handle_qxfer_capability },
{ "exec-file", handle_qxfer_exec_file},
{ "fdpic", handle_qxfer_fdpic},
{ "features", handle_qxfer_features },
gdb_assert_not_reached ("target op qxfer_siginfo not supported");
}
+bool
+process_stratum_target::supports_qxfer_capability ()
+{
+ return false;
+}
+
+int
+process_stratum_target::qxfer_capability (const CORE_ADDR address,
+ unsigned char *readbuf,
+ unsigned const char *writebuf,
+ CORE_ADDR offset, int len)
+{
+ gdb_assert_not_reached ("target op qxfer_capability not supported");
+}
+
bool
process_stratum_target::supports_non_stop ()
{
unsigned const char *writebuf,
CORE_ADDR offset, int len);
+ /* Return true if the qxfer_capability target op is supported. */
+ virtual bool supports_qxfer_capability ();
+
+ /* Read a capability using qXfer packets. */
+ virtual int qxfer_capability (const CORE_ADDR address, unsigned char *readbuf,
+ unsigned const char *writebuf, CORE_ADDR offset,
+ int len);
+
/* Return true if the qxfer_siginfo target op is supported. */
virtual bool supports_qxfer_siginfo ();