From: Luis Machado Date: Tue, 12 Jan 2021 18:53:00 +0000 (-0300) Subject: Enable capability writes to memory X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=69de3d06cf6102e9138274ebc87bcd48e21d8388;p=thirdparty%2Fbinutils-gdb.git Enable capability writes to memory Enable writing/forging capabilities to memory via the PTRACE_POKECAP ptrace request. This patch enables both GDB and gdbserver for Morello. For remote writes, we use the qXfer:capa:write packet. gdb/ChangeLog: 2021-03-17 Luis Machado * aarch64-linux-nat.c (aarch64_linux_nat_target) : New member function override. (aarch64_linux_nat_target::write_capability): New member function. (maint_print_cap_from_addr_cmd): Adjust printing format. (maint_set_capability_in_memory_cmd): New maintenance function. (add_show_debug_regs_command): Register new maintenance command. * nat/aarch64-cap-linux.c (aarch64_linux_write_capability): New function. * nat/aarch64-cap-linux.h (aarch64_linux_write_capability): New prototype. * remote.c (remote_target) : New member function override. Adjust enum documentation for PACKET_qXfer_capability. (remote_target::write_capability): New member function. (_initialize_remote): Adjust documentation for PACKET_qXfer_capability. * target-debug.h (target_debug_print_gdb_array_view_const_gdb_byte): New macro. * target-delegates.c: Regenerate. * target.c (target_write_capability): New function. * target.h (struct target_ops) : New virtual member function. (target_write_capability): New prototype. gdbserver/ChangeLog: 2021-03-17 Luis Machado * linux-aarch64-low.cc (aarch64_target::qxfer_capability): Handle capability writes. * server.cc (handle_qxfer_capability): Likewise. --- diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index 0d9e16b2349..189f3170251 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -111,6 +111,8 @@ public: const gdb::byte_vector &tags, int type) override; gdb::byte_vector read_capability (CORE_ADDR addr) override; + bool write_capability (CORE_ADDR addr, + gdb::array_view buffer) override; }; static aarch64_linux_nat_target the_aarch64_linux_nat_target; @@ -991,6 +993,25 @@ aarch64_linux_nat_target::read_capability (CORE_ADDR addr) return cap_vec; } +/* Implement the "write_capability" target_ops method. */ + +bool +aarch64_linux_nat_target::write_capability (CORE_ADDR addr, + gdb::array_view buffer) +{ + int tid = get_ptrace_pid (inferior_ptid); + + struct user_cap cap; + + memcpy (&cap.tag, buffer.data (), 1); + memcpy (&cap.val, buffer.data () + 1, 16); + + if (!aarch64_linux_write_capability (tid, addr, cap)) + perror_with_name (_("Unable to write capability from address.")); + + return true; +} + /* Implement the maintenance print capability tag command. */ static void @@ -1001,11 +1022,47 @@ maint_print_cap_from_addr_cmd (const char *args, int from_tty) cap = target_read_capability (addr); for (auto it : cap) - fprintf_unfiltered (gdb_stdlog, "%x ", it); + fprintf_unfiltered (gdb_stdlog, "%02x ", it); fputs_unfiltered ("\n", gdb_stdlog); } +/* 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.")); +} + void _initialize_aarch64_linux_nat (); void _initialize_aarch64_linux_nat () @@ -1016,6 +1073,10 @@ _initialize_aarch64_linux_nat () 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); + /* Register the target. */ linux_target = &the_aarch64_linux_nat_target; add_inf_child_target (&the_aarch64_linux_nat_target); diff --git a/gdb/nat/aarch64-cap-linux.c b/gdb/nat/aarch64-cap-linux.c index cd204d11e47..1b427803c16 100644 --- a/gdb/nat/aarch64-cap-linux.c +++ b/gdb/nat/aarch64-cap-linux.c @@ -35,3 +35,16 @@ aarch64_linux_read_capability (int tid, CORE_ADDR address, return true; } + +/* See aarch64-cap-linux.h */ + +bool +aarch64_linux_write_capability (int tid, CORE_ADDR address, + const user_cap &cap) +{ + /* Fetch the tag from ptrace. */ + if (ptrace (PTRACE_POKECAP, 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 b5394704e4f..3dc582a4c94 100644 --- a/gdb/nat/aarch64-cap-linux.h +++ b/gdb/nat/aarch64-cap-linux.h @@ -65,4 +65,11 @@ struct user_cap { extern bool aarch64_linux_read_capability (int tid, CORE_ADDR address, user_cap &cap); + +/* For thread TID, write capability CAP to the memory address ADDRESS. + + Return true if successful and false otherwise. */ + +extern bool aarch64_linux_write_capability (int tid, CORE_ADDR address, + const user_cap &cap); #endif /* NAT_AARCH64_CAP_LINUX_H */ diff --git a/gdb/remote.c b/gdb/remote.c index 68511621772..b941fbfdca4 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -690,6 +690,8 @@ public: const gdb::byte_vector &tags, int type) override; gdb::byte_vector read_capability (CORE_ADDR addr) override; + bool write_capability (CORE_ADDR addr, + gdb::array_view buffer) override; public: /* Remote specific methods. */ @@ -2215,7 +2217,7 @@ enum { packets and the tag violation stop replies. */ PACKET_memory_tagging_feature, - /* Support for the qXfer:capa:read packet. */ + /* Support for the qXfer:capa:read and qXfer:capa:write packets. */ PACKET_qXfer_capability, PACKET_MAX @@ -11277,6 +11279,10 @@ remote_target::xfer_partial (enum target_object object, readbuf, offset, len, xfered_len, &remote_protocol_packets [PACKET_qXfer_capability]); + else if (writebuf) + return remote_write_qxfer ("capa", annex, writebuf, offset, len, + xfered_len, &remote_protocol_packets + [PACKET_qXfer_capability]); else return TARGET_XFER_E_IO; } @@ -14945,6 +14951,28 @@ remote_target::read_capability (CORE_ADDR addr) return cap_vec; } +/* Implementation of the write_capability method. */ + +bool +remote_target::write_capability (CORE_ADDR addr, + gdb::array_view buffer) +{ + gdb_assert (!buffer.empty ()); + std::string addr_str = string_printf ("%s", phex_nz (addr, 0)); + ULONGEST xfered_len; + enum target_xfer_status status; + + status = target_xfer_partial (current_inferior ()->top_target (), + TARGET_OBJECT_CAPABILITY, addr_str.c_str (), + nullptr, buffer.data (), 0, buffer.size (), + &xfered_len); + + if (status != TARGET_XFER_OK) + perror_with_name (_("Unable to write capability to address.")); + + return true; +} + void _initialize_remote (); void _initialize_remote () @@ -15342,7 +15370,7 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, "memory-tagging-feature", "memory-tagging-feature", 0); add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_capability], - "qXfer:capa:read", "read-capability", 0); + "qXfer:capa:read", "read-write-capability", 0); /* Assert that we've registered "set remote foo-packet" commands for all packet configs. */ diff --git a/gdb/target-debug.h b/gdb/target-debug.h index e7a509858a7..86c856d4f94 100644 --- a/gdb/target-debug.h +++ b/gdb/target-debug.h @@ -160,6 +160,8 @@ target_debug_do_print (host_address_to_string (X.get ())) #define target_debug_print_gdb_array_view_const_int(X) \ target_debug_do_print (host_address_to_string (X.data ())) +#define target_debug_print_gdb_array_view_const_gdb_byte(X) \ + target_debug_do_print (host_address_to_string (X.data ())) #define target_debug_print_inferior_p(inf) \ target_debug_do_print (host_address_to_string (inf)) #define target_debug_print_record_print_flags(X) \ diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c index c2e4d6e2666..693c9f13e6b 100644 --- a/gdb/target-delegates.c +++ b/gdb/target-delegates.c @@ -197,6 +197,7 @@ struct dummy_target : public target_ops 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; + bool write_capability (CORE_ADDR arg0, gdb::array_view arg1) override; }; struct debug_target : public target_ops @@ -372,6 +373,7 @@ struct debug_target : public target_ops 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; + bool write_capability (CORE_ADDR arg0, gdb::array_view arg1) override; }; void @@ -4564,3 +4566,31 @@ debug_target::read_capability (CORE_ADDR arg0) return result; } +bool +target_ops::write_capability (CORE_ADDR arg0, gdb::array_view arg1) +{ + return this->beneath ()->write_capability (arg0, arg1); +} + +bool +dummy_target::write_capability (CORE_ADDR arg0, gdb::array_view arg1) +{ + tcomplain (); +} + +bool +debug_target::write_capability (CORE_ADDR arg0, gdb::array_view arg1) +{ + bool result; + fprintf_unfiltered (gdb_stdlog, "-> %s->write_capability (...)\n", this->beneath ()->shortname ()); + result = this->beneath ()->write_capability (arg0, arg1); + fprintf_unfiltered (gdb_stdlog, "<- %s->write_capability (", this->beneath ()->shortname ()); + target_debug_print_CORE_ADDR (arg0); + fputs_unfiltered (", ", gdb_stdlog); + target_debug_print_gdb_array_view_const_gdb_byte (arg1); + fputs_unfiltered (") = ", gdb_stdlog); + target_debug_print_bool (result); + fputs_unfiltered ("\n", gdb_stdlog); + return result; +} + diff --git a/gdb/target.c b/gdb/target.c index 529d28af7ca..b929e443722 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -4299,6 +4299,15 @@ target_read_capability (CORE_ADDR addr) return current_inferior ()->top_target ()->read_capability (addr); } +/* See target.h. */ + +bool +target_write_capability (CORE_ADDR addr, + gdb::array_view buffer) +{ + return current_inferior ()->top_target ()->write_capability (addr, buffer); +} + static char targ_desc[] = diff --git a/gdb/target.h b/gdb/target.h index eb99cff174b..7bfe6b2f1f0 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -1326,6 +1326,11 @@ struct target_ops /* Read a capability from ADDR. */ virtual gdb::byte_vector read_capability (CORE_ADDR addr) TARGET_DEFAULT_NORETURN (tcomplain ()); + + /* Write CAPABILITY to ADDR. */ + virtual bool write_capability (CORE_ADDR addr, + gdb::array_view buffer) + TARGET_DEFAULT_NORETURN (tcomplain ()); }; /* Deleter for std::unique_ptr. See comments in @@ -2589,4 +2594,8 @@ extern void target_done_generating_core (void); /* See read_capability. */ extern gdb::byte_vector target_read_capability (CORE_ADDR addr); +/* See write_capability. */ +extern bool target_write_capability (CORE_ADDR addr, + gdb::array_view buffer); + #endif /* !defined (TARGET_H) */ diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc index c9c05c06550..9c90f61a6d6 100644 --- a/gdbserver/linux-aarch64-low.cc +++ b/gdbserver/linux-aarch64-low.cc @@ -3430,14 +3430,31 @@ aarch64_target::qxfer_capability (const CORE_ADDR address, struct user_cap cap; - if (!aarch64_linux_read_capability (tid, address, cap)) + if (readbuf != nullptr) { - warning (_("Unable to read capability from address.")); - return 0; + if (!aarch64_linux_read_capability (tid, address, cap)) + { + warning (_("Unable to read capability from address.")); + return 0; + } + + /* Copy data to readbuf. */ + memcpy (readbuf, &cap.tag, 1); + memcpy (readbuf + 1, &cap.val, 16); } + else + { + /* Copy data from writebuf. */ + memcpy (&cap.tag, writebuf, 1); + memcpy (&cap.val, writebuf + 1, 16); + memset (&cap.__reserved, 0, 15); - memcpy (readbuf, &cap.tag, 1); - memcpy (readbuf + 1, &cap.val, 16); + if (!aarch64_linux_write_capability (tid, address, cap)) + { + warning (_("Unable to write capability to address.")); + return 0; + } + } return sizeof (cap.val) + 1; } diff --git a/gdbserver/server.cc b/gdbserver/server.cc index 99dff6a383c..6153ff54fda 100644 --- a/gdbserver/server.cc +++ b/gdbserver/server.cc @@ -2016,14 +2016,27 @@ 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) + if (!the_target->supports_qxfer_capability ()) return -2; + gdb_assert (readbuf != nullptr || writebuf != nullptr); + CORE_ADDR addr; unpack_varlen_hex (annex, &addr); - /* Read a capability and its tag. */ - return the_target->qxfer_capability (addr, readbuf, NULL, offset, len); + if (readbuf != nullptr) + { + /* Read a capability and its tag. */ + return the_target->qxfer_capability (addr, readbuf, nullptr, offset, len); + } + else + { + /* Write a capability to memory. */ + return the_target->qxfer_capability (addr, nullptr, writebuf, offset, + len); + } + + return -2; } static const struct qxfer qxfer_packets[] =