AArch64: Implement the memory tagging gdbarch hooks

This patch implements the memory tagging gdbarch hooks for AArch64, for
the MTE feature.

gdb/ChangeLog:

2021-03-24  Luis Machado  <luis.machado@linaro.org>

	* aarch64-linux-tdep.c: Include target.h, arch-utils.h, value.h.
	(aarch64_mte_get_atag, aarch64_linux_tagged_address_p)
	(aarch64_linux_memtag_mismatch_p, aarch64_linux_set_memtags)
	(aarch64_linux_get_memtag, aarch64_linux_memtag_to_string): New
	functions.
	(aarch64_linux_init_abi): Initialize MTE-related gdbarch hooks.
	* arch/aarch64-mte-linux.c (aarch64_mte_make_ltag_bits)
	(aarch64_mte_make_ltag, aarch64_linux_set_ltag)
	(aarch64_linux_get_ltag): New functions.
	* arch/aarch64-mte-linux.h (AARCH64_MTE_LOGICAL_TAG_START_BIT)
	(AARCH64_MTE_LOGICAL_MAX_VALUE): Define.
	(aarch64_mte_make_ltag_bits, aarch64_mte_make_ltag)
	(aarch64_mte_set_ltag, aarch64_mte_get_ltag): New prototypes.
This commit is contained in:
Luis Machado 2020-06-19 17:37:33 -03:00
parent 1e735120b9
commit c7782e50b1
4 changed files with 277 additions and 0 deletions

View File

@ -1,3 +1,19 @@
2021-03-24 Luis Machado <luis.machado@linaro.org>
* aarch64-linux-tdep.c: Include target.h, arch-utils.h, value.h.
(aarch64_mte_get_atag, aarch64_linux_tagged_address_p)
(aarch64_linux_memtag_mismatch_p, aarch64_linux_set_memtags)
(aarch64_linux_get_memtag, aarch64_linux_memtag_to_string): New
functions.
(aarch64_linux_init_abi): Initialize MTE-related gdbarch hooks.
* arch/aarch64-mte-linux.c (aarch64_mte_make_ltag_bits)
(aarch64_mte_make_ltag, aarch64_linux_set_ltag)
(aarch64_linux_get_ltag): New functions.
* arch/aarch64-mte-linux.h (AARCH64_MTE_LOGICAL_TAG_START_BIT)
(AARCH64_MTE_LOGICAL_MAX_VALUE): Define.
(aarch64_mte_make_ltag_bits, aarch64_mte_make_ltag)
(aarch64_mte_set_ltag, aarch64_mte_get_ltag): New prototypes.
2021-03-24 Luis Machado <luis.machado@linaro.org>
* linux-tdep.c (struct smaps_vmflags) <memory_tagging>: New flag

View File

