From: Luis Machado Date: Mon, 21 Dec 2020 14:44:23 +0000 (-0300) Subject: Read capability tags from memory X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=de798e03c7915347f6025847cc9cd658d715f286;p=thirdparty%2Fbinutils-gdb.git Read capability tags from memory 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 * aarch64-linux-nat.c (aarch64_linux_nat_target) : 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) : 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) : New enum field. (struct target_ops) : 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 * configure.srv: Add nat/aarch64-cap-linux.o to the list of object files for AArch64-Linux * linux-aarch64-low.cc (aarch64_target) : 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) : New virtual member functions. --- diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index 5a5ebcc96e8..cffd4145567 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -109,6 +109,8 @@ public: /* Write allocation tags to memory via PTRACE. */ bool store_memtags (CORE_ADDR address, size_t len, const gdb::byte_vector &tags, int type) override; + + gdb::byte_vector read_capability (CORE_ADDR addr) override; }; static aarch64_linux_nat_target the_aarch64_linux_nat_target; @@ -939,12 +941,50 @@ aarch64_linux_nat_target::store_memtags (CORE_ADDR address, size_t len, return false; } +/* 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); +} + void _initialize_aarch64_linux_nat (); void _initialize_aarch64_linux_nat () { aarch64_initialize_hw_point (); + add_cmd ("cap_from_addr", class_maintenance, maint_print_cap_from_addr_cmd, _("\ +Print the capability from addr."), + &maintenanceprintlist); + /* Register the target. */ linux_target = &the_aarch64_linux_nat_target; add_inf_child_target (&the_aarch64_linux_nat_target); diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index 98d17b2deb9..66d3a929af2 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -2109,6 +2109,22 @@ aarch64_linux_decode_memtag_section (struct gdbarch *gdbarch, return tags; } +/* 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) { @@ -2389,6 +2405,8 @@ 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 { diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index 46af565f324..66c19059db2 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -1110,6 +1110,13 @@ default_register_tag (struct gdbarch *gdbarch, return false; } +/* See arch-utils.h. */ +bool default_get_cap_tag_from_address (struct gdbarch *gdbarch, + CORE_ADDR addr) +{ + return false; +} + /* Static function declarations */ static void alloc_gdbarch_data (struct gdbarch *); diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h index 67d96e529f8..90469d2cb6a 100644 --- a/gdb/arch-utils.h +++ b/gdb/arch-utils.h @@ -310,4 +310,8 @@ extern bool default_register_has_tag (struct gdbarch *gdbarch, 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 */ diff --git a/gdb/configure.nat b/gdb/configure.nat index d219d6a960c..649f9a2ec74 100644 --- a/gdb/configure.nat +++ b/gdb/configure.nat @@ -237,7 +237,7 @@ case ${gdb_host} in NATDEPFILES="${NATDEPFILES} aarch64-nat.o aarch64-linux-nat.o \ aarch32-linux-nat.o nat/aarch64-hw-point.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 \ nat/aarch64-mte-linux-ptrace.o" ;; diff --git a/gdb/gdbarch-components.py b/gdb/gdbarch-components.py index 18b5dbc3baa..10e8da1732a 100644 --- a/gdb/gdbarch-components.py +++ b/gdb/gdbarch-components.py @@ -1022,6 +1022,17 @@ register. Normally the identity mapping. invalid=False, ) +Method( + comment=""" +Return the tag from a capability stored at address ADDR. +""", + type="bool", + name="get_cap_tag_from_address", + params=[("CORE_ADDR", "addr")], + predefault="default_get_cap_tag_from_address", + invalid=False, +) + Function( comment=""" Fetch the target specific address used to represent a load module. diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index 770601cf53f..bc3ab2a7f87 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -547,6 +547,12 @@ typedef int (gdbarch_remote_register_number_ftype) (struct gdbarch *gdbarch, int 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 bool gdbarch_fetch_tls_load_module_address_p (struct gdbarch *gdbarch); diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index e9d6ccd2e1f..27ed53950be 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -130,6 +130,7 @@ struct gdbarch 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; @@ -328,6 +329,7 @@ gdbarch_alloc (const struct gdbarch_info *info, 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; @@ -495,6 +497,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* 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 */ @@ -946,6 +949,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) fprintf_filtered (file, "gdbarch_dump: remote_register_number = <%s>\n", host_address_to_string (gdbarch->remote_register_number)); + fprintf_filtered (file, + "gdbarch_dump: get_cap_tag_from_address = <%s>\n", + host_address_to_string (gdbarch->get_cap_tag_from_address)); fprintf_filtered (file, "gdbarch_dump: gdbarch_fetch_tls_load_module_address_p() = %d\n", gdbarch_fetch_tls_load_module_address_p (gdbarch)); @@ -2992,6 +2998,23 @@ set_gdbarch_remote_register_number (struct gdbarch *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; +} + bool gdbarch_fetch_tls_load_module_address_p (struct gdbarch *gdbarch) { diff --git a/gdb/nat/aarch64-cap-linux.c b/gdb/nat/aarch64-cap-linux.c new file mode 100644 index 00000000000..cd204d11e47 --- /dev/null +++ b/gdb/nat/aarch64-cap-linux.c @@ -0,0 +1,37 @@ +/* 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 . */ + +#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; +} diff --git a/gdb/nat/aarch64-cap-linux.h b/gdb/nat/aarch64-cap-linux.h index 2b3c1dc5f3e..b5394704e4f 100644 --- a/gdb/nat/aarch64-cap-linux.h +++ b/gdb/nat/aarch64-cap-linux.h @@ -58,4 +58,11 @@ struct user_cap { 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 */ diff --git a/gdb/remote.c b/gdb/remote.c index 1a34812a4b8..68511621772 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -689,6 +689,8 @@ public: bool store_memtags (CORE_ADDR address, size_t len, const gdb::byte_vector &tags, int type) override; + gdb::byte_vector read_capability (CORE_ADDR addr) override; + public: /* Remote specific methods. */ void remote_download_command_source (int num, ULONGEST addr, @@ -2213,6 +2215,9 @@ enum { packets and the tag violation stop replies. */ PACKET_memory_tagging_feature, + /* Support for the qXfer:capa:read packet. */ + PACKET_qXfer_capability, + PACKET_MAX }; @@ -11263,6 +11268,19 @@ remote_target::xfer_partial (enum target_object object, 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) { @@ -14906,6 +14924,27 @@ test_memory_tagging_functions () } // namespace selftests #endif /* GDB_SELF_TEST */ +/* Implementation of the read_capability method. */ + +gdb::byte_vector +remote_target::read_capability (CORE_ADDR addr) +{ + gdb::optional cap; + gdb::byte_vector cap_vec; + + std::string addr_str = string_printf ("%s", phex_nz (addr, 0)); + + cap = target_read_alloc (current_inferior ()->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 () @@ -15302,6 +15341,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, add_packet_config_cmd (&remote_protocol_packets[PACKET_memory_tagging_feature], "memory-tagging-feature", "memory-tagging-feature", 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. */ { diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c index fd01b640f7c..c2e4d6e2666 100644 --- a/gdb/target-delegates.c +++ b/gdb/target-delegates.c @@ -196,6 +196,7 @@ struct dummy_target : public target_ops bool supports_memory_tagging () override; bool fetch_memtags (CORE_ADDR arg0, size_t arg1, gdb::byte_vector &arg2, int arg3) override; bool store_memtags (CORE_ADDR arg0, size_t arg1, const gdb::byte_vector &arg2, int arg3) override; + gdb::byte_vector read_capability (CORE_ADDR arg0) override; }; struct debug_target : public target_ops @@ -370,6 +371,7 @@ struct debug_target : public target_ops bool supports_memory_tagging () override; bool fetch_memtags (CORE_ADDR arg0, size_t arg1, gdb::byte_vector &arg2, int arg3) override; bool store_memtags (CORE_ADDR arg0, size_t arg1, const gdb::byte_vector &arg2, int arg3) override; + gdb::byte_vector read_capability (CORE_ADDR arg0) override; }; void @@ -4536,3 +4538,29 @@ debug_target::store_memtags (CORE_ADDR arg0, size_t arg1, const gdb::byte_vector return result; } +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; +} + diff --git a/gdb/target.c b/gdb/target.c index 1ee051b520a..529d28af7ca 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -4291,6 +4291,14 @@ target_done_generating_core (void) current_inferior ()->top_target ()->done_generating_core (); } +/* See target.h. */ + +gdb::byte_vector +target_read_capability (CORE_ADDR addr) +{ + return current_inferior ()->top_target ()->read_capability (addr); +} + static char targ_desc[] = diff --git a/gdb/target.h b/gdb/target.h index 4cc79df05b4..eb99cff174b 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -204,6 +204,8 @@ enum target_object TARGET_OBJECT_FREEBSD_VMMAP, /* FreeBSD process strings. */ TARGET_OBJECT_FREEBSD_PS_STRINGS, + /* CHERI capabilities. */ + TARGET_OBJECT_CAPABILITY, /* Possible future objects: TARGET_OBJECT_FILE, ... */ }; @@ -1320,6 +1322,10 @@ struct target_ops virtual bool store_memtags (CORE_ADDR address, size_t len, const gdb::byte_vector &tags, int type) TARGET_DEFAULT_NORETURN (tcomplain ()); + + /* 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 @@ -2580,4 +2586,7 @@ extern void target_prepare_to_generate_core (void); /* 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) */ diff --git a/gdb/valprint.c b/gdb/valprint.c index fc54a9f2fba..9830c5c4e56 100644 --- a/gdb/valprint.c +++ b/gdb/valprint.c @@ -526,9 +526,10 @@ generic_value_print_capability (struct value *val, struct ui_file *stream, 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 = type->arch (); + tag = gdbarch_get_cap_tag_from_address (gdbarch, value_address (val)); + } break; default: break; diff --git a/gdb/value.c b/gdb/value.c index 92769824e61..9ac3e014776 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -3961,6 +3961,13 @@ value_fetch_lazy_memory (struct value *val) read_value_memory (val, 0, value_stack (val), addr, value_contents_all_raw (val).data (), 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. */ diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv index d37053628fc..682b3919ea7 100644 --- a/gdbserver/configure.srv +++ b/gdbserver/configure.srv @@ -45,6 +45,7 @@ case "${gdbserver_host}" in 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" diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc index 78c57b031e3..cd68eaecc35 100644 --- a/gdbserver/linux-aarch64-low.cc +++ b/gdbserver/linux-aarch64-low.cc @@ -97,6 +97,12 @@ public: bool store_memtags (CORE_ADDR address, size_t len, const gdb::byte_vector &tags, int type) 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; @@ -3372,6 +3378,40 @@ aarch64_target::store_memtags (CORE_ADDR address, size_t len, return false; } +/* 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; diff --git a/gdbserver/server.cc b/gdbserver/server.cc index 8e53f226d3c..99dff6a383c 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -2009,11 +2009,29 @@ handle_qxfer_btrace_conf (const char *annex, 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 }, diff --git a/gdbserver/target.cc b/gdbserver/target.cc index 5009146d663..b20e07f93bc 100644 --- a/gdbserver/target.cc +++ b/gdbserver/target.cc @@ -532,6 +532,21 @@ process_stratum_target::qxfer_siginfo (const char *annex, 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 () { diff --git a/gdbserver/target.h b/gdbserver/target.h index aaa9dab742c..55ce2dec366 100644 --- a/gdbserver/target.h +++ b/gdbserver/target.h @@ -264,6 +264,14 @@ public: 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 ();