mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
Compare commits
23 Commits
gdb-15-bra
...
users/luis
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46195acfb8 | ||
|
|
59bdb29c7e | ||
|
|
df7edbc7ee | ||
|
|
c809eaadc1 | ||
|
|
e92884efd1 | ||
|
|
8b8f1889f5 | ||
|
|
2976b5d8eb | ||
|
|
e2d315328c | ||
|
|
24e4b061dd | ||
|
|
b2c6c98b79 | ||
|
|
bf48f39ca7 | ||
|
|
82fb00d0bc | ||
|
|
dbf9c97719 | ||
|
|
61228fd03d | ||
|
|
a7316504cb | ||
|
|
3e1f0bdf46 | ||
|
|
ba84cc1e85 | ||
|
|
ef0b92bbf4 | ||
|
|
4dbf3463f8 | ||
|
|
8bde5d89ad | ||
|
|
31a8e7b4b2 | ||
|
|
507666716c | ||
|
|
10d0e3a8a3 |
@@ -682,6 +682,7 @@ ALL_64_TARGET_OBS = \
|
||||
amd64-windows-tdep.o \
|
||||
arch/aarch64.o \
|
||||
arch/aarch64-insn.o \
|
||||
arch/aarch64-mte-linux.o \
|
||||
arch/amd64.o \
|
||||
ia64-linux-tdep.o \
|
||||
ia64-tdep.o \
|
||||
@@ -1446,6 +1447,7 @@ HFILES_NO_SRCDIR = \
|
||||
arch/aarch32.h \
|
||||
arch/aarch64.h \
|
||||
arch/aarch64-insn.h \
|
||||
arch/aarch64-mte-linux.h \
|
||||
arch/arc.h \
|
||||
arch/arm.h \
|
||||
arch/i386.h \
|
||||
@@ -1482,6 +1484,7 @@ HFILES_NO_SRCDIR = \
|
||||
nat/aarch64-linux.h \
|
||||
nat/aarch64-linux-hw-point.h \
|
||||
nat/aarch64-sve-linux-ptrace.h \
|
||||
nat/aarch64-mte-linux-ptrace.h \
|
||||
nat/amd64-linux-siginfo.h \
|
||||
nat/gdb_ptrace.h \
|
||||
nat/gdb_thread_db.h \
|
||||
|
||||
32
gdb/NEWS
32
gdb/NEWS
@@ -3,6 +3,19 @@
|
||||
|
||||
*** Changes since GDB 9
|
||||
|
||||
* GDB now supports general memory tagging functionality if the underlying
|
||||
architecture supports the proper primitives and hooks. Currently this is
|
||||
enabled only for AArch64 MTE.
|
||||
|
||||
This includes additional information when the inferior crashes with a
|
||||
SIGSEGV caused by a memory tag violation.
|
||||
|
||||
* The "x" command supports the 'm' modifier to display allocation tags for
|
||||
a particular memory range.
|
||||
|
||||
* The "print" command will display memory tag mismatches for addresses and
|
||||
pointers, if memory tagging is supported by the architecture.
|
||||
|
||||
* Help and apropos commands will now show the documentation of a
|
||||
command only once, even if that command has one or more aliases.
|
||||
These commands now show the command name, then all of its aliases,
|
||||
@@ -63,8 +76,27 @@
|
||||
* On Windows targets, it is now possible to debug 32-bit programs with a
|
||||
64-bit GDB.
|
||||
|
||||
* New remote packets
|
||||
|
||||
qMemTags
|
||||
Request the remote to send allocation tags for a particular memory range.
|
||||
QMemTags
|
||||
Request the remote to store the specified allocation tags to the requested
|
||||
memory range.
|
||||
|
||||
* New commands
|
||||
|
||||
mtag showltag ADDRESS
|
||||
Show the logical tag for ADDRESS.
|
||||
mtag setltag ADDRESS TAG
|
||||
Set the logical tag for ADDRESS to TAG.
|
||||
mtag showatag ADDRESS
|
||||
Show the allocation tag for ADDRESS.
|
||||
mtag setatag ADDRESS LENGTH TAGS
|
||||
Set the allocation tag for [ADDRESS, ADDRESS + LENGTH) to TAGS.
|
||||
mtag check ADDRESS
|
||||
Validate that ADDRESS' logical tag matches the allocation tag.
|
||||
|
||||
set exec-file-mismatch -- Set exec-file-mismatch handling (ask|warn|off).
|
||||
show exec-file-mismatch -- Show exec-file-mismatch handling (ask|warn|off).
|
||||
Set or show the option 'exec-file-mismatch'. When GDB attaches to a
|
||||
|
||||
@@ -50,6 +50,10 @@
|
||||
#include "gdb_proc_service.h"
|
||||
#include "arch-utils.h"
|
||||
|
||||
#include "arch/aarch64-mte-linux.h"
|
||||
|
||||
#include "nat/aarch64-mte-linux-ptrace.h"
|
||||
|
||||
#ifndef TRAP_HWBKPT
|
||||
#define TRAP_HWBKPT 0x0004
|
||||
#endif
|
||||
@@ -100,6 +104,16 @@ public:
|
||||
override;
|
||||
|
||||
struct gdbarch *thread_architecture (ptid_t) override;
|
||||
|
||||
bool supports_memory_tagging () override;
|
||||
|
||||
/* Read memory allocation tags from memory via PTRACE. */
|
||||
int fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags) override;
|
||||
|
||||
/* Write allocation tags to memory via PTRACE. */
|
||||
int store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags) override;
|
||||
};
|
||||
|
||||
static aarch64_linux_nat_target the_aarch64_linux_nat_target;
|
||||
@@ -459,6 +473,60 @@ fetch_pauth_masks_from_thread (struct regcache *regcache)
|
||||
&pauth_regset[1]);
|
||||
}
|
||||
|
||||
/* Fill GDB's register array with the MTE register values from
|
||||
the current thread. */
|
||||
|
||||
static void
|
||||
fetch_mteregs_from_thread (struct regcache *regcache)
|
||||
{
|
||||
struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ());
|
||||
int regno = tdep->mte_reg_base;
|
||||
|
||||
gdb_assert (regno != -1);
|
||||
|
||||
uint64_t tag_ctl = 0;
|
||||
struct iovec iovec;
|
||||
|
||||
iovec.iov_base = &tag_ctl;
|
||||
iovec.iov_len = sizeof (tag_ctl);
|
||||
|
||||
int tid = regcache->ptid ().lwp ();
|
||||
if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_TAGGED_ADDR_CTRL,
|
||||
&iovec) != 0)
|
||||
perror_with_name (_("unable to fetch MTE registers."));
|
||||
|
||||
regcache->raw_supply (regno, &tag_ctl);
|
||||
}
|
||||
|
||||
/* Store to the current thread the valid MTE register set in the GDB's
|
||||
register array. */
|
||||
|
||||
static void
|
||||
store_mteregs_to_thread (struct regcache *regcache)
|
||||
{
|
||||
struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ());
|
||||
int regno = tdep->mte_reg_base;
|
||||
|
||||
gdb_assert (regno != -1);
|
||||
|
||||
uint64_t tag_ctl = 0;
|
||||
|
||||
if (REG_VALID != regcache->get_register_status (regno))
|
||||
return;
|
||||
|
||||
regcache->raw_collect (regno, (char *) &tag_ctl);
|
||||
|
||||
struct iovec iovec;
|
||||
|
||||
iovec.iov_base = &tag_ctl;
|
||||
iovec.iov_len = sizeof (tag_ctl);
|
||||
|
||||
int tid = regcache->ptid ().lwp ();
|
||||
if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_TAGGED_ADDR_CTRL,
|
||||
&iovec) != 0)
|
||||
perror_with_name (_("unable to store MTE registers."));
|
||||
}
|
||||
|
||||
/* Implement the "fetch_registers" target_ops method. */
|
||||
|
||||
void
|
||||
@@ -477,6 +545,9 @@ aarch64_linux_nat_target::fetch_registers (struct regcache *regcache,
|
||||
|
||||
if (tdep->has_pauth ())
|
||||
fetch_pauth_masks_from_thread (regcache);
|
||||
|
||||
if (tdep->has_mte ())
|
||||
fetch_mteregs_from_thread (regcache);
|
||||
}
|
||||
else if (regno < AARCH64_V0_REGNUM)
|
||||
fetch_gregs_from_thread (regcache);
|
||||
@@ -491,6 +562,11 @@ aarch64_linux_nat_target::fetch_registers (struct regcache *regcache,
|
||||
|| regno == AARCH64_PAUTH_CMASK_REGNUM (tdep->pauth_reg_base))
|
||||
fetch_pauth_masks_from_thread (regcache);
|
||||
}
|
||||
|
||||
/* Fetch individual MTE registers. */
|
||||
if (tdep->has_mte ()
|
||||
&& (regno == tdep->mte_reg_base))
|
||||
fetch_mteregs_from_thread (regcache);
|
||||
}
|
||||
|
||||
/* Implement the "store_registers" target_ops method. */
|
||||
@@ -508,6 +584,9 @@ aarch64_linux_nat_target::store_registers (struct regcache *regcache,
|
||||
store_sveregs_to_thread (regcache);
|
||||
else
|
||||
store_fpregs_to_thread (regcache);
|
||||
|
||||
if (tdep->has_mte ())
|
||||
store_mteregs_to_thread (regcache);
|
||||
}
|
||||
else if (regno < AARCH64_V0_REGNUM)
|
||||
store_gregs_to_thread (regcache);
|
||||
@@ -515,6 +594,11 @@ aarch64_linux_nat_target::store_registers (struct regcache *regcache,
|
||||
store_sveregs_to_thread (regcache);
|
||||
else
|
||||
store_fpregs_to_thread (regcache);
|
||||
|
||||
/* Store MTE registers. */
|
||||
if (tdep->has_mte ()
|
||||
&& (regno == tdep->mte_reg_base))
|
||||
store_mteregs_to_thread (regcache);
|
||||
}
|
||||
|
||||
/* Fill register REGNO (if it is a general-purpose register) in
|
||||
@@ -651,9 +735,12 @@ aarch64_linux_nat_target::read_description ()
|
||||
return aarch32_read_description ();
|
||||
|
||||
CORE_ADDR hwcap = linux_get_hwcap (this);
|
||||
CORE_ADDR hwcap2 = linux_get_hwcap2 (this);
|
||||
|
||||
return aarch64_read_description (aarch64_sve_get_vq (tid),
|
||||
hwcap & AARCH64_HWCAP_PACA);
|
||||
bool pauth_p = hwcap & AARCH64_HWCAP_PACA;
|
||||
bool mte_p = hwcap2 & HWCAP2_MTE;
|
||||
|
||||
return aarch64_read_description (aarch64_sve_get_vq (tid), pauth_p, mte_p);
|
||||
}
|
||||
|
||||
/* Convert a native/host siginfo object, into/from the siginfo in the
|
||||
@@ -975,6 +1062,36 @@ aarch64_linux_nat_target::thread_architecture (ptid_t ptid)
|
||||
return gdbarch_find_by_info (info);
|
||||
}
|
||||
|
||||
/* Implement the "supports_memory_tagging" target_ops method. */
|
||||
|
||||
bool
|
||||
aarch64_linux_nat_target::supports_memory_tagging ()
|
||||
{
|
||||
return (linux_get_hwcap2 (this) & HWCAP2_MTE) != 0;
|
||||
}
|
||||
|
||||
/* Implement the "fetch_memtags" target_ops method. */
|
||||
|
||||
int
|
||||
aarch64_linux_nat_target::fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags)
|
||||
{
|
||||
int tid = inferior_ptid.lwp ();
|
||||
|
||||
return aarch64_mte_fetch_memtags (tid, address, len, tags);
|
||||
}
|
||||
|
||||
/* Implement the "store_memtags" target_ops method. */
|
||||
|
||||
int
|
||||
aarch64_linux_nat_target::store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags)
|
||||
{
|
||||
int tid = inferior_ptid.lwp ();
|
||||
|
||||
return aarch64_mte_store_memtags (tid, address, len, tags);
|
||||
}
|
||||
|
||||
/* Define AArch64 maintenance commands. */
|
||||
|
||||
static void
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "symtab.h"
|
||||
#include "tramp-frame.h"
|
||||
#include "trad-frame.h"
|
||||
#include "target.h"
|
||||
#include "target/target.h"
|
||||
|
||||
#include "regcache.h"
|
||||
@@ -44,6 +45,13 @@
|
||||
#include "record-full.h"
|
||||
#include "linux-record.h"
|
||||
|
||||
#include "arch/aarch64-mte-linux.h"
|
||||
|
||||
#include "arch-utils.h"
|
||||
#include "value.h"
|
||||
|
||||
#include "gdbsupport/selftest.h"
|
||||
|
||||
/* Signal frame handling.
|
||||
|
||||
+------------+ ^
|
||||
@@ -652,9 +660,12 @@ aarch64_linux_core_read_description (struct gdbarch *gdbarch,
|
||||
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 mte_p = hwcap2 & HWCAP2_MTE;
|
||||
return aarch64_read_description (aarch64_linux_core_read_vq (gdbarch, abfd),
|
||||
hwcap & AARCH64_HWCAP_PACA);
|
||||
pauth_p, mte_p);
|
||||
}
|
||||
|
||||
/* Implementation of `gdbarch_stap_is_single_operand', as defined in
|
||||
@@ -1432,6 +1443,244 @@ aarch64_linux_gcc_target_options (struct gdbarch *gdbarch)
|
||||
return {};
|
||||
}
|
||||
|
||||
/* Helper to get the allocation tag from a 64-bit ADDRESS.
|
||||
|
||||
Return 0 for success and non-zero otherwise. */
|
||||
|
||||
static int
|
||||
aarch64_linux_get_atag (CORE_ADDR address, CORE_ADDR *tag)
|
||||
{
|
||||
gdb::byte_vector tags;
|
||||
|
||||
/* Attempt to fetch the allocation tag. */
|
||||
if (target_fetch_memtags (address, 0, tags) != 0)
|
||||
return 1;
|
||||
|
||||
/* Only one tag should've been returned. Make sure we got exactly that. */
|
||||
gdb_assert (tags.size () == 1);
|
||||
|
||||
/* Although our tags are 4 bits in size, they are stored in a
|
||||
byte. */
|
||||
*tag = tags[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Implement the tagged_address_p gdbarch method. */
|
||||
|
||||
static bool
|
||||
aarch64_linux_tagged_address_p (struct gdbarch *gdbarch, struct value *address)
|
||||
{
|
||||
gdb_assert (address != nullptr);
|
||||
|
||||
CORE_ADDR addr = value_as_address (address);
|
||||
|
||||
/* Remove the top byte for the memory range check. */
|
||||
addr = address_significant (gdbarch, addr);
|
||||
|
||||
/* Check if the page that contains ADDRESS is mapped with PROT_MTE. */
|
||||
if (!linux_address_in_memtag_page (addr))
|
||||
return false;
|
||||
|
||||
/* We have a valid tag in the top byte of the 64-bit address. */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Implement the memtag_mismatch_p gdbarch method. */
|
||||
|
||||
static bool
|
||||
aarch64_linux_memtag_mismatch_p (struct gdbarch *gdbarch,
|
||||
struct value *address)
|
||||
{
|
||||
gdb_assert (address != nullptr);
|
||||
|
||||
/* Make sure we are dealing with a tagged address to begin with. */
|
||||
if (!aarch64_linux_tagged_address_p (gdbarch, address))
|
||||
return false;
|
||||
|
||||
CORE_ADDR addr = value_as_address (address);
|
||||
|
||||
/* Fetch the allocation tag for ADDRESS. */
|
||||
CORE_ADDR atag = 0;
|
||||
|
||||
if (aarch64_linux_get_atag (addr, &atag) != 0)
|
||||
return false;
|
||||
|
||||
/* Fetch the logical tag for ADDRESS. */
|
||||
gdb_byte ltag = aarch64_linux_get_ltag (addr);
|
||||
|
||||
/* Are the tags the same? */
|
||||
if (ltag == atag)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Implement the set_memtags gdbarch method. */
|
||||
|
||||
static int
|
||||
aarch64_linux_set_memtags (struct gdbarch *gdbarch, struct value *address,
|
||||
size_t length, const gdb::byte_vector &tags,
|
||||
enum memtag_type tag_type)
|
||||
{
|
||||
if (tags.empty ())
|
||||
return 0;
|
||||
|
||||
gdb_assert (address != nullptr);
|
||||
|
||||
CORE_ADDR addr = value_as_address (address);
|
||||
|
||||
/* Set the logical tag or the allocation tag. */
|
||||
if (tag_type == tag_logical)
|
||||
{
|
||||
/* When setting logical tags, we don't care about the length, since
|
||||
we are only setting a single logical tag. */
|
||||
addr = aarch64_linux_set_ltag (addr, tags[0]);
|
||||
|
||||
/* Update the value's content with the tag. */
|
||||
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
|
||||
gdb_byte *srcbuf = value_contents_raw (address);
|
||||
store_unsigned_integer (srcbuf, sizeof (addr), byte_order, addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Make sure we are dealing with a tagged address to begin with. */
|
||||
if (!aarch64_linux_tagged_address_p (gdbarch, address))
|
||||
return 1;
|
||||
|
||||
/* With G being the number of tag granules and N the number of tags
|
||||
passed in, we can have the following cases:
|
||||
|
||||
1 - G == N: Store all the N tags to memory.
|
||||
|
||||
2 - G < N : Warn about having more tags than granules, but write G
|
||||
tags.
|
||||
|
||||
3 - G > N : This is a "fill tags" operation. We should use the tags
|
||||
as a pattern to fill the granules repeatedly until we have
|
||||
written G tags to memory.
|
||||
*/
|
||||
|
||||
size_t g = get_tag_granules (addr, length, MTE_GRANULE_SIZE);
|
||||
size_t n = tags.size ();
|
||||
|
||||
if (g < n)
|
||||
{
|
||||
warning (_("Got more tags than memory granules. Tags will be "
|
||||
"truncated."));
|
||||
}
|
||||
else if (g > n)
|
||||
warning (_("Using tag pattern to fill memory range."));
|
||||
|
||||
if (target_store_memtags (addr, length, tags) != 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Implement the get_memtag gdbarch method. */
|
||||
|
||||
static struct value *
|
||||
aarch64_linux_get_memtag (struct gdbarch *gdbarch, struct value *address,
|
||||
enum memtag_type tag_type)
|
||||
{
|
||||
gdb_assert (address != nullptr);
|
||||
|
||||
CORE_ADDR addr = value_as_address (address);
|
||||
CORE_ADDR tag = 0;
|
||||
|
||||
/* Get the logical tag or the allocation tag. */
|
||||
if (tag_type == tag_logical)
|
||||
tag = aarch64_linux_get_ltag (addr);
|
||||
else
|
||||
{
|
||||
/* Make sure we are dealing with a tagged address to begin with. */
|
||||
if (!aarch64_linux_tagged_address_p (gdbarch, address))
|
||||
return nullptr;
|
||||
|
||||
if (aarch64_linux_get_atag (addr, &tag) != 0)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Convert the tag to a value. */
|
||||
return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
|
||||
tag);
|
||||
}
|
||||
|
||||
/* Implement the memtag_to_string gdbarch method. */
|
||||
|
||||
static std::string
|
||||
aarch64_linux_memtag_to_string (struct gdbarch *gdbarch,
|
||||
struct value *address,
|
||||
enum memtag_type tag_type)
|
||||
{
|
||||
gdb_assert (address != nullptr);
|
||||
|
||||
struct value *v_tag = aarch64_linux_get_memtag (gdbarch, address, tag_type);
|
||||
|
||||
if (v_tag == nullptr && tag_allocation)
|
||||
error (_("Error getting tag from target"));
|
||||
|
||||
CORE_ADDR tag = value_as_address (v_tag);
|
||||
|
||||
return string_printf ("0x%s", phex_nz (tag, sizeof (tag)));
|
||||
}
|
||||
|
||||
/* AArch64 Linux implementation of the handle_segmentation_fault gdbarch
|
||||
hook. Displays information about possible memory tag violations. */
|
||||
|
||||
static void
|
||||
aarch64_linux_handle_segmentation_fault (struct gdbarch *gdbarch,
|
||||
struct ui_out *uiout)
|
||||
{
|
||||
CORE_ADDR fault_addr = 0;
|
||||
long si_code = 0;
|
||||
|
||||
try
|
||||
{
|
||||
/* Sigcode tells us if the segfault is actually a memory tag
|
||||
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 memory tag violation, just return. */
|
||||
if (si_code != SEGV_MTEAERR && si_code != SEGV_MTESERR)
|
||||
return;
|
||||
|
||||
uiout->text ("\n");
|
||||
|
||||
uiout->field_string ("sigcode-meaning", _("Memory tag violation"));
|
||||
|
||||
/* For synchronous faults, show additional information. */
|
||||
if (si_code == SEGV_MTESERR)
|
||||
{
|
||||
uiout->text (_(" while accessing address "));
|
||||
uiout->field_core_addr ("fault-addr", gdbarch, fault_addr);
|
||||
uiout->text ("\n");
|
||||
|
||||
CORE_ADDR atag;
|
||||
if (aarch64_linux_get_atag (fault_addr, &atag) != 0)
|
||||
uiout->text (_("Allocation tag unavailable"));
|
||||
else
|
||||
{
|
||||
uiout->text (_("Allocation tag "));
|
||||
uiout->field_core_addr ("allocation-tag", gdbarch, atag);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uiout->text ("\n");
|
||||
uiout->text (_("Fault address unavailable"));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
|
||||
{
|
||||
@@ -1489,6 +1738,34 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
|
||||
data associated with the address. */
|
||||
set_gdbarch_significant_addr_bit (gdbarch, 56);
|
||||
|
||||
/* MTE-specific settings and hooks. */
|
||||
if (tdep->has_mte ())
|
||||
{
|
||||
/* Register a hook for checking if an address is tagged or not. */
|
||||
set_gdbarch_tagged_address_p (gdbarch, aarch64_linux_tagged_address_p);
|
||||
|
||||
/* Register a hook for checking if there is a memory tag mismatch. */
|
||||
set_gdbarch_memtag_mismatch_p (gdbarch,
|
||||
aarch64_linux_memtag_mismatch_p);
|
||||
|
||||
/* Register a hook for setting the logical/allocation tags for
|
||||
a range of addresses. */
|
||||
set_gdbarch_set_memtags (gdbarch, aarch64_linux_set_memtags);
|
||||
|
||||
/* Register a hook for extracting the logical/allocation tag from an
|
||||
address. */
|
||||
set_gdbarch_get_memtag (gdbarch, aarch64_linux_get_memtag);
|
||||
|
||||
/* Set the allocation tag granule size to 16 bytes. */
|
||||
set_gdbarch_memtag_granule_size (gdbarch, MTE_GRANULE_SIZE);
|
||||
|
||||
/* Register a hook for converting a memory tag to a string. */
|
||||
set_gdbarch_memtag_to_string (gdbarch, aarch64_linux_memtag_to_string);
|
||||
|
||||
set_gdbarch_handle_segmentation_fault (gdbarch,
|
||||
aarch64_linux_handle_segmentation_fault);
|
||||
}
|
||||
|
||||
/* Initialize the aarch64_linux_record_tdep. */
|
||||
/* These values are the size of the type that will be used in a system
|
||||
call. They are obtained from Linux Kernel source. */
|
||||
@@ -1665,10 +1942,39 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
|
||||
set_gdbarch_gcc_target_options (gdbarch, aarch64_linux_gcc_target_options);
|
||||
}
|
||||
|
||||
#if GDB_SELF_TEST
|
||||
|
||||
namespace selftests {
|
||||
|
||||
/* Verify functions to read and write logical tags. */
|
||||
|
||||
static void
|
||||
aarch64_linux_ltag_tests (void)
|
||||
{
|
||||
/* We have 4 bits of tags, but we test writing all the bits of the top
|
||||
byte of address. */
|
||||
for (int i = 0; i < 1 << 8; i++)
|
||||
{
|
||||
CORE_ADDR addr = ((CORE_ADDR) i << 56) | 0xdeadbeef;
|
||||
SELF_CHECK (aarch64_linux_get_ltag (addr) == (i & 0xf));
|
||||
|
||||
addr = aarch64_linux_set_ltag (0xdeadbeef, i);
|
||||
SELF_CHECK (addr = ((CORE_ADDR) (i & 0xf) << 56) | 0xdeadbeef);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace selftests
|
||||
#endif /* GDB_SELF_TEST */
|
||||
|
||||
void _initialize_aarch64_linux_tdep ();
|
||||
void
|
||||
_initialize_aarch64_linux_tdep ()
|
||||
{
|
||||
gdbarch_register_osabi (bfd_arch_aarch64, 0, GDB_OSABI_LINUX,
|
||||
aarch64_linux_init_abi);
|
||||
|
||||
#if GDB_SELF_TEST
|
||||
selftests::register_test ("aarch64-linux-tagged-address",
|
||||
selftests::aarch64_linux_ltag_tests);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
#define HA_MAX_NUM_FLDS 4
|
||||
|
||||
/* All possible aarch64 target descriptors. */
|
||||
struct target_desc *tdesc_aarch64_list[AARCH64_MAX_SVE_VQ + 1][2/*pauth*/];
|
||||
struct target_desc *tdesc_aarch64_list[AARCH64_MAX_SVE_VQ + 1][2/*pauth*/][2 /* mte */];
|
||||
|
||||
/* The standard register names, and all the valid aliases for them. */
|
||||
static const struct
|
||||
@@ -176,6 +176,12 @@ static const char *const aarch64_pauth_register_names[] =
|
||||
"pauth_cmask"
|
||||
};
|
||||
|
||||
static const char *const aarch64_mte_register_names[] =
|
||||
{
|
||||
/* Tag Control Register. */
|
||||
"tag_ctl"
|
||||
};
|
||||
|
||||
/* AArch64 prologue cache structure. */
|
||||
struct aarch64_prologue_cache
|
||||
{
|
||||
@@ -3139,21 +3145,23 @@ aarch64_displaced_step_hw_singlestep (struct gdbarch *gdbarch,
|
||||
|
||||
/* Get the correct target description for the given VQ value.
|
||||
If VQ is zero then it is assumed SVE is not supported.
|
||||
(It is not possible to set VQ to zero on an SVE system). */
|
||||
(It is not possible to set VQ to zero on an SVE system).
|
||||
|
||||
MTE_P indicates the presence of the Memory Tagging Extension feature. */
|
||||
|
||||
const target_desc *
|
||||
aarch64_read_description (uint64_t vq, bool pauth_p)
|
||||
aarch64_read_description (uint64_t vq, bool pauth_p, bool mte_p)
|
||||
{
|
||||
if (vq > AARCH64_MAX_SVE_VQ)
|
||||
error (_("VQ is %" PRIu64 ", maximum supported value is %d"), vq,
|
||||
AARCH64_MAX_SVE_VQ);
|
||||
|
||||
struct target_desc *tdesc = tdesc_aarch64_list[vq][pauth_p];
|
||||
struct target_desc *tdesc = tdesc_aarch64_list[vq][pauth_p][mte_p];
|
||||
|
||||
if (tdesc == NULL)
|
||||
{
|
||||
tdesc = aarch64_create_target_description (vq, pauth_p);
|
||||
tdesc_aarch64_list[vq][pauth_p] = tdesc;
|
||||
tdesc = aarch64_create_target_description (vq, pauth_p, mte_p);
|
||||
tdesc_aarch64_list[vq][pauth_p][mte_p] = tdesc;
|
||||
}
|
||||
|
||||
return tdesc;
|
||||
@@ -3223,6 +3231,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
||||
bool valid_p = true;
|
||||
int i, num_regs = 0, num_pseudo_regs = 0;
|
||||
int first_pauth_regnum = -1, pauth_ra_state_offset = -1;
|
||||
int first_mte_regnum = -1;
|
||||
|
||||
/* Use the vector length passed via the target info. Here -1 is used for no
|
||||
SVE, and 0 is unset. If unset then use the vector length from the existing
|
||||
@@ -3253,13 +3262,15 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
||||
value. */
|
||||
const struct target_desc *tdesc = info.target_desc;
|
||||
if (!tdesc_has_registers (tdesc) || vq != aarch64_get_tdesc_vq (tdesc))
|
||||
tdesc = aarch64_read_description (vq, false);
|
||||
tdesc = aarch64_read_description (vq, false, false);
|
||||
gdb_assert (tdesc);
|
||||
|
||||
feature_core = tdesc_find_feature (tdesc,"org.gnu.gdb.aarch64.core");
|
||||
feature_fpu = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.fpu");
|
||||
feature_sve = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sve");
|
||||
feature_pauth = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.pauth");
|
||||
const struct tdesc_feature *feature_mte
|
||||
= tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.mte");
|
||||
|
||||
if (feature_core == nullptr)
|
||||
return nullptr;
|
||||
@@ -3330,6 +3341,20 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
||||
num_pseudo_regs += 1; /* Count RA_STATE pseudo register. */
|
||||
}
|
||||
|
||||
/* Add the MTE registers. */
|
||||
if (feature_mte != NULL)
|
||||
{
|
||||
first_mte_regnum = num_regs;
|
||||
/* Validate the descriptor provides the mandatory MTE registers and
|
||||
allocate their numbers. */
|
||||
for (i = 0; i < ARRAY_SIZE (aarch64_mte_register_names); i++)
|
||||
valid_p &= tdesc_numbered_register (feature_mte, tdesc_data,
|
||||
first_mte_regnum + i,
|
||||
aarch64_mte_register_names[i]);
|
||||
|
||||
num_regs += i;
|
||||
}
|
||||
|
||||
if (!valid_p)
|
||||
{
|
||||
tdesc_data_cleanup (tdesc_data);
|
||||
@@ -3350,6 +3375,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
||||
tdep->pauth_reg_base = first_pauth_regnum;
|
||||
tdep->pauth_ra_state_regnum = (feature_pauth == NULL) ? -1
|
||||
: pauth_ra_state_offset + num_regs;
|
||||
tdep->mte_reg_base = first_mte_regnum;
|
||||
|
||||
set_gdbarch_push_dummy_call (gdbarch, aarch64_push_dummy_call);
|
||||
set_gdbarch_frame_align (gdbarch, aarch64_frame_align);
|
||||
|
||||
@@ -100,9 +100,19 @@ struct gdbarch_tdep
|
||||
{
|
||||
return pauth_reg_base != -1;
|
||||
}
|
||||
|
||||
/* First MTE register. This is -1 if no MTE registers are available. */
|
||||
int mte_reg_base;
|
||||
|
||||
/* Returns true if the target supports MTE. */
|
||||
bool has_mte () const
|
||||
{
|
||||
return mte_reg_base != -1;
|
||||
}
|
||||
};
|
||||
|
||||
const target_desc *aarch64_read_description (uint64_t vq, bool pauth_p);
|
||||
const target_desc *aarch64_read_description (uint64_t vq, bool pauth_p,
|
||||
bool mte_p);
|
||||
|
||||
extern int aarch64_process_record (struct gdbarch *gdbarch,
|
||||
struct regcache *regcache, CORE_ADDR addr);
|
||||
|
||||
@@ -78,6 +78,56 @@ legacy_register_sim_regno (struct gdbarch *gdbarch, int regnum)
|
||||
return LEGACY_SIM_REGNO_IGNORE;
|
||||
}
|
||||
|
||||
|
||||
/* See arch-utils.h */
|
||||
|
||||
std::string
|
||||
default_memtag_to_string (struct gdbarch *gdbarch, struct value *address,
|
||||
enum memtag_type tag_type)
|
||||
{
|
||||
/* By default, assume the address is untagged. */
|
||||
return "";
|
||||
}
|
||||
|
||||
/* See arch-utils.h */
|
||||
|
||||
bool
|
||||
default_tagged_address_p (struct gdbarch *gdbarch, struct value *address)
|
||||
{
|
||||
/* By default, assume the address is untagged. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* See arch-utils.h */
|
||||
|
||||
bool
|
||||
default_memtag_mismatch_p (struct gdbarch *gdbarch, struct value *address)
|
||||
{
|
||||
/* By default, assume there is no mismatch. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* See arch-utils.h */
|
||||
|
||||
int
|
||||
default_set_memtags (struct gdbarch *gdbarch, struct value *address,
|
||||
size_t length, const gdb::byte_vector &tags,
|
||||
enum memtag_type tag_type)
|
||||
{
|
||||
/* By default, return 0; */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See arch-utils.h */
|
||||
|
||||
struct value *
|
||||
default_get_memtag (struct gdbarch *gdbarch, struct value *address,
|
||||
enum memtag_type tag_type)
|
||||
{
|
||||
/* By default, return no tag. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CORE_ADDR
|
||||
generic_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
|
||||
{
|
||||
|
||||
@@ -134,6 +134,29 @@ extern const struct floatformat **
|
||||
default_floatformat_for_type (struct gdbarch *gdbarch,
|
||||
const char *name, int len);
|
||||
|
||||
/* Default implementation of gdbarch_tagged_address_p. */
|
||||
extern std::string default_memtag_to_string (struct gdbarch *gdbarch,
|
||||
struct value *address,
|
||||
enum memtag_type tag_type);
|
||||
|
||||
/* Default implementation of gdbarch_tagged_address_p. */
|
||||
bool default_tagged_address_p (struct gdbarch *gdbarch, struct value *address);
|
||||
|
||||
/* Default implementation of gdbarch_memtag_mismatch_p. */
|
||||
extern bool default_memtag_mismatch_p (struct gdbarch *gdbarch,
|
||||
struct value *address);
|
||||
|
||||
/* Default implementation of gdbarch_set_memtags. */
|
||||
int default_set_memtags (struct gdbarch *gdbarch,
|
||||
struct value *address, size_t length,
|
||||
const gdb::byte_vector &tags,
|
||||
enum memtag_type tag_type);
|
||||
|
||||
/* Default implementation of gdbarch_get_memtag. */
|
||||
struct value *default_get_memtag (struct gdbarch *gdbarch,
|
||||
struct value *address,
|
||||
enum memtag_type tag_type);
|
||||
|
||||
extern CORE_ADDR generic_skip_trampoline_code (struct frame_info *frame,
|
||||
CORE_ADDR pc);
|
||||
|
||||
|
||||
70
gdb/arch/aarch64-mte-linux.c
Normal file
70
gdb/arch/aarch64-mte-linux.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/* Common Linux target-dependent functionality for AArch64 MTE
|
||||
|
||||
Copyright (C) 2020 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 "arch/aarch64-mte-linux.h"
|
||||
|
||||
/* See arch/aarch64-mte-linux.h */
|
||||
|
||||
size_t
|
||||
get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size)
|
||||
{
|
||||
/* Start address */
|
||||
CORE_ADDR s_addr = align_down (addr, granule_size);
|
||||
/* End address */
|
||||
CORE_ADDR e_addr = align_down (addr + len, granule_size);
|
||||
|
||||
/* We always have at least 1 granule. */
|
||||
return 1 + (e_addr - s_addr) / granule_size;
|
||||
}
|
||||
|
||||
/* See arch/aarch64-mte-linux.h */
|
||||
|
||||
CORE_ADDR
|
||||
make_ltag_bits (CORE_ADDR value)
|
||||
{
|
||||
return value & MTE_LOGICAL_MAX_VALUE;
|
||||
}
|
||||
|
||||
/* See arch/aarch64-mte-linux.h */
|
||||
|
||||
CORE_ADDR
|
||||
make_ltag (CORE_ADDR value)
|
||||
{
|
||||
return make_ltag_bits (value) << MTE_LOGICAL_TAG_START_BIT;
|
||||
}
|
||||
|
||||
/* See arch/aarch64-mte-linux.h */
|
||||
|
||||
CORE_ADDR
|
||||
aarch64_linux_set_ltag (CORE_ADDR address, CORE_ADDR tag)
|
||||
{
|
||||
/* Remove the existing tag. */
|
||||
address &= ~make_ltag (MTE_LOGICAL_MAX_VALUE);
|
||||
|
||||
/* Return the new tagged address. */
|
||||
return address | make_ltag (tag);
|
||||
}
|
||||
|
||||
/* See arch/aarch64-mte-linux.h */
|
||||
|
||||
CORE_ADDR
|
||||
aarch64_linux_get_ltag (CORE_ADDR address)
|
||||
{
|
||||
return make_ltag_bits (address >> MTE_LOGICAL_TAG_START_BIT);
|
||||
}
|
||||
66
gdb/arch/aarch64-mte-linux.h
Normal file
66
gdb/arch/aarch64-mte-linux.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* Common Linux target-dependent definitions for AArch64 MTE
|
||||
|
||||
Copyright (C) 2020 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/>. */
|
||||
|
||||
#ifndef ARCH_AARCH64_LINUX_H
|
||||
#define ARCH_AARCH64_LINUX_H
|
||||
|
||||
#include "gdbsupport/common-defs.h"
|
||||
|
||||
/* Feature check for Memory Tagging Extension. */
|
||||
#ifndef HWCAP2_MTE
|
||||
#define HWCAP2_MTE (1 << 18)
|
||||
#endif
|
||||
|
||||
/* The MTE regset consists of a single 64-bit register. */
|
||||
#define AARCH64_LINUX_SIZEOF_MTE 8
|
||||
|
||||
/* We have one tag per 16 bytes of memory. */
|
||||
#define MTE_GRANULE_SIZE 16
|
||||
#define MTE_LOGICAL_TAG_START_BIT 56
|
||||
#define MTE_LOGICAL_MAX_VALUE 0xf
|
||||
|
||||
/* Memory tagging definitions. */
|
||||
#ifndef SEGV_MTEAERR
|
||||
# define SEGV_MTEAERR 8
|
||||
# define SEGV_MTESERR 9
|
||||
#endif
|
||||
|
||||
/* Return the number of tag granules in the memory range
|
||||
[ADDR, ADDR + LEN) given GRANULE_SIZE. */
|
||||
extern size_t get_tag_granules (CORE_ADDR addr, size_t len,
|
||||
size_t granule_size);
|
||||
|
||||
/* Return the 4-bit tag made from VALUE. */
|
||||
extern CORE_ADDR make_ltag_bits (CORE_ADDR value);
|
||||
|
||||
/* Return the 4-bit tag that can be OR-ed to an address. */
|
||||
extern CORE_ADDR make_ltag (CORE_ADDR value);
|
||||
|
||||
/* Helper to set the logical TAG for a 64-bit ADDRESS.
|
||||
|
||||
It is always possible to set the logical tag. */
|
||||
extern CORE_ADDR aarch64_linux_set_ltag (CORE_ADDR address,
|
||||
CORE_ADDR tag);
|
||||
|
||||
/* Helper to get the logical tag from a 64-bit ADDRESS.
|
||||
|
||||
It is always possible to get the logical tag. */
|
||||
extern CORE_ADDR aarch64_linux_get_ltag (CORE_ADDR address);
|
||||
|
||||
#endif /* ARCH_AARCH64_LINUX_H */
|
||||
@@ -23,11 +23,12 @@
|
||||
#include "../features/aarch64-fpu.c"
|
||||
#include "../features/aarch64-sve.c"
|
||||
#include "../features/aarch64-pauth.c"
|
||||
#include "../features/aarch64-mte.c"
|
||||
|
||||
/* See arch/aarch64.h. */
|
||||
|
||||
target_desc *
|
||||
aarch64_create_target_description (uint64_t vq, bool pauth_p)
|
||||
aarch64_create_target_description (uint64_t vq, bool pauth_p, bool mte_p)
|
||||
{
|
||||
target_desc *tdesc = allocate_target_description ();
|
||||
|
||||
@@ -47,5 +48,9 @@ aarch64_create_target_description (uint64_t vq, bool pauth_p)
|
||||
if (pauth_p)
|
||||
regnum = create_feature_aarch64_pauth (tdesc, regnum);
|
||||
|
||||
/* Memory tagging extension registers. */
|
||||
if (mte_p)
|
||||
regnum = create_feature_aarch64_mte (tdesc, regnum);
|
||||
|
||||
return tdesc;
|
||||
}
|
||||
|
||||
@@ -25,9 +25,12 @@
|
||||
/* Create the aarch64 target description. A non zero VQ value indicates both
|
||||
the presence of SVE and the Vector Quotient - the number of 128bit chunks in
|
||||
an SVE Z register. HAS_PAUTH_P indicates the presence of the PAUTH
|
||||
feature. */
|
||||
feature.
|
||||
|
||||
target_desc *aarch64_create_target_description (uint64_t vq, bool has_pauth_p);
|
||||
MTE_P indicates the presence of the Memory Tagging Extension feature. */
|
||||
|
||||
target_desc *aarch64_create_target_description (uint64_t vq, bool has_pauth_p,
|
||||
bool mte_p);
|
||||
|
||||
/* Register numbers of various important registers.
|
||||
Note that on SVE, the Z registers reuse the V register numbers and the V
|
||||
|
||||
@@ -236,7 +236,8 @@ case ${gdb_host} in
|
||||
NATDEPFILES="${NATDEPFILES} aarch64-linux-nat.o \
|
||||
aarch32-linux-nat.o nat/aarch64-linux-hw-point.o \
|
||||
nat/aarch64-linux.o \
|
||||
nat/aarch64-sve-linux-ptrace.o"
|
||||
nat/aarch64-sve-linux-ptrace.o \
|
||||
nat/aarch64-mte-linux-ptrace.o"
|
||||
;;
|
||||
arm)
|
||||
# Host: ARM based machine running GNU/Linux
|
||||
|
||||
@@ -124,6 +124,7 @@ aarch64*-*-freebsd*)
|
||||
aarch64*-*-linux*)
|
||||
# Target: AArch64 linux
|
||||
gdb_target_obs="aarch64-linux-tdep.o arch/aarch64.o\
|
||||
arch/aarch64-mte-linux.o \
|
||||
arch/arm.o arch/arm-linux.o arch/arm-get-next-pcs.o \
|
||||
arm-tdep.o arm-linux-tdep.o \
|
||||
glibc-tdep.o linux-tdep.o solib-svr4.o \
|
||||
|
||||
@@ -9893,6 +9893,10 @@ If you omit @var{expr}, @value{GDBN} displays the last value again (from the
|
||||
conveniently inspect the same value in an alternative format.
|
||||
@end table
|
||||
|
||||
If the architecture supports memory tagging, the @code{print} command will
|
||||
display pointer/memory tag mismatches if what is being printed is a pointer
|
||||
or reference type.
|
||||
|
||||
A more low-level way of examining data is with the @code{x} command.
|
||||
It examines data in memory at a specified address and prints it in a
|
||||
specified format. @xref{Memory, ,Examining Memory}.
|
||||
@@ -10660,7 +10664,8 @@ number is specified, memory is examined backward from @var{addr}.
|
||||
@item @var{f}, the display format
|
||||
The display format is one of the formats used by @code{print}
|
||||
(@samp{x}, @samp{d}, @samp{u}, @samp{o}, @samp{t}, @samp{a}, @samp{c},
|
||||
@samp{f}, @samp{s}), and in addition @samp{i} (for machine instructions).
|
||||
@samp{f}, @samp{s}), @samp{i} (for machine instructions) and
|
||||
@samp{m} (for displaying memory tags).
|
||||
The default is @samp{x} (hexadecimal) initially. The default changes
|
||||
each time you use either @code{x} or @code{print}.
|
||||
|
||||
@@ -10755,6 +10760,20 @@ counter is shown with a @code{=>} marker. For example:
|
||||
0x804838c <main+24>: call 0x80482d4 <puts@@plt>
|
||||
@end smallexample
|
||||
|
||||
If the architecture supports memory tagging, the tags can be displayed by
|
||||
using @samp{m}. The information will be displayed once per granule size
|
||||
(the amount of bytes a particular memory tag covers). For example, AArch64
|
||||
has a granule size of 16 bytes, so it will display a tag every 16 bytes.
|
||||
|
||||
Due to the way @value{GDBN} prints information with the @code{x} command (not
|
||||
aligned to a particular boundary), the tag information will refer to the
|
||||
initial address displayed on a particular line. If a memory tag boundary
|
||||
is crossed in the middle of a line displayed by the @code{x} command, it
|
||||
will be displayed in the next line.
|
||||
|
||||
The @samp{m} format doesn't affect any other specified formats that were
|
||||
passed to the @code{x} command.
|
||||
|
||||
@cindex @code{$_}, @code{$__}, and value history
|
||||
The addresses and contents printed by the @code{x} command are not saved
|
||||
in the value history because there is often too much of them and they
|
||||
@@ -10806,6 +10825,58 @@ target supports computing the CRC checksum of a block of memory
|
||||
(@pxref{qCRC packet}).
|
||||
@end table
|
||||
|
||||
@subsection Memory Tagging
|
||||
|
||||
Memory tagging is a memory protection technology that validates accesses
|
||||
through pointers via a tag. Both the pointer tag and the memory tag in the
|
||||
physical address space must match for the memory access to be validated.
|
||||
|
||||
There are two types of tags: logical and allocation. The logical tag is
|
||||
stored in the pointers themselves. The allocation tag is the tag associated
|
||||
with the physical address space, against which the logical tags from pointers
|
||||
are validated.
|
||||
|
||||
If the underlying architecture supports memory tagging, like AArch64,
|
||||
@value{GDBN} can make use of it to validate addresses and pointers against
|
||||
memory allocation tags.
|
||||
|
||||
The @code{print} and @code{x} commands will display tag information when
|
||||
appropriate, and a command prefix of @code{mtag} gives access to the
|
||||
various memory tagging commands.
|
||||
|
||||
The @code{print} command will automatically attempt to validate the logical
|
||||
tag against the allocation tag for pointers and addresses, and will display
|
||||
a message in case of failure.
|
||||
|
||||
The @code{x} command has a @code{m} modifier. When present, this modifier
|
||||
will make the @code{x} command output allocation tag information for a given
|
||||
memory region that is being examined.
|
||||
|
||||
The @code{mtag} commands are the following:
|
||||
|
||||
@table @code
|
||||
@kindex mtag showltag
|
||||
@item mtag showltag @var{address_expression}
|
||||
Show the logical tag contained in the pointer resulting from evaluating the
|
||||
argument expression.
|
||||
@kindex mtag setltag
|
||||
@item mtag setltag @var{address_expression} @var{tag_bytes}
|
||||
Print the resulting pointer from evaluating the argument expression with a
|
||||
logical tag of @var{tag_bytes}.
|
||||
@kindex mtag showatag
|
||||
@item mtag showatag @var{address_expression}
|
||||
Show the allocation tag from the memory address pointed to by the evaluation
|
||||
of the argument expression.
|
||||
@kindex mtag setatag
|
||||
@item mtag setatag @var{starting_address} @var{length} @var{tag_bytes}
|
||||
Set the allocation tag for memory range @r{[}@var{starting_address},
|
||||
@var{starting_address} + @var{length}@r{)} to @var{tag_bytes}.
|
||||
@kindex mtag check
|
||||
@item mtag check @var{address_expression}
|
||||
Given the pointer resulting from evaluating the argument expression, check that
|
||||
the logical tag and the allocation tags match.
|
||||
@end table
|
||||
|
||||
@node Auto Display
|
||||
@section Automatic Display
|
||||
@cindex automatic display
|
||||
@@ -24846,6 +24917,23 @@ When GDB prints a backtrace, any addresses that required unmasking will be
|
||||
postfixed with the marker [PAC]. When using the MI, this is printed as part
|
||||
of the @code{addr_flags} field.
|
||||
|
||||
@subsubsection AArch64 Memory Tagging Extension.
|
||||
@cindex AArch64 Memory Tagging Extension.
|
||||
|
||||
When @value{GDBN} is debugging the AArch64 architecture, the program is
|
||||
using the v8.5-A feature Memory Tagging Extension (MTE) and there is support
|
||||
in the kernel for MTE, @value{GDBN} will make memory tagging functionality
|
||||
available for inspection and editing of logical and allocation tags.
|
||||
|
||||
To aid debugging, @value{GDBN} will output additional information when SIGSEGV
|
||||
signals are generated as a result of memory tag failures.
|
||||
|
||||
A new register, @code{tag_ctl}, is made available through the
|
||||
@code{org.gnu.gdb.aarch64.mte} feature. This register exposes some
|
||||
options that can be controlled at runtime and emulates the @code{prctl}
|
||||
option @code{PR_SET_TAGGED_ADDR_CTRL}. For further information, see the
|
||||
documentation in the Linux kernel.
|
||||
|
||||
@node i386
|
||||
@subsection x86 Architecture-specific Issues
|
||||
|
||||
@@ -40654,6 +40742,77 @@ is a sequence of thread IDs, @var{threadid} (eight hex
|
||||
digits), from the target. See @code{remote.c:parse_threadlist_response()}.
|
||||
@end table
|
||||
|
||||
@item qMemTags:@var{start address}:@var{length}
|
||||
@cindex fetch memory tags
|
||||
@cindex @samp{qMemTags} packet
|
||||
Fetch memory tags in the address range @r{[}@var{start address},
|
||||
@var{start address} + @var{length}@r{)}. The target is responsible for
|
||||
calculating how many tags will be returned, as this is architecture-specific.
|
||||
|
||||
@var{start address} is the starting address of the memory range.
|
||||
|
||||
@var{length} is the length, in bytes, of the memory range.
|
||||
|
||||
Reply:
|
||||
@table @samp
|
||||
@item @var{mXX}@dots{}
|
||||
Hex encoded sequence of uninterpreted bytes representing the tags found in
|
||||
the request memory range.
|
||||
|
||||
@item E @var{nn}
|
||||
An error occured. This means that fetching of memory tags failed for some
|
||||
reason.
|
||||
|
||||
@item @w{}
|
||||
An empty reply indicates that @samp{qMemTags} is not supported by the stub,
|
||||
although this should not happen given @value{GDBN} will only send this packet
|
||||
if the stub has advertised support for memory tagging via @samp{qSupported}.
|
||||
@end table
|
||||
|
||||
@item QMemTags:@var{start address}:@var{length}:@var{tag bytes}
|
||||
@cindex store memory tags
|
||||
@cindex @samp{QMemTags} packet
|
||||
Store memory tags to the address range @r{[}@var{start address},
|
||||
@var{start address} + @var{length}@r{)}. The target is responsible for
|
||||
interpreting the tag bytes and modifying the memory tag granules
|
||||
accordingly, given this is architecture-specific.
|
||||
|
||||
The interpretation of how many tags should be written to how many memory tag
|
||||
granules is also architecture-specific. The behavior is
|
||||
implementation-specific, but the following is suggested.
|
||||
|
||||
If the number of memory tags, @var{N}, is greater than or equal to the number
|
||||
of memory tag granules, @var{G}, only @var{G} tags will be stored.
|
||||
|
||||
If @var{N} is less than @var{G}, the behavior is that of a fill operation,
|
||||
and the tag bytes will be used as a pattern that will get repeated until
|
||||
@var{G} tags are stored.
|
||||
|
||||
@var{start address} is the starting address of the memory range. The address
|
||||
does not have any restriction on alignment or size.
|
||||
|
||||
@var{length} is the length, in bytes, of the memory range.
|
||||
|
||||
@var{tag bytes} is a sequence of hex encoded uninterpreted bytes which will be
|
||||
interpreted by the target. Each pair of hex digits is interpreted as a
|
||||
single byte.
|
||||
|
||||
Reply:
|
||||
@table @samp
|
||||
@item OK
|
||||
The request was successful and the memory tag granules were modified
|
||||
accordingly.
|
||||
|
||||
@item E @var{nn}
|
||||
An error occured. This means that modifying the memory tag granules failed
|
||||
for some reason.
|
||||
|
||||
@item @w{}
|
||||
An empty reply indicates that @samp{QMemTags} is not supported by the stub,
|
||||
although this should not happen given @value{GDBN} will only send this packet
|
||||
if the stub has advertised support for memory tagging via @samp{qSupported}.
|
||||
@end table
|
||||
|
||||
@item qOffsets
|
||||
@cindex section offsets, remote request
|
||||
@cindex @samp{qOffsets} packet
|
||||
@@ -41321,6 +41480,11 @@ These are the currently defined stub features and their properties:
|
||||
@tab @samp{-}
|
||||
@tab No
|
||||
|
||||
@item @samp{memory-tagging}
|
||||
@tab No
|
||||
@tab @samp{-}
|
||||
@tab No
|
||||
|
||||
@end multitable
|
||||
|
||||
These are the currently defined stub features, in more detail:
|
||||
@@ -41535,6 +41699,14 @@ The remote stub understands the @samp{QThreadEvents} packet.
|
||||
@item no-resumed
|
||||
The remote stub reports the @samp{N} stop reply.
|
||||
|
||||
@item memory-tagging
|
||||
The remote stub supports and implements the required memory tagging
|
||||
functionality and understands the @samp{qMemTags} and @samp{QMemTags} packets.
|
||||
|
||||
For AArch64 GNU/Linux systems, this feature also requires access to the smaps
|
||||
file in the proc filesystem so memory mapping page flags can be inspected. This
|
||||
is done via the @samp{vFile} requests.
|
||||
|
||||
@end table
|
||||
|
||||
@item qSymbol::
|
||||
|
||||
@@ -201,6 +201,7 @@ $(outdir)/%.dat: %.xml number-regs.xsl sort-regs.xsl gdbserver-regs.xsl
|
||||
FEATURE_XMLFILES = aarch64-core.xml \
|
||||
aarch64-fpu.xml \
|
||||
aarch64-pauth.xml \
|
||||
aarch64-mte.xml \
|
||||
arc/core-v2.xml \
|
||||
arc/aux-v2.xml \
|
||||
arc/core-arcompact.xml \
|
||||
|
||||
14
gdb/features/aarch64-mte.c
Normal file
14
gdb/features/aarch64-mte.c
Normal file
@@ -0,0 +1,14 @@
|
||||
/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
|
||||
Original: aarch64-mte.xml */
|
||||
|
||||
#include "gdbsupport/tdesc.h"
|
||||
|
||||
static int
|
||||
create_feature_aarch64_mte (struct target_desc *result, long regnum)
|
||||
{
|
||||
struct tdesc_feature *feature;
|
||||
|
||||
feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.mte");
|
||||
tdesc_create_reg (feature, "tag_ctl", regnum++, 0, "system", 64, "uint64");
|
||||
return regnum;
|
||||
}
|
||||
11
gdb/features/aarch64-mte.xml
Normal file
11
gdb/features/aarch64-mte.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. -->
|
||||
|
||||
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
||||
<feature name="org.gnu.gdb.aarch64.mte">
|
||||
<reg name="tag_ctl" bitsize="64" type="uint64" group="system" save-restore="no"/>
|
||||
</feature>
|
||||
137
gdb/gdbarch.c
137
gdb/gdbarch.c
@@ -251,6 +251,12 @@ struct gdbarch
|
||||
gdbarch_convert_from_func_ptr_addr_ftype *convert_from_func_ptr_addr;
|
||||
gdbarch_addr_bits_remove_ftype *addr_bits_remove;
|
||||
int significant_addr_bit;
|
||||
gdbarch_memtag_to_string_ftype *memtag_to_string;
|
||||
gdbarch_tagged_address_p_ftype *tagged_address_p;
|
||||
gdbarch_memtag_mismatch_p_ftype *memtag_mismatch_p;
|
||||
gdbarch_set_memtags_ftype *set_memtags;
|
||||
gdbarch_get_memtag_ftype *get_memtag;
|
||||
CORE_ADDR memtag_granule_size;
|
||||
gdbarch_software_single_step_ftype *software_single_step;
|
||||
gdbarch_single_step_through_delay_ftype *single_step_through_delay;
|
||||
gdbarch_print_insn_ftype *print_insn;
|
||||
@@ -426,6 +432,11 @@ gdbarch_alloc (const struct gdbarch_info *info,
|
||||
gdbarch->stabs_argument_has_addr = default_stabs_argument_has_addr;
|
||||
gdbarch->convert_from_func_ptr_addr = convert_from_func_ptr_addr_identity;
|
||||
gdbarch->addr_bits_remove = core_addr_identity;
|
||||
gdbarch->memtag_to_string = default_memtag_to_string;
|
||||
gdbarch->tagged_address_p = default_tagged_address_p;
|
||||
gdbarch->memtag_mismatch_p = default_memtag_mismatch_p;
|
||||
gdbarch->set_memtags = default_set_memtags;
|
||||
gdbarch->get_memtag = default_get_memtag;
|
||||
gdbarch->print_insn = default_print_insn;
|
||||
gdbarch->skip_trampoline_code = generic_skip_trampoline_code;
|
||||
gdbarch->skip_solib_resolver = generic_skip_solib_resolver;
|
||||
@@ -615,6 +626,12 @@ verify_gdbarch (struct gdbarch *gdbarch)
|
||||
/* Skip verify of convert_from_func_ptr_addr, invalid_p == 0 */
|
||||
/* Skip verify of addr_bits_remove, invalid_p == 0 */
|
||||
/* Skip verify of significant_addr_bit, invalid_p == 0 */
|
||||
/* Skip verify of memtag_to_string, invalid_p == 0 */
|
||||
/* Skip verify of tagged_address_p, invalid_p == 0 */
|
||||
/* Skip verify of memtag_mismatch_p, invalid_p == 0 */
|
||||
/* Skip verify of set_memtags, invalid_p == 0 */
|
||||
/* Skip verify of get_memtag, invalid_p == 0 */
|
||||
/* Skip verify of memtag_granule_size, invalid_p == 0 */
|
||||
/* Skip verify of software_single_step, has predicate. */
|
||||
/* Skip verify of single_step_through_delay, has predicate. */
|
||||
/* Skip verify of print_insn, invalid_p == 0 */
|
||||
@@ -1053,6 +1070,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: get_longjmp_target = <%s>\n",
|
||||
host_address_to_string (gdbarch->get_longjmp_target));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: get_memtag = <%s>\n",
|
||||
host_address_to_string (gdbarch->get_memtag));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: get_pc_address_flags = <%s>\n",
|
||||
host_address_to_string (gdbarch->get_pc_address_flags));
|
||||
@@ -1188,6 +1208,15 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: memory_remove_breakpoint = <%s>\n",
|
||||
host_address_to_string (gdbarch->memory_remove_breakpoint));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: memtag_granule_size = %s\n",
|
||||
core_addr_to_string_nz (gdbarch->memtag_granule_size));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: memtag_mismatch_p = <%s>\n",
|
||||
host_address_to_string (gdbarch->memtag_mismatch_p));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: memtag_to_string = <%s>\n",
|
||||
host_address_to_string (gdbarch->memtag_to_string));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: num_pseudo_regs = %s\n",
|
||||
plongest (gdbarch->num_pseudo_regs));
|
||||
@@ -1332,6 +1361,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: sdb_reg_to_regnum = <%s>\n",
|
||||
host_address_to_string (gdbarch->sdb_reg_to_regnum));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: set_memtags = <%s>\n",
|
||||
host_address_to_string (gdbarch->set_memtags));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: short_bit = %s\n",
|
||||
plongest (gdbarch->short_bit));
|
||||
@@ -1440,6 +1472,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: syscalls_info = %s\n",
|
||||
host_address_to_string (gdbarch->syscalls_info));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: tagged_address_p = <%s>\n",
|
||||
host_address_to_string (gdbarch->tagged_address_p));
|
||||
fprintf_unfiltered (file,
|
||||
"gdbarch_dump: target_desc = %s\n",
|
||||
host_address_to_string (gdbarch->target_desc));
|
||||
@@ -3212,6 +3247,108 @@ set_gdbarch_significant_addr_bit (struct gdbarch *gdbarch,
|
||||
gdbarch->significant_addr_bit = significant_addr_bit;
|
||||
}
|
||||
|
||||
std::string
|
||||
gdbarch_memtag_to_string (struct gdbarch *gdbarch, struct value *address, enum memtag_type tag_type)
|
||||
{
|
||||
gdb_assert (gdbarch != NULL);
|
||||
gdb_assert (gdbarch->memtag_to_string != NULL);
|
||||
if (gdbarch_debug >= 2)
|
||||
fprintf_unfiltered (gdb_stdlog, "gdbarch_memtag_to_string called\n");
|
||||
return gdbarch->memtag_to_string (gdbarch, address, tag_type);
|
||||
}
|
||||
|
||||
void
|
||||
set_gdbarch_memtag_to_string (struct gdbarch *gdbarch,
|
||||
gdbarch_memtag_to_string_ftype memtag_to_string)
|
||||
{
|
||||
gdbarch->memtag_to_string = memtag_to_string;
|
||||
}
|
||||
|
||||
bool
|
||||
gdbarch_tagged_address_p (struct gdbarch *gdbarch, struct value *address)
|
||||
{
|
||||
gdb_assert (gdbarch != NULL);
|
||||
gdb_assert (gdbarch->tagged_address_p != NULL);
|
||||
if (gdbarch_debug >= 2)
|
||||
fprintf_unfiltered (gdb_stdlog, "gdbarch_tagged_address_p called\n");
|
||||
return gdbarch->tagged_address_p (gdbarch, address);
|
||||
}
|
||||
|
||||
void
|
||||
set_gdbarch_tagged_address_p (struct gdbarch *gdbarch,
|
||||
gdbarch_tagged_address_p_ftype tagged_address_p)
|
||||
{
|
||||
gdbarch->tagged_address_p = tagged_address_p;
|
||||
}
|
||||
|
||||
bool
|
||||
gdbarch_memtag_mismatch_p (struct gdbarch *gdbarch, struct value *address)
|
||||
{
|
||||
gdb_assert (gdbarch != NULL);
|
||||
gdb_assert (gdbarch->memtag_mismatch_p != NULL);
|
||||
if (gdbarch_debug >= 2)
|
||||
fprintf_unfiltered (gdb_stdlog, "gdbarch_memtag_mismatch_p called\n");
|
||||
return gdbarch->memtag_mismatch_p (gdbarch, address);
|
||||
}
|
||||
|
||||
void
|
||||
set_gdbarch_memtag_mismatch_p (struct gdbarch *gdbarch,
|
||||
gdbarch_memtag_mismatch_p_ftype memtag_mismatch_p)
|
||||
{
|
||||
gdbarch->memtag_mismatch_p = memtag_mismatch_p;
|
||||
}
|
||||
|
||||
int
|
||||
gdbarch_set_memtags (struct gdbarch *gdbarch, struct value *address, size_t length, const gdb::byte_vector &tags, enum memtag_type tag_type)
|
||||
{
|
||||
gdb_assert (gdbarch != NULL);
|
||||
gdb_assert (gdbarch->set_memtags != NULL);
|
||||
if (gdbarch_debug >= 2)
|
||||
fprintf_unfiltered (gdb_stdlog, "gdbarch_set_memtags called\n");
|
||||
return gdbarch->set_memtags (gdbarch, address, length, tags, tag_type);
|
||||
}
|
||||
|
||||
void
|
||||
set_gdbarch_set_memtags (struct gdbarch *gdbarch,
|
||||
gdbarch_set_memtags_ftype set_memtags)
|
||||
{
|
||||
gdbarch->set_memtags = set_memtags;
|
||||
}
|
||||
|
||||
struct value *
|
||||
gdbarch_get_memtag (struct gdbarch *gdbarch, struct value *address, enum memtag_type tag_type)
|
||||
{
|
||||
gdb_assert (gdbarch != NULL);
|
||||
gdb_assert (gdbarch->get_memtag != NULL);
|
||||
if (gdbarch_debug >= 2)
|
||||
fprintf_unfiltered (gdb_stdlog, "gdbarch_get_memtag called\n");
|
||||
return gdbarch->get_memtag (gdbarch, address, tag_type);
|
||||
}
|
||||
|
||||
void
|
||||
set_gdbarch_get_memtag (struct gdbarch *gdbarch,
|
||||
gdbarch_get_memtag_ftype get_memtag)
|
||||
{
|
||||
gdbarch->get_memtag = get_memtag;
|
||||
}
|
||||
|
||||
CORE_ADDR
|
||||
gdbarch_memtag_granule_size (struct gdbarch *gdbarch)
|
||||
{
|
||||
gdb_assert (gdbarch != NULL);
|
||||
/* Skip verify of memtag_granule_size, invalid_p == 0 */
|
||||
if (gdbarch_debug >= 2)
|
||||
fprintf_unfiltered (gdb_stdlog, "gdbarch_memtag_granule_size called\n");
|
||||
return gdbarch->memtag_granule_size;
|
||||
}
|
||||
|
||||
void
|
||||
set_gdbarch_memtag_granule_size (struct gdbarch *gdbarch,
|
||||
CORE_ADDR memtag_granule_size)
|
||||
{
|
||||
gdbarch->memtag_granule_size = memtag_granule_size;
|
||||
}
|
||||
|
||||
int
|
||||
gdbarch_software_single_step_p (struct gdbarch *gdbarch)
|
||||
{
|
||||
|
||||
@@ -115,6 +115,18 @@ enum function_call_return_method
|
||||
return_method_struct,
|
||||
};
|
||||
|
||||
enum memtag_type
|
||||
{
|
||||
/* Logical tag, the tag that is stored in unused bits of a pointer to a
|
||||
virtual address. */
|
||||
tag_logical = 0,
|
||||
|
||||
/* Allocation tag, the tag that is associated with every granule of memory in
|
||||
the physical address space. Allocation tags are used to validate memory
|
||||
accesses via pointers containing logical tags. */
|
||||
tag_allocation,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* The following are pre-initialized by GDBARCH. */
|
||||
@@ -705,6 +717,47 @@ extern void set_gdbarch_addr_bits_remove (struct gdbarch *gdbarch, gdbarch_addr_
|
||||
extern int gdbarch_significant_addr_bit (struct gdbarch *gdbarch);
|
||||
extern void set_gdbarch_significant_addr_bit (struct gdbarch *gdbarch, int significant_addr_bit);
|
||||
|
||||
/* Return a string representation of the memory tag TYPE of ADDRESS.
|
||||
If no tag is associated with such an address, return the empty string. */
|
||||
|
||||
typedef std::string (gdbarch_memtag_to_string_ftype) (struct gdbarch *gdbarch, struct value *address, enum memtag_type tag_type);
|
||||
extern std::string gdbarch_memtag_to_string (struct gdbarch *gdbarch, struct value *address, enum memtag_type tag_type);
|
||||
extern void set_gdbarch_memtag_to_string (struct gdbarch *gdbarch, gdbarch_memtag_to_string_ftype *memtag_to_string);
|
||||
|
||||
/* Return true if ADDRESS contains a tag and false otherwise. */
|
||||
|
||||
typedef bool (gdbarch_tagged_address_p_ftype) (struct gdbarch *gdbarch, struct value *address);
|
||||
extern bool gdbarch_tagged_address_p (struct gdbarch *gdbarch, struct value *address);
|
||||
extern void set_gdbarch_tagged_address_p (struct gdbarch *gdbarch, gdbarch_tagged_address_p_ftype *tagged_address_p);
|
||||
|
||||
/* Return true if the tag from ADDRESS does not match the memory tag for that
|
||||
particular address. Return false otherwise. */
|
||||
|
||||
typedef bool (gdbarch_memtag_mismatch_p_ftype) (struct gdbarch *gdbarch, struct value *address);
|
||||
extern bool gdbarch_memtag_mismatch_p (struct gdbarch *gdbarch, struct value *address);
|
||||
extern void set_gdbarch_memtag_mismatch_p (struct gdbarch *gdbarch, gdbarch_memtag_mismatch_p_ftype *memtag_mismatch_p);
|
||||
|
||||
/* Set the tags for the address range [ADDRESS, ADDRESS + LENGTH) to TAGS
|
||||
Return 0 if successful and non-zero otherwise. */
|
||||
|
||||
typedef int (gdbarch_set_memtags_ftype) (struct gdbarch *gdbarch, struct value *address, size_t length, const gdb::byte_vector &tags, enum memtag_type tag_type);
|
||||
extern int gdbarch_set_memtags (struct gdbarch *gdbarch, struct value *address, size_t length, const gdb::byte_vector &tags, enum memtag_type tag_type);
|
||||
extern void set_gdbarch_set_memtags (struct gdbarch *gdbarch, gdbarch_set_memtags_ftype *set_memtags);
|
||||
|
||||
/* Return the tag portion of ADDRESS, assuming ADDRESS is tagged. */
|
||||
|
||||
typedef struct value * (gdbarch_get_memtag_ftype) (struct gdbarch *gdbarch, struct value *address, enum memtag_type tag_type);
|
||||
extern struct value * gdbarch_get_memtag (struct gdbarch *gdbarch, struct value *address, enum memtag_type tag_type);
|
||||
extern void set_gdbarch_get_memtag (struct gdbarch *gdbarch, gdbarch_get_memtag_ftype *get_memtag);
|
||||
|
||||
/* memtag_granule_size is the size of the allocation tag granule, for
|
||||
architectures that support memory tagging.
|
||||
This is 0 for architectures that do not support memory tagging.
|
||||
For a non-zero value, this represents the number of bytes of memory per tag. */
|
||||
|
||||
extern CORE_ADDR gdbarch_memtag_granule_size (struct gdbarch *gdbarch);
|
||||
extern void set_gdbarch_memtag_granule_size (struct gdbarch *gdbarch, CORE_ADDR memtag_granule_size);
|
||||
|
||||
/* FIXME/cagney/2001-01-18: This should be split in two. A target method that
|
||||
indicates if the target needs software single step. An ISA method to
|
||||
implement it.
|
||||
|
||||
@@ -604,6 +604,30 @@ m;CORE_ADDR;addr_bits_remove;CORE_ADDR addr;addr;;core_addr_identity;;0
|
||||
# additional data associated with the address.
|
||||
v;int;significant_addr_bit;;;;;;0
|
||||
|
||||
# Return a string representation of the memory tag TYPE of ADDRESS.
|
||||
# If no tag is associated with such an address, return the empty string.
|
||||
+m;std::string;memtag_to_string;struct value *address, enum memtag_type tag_type;address, tag_type;;default_memtag_to_string;;0
|
||||
|
||||
# Return true if ADDRESS contains a tag and false otherwise.
|
||||
+m;bool;tagged_address_p;struct value *address;address;;default_tagged_address_p;;0
|
||||
|
||||
# Return true if the tag from ADDRESS does not match the memory tag for that
|
||||
# particular address. Return false otherwise.
|
||||
+m;bool;memtag_mismatch_p;struct value *address;address;;default_memtag_mismatch_p;;0
|
||||
|
||||
# Set the tags for the address range [ADDRESS, ADDRESS + LENGTH) to TAGS
|
||||
# Return 0 if successful and non-zero otherwise.
|
||||
+m;int;set_memtags;struct value *address, size_t length, const gdb::byte_vector \&tags, enum memtag_type tag_type;address, length, tags, tag_type;;default_set_memtags;;0
|
||||
|
||||
# Return the tag portion of ADDRESS, assuming ADDRESS is tagged.
|
||||
+m;struct value *;get_memtag;struct value *address, enum memtag_type tag_type;address, tag_type;;default_get_memtag;;0
|
||||
|
||||
# memtag_granule_size is the size of the allocation tag granule, for
|
||||
# architectures that support memory tagging.
|
||||
# This is 0 for architectures that do not support memory tagging.
|
||||
# For a non-zero value, this represents the number of bytes of memory per tag.
|
||||
v;CORE_ADDR;memtag_granule_size;;;;;;0
|
||||
|
||||
# FIXME/cagney/2001-01-18: This should be split in two. A target method that
|
||||
# indicates if the target needs software single step. An ISA method to
|
||||
# implement it.
|
||||
@@ -1351,6 +1375,18 @@ enum function_call_return_method
|
||||
return_method_struct,
|
||||
};
|
||||
|
||||
enum memtag_type
|
||||
{
|
||||
/* Logical tag, the tag that is stored in unused bits of a pointer to a
|
||||
virtual address. */
|
||||
tag_logical = 0,
|
||||
|
||||
/* Allocation tag, the tag that is associated with every granule of memory in
|
||||
the physical address space. Allocation tags are used to validate memory
|
||||
accesses via pointers containing logical tags. */
|
||||
tag_allocation,
|
||||
};
|
||||
|
||||
EOF
|
||||
|
||||
# function typedef's
|
||||
|
||||
356
gdb/linux-tdep.c
356
gdb/linux-tdep.c
@@ -86,8 +86,33 @@ struct smaps_vmflags
|
||||
/* Is this a MAP_SHARED mapping (VM_SHARED, "sh"). */
|
||||
|
||||
unsigned int shared_mapping : 1;
|
||||
|
||||
/* Memory map has memory tagging enabled. */
|
||||
|
||||
unsigned int memory_tagging : 1;
|
||||
};
|
||||
|
||||
/* Data structure that holds the information contained in the
|
||||
/proc/<pid>/smaps file. */
|
||||
|
||||
struct smaps_data
|
||||
{
|
||||
ULONGEST start_address;
|
||||
ULONGEST end_address;
|
||||
std::string filename;
|
||||
struct smaps_vmflags vmflags;
|
||||
bool read;
|
||||
bool write;
|
||||
bool exec;
|
||||
bool priv;
|
||||
bool has_anonymous;
|
||||
bool mapping_anon_p;
|
||||
bool mapping_file_p;
|
||||
|
||||
ULONGEST inode;
|
||||
ULONGEST offset;
|
||||
};
|
||||
|
||||
/* Whether to take the /proc/PID/coredump_filter into account when
|
||||
generating a corefile. */
|
||||
|
||||
@@ -472,6 +497,8 @@ decode_vmflags (char *p, struct smaps_vmflags *v)
|
||||
v->exclude_coredump = 1;
|
||||
else if (strcmp (s, "sh") == 0)
|
||||
v->shared_mapping = 1;
|
||||
else if (strcmp (s, "mt") == 0)
|
||||
v->memory_tagging = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1172,6 +1199,185 @@ typedef int linux_find_memory_region_ftype (ULONGEST vaddr, ULONGEST size,
|
||||
const char *filename,
|
||||
void *data);
|
||||
|
||||
/* Helper function to parse the contents of /proc/<pid>/smaps into a data
|
||||
structure, for easy access.
|
||||
|
||||
DATA is the contents of the smaps file. The parsed contents are stored
|
||||
into the SMAPS vector. */
|
||||
|
||||
static int
|
||||
parse_smaps_data (const char *data,
|
||||
std::vector<struct smaps_data> &smaps,
|
||||
const char *mapsfilename)
|
||||
{
|
||||
char *line, *t;
|
||||
|
||||
gdb_assert (data != nullptr);
|
||||
|
||||
smaps.clear ();
|
||||
|
||||
line = strtok_r ((char *) data, "\n", &t);
|
||||
|
||||
while (line != NULL)
|
||||
{
|
||||
ULONGEST addr, endaddr, offset, inode;
|
||||
const char *permissions, *device, *filename;
|
||||
struct smaps_vmflags v;
|
||||
size_t permissions_len, device_len;
|
||||
int read, write, exec, priv;
|
||||
int has_anonymous = 0;
|
||||
int mapping_anon_p;
|
||||
int mapping_file_p;
|
||||
|
||||
memset (&v, 0, sizeof (v));
|
||||
read_mapping (line, &addr, &endaddr, &permissions, &permissions_len,
|
||||
&offset, &device, &device_len, &inode, &filename);
|
||||
mapping_anon_p = mapping_is_anonymous_p (filename);
|
||||
/* If the mapping is not anonymous, then we can consider it
|
||||
to be file-backed. These two states (anonymous or
|
||||
file-backed) seem to be exclusive, but they can actually
|
||||
coexist. For example, if a file-backed mapping has
|
||||
"Anonymous:" pages (see more below), then the Linux
|
||||
kernel will dump this mapping when the user specified
|
||||
that she only wants anonymous mappings in the corefile
|
||||
(*even* when she explicitly disabled the dumping of
|
||||
file-backed mappings). */
|
||||
mapping_file_p = !mapping_anon_p;
|
||||
|
||||
/* Decode permissions. */
|
||||
read = (memchr (permissions, 'r', permissions_len) != 0);
|
||||
write = (memchr (permissions, 'w', permissions_len) != 0);
|
||||
exec = (memchr (permissions, 'x', permissions_len) != 0);
|
||||
/* 'private' here actually means VM_MAYSHARE, and not
|
||||
VM_SHARED. In order to know if a mapping is really
|
||||
private or not, we must check the flag "sh" in the
|
||||
VmFlags field. This is done by decode_vmflags. However,
|
||||
if we are using a Linux kernel released before the commit
|
||||
834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will
|
||||
not have the VmFlags there. In this case, there is
|
||||
really no way to know if we are dealing with VM_SHARED,
|
||||
so we just assume that VM_MAYSHARE is enough. */
|
||||
priv = memchr (permissions, 'p', permissions_len) != 0;
|
||||
|
||||
/* Try to detect if region should be dumped by parsing smaps
|
||||
counters. */
|
||||
for (line = strtok_r (NULL, "\n", &t);
|
||||
line != NULL && line[0] >= 'A' && line[0] <= 'Z';
|
||||
line = strtok_r (NULL, "\n", &t))
|
||||
{
|
||||
char keyword[64 + 1];
|
||||
|
||||
if (sscanf (line, "%64s", keyword) != 1)
|
||||
{
|
||||
warning (_("Error parsing {s,}maps file '%s'"), mapsfilename);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcmp (keyword, "Anonymous:") == 0)
|
||||
{
|
||||
/* Older Linux kernels did not support the
|
||||
"Anonymous:" counter. Check it here. */
|
||||
has_anonymous = 1;
|
||||
}
|
||||
else if (strcmp (keyword, "VmFlags:") == 0)
|
||||
decode_vmflags (line, &v);
|
||||
|
||||
if (strcmp (keyword, "AnonHugePages:") == 0
|
||||
|| strcmp (keyword, "Anonymous:") == 0)
|
||||
{
|
||||
unsigned long number;
|
||||
|
||||
if (sscanf (line, "%*s%lu", &number) != 1)
|
||||
{
|
||||
warning (_("Error parsing {s,}maps file '%s' number"),
|
||||
mapsfilename);
|
||||
break;
|
||||
}
|
||||
if (number > 0)
|
||||
{
|
||||
/* Even if we are dealing with a file-backed
|
||||
mapping, if it contains anonymous pages we
|
||||
consider it to be *also* an anonymous
|
||||
mapping, because this is what the Linux
|
||||
kernel does:
|
||||
|
||||
// Dump segments that have been written to.
|
||||
if (vma->anon_vma && FILTER(ANON_PRIVATE))
|
||||
goto whole;
|
||||
|
||||
Note that if the mapping is already marked as
|
||||
file-backed (i.e., mapping_file_p is
|
||||
non-zero), then this is a special case, and
|
||||
this mapping will be dumped either when the
|
||||
user wants to dump file-backed *or* anonymous
|
||||
mappings. */
|
||||
mapping_anon_p = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Save the smaps entry to the vector. */
|
||||
struct smaps_data map;
|
||||
std::string fname (filename);
|
||||
|
||||
map.start_address = addr;
|
||||
map.end_address = endaddr;
|
||||
map.filename = fname;
|
||||
map.vmflags = v;
|
||||
map.read = read? true : false;
|
||||
map.write = write? true : false;
|
||||
map.exec = exec? true : false;
|
||||
map.priv = priv? true : false;
|
||||
map.has_anonymous = has_anonymous;
|
||||
map.mapping_anon_p = mapping_anon_p? true : false;
|
||||
map.mapping_file_p = mapping_file_p? true : false;
|
||||
map.offset = offset;
|
||||
map.inode = inode;
|
||||
|
||||
smaps.emplace_back (map);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See linux-tdep.h. */
|
||||
|
||||
bool
|
||||
linux_address_in_memtag_page (CORE_ADDR address)
|
||||
{
|
||||
if (current_inferior ()->fake_pid_p)
|
||||
return false;
|
||||
|
||||
pid_t pid = current_inferior ()->pid;
|
||||
|
||||
std::string smaps_file = string_printf ("/proc/%d/smaps", pid);
|
||||
|
||||
gdb::unique_xmalloc_ptr<char> data
|
||||
= target_fileio_read_stralloc (NULL, smaps_file.c_str ());
|
||||
|
||||
if (data == nullptr)
|
||||
return false;
|
||||
|
||||
std::vector<struct smaps_data> smaps;
|
||||
|
||||
/* Parse the contents of smaps into a vector. */
|
||||
parse_smaps_data (data.get (), smaps, smaps_file.c_str ());
|
||||
|
||||
if (!smaps.empty ())
|
||||
{
|
||||
for (struct smaps_data map : smaps)
|
||||
{
|
||||
/* Is the address within [start_address, end_address) in a page
|
||||
mapped with memory tagging? */
|
||||
if (address >= map.start_address
|
||||
&& address < map.end_address
|
||||
&& map.vmflags.memory_tagging)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* List memory regions in the inferior for a corefile. */
|
||||
|
||||
static int
|
||||
@@ -1179,8 +1385,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
|
||||
linux_find_memory_region_ftype *func,
|
||||
void *obfd)
|
||||
{
|
||||
char mapsfilename[100];
|
||||
char coredumpfilter_name[100];
|
||||
std::string coredumpfilter_name;
|
||||
pid_t pid;
|
||||
/* Default dump behavior of coredump_filter (0x33), according to
|
||||
Documentation/filesystems/proc.txt from the Linux kernel
|
||||
@@ -1198,10 +1403,9 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
|
||||
|
||||
if (use_coredump_filter)
|
||||
{
|
||||
xsnprintf (coredumpfilter_name, sizeof (coredumpfilter_name),
|
||||
"/proc/%d/coredump_filter", pid);
|
||||
coredumpfilter_name = string_printf ("/proc/%d/coredump_filter", pid);
|
||||
gdb::unique_xmalloc_ptr<char> coredumpfilterdata
|
||||
= target_fileio_read_stralloc (NULL, coredumpfilter_name);
|
||||
= target_fileio_read_stralloc (NULL, coredumpfilter_name.c_str ());
|
||||
if (coredumpfilterdata != NULL)
|
||||
{
|
||||
unsigned int flags;
|
||||
@@ -1211,124 +1415,37 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
|
||||
}
|
||||
}
|
||||
|
||||
xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/smaps", pid);
|
||||
std::string mapsfilename = string_printf ("/proc/%d/smaps", pid);
|
||||
gdb::unique_xmalloc_ptr<char> data
|
||||
= target_fileio_read_stralloc (NULL, mapsfilename);
|
||||
= target_fileio_read_stralloc (NULL, mapsfilename.c_str ());
|
||||
if (data == NULL)
|
||||
{
|
||||
/* Older Linux kernels did not support /proc/PID/smaps. */
|
||||
xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/maps", pid);
|
||||
data = target_fileio_read_stralloc (NULL, mapsfilename);
|
||||
mapsfilename = string_printf ("/proc/%d/maps", pid);
|
||||
data = target_fileio_read_stralloc (NULL, mapsfilename.c_str ());
|
||||
}
|
||||
|
||||
if (data != NULL)
|
||||
if (data == nullptr)
|
||||
return 1;
|
||||
|
||||
std::vector<struct smaps_data> smaps;
|
||||
|
||||
/* Parse the contents of smaps into a vector. */
|
||||
parse_smaps_data (data.get (), smaps, mapsfilename.c_str ());
|
||||
|
||||
if (!smaps.empty ())
|
||||
{
|
||||
char *line, *t;
|
||||
|
||||
line = strtok_r (data.get (), "\n", &t);
|
||||
while (line != NULL)
|
||||
for (struct smaps_data map : smaps)
|
||||
{
|
||||
ULONGEST addr, endaddr, offset, inode;
|
||||
const char *permissions, *device, *filename;
|
||||
struct smaps_vmflags v;
|
||||
size_t permissions_len, device_len;
|
||||
int read, write, exec, priv;
|
||||
int has_anonymous = 0;
|
||||
int should_dump_p = 0;
|
||||
int mapping_anon_p;
|
||||
int mapping_file_p;
|
||||
|
||||
memset (&v, 0, sizeof (v));
|
||||
read_mapping (line, &addr, &endaddr, &permissions, &permissions_len,
|
||||
&offset, &device, &device_len, &inode, &filename);
|
||||
mapping_anon_p = mapping_is_anonymous_p (filename);
|
||||
/* If the mapping is not anonymous, then we can consider it
|
||||
to be file-backed. These two states (anonymous or
|
||||
file-backed) seem to be exclusive, but they can actually
|
||||
coexist. For example, if a file-backed mapping has
|
||||
"Anonymous:" pages (see more below), then the Linux
|
||||
kernel will dump this mapping when the user specified
|
||||
that she only wants anonymous mappings in the corefile
|
||||
(*even* when she explicitly disabled the dumping of
|
||||
file-backed mappings). */
|
||||
mapping_file_p = !mapping_anon_p;
|
||||
|
||||
/* Decode permissions. */
|
||||
read = (memchr (permissions, 'r', permissions_len) != 0);
|
||||
write = (memchr (permissions, 'w', permissions_len) != 0);
|
||||
exec = (memchr (permissions, 'x', permissions_len) != 0);
|
||||
/* 'private' here actually means VM_MAYSHARE, and not
|
||||
VM_SHARED. In order to know if a mapping is really
|
||||
private or not, we must check the flag "sh" in the
|
||||
VmFlags field. This is done by decode_vmflags. However,
|
||||
if we are using a Linux kernel released before the commit
|
||||
834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will
|
||||
not have the VmFlags there. In this case, there is
|
||||
really no way to know if we are dealing with VM_SHARED,
|
||||
so we just assume that VM_MAYSHARE is enough. */
|
||||
priv = memchr (permissions, 'p', permissions_len) != 0;
|
||||
|
||||
/* Try to detect if region should be dumped by parsing smaps
|
||||
counters. */
|
||||
for (line = strtok_r (NULL, "\n", &t);
|
||||
line != NULL && line[0] >= 'A' && line[0] <= 'Z';
|
||||
line = strtok_r (NULL, "\n", &t))
|
||||
{
|
||||
char keyword[64 + 1];
|
||||
|
||||
if (sscanf (line, "%64s", keyword) != 1)
|
||||
{
|
||||
warning (_("Error parsing {s,}maps file '%s'"), mapsfilename);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcmp (keyword, "Anonymous:") == 0)
|
||||
{
|
||||
/* Older Linux kernels did not support the
|
||||
"Anonymous:" counter. Check it here. */
|
||||
has_anonymous = 1;
|
||||
}
|
||||
else if (strcmp (keyword, "VmFlags:") == 0)
|
||||
decode_vmflags (line, &v);
|
||||
|
||||
if (strcmp (keyword, "AnonHugePages:") == 0
|
||||
|| strcmp (keyword, "Anonymous:") == 0)
|
||||
{
|
||||
unsigned long number;
|
||||
|
||||
if (sscanf (line, "%*s%lu", &number) != 1)
|
||||
{
|
||||
warning (_("Error parsing {s,}maps file '%s' number"),
|
||||
mapsfilename);
|
||||
break;
|
||||
}
|
||||
if (number > 0)
|
||||
{
|
||||
/* Even if we are dealing with a file-backed
|
||||
mapping, if it contains anonymous pages we
|
||||
consider it to be *also* an anonymous
|
||||
mapping, because this is what the Linux
|
||||
kernel does:
|
||||
|
||||
// Dump segments that have been written to.
|
||||
if (vma->anon_vma && FILTER(ANON_PRIVATE))
|
||||
goto whole;
|
||||
|
||||
Note that if the mapping is already marked as
|
||||
file-backed (i.e., mapping_file_p is
|
||||
non-zero), then this is a special case, and
|
||||
this mapping will be dumped either when the
|
||||
user wants to dump file-backed *or* anonymous
|
||||
mappings. */
|
||||
mapping_anon_p = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (has_anonymous)
|
||||
should_dump_p = dump_mapping_p (filterflags, &v, priv,
|
||||
mapping_anon_p, mapping_file_p,
|
||||
filename, addr, offset);
|
||||
if (map.has_anonymous)
|
||||
should_dump_p = dump_mapping_p (filterflags, &map.vmflags, map.priv,
|
||||
map.mapping_anon_p,
|
||||
map.mapping_file_p,
|
||||
map.filename.c_str (),
|
||||
map.start_address,
|
||||
map.offset);
|
||||
else
|
||||
{
|
||||
/* Older Linux kernels did not support the "Anonymous:" counter.
|
||||
@@ -1338,16 +1455,15 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
|
||||
|
||||
/* Invoke the callback function to create the corefile segment. */
|
||||
if (should_dump_p)
|
||||
func (addr, endaddr - addr, offset, inode,
|
||||
read, write, exec, 1, /* MODIFIED is true because we
|
||||
want to dump the mapping. */
|
||||
filename, obfd);
|
||||
func (map.start_address, map.end_address - map.start_address,
|
||||
map.offset, map.inode, map.read, map.write, map.exec,
|
||||
1, /* MODIFIED is true because we want to dump
|
||||
the mapping. */
|
||||
map.filename.c_str (), obfd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A structure for passing information through
|
||||
|
||||
@@ -41,6 +41,10 @@ DEF_ENUM_FLAGS_TYPE (enum linux_siginfo_extra_field_values,
|
||||
struct type *linux_get_siginfo_type_with_fields (struct gdbarch *gdbarch,
|
||||
linux_siginfo_extra_fields);
|
||||
|
||||
/* Return true if ADDRESS is within the boundaries of a page mapped with
|
||||
memory tagging protection. */
|
||||
bool linux_address_in_memtag_page (CORE_ADDR address);
|
||||
|
||||
typedef char *(*linux_collect_thread_registers_ftype) (const struct regcache *,
|
||||
ptid_t,
|
||||
bfd *, char *, int *,
|
||||
|
||||
200
gdb/nat/aarch64-mte-linux-ptrace.c
Normal file
200
gdb/nat/aarch64-mte-linux-ptrace.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/* Common Linux native ptrace code for AArch64 MTE.
|
||||
|
||||
Copyright (C) 2020 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 "gdbsupport/byte-vector.h"
|
||||
|
||||
#include "arch/aarch64.h"
|
||||
#include "arch/aarch64-mte-linux.h"
|
||||
#include "nat/aarch64-linux.h"
|
||||
#include "nat/aarch64-mte-linux-ptrace.h"
|
||||
|
||||
#include "linux-ptrace.h"
|
||||
#include <sys/uio.h>
|
||||
|
||||
/* Helper function to display various possible errors when reading
|
||||
MTE tags. */
|
||||
|
||||
static void
|
||||
aarch64_mte_linux_peek_error (int error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case EIO:
|
||||
perror_with_name (_("PEEKMTETAGS not supported"));
|
||||
break;
|
||||
case EFAULT:
|
||||
perror_with_name (_("Couldn't fetch allocation tags"));
|
||||
break;
|
||||
case EOPNOTSUPP:
|
||||
perror_with_name (_("PROT_ME not enabled for requested address"));
|
||||
default:
|
||||
perror_with_name (_("Unknown MTE error"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to display various possible errors when writing
|
||||
MTE tags. */
|
||||
|
||||
static void
|
||||
aarch64_mte_linux_poke_error (int error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case EIO:
|
||||
perror_with_name (_("POKEMTETAGS not supported"));
|
||||
break;
|
||||
case EFAULT:
|
||||
perror_with_name (_("Couldn't store allocation tags"));
|
||||
break;
|
||||
case EOPNOTSUPP:
|
||||
perror_with_name (_("PROT_ME not enabled for requested address"));
|
||||
default:
|
||||
perror_with_name (_("Unknown MTE error"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper to prepare a vector of tags to be passed on to the kernel. The
|
||||
main purpose of this function is to optimize the number of calls to
|
||||
ptrace if we're writing too many tags at once, like a pattern fill
|
||||
request.
|
||||
|
||||
Return a vector of tags of up to MAX_SIZE size, containing the tags that
|
||||
must be passed on to the kernel, extracted from TAGS, starting at POS.
|
||||
GRANULES is the number of tag granules to be modified. */
|
||||
|
||||
static gdb::byte_vector
|
||||
prepare_tag_vector (size_t granules, const gdb::byte_vector &tags, size_t pos,
|
||||
size_t max_size)
|
||||
{
|
||||
gdb::byte_vector t;
|
||||
|
||||
if (granules == 0)
|
||||
{
|
||||
t.clear ();
|
||||
return t;
|
||||
}
|
||||
|
||||
gdb_assert (tags.size () > 0 && max_size > 0);
|
||||
|
||||
if (granules > TAGS_MAX_SIZE)
|
||||
t.resize (TAGS_MAX_SIZE);
|
||||
else
|
||||
t.resize (granules);
|
||||
|
||||
size_t tag_count = tags.size ();
|
||||
|
||||
for (size_t i = 0; i < t.size (); i++)
|
||||
t[i] = tags[(pos + i) % tag_count];
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* See nat/aarch64-mte-linux-ptrace.h */
|
||||
|
||||
int
|
||||
aarch64_mte_fetch_memtags (int tid, CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags)
|
||||
{
|
||||
size_t ntags = get_tag_granules (address, len, MTE_GRANULE_SIZE);
|
||||
gdb_byte tagbuf[ntags];
|
||||
|
||||
struct iovec iovec;
|
||||
iovec.iov_base = tagbuf;
|
||||
iovec.iov_len = ntags;
|
||||
|
||||
tags.clear ();
|
||||
bool done_reading = false;
|
||||
|
||||
/* The kernel may return less tags than we requested. Loop until we've read
|
||||
all the requested tags or until we get an error. */
|
||||
while (!done_reading)
|
||||
{
|
||||
/* Attempt to read ntags allocation tags from the kernel. */
|
||||
if (ptrace (PTRACE_PEEKMTETAGS, tid, address, &iovec) < 0)
|
||||
aarch64_mte_linux_peek_error (errno);
|
||||
|
||||
/* Make sure the kernel returned at least one tag. */
|
||||
if (iovec.iov_len <= 0)
|
||||
{
|
||||
tags.clear ();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Copy the tags the kernel returned. */
|
||||
for (size_t i = 0; i < iovec.iov_len; i++)
|
||||
tags.push_back (tagbuf[i]);
|
||||
|
||||
/* Are we done reading tags? */
|
||||
if (tags.size () == ntags)
|
||||
done_reading = true;
|
||||
else
|
||||
{
|
||||
address += iovec.iov_len * MTE_GRANULE_SIZE;
|
||||
iovec.iov_len = ntags - iovec.iov_len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* See nat/aarch64-mte-linux-ptrace.h */
|
||||
|
||||
int
|
||||
aarch64_mte_store_memtags (int tid, CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags)
|
||||
{
|
||||
if (tags.size () == 0)
|
||||
return 0;
|
||||
|
||||
/* Get the number of tags we need to write. */
|
||||
size_t ntags = get_tag_granules (address, len, MTE_GRANULE_SIZE);
|
||||
bool done_writing = false;
|
||||
size_t tags_written = 0;
|
||||
|
||||
/* Write all the tags, TAGS_MAX_SIZE blocks at a time. */
|
||||
while (!done_writing)
|
||||
{
|
||||
gdb::byte_vector t = prepare_tag_vector (ntags - tags_written, tags,
|
||||
tags_written, TAGS_MAX_SIZE);
|
||||
|
||||
struct iovec iovec;
|
||||
iovec.iov_base = t.data ();
|
||||
iovec.iov_len = t.size ();
|
||||
|
||||
/* Request the kernel to update the allocation tags. */
|
||||
if (ptrace (PTRACE_POKEMTETAGS, tid, address, &iovec) < 0)
|
||||
aarch64_mte_linux_poke_error (errno);
|
||||
|
||||
/* Make sure the kernel wrote at least one tag. */
|
||||
if (iovec.iov_len <= 0)
|
||||
return 1;
|
||||
|
||||
tags_written += iovec.iov_len;
|
||||
|
||||
/* Are we done writing tags? */
|
||||
if (tags_written == ntags)
|
||||
done_writing = true;
|
||||
else
|
||||
address += iovec.iov_len * MTE_GRANULE_SIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
50
gdb/nat/aarch64-mte-linux-ptrace.h
Normal file
50
gdb/nat/aarch64-mte-linux-ptrace.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* Common native Linux definitions for AArch64 MTE.
|
||||
|
||||
Copyright (C) 2018-2020 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/>. */
|
||||
|
||||
#ifndef NAT_AARCH64_MTE_LINUX_PTRACE_H
|
||||
#define NAT_AARCH64_MTE_LINUX_PTRACE_H
|
||||
|
||||
/* MTE allocation tag access */
|
||||
|
||||
#ifndef PTRACE_PEEKMTETAGS
|
||||
#define PTRACE_PEEKMTETAGS 33
|
||||
#endif
|
||||
|
||||
#ifndef PTRACE_POKEMTETAGS
|
||||
#define PTRACE_POKEMTETAGS 34
|
||||
#endif
|
||||
|
||||
/* Maximum number of tags to pass at once to the kernel. */
|
||||
#define TAGS_MAX_SIZE 4096
|
||||
|
||||
/* Read the allocation tags from memory range [ADDRESS, ADDRESS + LEN)
|
||||
into TAGS.
|
||||
|
||||
Return 0 if successful and non-zero otherwise. */
|
||||
extern int aarch64_mte_fetch_memtags (int tid, CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags);
|
||||
|
||||
/* Write the TAGS allocation tags to the memory range
|
||||
[ADDRESS, ADDRESS + LEN).
|
||||
|
||||
Return 0 if successful and non-zero otherwise. */
|
||||
extern int aarch64_mte_store_memtags (int tid, CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags);
|
||||
|
||||
#endif /* NAT_AARCH64_MTE_LINUX_PTRACE_H */
|
||||
468
gdb/printcmd.c
468
gdb/printcmd.c
@@ -53,6 +53,12 @@
|
||||
#include "source.h"
|
||||
#include "gdbsupport/byte-vector.h"
|
||||
#include "gdbsupport/gdb_optional.h"
|
||||
#include "gdbsupport/rsp-low.h"
|
||||
#include "infrun.h" /* For memtag setting. */
|
||||
|
||||
/* Chain containing all defined mtag subcommands. */
|
||||
|
||||
struct cmd_list_element *mtaglist;
|
||||
|
||||
/* Last specified output format. */
|
||||
|
||||
@@ -84,6 +90,24 @@ static CORE_ADDR last_examine_address;
|
||||
|
||||
static value_ref_ptr last_examine_value;
|
||||
|
||||
/* If TRUE (default), and the architecture supports it, GDB will attempt to use
|
||||
the memory tagging infrastructure to validate certain memory accesses. It
|
||||
will also report memory tag violations alongside a SIGSEGV signal.
|
||||
|
||||
If FALSE, GDB will not use memory tagging in any way, and debugging will work
|
||||
in the standard way. */
|
||||
static bool memtag = true;
|
||||
|
||||
static void
|
||||
show_memtag (struct ui_file *file, int from_tty,
|
||||
struct cmd_list_element *c,
|
||||
const char *value)
|
||||
{
|
||||
fprintf_filtered (file,
|
||||
_("Use of memory tagging infrastructure is \"%s\".\n"),
|
||||
value);
|
||||
}
|
||||
|
||||
/* Largest offset between a symbolic value and an address, that will be
|
||||
printed as `0x1234 <symbol+offset>'. */
|
||||
|
||||
@@ -187,6 +211,7 @@ decode_format (const char **string_ptr, int oformat, int osize)
|
||||
val.size = '?';
|
||||
val.count = 1;
|
||||
val.raw = 0;
|
||||
val.print_tags = false;
|
||||
|
||||
if (*p == '-')
|
||||
{
|
||||
@@ -209,6 +234,11 @@ decode_format (const char **string_ptr, int oformat, int osize)
|
||||
val.raw = 1;
|
||||
p++;
|
||||
}
|
||||
else if (*p == 'm')
|
||||
{
|
||||
val.print_tags = true;
|
||||
p++;
|
||||
}
|
||||
else if (*p >= 'a' && *p <= 'z')
|
||||
val.format = *p++;
|
||||
else
|
||||
@@ -1082,12 +1112,47 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
|
||||
need_to_update_next_address = 1;
|
||||
}
|
||||
|
||||
/* Whether we need to print the memory tag information for the current
|
||||
address range. */
|
||||
bool print_range_tag = true;
|
||||
uint32_t gsize = gdbarch_memtag_granule_size (gdbarch);
|
||||
|
||||
/* Print as many objects as specified in COUNT, at most maxelts per line,
|
||||
with the address of the next one at the start of each line. */
|
||||
|
||||
while (count > 0)
|
||||
{
|
||||
QUIT;
|
||||
|
||||
CORE_ADDR tag_laddr = 0, tag_haddr = 0;
|
||||
|
||||
/* Print the memory tag information if requested. */
|
||||
if (fmt.print_tags && print_range_tag && memtag
|
||||
&& target_supports_memory_tagging ())
|
||||
{
|
||||
tag_laddr = align_down (next_address, gsize);
|
||||
tag_haddr = align_down (next_address + gsize, gsize);
|
||||
|
||||
struct value *v_addr
|
||||
= value_from_ulongest (builtin_type (gdbarch)->builtin_data_ptr,
|
||||
tag_laddr);
|
||||
|
||||
if (gdbarch_tagged_address_p (target_gdbarch (), v_addr))
|
||||
{
|
||||
std::string atag = gdbarch_memtag_to_string (gdbarch, v_addr,
|
||||
tag_allocation);
|
||||
|
||||
if (!atag.empty ())
|
||||
{
|
||||
printf_filtered (_("<Allocation Tag %s for range [%s,%s)>\n"),
|
||||
atag.c_str (),
|
||||
paddress (gdbarch, tag_laddr),
|
||||
paddress (gdbarch, tag_haddr));
|
||||
}
|
||||
}
|
||||
print_range_tag = false;
|
||||
}
|
||||
|
||||
if (format == 'i')
|
||||
fputs_filtered (pc_prefix (next_address), gdb_stdout);
|
||||
print_address (next_gdbarch, next_address, gdb_stdout);
|
||||
@@ -1118,6 +1183,11 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
|
||||
/* Display any branch delay slots following the final insn. */
|
||||
if (format == 'i' && count == 1)
|
||||
count += branch_delay_insns;
|
||||
|
||||
/* Update the tag range based on the current address being
|
||||
processed. */
|
||||
if (tag_haddr <= next_address)
|
||||
print_range_tag = true;
|
||||
}
|
||||
printf_filtered ("\n");
|
||||
}
|
||||
@@ -1190,35 +1260,76 @@ print_value (value *val, const value_print_options &opts)
|
||||
annotate_value_history_end ();
|
||||
}
|
||||
|
||||
/* Implementation of the "print" and "call" commands. */
|
||||
/* Returns true if memory tags should be validated. False otherwise. */
|
||||
|
||||
static void
|
||||
print_command_1 (const char *args, int voidprint)
|
||||
static bool
|
||||
should_validate_memtags (struct value *value)
|
||||
{
|
||||
struct value *val;
|
||||
value_print_options print_opts;
|
||||
if (memtag && target_supports_memory_tagging ()
|
||||
&& gdbarch_tagged_address_p (target_gdbarch (), value))
|
||||
{
|
||||
gdb_assert (value && value_type (value));
|
||||
|
||||
get_user_print_options (&print_opts);
|
||||
enum type_code code = value_type (value)->code ();
|
||||
|
||||
return (code == TYPE_CODE_PTR
|
||||
|| code == TYPE_CODE_REF
|
||||
|| code == TYPE_CODE_METHODPTR
|
||||
|| code == TYPE_CODE_MEMBERPTR);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Helper for parsing arguments for print_command_1. */
|
||||
|
||||
static struct value *
|
||||
process_print_command_args (const char *args, value_print_options *print_opts)
|
||||
{
|
||||
get_user_print_options (print_opts);
|
||||
/* Override global settings with explicit options, if any. */
|
||||
auto group = make_value_print_options_def_group (&print_opts);
|
||||
auto group = make_value_print_options_def_group (print_opts);
|
||||
gdb::option::process_options
|
||||
(&args, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER, group);
|
||||
|
||||
print_command_parse_format (&args, "print", &print_opts);
|
||||
print_command_parse_format (&args, "print", print_opts);
|
||||
|
||||
const char *exp = args;
|
||||
|
||||
if (exp != nullptr && *exp)
|
||||
{
|
||||
expression_up expr = parse_expression (exp);
|
||||
val = evaluate_expression (expr.get ());
|
||||
return evaluate_expression (expr.get ());
|
||||
}
|
||||
else
|
||||
val = access_value_history (0);
|
||||
|
||||
return access_value_history (0);
|
||||
}
|
||||
|
||||
/* Implementation of the "print" and "call" commands. */
|
||||
|
||||
static void
|
||||
print_command_1 (const char *args, int voidprint)
|
||||
{
|
||||
value_print_options print_opts;
|
||||
|
||||
struct value *val = process_print_command_args (args, &print_opts);
|
||||
|
||||
if (voidprint || (val && value_type (val) &&
|
||||
value_type (val)->code () != TYPE_CODE_VOID))
|
||||
print_value (val, print_opts);
|
||||
{
|
||||
/* If memory tagging validation is on, check if the tag is valid. */
|
||||
if (should_validate_memtags (val)
|
||||
&& gdbarch_memtag_mismatch_p (target_gdbarch (), val))
|
||||
{
|
||||
std::string ltag = gdbarch_memtag_to_string (target_gdbarch (),
|
||||
val, tag_logical);
|
||||
std::string atag = gdbarch_memtag_to_string (target_gdbarch (),
|
||||
val, tag_allocation);
|
||||
printf_filtered (_("Logical tag (%s) does not match the "
|
||||
"allocation tag (%s).\n"),
|
||||
ltag.c_str (), atag.c_str ());
|
||||
}
|
||||
print_value (val, print_opts);
|
||||
}
|
||||
}
|
||||
|
||||
/* See valprint.h. */
|
||||
@@ -2682,6 +2793,273 @@ eval_command (const char *arg, int from_tty)
|
||||
execute_command (expanded.c_str (), from_tty);
|
||||
}
|
||||
|
||||
/* Convenience function for error checking in mtag commands. */
|
||||
|
||||
static void
|
||||
show_addr_not_tagged (CORE_ADDR address)
|
||||
{
|
||||
error (_("Address %s not in a region mapped with a memory tagging flag."),
|
||||
paddress (target_gdbarch (), address));
|
||||
}
|
||||
|
||||
/* Convenience function for error checking in mtag commands. */
|
||||
|
||||
static void
|
||||
show_memtag_unsupported (void)
|
||||
{
|
||||
error (_("Memory tagging not supported or disabled by the current"
|
||||
" architecture."));
|
||||
}
|
||||
|
||||
/* Implement the "mtag" prefix command. */
|
||||
|
||||
static void
|
||||
mtag_command (const char *arg, int from_tty)
|
||||
{
|
||||
help_list (mtaglist, "mtag ", all_commands, gdb_stdout);
|
||||
}
|
||||
|
||||
/* Helper for showltag and showatag. */
|
||||
|
||||
static void
|
||||
mtag_showtag_command (const char *args, enum memtag_type tag_type)
|
||||
{
|
||||
if (args == nullptr)
|
||||
error_no_arg (_("address or pointer"));
|
||||
|
||||
/* Parse args into a value. If the value is a pointer or an address,
|
||||
then fetch the logical or allocation tag. */
|
||||
value_print_options print_opts;
|
||||
|
||||
struct value *val = process_print_command_args (args, &print_opts);
|
||||
|
||||
/* If the address is not in a region memory mapped with a memory tagging
|
||||
flag, it is no use trying to access/manipulate its allocation tag.
|
||||
|
||||
It is OK to manipulate the logical tag though. */
|
||||
if (tag_type == tag_allocation
|
||||
&& !gdbarch_tagged_address_p (target_gdbarch (), val))
|
||||
show_addr_not_tagged (value_as_address (val));
|
||||
|
||||
std::string tag = gdbarch_memtag_to_string (target_gdbarch (),
|
||||
val, tag_type);
|
||||
if (tag.empty ())
|
||||
printf_filtered (_("%s tag unavailable.\n"),
|
||||
tag_type == tag_logical? "Logical" : "Allocation");
|
||||
|
||||
struct value *v_tag = process_print_command_args (tag.c_str (),
|
||||
&print_opts);
|
||||
print_opts.output_format = 'x';
|
||||
print_value (v_tag, print_opts);
|
||||
}
|
||||
|
||||
/* Implement the "mtag showltag" command. */
|
||||
|
||||
static void
|
||||
mtag_showltag_command (const char *args, int from_tty)
|
||||
{
|
||||
if (!memtag || !target_supports_memory_tagging ())
|
||||
show_memtag_unsupported ();
|
||||
|
||||
mtag_showtag_command (args, tag_logical);
|
||||
}
|
||||
|
||||
/* Implement the "mtag showatag" command. */
|
||||
|
||||
static void
|
||||
mtag_showatag_command (const char *args, int from_tty)
|
||||
{
|
||||
if (!memtag || !target_supports_memory_tagging ())
|
||||
show_memtag_unsupported ();
|
||||
|
||||
mtag_showtag_command (args, tag_allocation);
|
||||
}
|
||||
|
||||
/* Parse ARGS and extract ADDR and TAG.
|
||||
ARGS should have format <expression> <tag bytes>. */
|
||||
|
||||
static void
|
||||
parse_setltag_input (const char *args, struct value **val,
|
||||
gdb::byte_vector &tags, value_print_options *print_opts)
|
||||
{
|
||||
/* Given <expression> can be reasonably complex, we parse things backwards
|
||||
so we can isolate the <tag bytes> portion. */
|
||||
|
||||
/* Fetch the address. */
|
||||
std::string s_address = extract_string_maybe_quoted (&args);
|
||||
|
||||
/* Parse the address into a value. */
|
||||
*val = process_print_command_args (s_address.c_str (), print_opts);
|
||||
|
||||
/* Fetch the tag bytes. */
|
||||
std::string s_tag = extract_string_maybe_quoted (&args);
|
||||
|
||||
/* Validate the input. */
|
||||
if (s_address.empty () || s_tag.empty ())
|
||||
error (_("Missing arguments."));
|
||||
|
||||
if (s_tag.length () % 2)
|
||||
error (_("Error parsing tags argument. The tag should be 2 digits."));
|
||||
|
||||
tags = hex2bin (s_tag.c_str ());
|
||||
}
|
||||
|
||||
/* Implement the "mtag setltag" command. */
|
||||
|
||||
static void
|
||||
mtag_setltag_command (const char *args, int from_tty)
|
||||
{
|
||||
if (!memtag || !target_supports_memory_tagging ())
|
||||
show_memtag_unsupported ();
|
||||
|
||||
if (args == nullptr)
|
||||
error_no_arg (_("<address> <tag>"));
|
||||
|
||||
gdb::byte_vector tags;
|
||||
struct value *val;
|
||||
value_print_options print_opts;
|
||||
|
||||
/* Parse the input. */
|
||||
parse_setltag_input (args, &val, tags, &print_opts);
|
||||
|
||||
/* Setting the logical tag is just a local operation that does not touch
|
||||
any memory from the target. Given an input value, we modify the value
|
||||
to include the appropriate tag.
|
||||
|
||||
For this reason we need to cast the argument value to a
|
||||
(void *) pointer. This is so we have the right the for the gdbarch
|
||||
hook to manipulate the value and insert the tag.
|
||||
|
||||
Otherwise, this would fail if, for example, GDB parsed the argument value
|
||||
into an int-sized value and the pointer value has a type of greater
|
||||
length. */
|
||||
|
||||
/* Cast to (void *). */
|
||||
val = value_cast (builtin_type (target_gdbarch ())->builtin_data_ptr,
|
||||
val);
|
||||
|
||||
if (gdbarch_set_memtags (target_gdbarch (), val, 0, tags,
|
||||
tag_logical) != 0)
|
||||
printf_filtered (_("Could not update the logical tag data.\n"));
|
||||
else
|
||||
{
|
||||
/* Always print it in hex format. */
|
||||
print_opts.output_format = 'x';
|
||||
print_value (val, print_opts);
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse ARGS and extract ADDR, LENGTH and TAGS. */
|
||||
|
||||
static void
|
||||
parse_setatag_input (const char *args, struct value **val, size_t *length,
|
||||
gdb::byte_vector &tags)
|
||||
{
|
||||
/* Fetch the address. */
|
||||
std::string s_address = extract_string_maybe_quoted (&args);
|
||||
|
||||
/* Parse the address into a value. */
|
||||
value_print_options print_opts;
|
||||
*val = process_print_command_args (s_address.c_str (), &print_opts);
|
||||
|
||||
/* Fetch the length. */
|
||||
std::string s_length = extract_string_maybe_quoted (&args);
|
||||
|
||||
/* Fetch the tag bytes. */
|
||||
std::string s_tags = extract_string_maybe_quoted (&args);
|
||||
|
||||
/* Validate the input. */
|
||||
if (s_address.empty () || s_length.empty () || s_tags.empty ())
|
||||
error (_("Missing arguments."));
|
||||
|
||||
errno = 0;
|
||||
*length = strtoulst (s_length.c_str (), NULL, 10);
|
||||
if (errno != 0)
|
||||
error (_("Error parsing length argument."));
|
||||
|
||||
if (s_tags.length () % 2)
|
||||
error (_("Error parsing tags argument. Tags should be 2 digits per byte."));
|
||||
|
||||
tags = hex2bin (s_tags.c_str ());
|
||||
|
||||
/* If the address is not in a region memory mapped with a memory tagging
|
||||
flag, it is no use trying to access/manipulate its allocation tag. */
|
||||
if (!gdbarch_tagged_address_p (target_gdbarch (), *val))
|
||||
show_addr_not_tagged (value_as_address (*val));
|
||||
}
|
||||
|
||||
/* Implement the "mtag setatag" command.
|
||||
ARGS should be in the format <address> <length> <tags>. */
|
||||
|
||||
static void
|
||||
mtag_setatag_command (const char *args, int from_tty)
|
||||
{
|
||||
if (!memtag || !target_supports_memory_tagging ())
|
||||
show_memtag_unsupported ();
|
||||
|
||||
if (args == nullptr)
|
||||
error_no_arg (_("<starting address> <length> <tag bytes>"));
|
||||
|
||||
gdb::byte_vector tags;
|
||||
size_t length = 0;
|
||||
struct value *val;
|
||||
|
||||
/* Parse the input. */
|
||||
parse_setatag_input (args, &val, &length, tags);
|
||||
|
||||
if (gdbarch_set_memtags (target_gdbarch (), val, length, tags,
|
||||
tag_allocation) != 0)
|
||||
printf_filtered (_("Could not update the allocation tag(s).\n"));
|
||||
else
|
||||
printf_filtered (_("Allocation tag(s) updated successfully.\n"));
|
||||
}
|
||||
|
||||
/* Implement the "mtag check" command. */
|
||||
|
||||
static void
|
||||
mtag_check_command (const char *args, int from_tty)
|
||||
{
|
||||
if (!memtag || !target_supports_memory_tagging ())
|
||||
show_memtag_unsupported ();
|
||||
|
||||
if (args == nullptr)
|
||||
error (_("Argument required (address or pointer)"));
|
||||
|
||||
/* Parse the expression into a value. If the value is an address or
|
||||
pointer, then check its logical tag against the allocation tag. */
|
||||
value_print_options print_opts;
|
||||
|
||||
struct value *val = process_print_command_args (args, &print_opts);
|
||||
|
||||
/* If the address is not in a region memory mapped with a memory tagging
|
||||
flag, it is no use trying to access/manipulate its allocation tag. */
|
||||
if (!gdbarch_tagged_address_p (target_gdbarch (), val))
|
||||
show_addr_not_tagged (value_as_address (val));
|
||||
|
||||
CORE_ADDR addr = value_as_address (val);
|
||||
|
||||
/* If memory tagging validation is on, check if the tag is valid. */
|
||||
if (gdbarch_memtag_mismatch_p (target_gdbarch (), val))
|
||||
{
|
||||
std::string ltag = gdbarch_memtag_to_string (target_gdbarch (),
|
||||
val, tag_logical);
|
||||
std::string atag = gdbarch_memtag_to_string (target_gdbarch (),
|
||||
val, tag_allocation);
|
||||
|
||||
printf_filtered (_("Logical tag (%s) does not match"
|
||||
" the allocation tag (%s) for address %s.\n"),
|
||||
ltag.c_str (), atag.c_str (),
|
||||
paddress (target_gdbarch (), addr));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string ltag = gdbarch_memtag_to_string (target_gdbarch (),
|
||||
val, tag_logical);
|
||||
printf_filtered (_("Memory tags for address %s match (%s).\n"),
|
||||
paddress (target_gdbarch (), addr), ltag.c_str ());
|
||||
}
|
||||
}
|
||||
|
||||
void _initialize_printcmd ();
|
||||
void
|
||||
_initialize_printcmd ()
|
||||
@@ -2882,4 +3260,70 @@ Construct a GDB command and then evaluate it.\n\
|
||||
Usage: eval \"format string\", ARG1, ARG2, ARG3, ..., ARGN\n\
|
||||
Convert the arguments to a string as \"printf\" would, but then\n\
|
||||
treat this string as a command line, and evaluate it."));
|
||||
|
||||
add_setshow_boolean_cmd ("memory-tagging", class_support,
|
||||
&memtag, _("\
|
||||
Set whether the debugger should use memory tagging infrastructure."), _("\
|
||||
Show whether the debugger should use memory tagging infrastructure."), _("\
|
||||
If on, gdb will attempt to validate memory tags and will warn the user if\n\
|
||||
certain operations have illegal tags."),
|
||||
NULL,
|
||||
show_memtag,
|
||||
&setlist, &showlist);
|
||||
|
||||
/* Memory tagging commands. */
|
||||
add_prefix_cmd ("mtag", class_vars, mtag_command, _("\
|
||||
Generic command for showing and manipulating memory tag properties."),
|
||||
&mtaglist, "mtag ", 0, &cmdlist);
|
||||
add_cmd ("showltag", class_vars, mtag_showltag_command,
|
||||
("Show the logical tag for an address.\n\
|
||||
Usage: mtag showltag <address>.\n\
|
||||
<address> is an expression that evaluates to a pointer or memory address.\n\
|
||||
GDB will show the logical tag associated with <address>. The tag\n\
|
||||
interpretation is architecture-specific."),
|
||||
&mtaglist);
|
||||
add_cmd ("showatag", class_vars, mtag_showatag_command,
|
||||
_("Show the allocation tag for an address.\n\
|
||||
Usage: mtag showatag <address>.\n\
|
||||
<address> is an expression that evaluates to a pointer or memory address.\n\
|
||||
GDB will show the allocation tag associated with <address>. The tag\n\
|
||||
interpretation is architecture-specific."),
|
||||
&mtaglist);
|
||||
add_cmd ("setltag", class_vars, mtag_setltag_command,
|
||||
_("Set the logical tag for an address.\n\
|
||||
Usage: mtag setltag <address> <tag>\n\
|
||||
<address> is an expression that evaluates to a pointer or memory address.\n\
|
||||
<tag> is a sequence of hex bytes that will be interpreted by the\n\
|
||||
architecture as a single memory tag.\n\
|
||||
GDB will set the logical tag for <address> to <tag>, and will show the\n\
|
||||
resulting address with the updated tag."),
|
||||
&mtaglist);
|
||||
add_cmd ("setatag", class_vars, mtag_setatag_command,
|
||||
_("Set the allocation tag for an address.\n\
|
||||
Usage: mtag setatag <address> <length> <tag_bytes>\n\
|
||||
<address> is an expression that evaluates to a pointer or memory address\n\
|
||||
<length> is the number of bytes that will get added to <address> to calculate\n\
|
||||
the memory range.\n\
|
||||
<tag bytes> is a sequence of hex bytes that will be interpreted by the\n\
|
||||
architecture as one or more memory tags.\n\
|
||||
Sets the tags of the memory range [<address>, <address> + <length>)\n\
|
||||
to the specified tag bytes.\n\
|
||||
\n\
|
||||
If the number of tags is greater than or equal to the number of tag granules\n\
|
||||
in the [<address>, <address> + <length) range, only the tags up to the\n\
|
||||
number of tag granules will be stored.\n\
|
||||
\n\
|
||||
If the number of tags is less than the number of tag granules, then the\n\
|
||||
command is a fill operation. The tag bytes are interpreted as a pattern\n\
|
||||
that will get repeated until the number of tag granules in the memory range\n\
|
||||
[<address>, <address> + <length>] is stored to."),
|
||||
&mtaglist);
|
||||
add_cmd ("check", class_vars, mtag_check_command,
|
||||
_("Validate the logical tag against the allocation tag.\n\
|
||||
Usage: mtag check <address>\n\
|
||||
<address> is an expression that evaluates to a pointer or memory address\n\
|
||||
GDB will fetch the logical and allocation tags for <address> and will\n\
|
||||
compare them for equality. If the tags do not match, GDB will show\n\
|
||||
additional information about the mismatch."),
|
||||
&mtaglist);
|
||||
}
|
||||
|
||||
230
gdb/remote.c
230
gdb/remote.c
@@ -78,6 +78,7 @@
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include "async-event.h"
|
||||
#include "gdbsupport/selftest.h"
|
||||
|
||||
/* The remote target. */
|
||||
|
||||
@@ -685,6 +686,16 @@ public:
|
||||
int remove_exec_catchpoint (int) override;
|
||||
enum exec_direction_kind execution_direction () override;
|
||||
|
||||
bool supports_memory_tagging () override;
|
||||
|
||||
/* Read memory tags via the qMemTags packet */
|
||||
int fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags) override;
|
||||
|
||||
/* Write allocation tags via the QMemTags packet. */
|
||||
int store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags) override;
|
||||
|
||||
public: /* Remote specific methods. */
|
||||
|
||||
void remote_download_command_source (int num, ULONGEST addr,
|
||||
@@ -2093,6 +2104,10 @@ enum {
|
||||
/* Support TARGET_WAITKIND_NO_RESUMED. */
|
||||
PACKET_no_resumed,
|
||||
|
||||
/* Support for memory tagging, allocation tag fetch/store
|
||||
packets and the tag violation stop replies. */
|
||||
PACKET_memory_tagging_feature,
|
||||
|
||||
PACKET_MAX
|
||||
};
|
||||
|
||||
@@ -2234,6 +2249,14 @@ remote_exec_event_p (struct remote_state *rs)
|
||||
return packet_support (PACKET_exec_event_feature) == PACKET_ENABLE;
|
||||
}
|
||||
|
||||
/* Returns true if memory tagging is supported. */
|
||||
|
||||
static bool
|
||||
remote_memory_tagging_p (void)
|
||||
{
|
||||
return packet_support (PACKET_memory_tagging_feature) == PACKET_ENABLE;
|
||||
}
|
||||
|
||||
/* Insert fork catchpoint target routine. If fork events are enabled
|
||||
then return success, nothing more to do. */
|
||||
|
||||
@@ -5235,6 +5258,8 @@ static const struct protocol_feature remote_protocol_features[] = {
|
||||
{ "vContSupported", PACKET_DISABLE, remote_supported_packet, PACKET_vContSupported },
|
||||
{ "QThreadEvents", PACKET_DISABLE, remote_supported_packet, PACKET_QThreadEvents },
|
||||
{ "no-resumed", PACKET_DISABLE, remote_supported_packet, PACKET_no_resumed },
|
||||
{ "memory-tagging", PACKET_DISABLE, remote_supported_packet,
|
||||
PACKET_memory_tagging_feature },
|
||||
};
|
||||
|
||||
static char *remote_support_xml;
|
||||
@@ -5329,6 +5354,10 @@ remote_target::remote_query_supported ()
|
||||
if (packet_set_cmd_state (PACKET_no_resumed) != AUTO_BOOLEAN_FALSE)
|
||||
remote_query_supported_append (&q, "no-resumed+");
|
||||
|
||||
if (packet_set_cmd_state (PACKET_memory_tagging_feature)
|
||||
!= AUTO_BOOLEAN_FALSE)
|
||||
remote_query_supported_append (&q, "memory-tagging+");
|
||||
|
||||
/* Keep this one last to work around a gdbserver <= 7.10 bug in
|
||||
the qSupported:xmlRegisters=i386 handling. */
|
||||
if (remote_support_xml != NULL
|
||||
@@ -14378,6 +14407,199 @@ set_range_stepping (const char *ignore_args, int from_tty,
|
||||
}
|
||||
}
|
||||
|
||||
/* Implement the "supports_memory_tagging" target_ops method. */
|
||||
|
||||
bool
|
||||
remote_target::supports_memory_tagging ()
|
||||
{
|
||||
return remote_memory_tagging_p ();
|
||||
}
|
||||
|
||||
/* Create the qMemTags packet given ADDRESS and LEN.
|
||||
|
||||
Return 0 if successful, non-zero otherwise. */
|
||||
|
||||
static void
|
||||
create_fmemtags_request (gdb::char_vector &packet, CORE_ADDR address,
|
||||
size_t len)
|
||||
{
|
||||
int addr_size = gdbarch_addr_bit (target_gdbarch ()) / 8;
|
||||
|
||||
std::string request = string_printf ("qMemTags:%s,%s",
|
||||
phex_nz (address, addr_size),
|
||||
phex_nz (len, sizeof (len)));
|
||||
|
||||
strcpy (packet.data (), request.c_str ());
|
||||
}
|
||||
|
||||
/* Parse the qMemTags packet reply into TAGS.
|
||||
|
||||
Return 0 if successful, non-zero otherwise. */
|
||||
|
||||
static int
|
||||
parse_fmemtags_reply (gdb::char_vector &reply, gdb::byte_vector &tags)
|
||||
{
|
||||
if (reply.empty () || reply[0] == 'E' || reply[0] != 'm')
|
||||
return 1;
|
||||
|
||||
/* Copy the tag data. */
|
||||
tags = hex2bin (reply.data () + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create the QMemTags packet given ADDRESS, LEN and TAGS.
|
||||
|
||||
Return 0 if successful, non-zero otherwise. */
|
||||
|
||||
static void
|
||||
create_smemtags_request (gdb::char_vector &packet, CORE_ADDR address,
|
||||
size_t len, const gdb::byte_vector &tags)
|
||||
{
|
||||
int addr_size = gdbarch_addr_bit (target_gdbarch ()) / 8;
|
||||
|
||||
/* Put together the main packet, address and length. */
|
||||
std::string request = string_printf ("QMemTags:%s,%s:",
|
||||
phex_nz (address, addr_size),
|
||||
phex_nz (len, sizeof (len)));
|
||||
request += bin2hex (tags.data (), tags.size ());
|
||||
|
||||
/* Check if we have exceeded the maximum packet size. */
|
||||
if (packet.size () < request.length ())
|
||||
error (_("Contents too big for packet QMemTags."));
|
||||
|
||||
strcpy (packet.data (), request.c_str ());
|
||||
}
|
||||
|
||||
/* Implement the "fetch_memtags" target_ops method. */
|
||||
|
||||
int
|
||||
remote_target::fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags)
|
||||
{
|
||||
/* Make sure the qMemTags packet is supported. */
|
||||
if (!remote_memory_tagging_p ())
|
||||
gdb_assert_not_reached ("remote fetch_memtags called with packet disabled");
|
||||
|
||||
struct remote_state *rs = get_remote_state ();
|
||||
|
||||
create_fmemtags_request (rs->buf, address, len);
|
||||
|
||||
putpkt (rs->buf);
|
||||
getpkt (&rs->buf, 0);
|
||||
|
||||
parse_fmemtags_reply (rs->buf, tags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Implement the "store_memtags" target_ops method. */
|
||||
|
||||
int
|
||||
remote_target::store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags)
|
||||
{
|
||||
/* Make sure the QMemTags packet is supported. */
|
||||
if (!remote_memory_tagging_p ())
|
||||
gdb_assert_not_reached ("remote store_memtags called with packet disabled");
|
||||
|
||||
struct remote_state *rs = get_remote_state ();
|
||||
|
||||
create_smemtags_request (rs->buf, address, len, tags);
|
||||
|
||||
putpkt (rs->buf);
|
||||
getpkt (&rs->buf, 0);
|
||||
|
||||
/* Verify if the request was successful. */
|
||||
if (packet_check_result (rs->buf.data ()) != PACKET_OK)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if GDB_SELF_TEST
|
||||
|
||||
namespace selftests {
|
||||
|
||||
static void
|
||||
test_memory_tagging_functions (void)
|
||||
{
|
||||
remote_target remote;
|
||||
|
||||
struct packet_config *config
|
||||
= &remote_protocol_packets[PACKET_memory_tagging_feature];
|
||||
|
||||
/* Test memory tagging packet support. */
|
||||
config->support = PACKET_SUPPORT_UNKNOWN;
|
||||
SELF_CHECK (remote.supports_memory_tagging () == false);
|
||||
config->support = PACKET_DISABLE;
|
||||
SELF_CHECK (remote.supports_memory_tagging () == false);
|
||||
config->support = PACKET_ENABLE;
|
||||
SELF_CHECK (remote.supports_memory_tagging () == true);
|
||||
|
||||
/* Setup testing. */
|
||||
gdb::char_vector packet;
|
||||
gdb::byte_vector tags, bv;
|
||||
std::string expected, reply;
|
||||
packet.resize (32000);
|
||||
|
||||
/* Test creating a qMemTags request. */
|
||||
|
||||
expected = "qMemTags:0,0";
|
||||
create_fmemtags_request (packet, 0x0, 0x0);
|
||||
SELF_CHECK (strcmp (packet.data (), expected.c_str ()) == 0);
|
||||
|
||||
expected = "qMemTags:deadbeef,10";
|
||||
create_fmemtags_request (packet, 0xdeadbeef, 16);
|
||||
SELF_CHECK (strcmp (packet.data (), expected.c_str ()) == 0);
|
||||
|
||||
/* Test parsing a qMemTags reply. */
|
||||
|
||||
/* Error reply, tags vector unmodified. */
|
||||
reply = "E00";
|
||||
strcpy (packet.data (), reply.c_str ());
|
||||
tags.resize (0);
|
||||
SELF_CHECK (parse_fmemtags_reply (packet, tags) != 0);
|
||||
SELF_CHECK (tags.size () == 0);
|
||||
|
||||
/* Valid reply, tags vector updated. */
|
||||
tags.resize (0);
|
||||
bv.resize (0);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
bv.push_back (i);
|
||||
|
||||
reply = "m" + bin2hex (bv.data (), bv.size ());
|
||||
strcpy (packet.data (), reply.c_str ());
|
||||
|
||||
SELF_CHECK (parse_fmemtags_reply (packet, tags) == 0);
|
||||
SELF_CHECK (tags.size () == 5);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
SELF_CHECK (tags[i] == i);
|
||||
|
||||
/* Test creating a QMemTags request. */
|
||||
|
||||
/* Empty tag data. */
|
||||
tags.resize (0);
|
||||
expected = "QMemTags:0,0:";
|
||||
create_smemtags_request (packet, 0x0, 0x0, tags);
|
||||
SELF_CHECK (memcmp (packet.data (), expected.c_str (),
|
||||
expected.length ()) == 0);
|
||||
|
||||
/* Non-empty tag data. */
|
||||
tags.resize (0);
|
||||
for (int i = 0; i < 5; i++)
|
||||
tags.push_back (i);
|
||||
expected = "QMemTags:deadbeef,ff:0001020304";
|
||||
create_smemtags_request (packet, 0xdeadbeef, 255, tags);
|
||||
SELF_CHECK (memcmp (packet.data (), expected.c_str (),
|
||||
expected.length ()) == 0);
|
||||
}
|
||||
|
||||
} // namespace selftests
|
||||
#endif /* GDB_SELF_TEST */
|
||||
|
||||
void _initialize_remote ();
|
||||
void
|
||||
_initialize_remote ()
|
||||
@@ -14777,6 +14999,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
|
||||
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_memory_tagging_feature],
|
||||
"memory-tagging-feature", "memory-tagging-feature", 0);
|
||||
|
||||
/* Assert that we've registered "set remote foo-packet" commands
|
||||
for all packet configs. */
|
||||
{
|
||||
@@ -14888,4 +15113,9 @@ Specify \"unlimited\" to display all the characters."),
|
||||
|
||||
/* Eventually initialize fileio. See fileio.c */
|
||||
initialize_remote_fileio (&remote_set_cmdlist, &remote_show_cmdlist);
|
||||
|
||||
#if GDB_SELF_TEST
|
||||
selftests::register_test ("remote_memory_tagging",
|
||||
selftests::test_memory_tagging_functions);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -171,6 +171,11 @@ struct dummy_target : public target_ops
|
||||
const struct frame_unwind *get_tailcall_unwinder () override;
|
||||
void prepare_to_generate_core () override;
|
||||
void done_generating_core () override;
|
||||
bool supports_memory_tagging () override;
|
||||
int fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags) override;
|
||||
int store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags) override;
|
||||
};
|
||||
|
||||
struct debug_target : public target_ops
|
||||
@@ -340,6 +345,11 @@ 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;
|
||||
bool supports_memory_tagging () override;
|
||||
int fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags) override;
|
||||
int store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags) override;
|
||||
};
|
||||
|
||||
void
|
||||
@@ -4363,3 +4373,77 @@ debug_target::done_generating_core ()
|
||||
fputs_unfiltered (")\n", gdb_stdlog);
|
||||
}
|
||||
|
||||
bool
|
||||
target_ops::supports_memory_tagging ()
|
||||
{
|
||||
return this->beneath ()->supports_memory_tagging ();
|
||||
}
|
||||
|
||||
int
|
||||
target_ops::fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags)
|
||||
{
|
||||
return this->beneath ()->fetch_memtags (address, len, tags);
|
||||
}
|
||||
|
||||
int
|
||||
target_ops::store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags)
|
||||
{
|
||||
return this->beneath ()->store_memtags (address, len, tags);
|
||||
}
|
||||
|
||||
bool
|
||||
dummy_target::supports_memory_tagging ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
dummy_target::fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
dummy_target::store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
debug_target::supports_memory_tagging ()
|
||||
{
|
||||
bool result;
|
||||
fprintf_unfiltered (gdb_stdlog, "-> %s->supports_memory_tagging (...)\n", this->beneath ()->shortname ());
|
||||
result = this->beneath ()->supports_memory_tagging ();
|
||||
fprintf_unfiltered (gdb_stdlog, "<- %s->supports_memory_tagging (", this->beneath ()->shortname ());
|
||||
fputs_unfiltered (") = ", gdb_stdlog);
|
||||
target_debug_print_bool (result);
|
||||
fputs_unfiltered ("\n", gdb_stdlog);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
debug_target::fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags)
|
||||
{
|
||||
fprintf_unfiltered (gdb_stdlog, "-> %s->fetch_memtags (...)\n", this->beneath ()->shortname ());
|
||||
int result = this->beneath ()->fetch_memtags (address, len, tags);
|
||||
fprintf_unfiltered (gdb_stdlog, "<- %s->fetch_memtags (", this->beneath ()->shortname ());
|
||||
fputs_unfiltered (")\n", gdb_stdlog);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
debug_target::store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags)
|
||||
{
|
||||
fprintf_unfiltered (gdb_stdlog, "-> %s->store_memtags (...)\n", this->beneath ()->shortname ());
|
||||
int result = this->beneath ()->store_memtags (address, len, tags);
|
||||
fprintf_unfiltered (gdb_stdlog, "<- %s->store_memtags (", this->beneath ()->shortname ());
|
||||
fputs_unfiltered (")\n", gdb_stdlog);
|
||||
return result;
|
||||
}
|
||||
|
||||
25
gdb/target.h
25
gdb/target.h
@@ -1252,6 +1252,22 @@ struct target_ops
|
||||
/* Cleanup after generating a core file. */
|
||||
virtual void done_generating_core ()
|
||||
TARGET_DEFAULT_IGNORE ();
|
||||
|
||||
/* Returns true if the target supports memory tagging. */
|
||||
virtual bool supports_memory_tagging ()
|
||||
TARGET_DEFAULT_RETURN (false);
|
||||
|
||||
/* Return the allocated memory tags associated with
|
||||
[ADDRESS, ADDRESS + LEN) in TAGS. */
|
||||
virtual int fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags)
|
||||
TARGET_DEFAULT_IGNORE ();
|
||||
|
||||
/* Write the allocation tags contained in TAGS to the memory range
|
||||
[ADDRESS, ADDRESS + LEN). */
|
||||
virtual int store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags)
|
||||
TARGET_DEFAULT_IGNORE ();
|
||||
};
|
||||
|
||||
/* Deleter for std::unique_ptr. See comments in
|
||||
@@ -2308,6 +2324,15 @@ extern gdb::unique_xmalloc_ptr<char> target_fileio_read_stralloc
|
||||
#define target_augmented_libraries_svr4_read() \
|
||||
(current_top_target ()->augmented_libraries_svr4_read) ()
|
||||
|
||||
#define target_supports_memory_tagging() \
|
||||
((current_top_target ()->supports_memory_tagging) ())
|
||||
|
||||
#define target_fetch_memtags(address, len, tags) \
|
||||
(current_top_target ()->fetch_memtags) ((address), (len), (tags))
|
||||
|
||||
#define target_store_memtags(address, len, tags) \
|
||||
(current_top_target ()->store_memtags) ((address), (len), (tags))
|
||||
|
||||
/* Command logging facility. */
|
||||
|
||||
#define target_log_command(p) \
|
||||
|
||||
107
gdb/testsuite/gdb.arch/aarch64-mte.c
Normal file
107
gdb/testsuite/gdb.arch/aarch64-mte.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/* This test program is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2020 Free Software Foundation, Inc.
|
||||
|
||||
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/>. */
|
||||
|
||||
/* Exercise AArch64's Memory Tagging Extension with tagged pointers. */
|
||||
|
||||
/* This test was based on the documentation for the AArch64 Memory Tagging
|
||||
Extension from the Linux Kernel, found in the sources in
|
||||
Documentation/arm64/memory-tagging-extension.rst. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
/* From arch/arm64/include/uapi/asm/hwcap.h */
|
||||
#define HWCAP2_MTE (1 << 18)
|
||||
|
||||
/* From arch/arm64/include/uapi/asm/mman.h */
|
||||
#define PROT_MTE 0x20
|
||||
|
||||
/* From include/uapi/linux/prctl.h */
|
||||
#define PR_SET_TAGGED_ADDR_CTRL 55
|
||||
#define PR_GET_TAGGED_ADDR_CTRL 56
|
||||
#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
|
||||
#define PR_MTE_TCF_SHIFT 1
|
||||
#define PR_MTE_TCF_NONE (0UL << PR_MTE_TCF_SHIFT)
|
||||
#define PR_MTE_TCF_SYNC (1UL << PR_MTE_TCF_SHIFT)
|
||||
#define PR_MTE_TCF_ASYNC (2UL << PR_MTE_TCF_SHIFT)
|
||||
#define PR_MTE_TCF_MASK (3UL << PR_MTE_TCF_SHIFT)
|
||||
#define PR_MTE_TAG_SHIFT 3
|
||||
#define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT)
|
||||
|
||||
void
|
||||
access_memory (unsigned char *tagged_ptr, unsigned char *untagged_ptr)
|
||||
{
|
||||
tagged_ptr[0] = 'a';
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
unsigned char *tagged_ptr;
|
||||
unsigned char *untagged_ptr;
|
||||
unsigned long page_sz = sysconf (_SC_PAGESIZE);
|
||||
unsigned long hwcap2 = getauxval(AT_HWCAP2);
|
||||
|
||||
/* Bail out if MTE is not supported. */
|
||||
if (!(hwcap2 & HWCAP2_MTE))
|
||||
return 1;
|
||||
|
||||
/* Enable the tagged address ABI, synchronous MTE tag check faults and
|
||||
allow all non-zero tags in the randomly generated set. */
|
||||
if (prctl (PR_SET_TAGGED_ADDR_CTRL,
|
||||
PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC
|
||||
| (0xfffe << PR_MTE_TAG_SHIFT),
|
||||
0, 0, 0))
|
||||
{
|
||||
perror ("prctl () failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Create a mapping that will have PROT_MTE set. */
|
||||
tagged_ptr = mmap (0, page_sz, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (tagged_ptr == MAP_FAILED)
|
||||
{
|
||||
perror ("mmap () failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Create another mapping that won't have PROT_MTE set. */
|
||||
untagged_ptr = mmap (0, page_sz, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (untagged_ptr == MAP_FAILED)
|
||||
{
|
||||
perror ("mmap () failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Enable MTE on the above anonymous mmap. */
|
||||
if (mprotect (tagged_ptr, page_sz, PROT_READ | PROT_WRITE | PROT_MTE))
|
||||
{
|
||||
perror ("mprotect () failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
access_memory (tagged_ptr, untagged_ptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
371
gdb/testsuite/gdb.arch/aarch64-mte.exp
Normal file
371
gdb/testsuite/gdb.arch/aarch64-mte.exp
Normal file
@@ -0,0 +1,371 @@
|
||||
# Copyright (C) 2020 Free Software Foundation, Inc.
|
||||
|
||||
# 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/>.
|
||||
|
||||
# Test a binary with address signing works regardless of whether the target
|
||||
# supports pauth instructions. On non pauth systems, all pauth instructions
|
||||
# are treated as nops.
|
||||
|
||||
global hex
|
||||
global decimal
|
||||
|
||||
# Return TAG in hex format with no leading zeroes.
|
||||
proc get_hex_tag { tag } {
|
||||
return [format "%x" $tag]
|
||||
}
|
||||
|
||||
# Return TAG in the NN format where N is 4 bits of the byte.
|
||||
proc get_tag_nn { tag } {
|
||||
return [format "%02x" $tag]
|
||||
}
|
||||
|
||||
# Return the address of PTR with a tag of TAG.
|
||||
proc get_tagged_ptr { tag ptr } {
|
||||
set addr [get_hexadecimal_valueof $ptr -1]
|
||||
return [get_valueof "/x" \
|
||||
"${addr} & (0xf0ffffffffffffff) | ((unsigned long) ${tag} << 56)" \
|
||||
"0" "fetch pointer ${ptr} with tag ${tag}"]
|
||||
}
|
||||
|
||||
# Return the logical TAG from PTR.
|
||||
proc get_ltag_from_ptr { ptr } {
|
||||
set addr [get_hexadecimal_valueof $ptr -1]
|
||||
return [get_valueof "/x" "${addr} >> 56 & 0xf" -1 \
|
||||
"fetch tag from pointer ${ptr}"]
|
||||
}
|
||||
|
||||
if {![is_aarch64_target]} {
|
||||
verbose "Skipping ${gdb_test_file_name}."
|
||||
return
|
||||
}
|
||||
|
||||
standard_testfile
|
||||
if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
|
||||
return -1
|
||||
}
|
||||
|
||||
if ![runto_main] {
|
||||
untested "could not run to main"
|
||||
return -1
|
||||
}
|
||||
|
||||
# Targets that don't support memory tagging should not execute the
|
||||
# runtime memory tagging tests.
|
||||
if {![supports_memtag]} {
|
||||
untested "memory tagging unsupported"
|
||||
return -1
|
||||
}
|
||||
|
||||
gdb_breakpoint "access_memory"
|
||||
|
||||
if [gdb_continue "access_memory"] {
|
||||
fail "could not run to tagged memory test function"
|
||||
return -1
|
||||
}
|
||||
|
||||
# Fetch a known pointer to an area mapped with PROT_MTE.
|
||||
set tagged_ptr_symbol "tagged_ptr"
|
||||
set tagged_ptr_addr [get_hexadecimal_valueof $tagged_ptr_symbol -1]
|
||||
|
||||
if {$tagged_ptr_addr == -1} {
|
||||
untested "unexpected pointer or tag value"
|
||||
return -1
|
||||
}
|
||||
|
||||
# Fetch a known pointer to an area not mapped with PROT_MTE.
|
||||
set untagged_ptr_symbol "untagged_ptr"
|
||||
set untagged_ptr_addr [get_hexadecimal_valueof $untagged_ptr_symbol -1]
|
||||
|
||||
if {$untagged_ptr_addr == -1} {
|
||||
untested "unexpected pointer or tag value"
|
||||
return -1
|
||||
}
|
||||
|
||||
with_test_prefix "literals" {
|
||||
# Test inspecting an allocation tag from a pointer to a memory area that
|
||||
# is not mapped with PROT_MTE.
|
||||
set msg "Address ${untagged_ptr_addr} not in a region mapped with a memory tagging flag\."
|
||||
gdb_test "mtag showatag ${untagged_ptr_addr}" $msg \
|
||||
"mtag showatag with an untagged address"
|
||||
|
||||
gdb_test "mtag setatag ${untagged_ptr_addr} 0 00" $msg \
|
||||
"mtag setatag with an untagged address"
|
||||
|
||||
set addr_tagged 0
|
||||
set addr_tagged_valid 0
|
||||
|
||||
# Test setting and showing the logical tags for a literal address.
|
||||
for {set i 0} {$i < 32} {incr i} {
|
||||
with_test_prefix "tag ${i}" {
|
||||
set addr_tagged [get_tagged_ptr $i ${tagged_ptr_addr}]
|
||||
}
|
||||
|
||||
set tag_hexnz [get_hex_tag [expr $i % 16]]
|
||||
gdb_test "mtag showltag ${addr_tagged}" \
|
||||
" = 0x${tag_hexnz}" \
|
||||
"showltag with tag ${i}"
|
||||
|
||||
set tag_hexnn [get_tag_nn $i]
|
||||
gdb_test "mtag setltag ${addr_tagged} ${tag_hexnn}" \
|
||||
" = \\(void \\*\\) ${addr_tagged}" \
|
||||
"setltag with tag ${i}"
|
||||
}
|
||||
|
||||
set setatag_msg "Allocation tag\\(s\\) updated successfully\."
|
||||
# Test setting and showing the allocation tags.
|
||||
for {set i 0} {$i < 32} {incr i} {
|
||||
|
||||
set tag_hexnn [get_tag_nn $i]
|
||||
gdb_test "mtag setatag ${tagged_ptr_addr} 0 ${tag_hexnn}" \
|
||||
$setatag_msg \
|
||||
"setatag with tag ${i}"
|
||||
|
||||
set tag_hexnz [get_hex_tag [expr $i % 16]]
|
||||
gdb_test "mtag showatag ${tagged_ptr_addr}" " = 0x${tag_hexnz}" \
|
||||
"showatag with tag ${i}"
|
||||
}
|
||||
|
||||
# Test tag mismatches.
|
||||
with_test_prefix "tag mismatches" {
|
||||
for {set i 0} {$i < 32} {incr i} {
|
||||
|
||||
# Set the allocation tag to a known value.
|
||||
set tag_hexnn [get_tag_nn $i]
|
||||
gdb_test "mtag setatag ${tagged_ptr_addr} 0 ${tag_hexnn}" \
|
||||
$setatag_msg \
|
||||
"setatag with tag ${i}"
|
||||
|
||||
set atag_hexnz [get_hex_tag [expr $i % 16]]
|
||||
|
||||
# Validate that the logical tag matches the allocation tag.
|
||||
with_test_prefix "tag ${i}" {
|
||||
set addr_tagged [get_tagged_ptr $i ${tagged_ptr_addr}]
|
||||
}
|
||||
|
||||
gdb_test "mtag check ${addr_tagged}" \
|
||||
"Memory tags for address $hex match \\(0x${atag_hexnz}\\)\." \
|
||||
"check match with tag ${i}"
|
||||
|
||||
# Get a pointer with the logical tag that does not match the
|
||||
# allocation tag.
|
||||
set ltag [expr $i + 1]
|
||||
with_test_prefix "fetch mismatch tag ${i}" {
|
||||
set addr_tagged [get_tagged_ptr $ltag ${tagged_ptr_addr}]
|
||||
}
|
||||
|
||||
# Validate that the logical tag does not match the allocation
|
||||
# tag.
|
||||
set ltag_hexnz [get_hex_tag [expr [expr $i + 1]% 16]]
|
||||
gdb_test "mtag check ${addr_tagged}" \
|
||||
"Logical tag \\(0x${ltag_hexnz}\\) does not match the allocation tag \\(0x${atag_hexnz}\\) for address $hex\." \
|
||||
"check mismatch with tag ${i}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
with_test_prefix "symbolic" {
|
||||
# Test inspecting an allocation tag from a pointer to a memory area that
|
||||
# is not mapped with PROT_MTE.
|
||||
set msg "Address ${untagged_ptr_addr} not in a region mapped with a memory tagging flag\."
|
||||
gdb_test "mtag showatag ${untagged_ptr_symbol}" $msg \
|
||||
"mtag showatag with an untagged address"
|
||||
|
||||
gdb_test "mtag setatag ${untagged_ptr_symbol} 0 00" $msg \
|
||||
"mtag setatag with an untagged address"
|
||||
|
||||
# Test setting and showing the logical tags for a literal address.
|
||||
for {set i 0} {$i < 32} {incr i} {
|
||||
set addr_tagged 0
|
||||
|
||||
with_test_prefix "tag ${i}" {
|
||||
set addr_tagged [get_tagged_ptr $i ${tagged_ptr_addr}]
|
||||
gdb_test_no_output "set variable ${tagged_ptr_symbol} = ${addr_tagged}" \
|
||||
"update value of symbol ${tagged_ptr_symbol}"
|
||||
}
|
||||
|
||||
set tag_hexnz [get_hex_tag [expr $i % 16]]
|
||||
gdb_test "mtag showltag ${tagged_ptr_symbol}" \
|
||||
" = 0x${tag_hexnz}" \
|
||||
"showltag with tag ${i}"
|
||||
|
||||
set tag_hexnn [get_tag_nn $i]
|
||||
gdb_test "mtag setltag ${tagged_ptr_symbol} ${tag_hexnn}" \
|
||||
" = \\(void \\*\\) ${addr_tagged}" \
|
||||
"setltag with tag ${i}"
|
||||
}
|
||||
|
||||
# Reset the tagged ptr to its original value
|
||||
gdb_test_no_output "set variable ${tagged_ptr_symbol} = ${tagged_ptr_addr}" \
|
||||
"reset ${tagged_ptr_symbol} to ${tagged_ptr_addr}"
|
||||
|
||||
set setatag_msg "Allocation tag\\(s\\) updated successfully\."
|
||||
# Test setting and showing the allocation tags.
|
||||
for {set i 0} {$i < 32} {incr i} {
|
||||
|
||||
set tag_hexnn [get_tag_nn $i]
|
||||
gdb_test "mtag setatag ${tagged_ptr_symbol} 0 ${tag_hexnn}" \
|
||||
$setatag_msg \
|
||||
"setatag with tag ${i}"
|
||||
|
||||
set tag_hexnz [get_hex_tag [expr $i % 16]]
|
||||
gdb_test "mtag showatag ${tagged_ptr_symbol}" " = 0x${tag_hexnz}" \
|
||||
"showatag with tag ${i}"
|
||||
}
|
||||
|
||||
# Test tag mismatches.
|
||||
with_test_prefix "tag mismatches" {
|
||||
for {set i 0} {$i < 32} {incr i} {
|
||||
|
||||
# Set the allocation tag to a known value (0).
|
||||
set tag_hexnn [get_tag_nn $i]
|
||||
gdb_test "mtag setatag ${tagged_ptr_symbol} 0 ${tag_hexnn}" \
|
||||
$setatag_msg \
|
||||
"setatag with tag ${i}"
|
||||
|
||||
set atag_hexnz [get_hex_tag [expr $i % 16]]
|
||||
|
||||
# Validate that the logical tag matches the allocation tag.
|
||||
with_test_prefix "tag ${i}" {
|
||||
set addr_tagged [get_tagged_ptr $i ${tagged_ptr_addr}]
|
||||
}
|
||||
|
||||
with_test_prefix "tag ${i}" {
|
||||
gdb_test_no_output "set variable ${tagged_ptr_symbol} = ${addr_tagged}" \
|
||||
"set ${tagged_ptr_symbol} to a matching logical tag"
|
||||
}
|
||||
|
||||
gdb_test "mtag check ${tagged_ptr_symbol}" \
|
||||
"Memory tags for address $hex match \\(0x${atag_hexnz}\\)\." \
|
||||
"check match with tag ${i}"
|
||||
|
||||
# Get a pointer with the logical tag that does not match the
|
||||
# allocation tag.
|
||||
set ltag [expr $i + 1]
|
||||
with_test_prefix "fetch mismatch tag ${i}" {
|
||||
set addr_tagged [get_tagged_ptr $ltag ${tagged_ptr_addr}]
|
||||
}
|
||||
|
||||
with_test_prefix "tag ${i}" {
|
||||
gdb_test_no_output "set variable ${tagged_ptr_symbol} = ${addr_tagged}" \
|
||||
"set ${tagged_ptr_symbol} to a mismatching logical tag"
|
||||
}
|
||||
|
||||
# Validate that the logical tag does not match the allocation
|
||||
# tag.
|
||||
set ltag_hexnz [get_hex_tag [expr [expr $i + 1]% 16]]
|
||||
gdb_test "mtag check ${tagged_ptr_symbol}" \
|
||||
"Logical tag \\(0x${ltag_hexnz}\\) does not match the allocation tag \\(0x${atag_hexnz}\\) for address $hex\." \
|
||||
"check mismatch with tag ${i}"
|
||||
}
|
||||
# Reset the tagged ptr to its original value
|
||||
gdb_test_no_output "set variable ${tagged_ptr_symbol} = ${tagged_ptr_addr}" \
|
||||
"reset ${tagged_ptr_symbol} to ${tagged_ptr_addr}"
|
||||
}
|
||||
}
|
||||
|
||||
# Test the memory tagging extensions for the "print" command.
|
||||
with_test_prefix "print command" {
|
||||
set untagged_ptr [get_tagged_ptr 0 ${tagged_ptr_addr}]
|
||||
|
||||
with_test_prefix "fetch ltag" {
|
||||
set ltag [get_ltag_from_ptr ${tagged_ptr_addr}]
|
||||
}
|
||||
|
||||
if {$ltag == -1} {
|
||||
untested "unexpected tag value"
|
||||
return -1
|
||||
}
|
||||
|
||||
set atag [expr [expr $ltag + 1] % 16]
|
||||
set atag_hexnn [get_tag_nn $atag]
|
||||
|
||||
gdb_test "mtag setatag ${tagged_ptr_symbol} 0 ${atag_hexnn}" \
|
||||
$setatag_msg \
|
||||
"make atag and ltag different"
|
||||
|
||||
set atag_hexnz [get_hex_tag $atag]
|
||||
gdb_test "p/x ${tagged_ptr_symbol}" \
|
||||
[multi_line \
|
||||
"Logical tag \\(${ltag}\\) does not match the allocation tag \\(0x${atag_hexnz}\\)\." \
|
||||
"\\\$\[0-9\]+ = ${untagged_ptr}"] \
|
||||
"show tag mismatch"
|
||||
}
|
||||
|
||||
# Test the memory tagging extensions for the "x" command.
|
||||
with_test_prefix "x command" {
|
||||
|
||||
# Check if the allocation tags match what we expect.
|
||||
gdb_test "x/gxm ${tagged_ptr_symbol}" \
|
||||
[multi_line \
|
||||
"<Allocation Tag $hex for range \\\[$hex,$hex\\)>" \
|
||||
"$hex:\[ \t\]+$hex"] \
|
||||
"outputs tag information"
|
||||
|
||||
# Also make sure no tag information is output for memory areas without
|
||||
# PROT_MTE mappings.
|
||||
gdb_test "x/gxm ${untagged_ptr_symbol}" \
|
||||
"$hex:\[ \t\]+$hex" \
|
||||
"does not output tag information"
|
||||
}
|
||||
|
||||
# Validate the presence of the MTE registers.
|
||||
foreach reg {"tag_ctl" } {
|
||||
gdb_test "info registers $reg" \
|
||||
"$reg\[ \t\]+$hex\[ \t\]+$decimal" \
|
||||
"register $reg available"
|
||||
}
|
||||
|
||||
# Run until a crash and confirm GDB displays memory tag violation
|
||||
# information.
|
||||
gdb_test "continue" \
|
||||
[multi_line \
|
||||
"Program received signal SIGSEGV, Segmentation fault" \
|
||||
"Memory tag violation while accessing address $hex" \
|
||||
"Allocation tag $hex\." \
|
||||
"$hex in access_memory \\(.*\\) at .*" \
|
||||
".*tagged_ptr\\\[0\\\] = 'a';"] \
|
||||
"display tag violation information"
|
||||
|
||||
# Restart to execute the async tag fault test.
|
||||
with_test_prefix "async" {
|
||||
if ![runto_main] {
|
||||
untested "could not run to main"
|
||||
return -1
|
||||
}
|
||||
|
||||
gdb_breakpoint "access_memory"
|
||||
|
||||
if [gdb_continue "access_memory"] {
|
||||
fail "could not run to tagged memory test function"
|
||||
return -1
|
||||
}
|
||||
|
||||
# Force a tag fault.
|
||||
gdb_test "mtag setatag tagged_ptr 0 05" \
|
||||
$setatag_msg \
|
||||
"make atag and ltag different"
|
||||
|
||||
# Force the tag fault to be async.
|
||||
gdb_test_no_output "set \$tag_ctl=0x7fff5" "set tag_ctl to async"
|
||||
|
||||
# Run until a crash and confirm GDB displays memory tag violation
|
||||
# information for async mode
|
||||
gdb_test "continue" \
|
||||
[multi_line \
|
||||
"Program received signal SIGSEGV, Segmentation fault" \
|
||||
"Memory tag violation" \
|
||||
"Fault address unavailable\." \
|
||||
"$hex in .* \\(.*\\) .*"] \
|
||||
"display tag violation information"
|
||||
}
|
||||
22
gdb/testsuite/gdb.base/memtag.c
Normal file
22
gdb/testsuite/gdb.base/memtag.c
Normal file
@@ -0,0 +1,22 @@
|
||||
/* This test program is part of GDB, the GNU debugger.
|
||||
|
||||
Copyright 2020 Free Software Foundation, Inc.
|
||||
|
||||
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/>. */
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
64
gdb/testsuite/gdb.base/memtag.exp
Normal file
64
gdb/testsuite/gdb.base/memtag.exp
Normal file
@@ -0,0 +1,64 @@
|
||||
# Copyright 2020 Free Software Foundation, Inc.
|
||||
|
||||
# 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/>.
|
||||
|
||||
# Smoke testing for the various memory tagging commands in GDB.
|
||||
|
||||
set u_msg "Memory tagging not supported or disabled by the current architecture\."
|
||||
|
||||
standard_testfile
|
||||
if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} {
|
||||
return -1
|
||||
}
|
||||
|
||||
if {[target_info gdb_protocol] == "extended-remote"} {
|
||||
# Make sure we're disconnected, in case we're testing with an
|
||||
# extended-remote board, therefore already connected.
|
||||
gdb_test "disconnect" ".*"
|
||||
}
|
||||
|
||||
# Test commands without running the program.
|
||||
with_test_prefix "before program execution" {
|
||||
# These commands should all fails without a running program.
|
||||
foreach subcmd {"setltag" "showltag" "setatag" "showatag" "check"} {
|
||||
gdb_test "mtag $subcmd" $u_msg
|
||||
}
|
||||
}
|
||||
|
||||
clean_restart $testfile
|
||||
|
||||
if ![runto_main] {
|
||||
untested "could not run to main"
|
||||
return -1
|
||||
}
|
||||
|
||||
# Targets that don't support memory tagging should not execute the
|
||||
# runtime memory tagging tests.
|
||||
if {![supports_memtag]} {
|
||||
untested "memory tagging unsupported"
|
||||
return -1
|
||||
}
|
||||
|
||||
# With the program running, try to use the memory tagging commands.
|
||||
with_test_prefix "during program execution" {
|
||||
set msg "Argument required \\(address or pointer\\)\."
|
||||
|
||||
# Test the various mtag commands again.
|
||||
gdb_test "mtag showltag" $msg
|
||||
gdb_test "mtag showatag" $msg
|
||||
gdb_test "mtag setltag" "Argument required \\(<address> <tag>\\)\."
|
||||
gdb_test "mtag setatag" \
|
||||
"Argument required \\(<starting address> <length> <tag bytes>\\)\."
|
||||
gdb_test "mtag check" $msg
|
||||
}
|
||||
@@ -2635,6 +2635,22 @@ proc supports_get_siginfo_type {} {
|
||||
}
|
||||
}
|
||||
|
||||
# Return 1 if memory tagging is supported at runtime, otherwise return 0.
|
||||
|
||||
proc supports_memtag {} {
|
||||
global gdb_prompt
|
||||
|
||||
gdb_test_multiple "mtag check" "" {
|
||||
-re "Memory tagging not supported or disabled by the current architecture\..*$gdb_prompt $" {
|
||||
return 0
|
||||
}
|
||||
-re "Argument required \\(address or pointer\\).*$gdb_prompt $" {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
# Return 1 if the target supports hardware single stepping.
|
||||
|
||||
proc can_hardware_single_step {} {
|
||||
|
||||
@@ -241,6 +241,7 @@ struct format_data
|
||||
int count;
|
||||
char format;
|
||||
char size;
|
||||
bool print_tags;
|
||||
|
||||
/* True if the value should be printed raw -- that is, bypassing
|
||||
python-based formatters. */
|
||||
|
||||
@@ -212,6 +212,7 @@ SFILES = \
|
||||
$(srcdir)/../gdb/arch/ppc-linux-common.c \
|
||||
$(srcdir)/../gdb/arch/riscv.c \
|
||||
$(srcdir)/../gdb/nat/aarch64-sve-linux-ptrace.c \
|
||||
$(srcdir)/../gdb/nat/aarch64-mte-linux-ptrace.c \
|
||||
$(srcdir)/../gdb/nat/linux-btrace.c \
|
||||
$(srcdir)/../gdb/nat/linux-namespaces.c \
|
||||
$(srcdir)/../gdb/nat/linux-osdata.c \
|
||||
|
||||
@@ -52,8 +52,10 @@ case "${gdbserver_host}" in
|
||||
srv_tgtobj="$srv_tgtobj nat/aarch64-linux.o"
|
||||
srv_tgtobj="$srv_tgtobj arch/aarch64-insn.o"
|
||||
srv_tgtobj="$srv_tgtobj arch/aarch64.o"
|
||||
srv_tgtobj="$srv_tgtobj arch/aarch64-mte-linux.o"
|
||||
srv_tgtobj="$srv_tgtobj linux-aarch64-tdesc.o"
|
||||
srv_tgtobj="$srv_tgtobj nat/aarch64-sve-linux-ptrace.o"
|
||||
srv_tgtobj="$srv_tgtobj nat/aarch64-mte-linux-ptrace.o"
|
||||
srv_tgtobj="${srv_tgtobj} $srv_linux_obj"
|
||||
srv_linux_regsets=yes
|
||||
srv_linux_thread_db=yes
|
||||
|
||||
@@ -147,12 +147,12 @@ get_raw_reg (const unsigned char *raw_regs, int regnum)
|
||||
|
||||
/* Return target_desc to use for IPA, given the tdesc index passed by
|
||||
gdbserver. Index is ignored, since we have only one tdesc
|
||||
at the moment. SVE and pauth not yet supported. */
|
||||
at the moment. SVE, pauth and MTE not yet supported. */
|
||||
|
||||
const struct target_desc *
|
||||
get_ipa_tdesc (int idx)
|
||||
{
|
||||
return aarch64_linux_read_description (0, false);
|
||||
return aarch64_linux_read_description (0, false, false);
|
||||
}
|
||||
|
||||
/* Allocate buffer for the jump pads. The branch instruction has a reach
|
||||
@@ -204,6 +204,6 @@ alloc_jump_pad_buffer (size_t size)
|
||||
void
|
||||
initialize_low_tracepoint (void)
|
||||
{
|
||||
/* SVE and pauth not yet supported. */
|
||||
aarch64_linux_read_description (0, false);
|
||||
/* SVE, pauth and MTE not yet supported. */
|
||||
aarch64_linux_read_description (0, false, false);
|
||||
}
|
||||
|
||||
@@ -40,15 +40,21 @@
|
||||
|
||||
#include "gdb_proc_service.h"
|
||||
#include "arch/aarch64.h"
|
||||
#include "arch/aarch64-mte-linux.h"
|
||||
#include "linux-aarch32-tdesc.h"
|
||||
#include "linux-aarch64-tdesc.h"
|
||||
#include "nat/aarch64-sve-linux-ptrace.h"
|
||||
#include "nat/aarch64-mte-linux-ptrace.h"
|
||||
#include "tdesc.h"
|
||||
|
||||
#ifdef HAVE_SYS_REG_H
|
||||
#include <sys/reg.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETAUXVAL
|
||||
#include <sys/auxv.h>
|
||||
#endif
|
||||
|
||||
/* Linux target op definitions for the AArch64 architecture. */
|
||||
|
||||
class aarch64_target : public linux_process_target
|
||||
@@ -81,6 +87,14 @@ public:
|
||||
|
||||
struct emit_ops *emit_ops () override;
|
||||
|
||||
bool supports_memory_tagging () override;
|
||||
|
||||
int fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags) override;
|
||||
|
||||
int store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags) override;
|
||||
|
||||
protected:
|
||||
|
||||
void low_arch_setup () override;
|
||||
@@ -260,6 +274,29 @@ aarch64_store_pauthregset (struct regcache *regcache, const void *buf)
|
||||
&pauth_regset[1]);
|
||||
}
|
||||
|
||||
/* Fill BUF with the MTE registers from the regcache. */
|
||||
|
||||
static void
|
||||
aarch64_fill_mteregset (struct regcache *regcache, void *buf)
|
||||
{
|
||||
uint64_t *mte_regset = (uint64_t *) buf;
|
||||
int mte_base = find_regno (regcache->tdesc, "tag_ctl");
|
||||
|
||||
collect_register (regcache, mte_base, mte_regset);
|
||||
}
|
||||
|
||||
/* Store the MTE registers to regcache. */
|
||||
|
||||
static void
|
||||
aarch64_store_mteregset (struct regcache *regcache, const void *buf)
|
||||
{
|
||||
uint64_t *mte_regset = (uint64_t *) buf;
|
||||
int mte_base = find_regno (regcache->tdesc, "tag_ctl");
|
||||
|
||||
/* Tag Control register */
|
||||
supply_register (regcache, mte_base, mte_regset);
|
||||
}
|
||||
|
||||
bool
|
||||
aarch64_target::low_supports_breakpoints ()
|
||||
{
|
||||
@@ -641,9 +678,13 @@ aarch64_target::low_arch_setup ()
|
||||
{
|
||||
uint64_t vq = aarch64_sve_get_vq (tid);
|
||||
unsigned long hwcap = linux_get_hwcap (8);
|
||||
unsigned long hwcap2 = linux_get_hwcap2 (8);
|
||||
bool pauth_p = hwcap & AARCH64_HWCAP_PACA;
|
||||
/* MTE is AArch64-only. */
|
||||
bool mte_p = hwcap2 & HWCAP2_MTE;
|
||||
|
||||
current_process ()->tdesc = aarch64_linux_read_description (vq, pauth_p);
|
||||
current_process ()->tdesc
|
||||
= aarch64_linux_read_description (vq, pauth_p, mte_p);
|
||||
}
|
||||
else
|
||||
current_process ()->tdesc = aarch32_linux_read_description ();
|
||||
@@ -679,6 +720,9 @@ static struct regset_info aarch64_regsets[] =
|
||||
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK,
|
||||
AARCH64_PAUTH_REGS_SIZE, OPTIONAL_REGS,
|
||||
NULL, aarch64_store_pauthregset },
|
||||
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_TAGGED_ADDR_CTRL,
|
||||
AARCH64_LINUX_SIZEOF_MTE, OPTIONAL_REGS, aarch64_fill_mteregset,
|
||||
aarch64_store_mteregset },
|
||||
NULL_REGSET
|
||||
};
|
||||
|
||||
@@ -708,6 +752,9 @@ static struct regset_info aarch64_sve_regsets[] =
|
||||
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK,
|
||||
AARCH64_PAUTH_REGS_SIZE, OPTIONAL_REGS,
|
||||
NULL, aarch64_store_pauthregset },
|
||||
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_TAGGED_ADDR_CTRL,
|
||||
AARCH64_LINUX_SIZEOF_MTE, OPTIONAL_REGS, aarch64_fill_mteregset,
|
||||
aarch64_store_mteregset },
|
||||
NULL_REGSET
|
||||
};
|
||||
|
||||
@@ -3167,6 +3214,46 @@ aarch64_target::breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
|
||||
return arm_breakpoint_kind_from_current_state (pcptr);
|
||||
}
|
||||
|
||||
/* Returns true if memory tagging is supported. */
|
||||
bool
|
||||
aarch64_target::supports_memory_tagging ()
|
||||
{
|
||||
if (current_thread == NULL)
|
||||
{
|
||||
/* We don't have any processes running, so don't attempt to
|
||||
use linux_get_hwcap2 as it will try to fetch the current
|
||||
thread id. Instead, just fetch the auxv from the self
|
||||
PID. */
|
||||
#ifdef HAVE_GETAUXVAL
|
||||
return (getauxval (AT_HWCAP2) & HWCAP2_MTE) != 0;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
return (linux_get_hwcap2 (8) & HWCAP2_MTE) != 0;
|
||||
}
|
||||
|
||||
int
|
||||
aarch64_target::fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags)
|
||||
{
|
||||
/* Allocation tags are per-process, so any tid is fine. */
|
||||
int tid = lwpid_of (current_thread);
|
||||
|
||||
return aarch64_mte_fetch_memtags (tid, address, len, tags);
|
||||
}
|
||||
|
||||
int
|
||||
aarch64_target::store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags)
|
||||
{
|
||||
/* Allocation tags are per-process, so any tid is fine. */
|
||||
int tid = lwpid_of (current_thread);
|
||||
|
||||
return aarch64_mte_store_memtags (tid, address, len, tags);
|
||||
}
|
||||
|
||||
/* The linux target ops object. */
|
||||
|
||||
linux_process_target *the_linux_target = &the_aarch64_target;
|
||||
|
||||
@@ -27,22 +27,22 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
/* All possible aarch64 target descriptors. */
|
||||
struct target_desc *tdesc_aarch64_list[AARCH64_MAX_SVE_VQ + 1][2/*pauth*/];
|
||||
struct target_desc *tdesc_aarch64_list[AARCH64_MAX_SVE_VQ + 1][2/*pauth*/][2 /* mte */];
|
||||
|
||||
/* Create the aarch64 target description. */
|
||||
|
||||
const target_desc *
|
||||
aarch64_linux_read_description (uint64_t vq, bool pauth_p)
|
||||
aarch64_linux_read_description (uint64_t vq, bool pauth_p, bool mte_p)
|
||||
{
|
||||
if (vq > AARCH64_MAX_SVE_VQ)
|
||||
error (_("VQ is %" PRIu64 ", maximum supported value is %d"), vq,
|
||||
AARCH64_MAX_SVE_VQ);
|
||||
|
||||
struct target_desc *tdesc = tdesc_aarch64_list[vq][pauth_p];
|
||||
struct target_desc *tdesc = tdesc_aarch64_list[vq][pauth_p][mte_p];
|
||||
|
||||
if (tdesc == NULL)
|
||||
{
|
||||
tdesc = aarch64_create_target_description (vq, pauth_p);
|
||||
tdesc = aarch64_create_target_description (vq, pauth_p, mte_p);
|
||||
|
||||
static const char *expedite_regs_aarch64[] = { "x29", "sp", "pc", NULL };
|
||||
static const char *expedite_regs_aarch64_sve[] = { "x29", "sp", "pc",
|
||||
@@ -53,7 +53,7 @@ aarch64_linux_read_description (uint64_t vq, bool pauth_p)
|
||||
else
|
||||
init_target_desc (tdesc, expedite_regs_aarch64_sve);
|
||||
|
||||
tdesc_aarch64_list[vq][pauth_p] = tdesc;
|
||||
tdesc_aarch64_list[vq][pauth_p][mte_p] = tdesc;
|
||||
}
|
||||
|
||||
return tdesc;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#ifndef GDBSERVER_LINUX_AARCH64_TDESC_H
|
||||
#define GDBSERVER_LINUX_AARCH64_TDESC_H
|
||||
|
||||
const target_desc * aarch64_linux_read_description (uint64_t vq, bool pauth_p);
|
||||
const target_desc * aarch64_linux_read_description (uint64_t vq, bool pauth_p,
|
||||
bool mte_p);
|
||||
|
||||
#endif /* GDBSERVER_LINUX_AARCH64_TDESC_H */
|
||||
|
||||
@@ -1302,31 +1302,12 @@ prepare_resume_reply (char *buf, ptid_t ptid,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
decode_m_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
char ch;
|
||||
*mem_addr_ptr = *len_ptr = 0;
|
||||
/* Decode ADDR and LEN from a parameter of the form "addr,len<x>", with <x>
|
||||
being an end marker character. */
|
||||
|
||||
while ((ch = from[i++]) != ',')
|
||||
{
|
||||
*mem_addr_ptr = *mem_addr_ptr << 4;
|
||||
*mem_addr_ptr |= fromhex (ch) & 0x0f;
|
||||
}
|
||||
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
if ((ch = from[i++]) == 0)
|
||||
break;
|
||||
*len_ptr = *len_ptr << 4;
|
||||
*len_ptr |= fromhex (ch) & 0x0f;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
decode_M_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr,
|
||||
unsigned char **to_p)
|
||||
char *
|
||||
decode_m_packet_params (char *from, CORE_ADDR *mem_addr_ptr,
|
||||
unsigned int *len_ptr, const char end_marker)
|
||||
{
|
||||
int i = 0;
|
||||
char ch;
|
||||
@@ -1338,16 +1319,31 @@ decode_M_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr,
|
||||
*mem_addr_ptr |= fromhex (ch) & 0x0f;
|
||||
}
|
||||
|
||||
while ((ch = from[i++]) != ':')
|
||||
while ((ch = from[i++]) != end_marker)
|
||||
{
|
||||
*len_ptr = *len_ptr << 4;
|
||||
*len_ptr |= fromhex (ch) & 0x0f;
|
||||
}
|
||||
|
||||
return from + i;
|
||||
}
|
||||
|
||||
void
|
||||
decode_m_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr)
|
||||
{
|
||||
decode_m_packet_params (from, mem_addr_ptr, len_ptr, '\0');
|
||||
}
|
||||
|
||||
void
|
||||
decode_M_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr,
|
||||
unsigned char **to_p)
|
||||
{
|
||||
from = decode_m_packet_params (from, mem_addr_ptr, len_ptr, ':');
|
||||
|
||||
if (*to_p == NULL)
|
||||
*to_p = (unsigned char *) xmalloc (*len_ptr);
|
||||
|
||||
hex2bin (&from[i++], *to_p, *len_ptr);
|
||||
hex2bin (from, *to_p, *len_ptr);
|
||||
}
|
||||
|
||||
int
|
||||
|
||||
@@ -45,6 +45,8 @@ void prepare_resume_reply (char *buf, ptid_t ptid,
|
||||
|
||||
const char *decode_address_to_semicolon (CORE_ADDR *addrp, const char *start);
|
||||
void decode_address (CORE_ADDR *addrp, const char *start, int len);
|
||||
char *decode_m_packet_params (char *from, CORE_ADDR *mem_addr_ptr,
|
||||
unsigned int *len_ptr, const char end_marker);
|
||||
void decode_m_packet (char *from, CORE_ADDR * mem_addr_ptr,
|
||||
unsigned int *len_ptr);
|
||||
void decode_M_packet (char *from, CORE_ADDR * mem_addr_ptr,
|
||||
|
||||
@@ -545,12 +545,59 @@ handle_btrace_conf_general_set (char *own_buf)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Create the qMemTags packet reply given TAGS. */
|
||||
|
||||
static int
|
||||
create_fmemtags_reply (char *reply, const gdb::byte_vector &tags)
|
||||
{
|
||||
/* It is an error to pass a zero-sized tag vector. */
|
||||
if (tags.size () == 0)
|
||||
return 1;
|
||||
|
||||
std::string packet ("m");
|
||||
|
||||
/* Write the tag data. */
|
||||
packet += bin2hex (tags.data (), tags.size ());
|
||||
|
||||
/* Check if the reply is too big for the packet to handle. */
|
||||
if (PBUFSIZ < packet.size ())
|
||||
return 1;
|
||||
|
||||
strcpy (reply, packet.c_str ());
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse the QMemTags request into ADDR, LEN and TAGS.
|
||||
|
||||
Return 0 if successful, non-zero otherwise. */
|
||||
|
||||
static int
|
||||
parse_smemtags_request (char *request, CORE_ADDR *addr, size_t *len,
|
||||
gdb::byte_vector &tags)
|
||||
{
|
||||
if (!startswith (request, "QMemTags:"))
|
||||
return 1;
|
||||
|
||||
char *p = request + strlen ("QMemTags:");
|
||||
|
||||
/* Read address and length. */
|
||||
unsigned int length = 0;
|
||||
p = decode_m_packet_params (p, addr, &length, ':');
|
||||
*len = length;
|
||||
|
||||
/* Read the tag data. */
|
||||
tags = hex2bin (p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle all of the extended 'Q' packets. */
|
||||
|
||||
static void
|
||||
handle_general_set (char *own_buf)
|
||||
{
|
||||
client_state &cs = get_client_state ();
|
||||
|
||||
if (startswith (own_buf, "QPassSignals:"))
|
||||
{
|
||||
int numsigs = (int) GDB_SIGNAL_LAST, i;
|
||||
@@ -899,6 +946,30 @@ handle_general_set (char *own_buf)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Handle store memory tags packets. */
|
||||
if (startswith (own_buf, "QMemTags:")
|
||||
&& target_supports_memory_tagging ())
|
||||
{
|
||||
gdb::byte_vector tags;
|
||||
CORE_ADDR addr = 0;
|
||||
size_t len = 0;
|
||||
|
||||
require_running_or_return (own_buf);
|
||||
|
||||
int ret = parse_smemtags_request (own_buf, &addr, &len, tags);
|
||||
|
||||
if (ret == 0)
|
||||
ret = the_target->store_memtags (addr, len, tags);
|
||||
|
||||
if (ret)
|
||||
write_enn (own_buf);
|
||||
else
|
||||
write_ok (own_buf);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise we didn't know what packet it was. Say we didn't
|
||||
understand it. */
|
||||
own_buf[0] = 0;
|
||||
@@ -2119,6 +2190,26 @@ crc32 (CORE_ADDR base, int len, unsigned int crc)
|
||||
return (unsigned long long) crc;
|
||||
}
|
||||
|
||||
/* Parse the qMemTags packet request into ADDR and LEN.
|
||||
|
||||
Return 0 if successful, non-zero otherwise. */
|
||||
|
||||
static int
|
||||
parse_fmemtags_request (char *request, CORE_ADDR *addr, size_t *len)
|
||||
{
|
||||
if (!startswith (request, "qMemTags:"))
|
||||
return 1;
|
||||
|
||||
char *p = request + strlen ("qMemTags:");
|
||||
|
||||
/* Read address and length. */
|
||||
unsigned int length = 0;
|
||||
decode_m_packet (p, addr, &length);
|
||||
*len = length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add supported btrace packets to BUF. */
|
||||
|
||||
static void
|
||||
@@ -2337,6 +2428,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
|
||||
events. */
|
||||
report_no_resumed = true;
|
||||
}
|
||||
else if (strcmp (p, "memory-tagging+") == 0)
|
||||
{
|
||||
/* GDB supports memory tagging features. */
|
||||
if (target_supports_memory_tagging ())
|
||||
cs.memory_tagging_feature = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Move the unknown features all together. */
|
||||
@@ -2454,6 +2551,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
|
||||
|
||||
strcat (own_buf, ";no-resumed+");
|
||||
|
||||
if (target_supports_memory_tagging ())
|
||||
strcat (own_buf, ";memory-tagging+");
|
||||
|
||||
/* Reinitialize components as needed for the new connection. */
|
||||
hostio_handle_new_gdb_connection ();
|
||||
target_handle_new_gdb_connection ();
|
||||
@@ -2646,6 +2746,31 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
|
||||
if (target_supports_tracepoints () && handle_tracepoint_query (own_buf))
|
||||
return;
|
||||
|
||||
/* Handle fetch memory tags packets. */
|
||||
if (startswith (own_buf, "qMemTags:")
|
||||
&& target_supports_memory_tagging ())
|
||||
{
|
||||
gdb::byte_vector tags;
|
||||
CORE_ADDR addr = 0;
|
||||
size_t len = 0;
|
||||
|
||||
require_running_or_return (own_buf);
|
||||
|
||||
int ret = parse_fmemtags_request (own_buf, &addr, &len);
|
||||
|
||||
if (ret == 0)
|
||||
ret = the_target->fetch_memtags (addr, len, tags);
|
||||
|
||||
if (ret == 0)
|
||||
ret = create_fmemtags_reply (own_buf, tags);
|
||||
|
||||
if (ret)
|
||||
write_enn (own_buf);
|
||||
|
||||
*new_packet_len_p = strlen (own_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise we didn't know what packet it was. Say we didn't
|
||||
understand it. */
|
||||
own_buf[0] = 0;
|
||||
@@ -3555,6 +3680,91 @@ detach_or_kill_for_exit_cleanup ()
|
||||
}
|
||||
}
|
||||
|
||||
#if GDB_SELF_TEST
|
||||
|
||||
namespace selftests {
|
||||
|
||||
static void
|
||||
test_memory_tagging_functions (void)
|
||||
{
|
||||
/* Setup testing. */
|
||||
gdb::char_vector packet;
|
||||
gdb::byte_vector tags, bv;
|
||||
std::string expected;
|
||||
packet.resize (32000);
|
||||
CORE_ADDR addr;
|
||||
size_t len;
|
||||
|
||||
/* Test parsing a qMemTags request. */
|
||||
|
||||
/* Invalid request, addr and len unchanged. */
|
||||
addr = 0xff;
|
||||
len = 255;
|
||||
strcpy (packet.data (), "qMemTags_wrong:0,0");
|
||||
SELF_CHECK (parse_fmemtags_request (packet.data (), &addr, &len) != 0);
|
||||
SELF_CHECK (addr == 0xff && len == 255);
|
||||
|
||||
/* Valid request, addr and len updated. */
|
||||
addr = 0xff;
|
||||
len = 255;
|
||||
strcpy (packet.data (), "qMemTags:0,0");
|
||||
SELF_CHECK (parse_fmemtags_request (packet.data (), &addr, &len) == 0);
|
||||
SELF_CHECK (addr == 0 && len == 0);
|
||||
|
||||
/* Valid request, addr and len updated. */
|
||||
addr = 0;
|
||||
len = 0;
|
||||
strcpy (packet.data (), "qMemTags:deadbeef,ff");
|
||||
SELF_CHECK (parse_fmemtags_request (packet.data (), &addr, &len) == 0);
|
||||
SELF_CHECK (addr == 0xdeadbeef && len == 255);
|
||||
|
||||
/* Test creating a qMemTags reply. */
|
||||
|
||||
/* Non-empty tag data. */
|
||||
bv.resize (0);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
bv.push_back (i);
|
||||
|
||||
expected = "m0001020304";
|
||||
SELF_CHECK (create_fmemtags_reply (packet.data (), bv) == 0);
|
||||
SELF_CHECK (strcmp (packet.data (), expected.c_str ()) == 0);
|
||||
|
||||
/* Empty tag data (error). */
|
||||
bv.clear ();
|
||||
SELF_CHECK (create_fmemtags_reply (packet.data (), bv) != 0);
|
||||
|
||||
/* Test parsing a QMemTags request. */
|
||||
|
||||
/* Invalid request and non-empty tag data: addr, len and tags unchanged. */
|
||||
addr = 0xff;
|
||||
len = 255;
|
||||
tags.resize (5);
|
||||
strcpy (packet.data (), "QMemTags_wrong:0,0:");
|
||||
SELF_CHECK (parse_smemtags_request (packet.data (), &addr, &len, tags) != 0);
|
||||
SELF_CHECK (addr == 0xff && len == 255 && tags.size () == 5);
|
||||
|
||||
/* Valid request and empty tag data: addr, len and tags updated. */
|
||||
addr = 0xff;
|
||||
len = 255;
|
||||
tags.resize (5);
|
||||
strcpy (packet.data (), "QMemTags:0,0:");
|
||||
SELF_CHECK (parse_smemtags_request (packet.data (), &addr, &len, tags) == 0);
|
||||
SELF_CHECK (addr == 0 && len == 0 && tags.size () == 0);
|
||||
|
||||
/* Valid request and non-empty tag data: addr, len and tags updated. */
|
||||
addr = 0;
|
||||
len = 0;
|
||||
tags.resize (0);
|
||||
strcpy (packet.data (),
|
||||
"QMemTags:deadbeef,ff:0001020304");
|
||||
SELF_CHECK (parse_smemtags_request (packet.data (), &addr, &len, tags) == 0);
|
||||
SELF_CHECK (addr == 0xdeadbeef && len == 255 && tags.size () == 5);
|
||||
}
|
||||
|
||||
} // namespace selftests
|
||||
#endif /* GDB_SELF_TEST */
|
||||
|
||||
/* Main function. This is called by the real "main" function,
|
||||
wrapped in a TRY_CATCH that handles any uncaught exceptions. */
|
||||
|
||||
@@ -3572,6 +3782,9 @@ captured_main (int argc, char *argv[])
|
||||
bool selftest = false;
|
||||
#if GDB_SELF_TEST
|
||||
const char *selftest_filter = NULL;
|
||||
|
||||
selftests::register_test ("remote_memory_tagging",
|
||||
selftests::test_memory_tagging_functions);
|
||||
#endif
|
||||
|
||||
current_directory = getcwd (NULL, 0);
|
||||
@@ -3852,6 +4065,7 @@ captured_main (int argc, char *argv[])
|
||||
cs.swbreak_feature = 0;
|
||||
cs.hwbreak_feature = 0;
|
||||
cs.vCont_supported = 0;
|
||||
cs.memory_tagging_feature = false;
|
||||
|
||||
remote_open (port);
|
||||
|
||||
|
||||
@@ -190,6 +190,9 @@ struct client_state
|
||||
|
||||
int current_traceframe = -1;
|
||||
|
||||
/* If true, memory tagging features are supported. */
|
||||
bool memory_tagging_feature = false;
|
||||
|
||||
};
|
||||
|
||||
client_state &get_client_state ();
|
||||
|
||||
@@ -463,6 +463,26 @@ process_stratum_target::supports_read_offsets ()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
process_stratum_target::supports_memory_tagging ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
process_stratum_target::fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
process_stratum_target::store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
process_stratum_target::read_offsets (CORE_ADDR *text, CORE_ADDR *data)
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "gdbsupport/array-view.h"
|
||||
#include "gdbsupport/btrace-common.h"
|
||||
#include <vector>
|
||||
#include "gdbsupport/byte-vector.h"
|
||||
|
||||
struct emit_ops;
|
||||
struct buffer;
|
||||
@@ -499,6 +500,19 @@ public:
|
||||
|
||||
/* Return tdesc index for IPA. */
|
||||
virtual int get_ipa_tdesc_idx ();
|
||||
|
||||
/* Returns true if the target supports memory tagging facilities. */
|
||||
virtual bool supports_memory_tagging ();
|
||||
|
||||
/* Return the allocated memory tags associated with
|
||||
[ADDRESS, ADDRESS + LEN) in TAGS. */
|
||||
virtual int fetch_memtags (CORE_ADDR address, size_t len,
|
||||
gdb::byte_vector &tags);
|
||||
|
||||
/* Write the allocation tags contained in TAGS to the memory range
|
||||
[ADDRESS, ADDRESS + LEN). */
|
||||
virtual int store_memtags (CORE_ADDR address, size_t len,
|
||||
const gdb::byte_vector &tags);
|
||||
};
|
||||
|
||||
extern process_stratum_target *the_target;
|
||||
@@ -525,6 +539,9 @@ int kill_inferior (process_info *proc);
|
||||
#define target_supports_exec_events() \
|
||||
the_target->supports_exec_events ()
|
||||
|
||||
#define target_supports_memory_tagging() \
|
||||
the_target->supports_memory_tagging ()
|
||||
|
||||
#define target_handle_new_gdb_connection() \
|
||||
the_target->handle_new_gdb_connection ()
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ fromhex (int a)
|
||||
else if (a >= 'A' && a <= 'F')
|
||||
return a - 'A' + 10;
|
||||
else
|
||||
error (_("Reply contains invalid hex digit %d"), a);
|
||||
error (_("Invalid hex digit %d"), a);
|
||||
}
|
||||
|
||||
/* See rsp-low.h. */
|
||||
|
||||
@@ -653,6 +653,9 @@
|
||||
/* note name must be "LINUX". */
|
||||
#define NT_ARM_PAC_MASK 0x406 /* AArch pointer authentication code masks */
|
||||
/* note name must be "LINUX". */
|
||||
#define NT_ARM_TAGGED_ADDR_CTRL 0x409 /* AArch64 tagged address control
|
||||
(prctl()) */
|
||||
/* note name must be "LINUX". */
|
||||
#define NT_ARC_V2 0x600 /* ARC HS accumulator/extra registers. */
|
||||
/* note name must be "LINUX". */
|
||||
#define NT_SIGINFO 0x53494749 /* Fields of siginfo_t. */
|
||||
|
||||
Reference in New Issue
Block a user