@ -30,6 +30,7 @@
#include "symtab.h"
#include "tramp-frame.h"
#include "trad-frame.h"
#include "target.h"
#include "target/target.h"
#include "expop.h"
@ -47,6 +48,9 @@
#include "arch/aarch64-mte-linux.h"
#include "arch-utils.h"
#include "value.h"
/* Signal frame handling.
+------------+ ^
@ -1503,6 +1507,182 @@ aarch64_linux_gcc_target_options (struct gdbarch *gdbarch)
return {};
}
/* Helper to get the allocation tag from a 64-bit ADDRESS.
Return the allocation tag if successful and nullopt otherwise. */
static gdb::optional<CORE_ADDR>
aarch64_mte_get_atag (CORE_ADDR address)
{
gdb::byte_vector tags;
/* Attempt to fetch the allocation tag. */
if (!target_fetch_memtags (address, 1, tags,
static_cast<int> (memtag_type::allocation)))
return {};
/* Only one tag should've been returned. Make sure we got exactly that. */
if (tags.size () != 1)
error (_("Target returned an unexpected number of tags."));
/* Although our tags are 4 bits in size, they are stored in a
byte. */
return tags[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_matches_p gdbarch method. */
static bool
aarch64_linux_memtag_matches_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 true;
CORE_ADDR addr = value_as_address (address);
/* Fetch the allocation tag for ADDRESS. */
gdb::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr);
if (!atag.has_value ())
return true;
/* Fetch the logical tag for ADDRESS. */
gdb_byte ltag = aarch64_mte_get_ltag (addr);
/* Are the tags the same? */
return ltag == *atag;
}
/* Implement the set_memtags gdbarch method. */
static bool
aarch64_linux_set_memtags (struct gdbarch *gdbarch, struct value *address,
size_t length, const gdb::byte_vector &tags,
memtag_type tag_type)
{
gdb_assert (!tags.empty ());
gdb_assert (address != nullptr);
CORE_ADDR addr = value_as_address (address);
/* Set the logical tag or the allocation tag. */
if (tag_type == memtag_type::logical)
{
/* When setting logical tags, we don't care about the length, since
we are only setting a single logical tag. */
addr = aarch64_mte_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 false;
/* 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 = aarch64_mte_get_tag_granules (addr, length,
AARCH64_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,
static_cast<int> (memtag_type::allocation)))
return false;
}
return true;
}
/* Implement the get_memtag gdbarch method. */
static struct value *
aarch64_linux_get_memtag (struct gdbarch *gdbarch, struct value *address,
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 == memtag_type::logical)
tag = aarch64_mte_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;
gdb::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr);
if (!atag.has_value ())
return nullptr;
tag = *atag;
}
/* 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 *tag_value)
{
if (tag_value == nullptr)
return "";
CORE_ADDR tag = value_as_address (tag_value);
return string_printf ("0x%s", phex_nz (tag, sizeof (tag)));
}
static void
aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
@ -1560,6 +1740,31 @@ 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 match. */
set_gdbarch_memtag_matches_p (gdbarch,
aarch64_linux_memtag_matches_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, AARCH64_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);
}
/* 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. */

View File

@ -36,3 +36,41 @@ aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, size_t 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
aarch64_mte_make_ltag_bits (CORE_ADDR value)
{
return value & AARCH64_MTE_LOGICAL_MAX_VALUE;
}
/* See arch/aarch64-mte-linux.h */
CORE_ADDR
aarch64_mte_make_ltag (CORE_ADDR value)
{
return (aarch64_mte_make_ltag_bits (value)
<< AARCH64_MTE_LOGICAL_TAG_START_BIT);
}
/* See arch/aarch64-mte-linux.h */
CORE_ADDR
aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag)
{
/* Remove the existing tag. */
address &= ~aarch64_mte_make_ltag (AARCH64_MTE_LOGICAL_MAX_VALUE);
/* Return the new tagged address. */
return address | aarch64_mte_make_ltag (tag);
}
/* See arch/aarch64-mte-linux.h */
CORE_ADDR
aarch64_mte_get_ltag (CORE_ADDR address)
{
CORE_ADDR ltag_addr = address >> AARCH64_MTE_LOGICAL_TAG_START_BIT;
return aarch64_mte_make_ltag_bits (ltag_addr);
}

View File

@ -32,6 +32,8 @@
/* We have one tag per 16 bytes of memory. */
#define AARCH64_MTE_GRANULE_SIZE 16
#define AARCH64_MTE_LOGICAL_TAG_START_BIT 56
#define AARCH64_MTE_LOGICAL_MAX_VALUE 0xf
/* Memory tag types for AArch64. */
enum class aarch64_memtag_type
@ -47,4 +49,20 @@ enum class aarch64_memtag_type
extern size_t aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len,
size_t granule_size);
/* Return the 4-bit tag made from VALUE. */
extern CORE_ADDR aarch64_mte_make_ltag_bits (CORE_ADDR value);
/* Return the 4-bit tag that can be OR-ed to an address. */
extern CORE_ADDR aarch64_mte_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_mte_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_mte_get_ltag (CORE_ADDR address);
#endif /* ARCH_AARCH64_LINUX_H */