GNU Binutils with patches for OS216
Revision | 74e76b725d270a7d800ba472b780eaac0d9cfc78 (tree) |
---|---|
Zeit | 2020-06-26 01:23:38 |
Autor | Luis Machado <luis.machado@lina...> |
Commiter | Luis Machado |
AArch64: Implement the memory tagging gdbarch hooks
This patch implements the memory tagging gdbarch hooks for AArch64, for
the MTE feature.
gdb/ChangeLog:
YYYY-MM-DD Luis Machado <luis.machado@linaro.org>
* aarch64-linux-tdep.c: Include target.h, arch-utils.h, value.h.
(aarch64_linux_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 (make_ltag_bits, make_ltag)
(aarch64_linux_set_ltag, aarch64_linux_get_ltag): New functions.
* arch/aarch64-mte-linux.h (MTE_LOGICAL_TAG_START_BIT)
(MTE_LOGICAL_MAX_VALUE): Define.
(make_ltag_bits, make_ltag, aarch64_linux_set_ltag)
(aarch64_linux_get_ltag): New prototype.
@@ -30,6 +30,7 @@ | ||
30 | 30 | #include "symtab.h" |
31 | 31 | #include "tramp-frame.h" |
32 | 32 | #include "trad-frame.h" |
33 | +#include "target.h" | |
33 | 34 | #include "target/target.h" |
34 | 35 | |
35 | 36 | #include "regcache.h" |
@@ -46,6 +47,9 @@ | ||
46 | 47 | |
47 | 48 | #include "arch/aarch64-mte-linux.h" |
48 | 49 | |
50 | +#include "arch-utils.h" | |
51 | +#include "value.h" | |
52 | + | |
49 | 53 | /* Signal frame handling. |
50 | 54 | |
51 | 55 | +------------+ ^ |
@@ -1458,6 +1462,186 @@ aarch64_linux_gcc_target_options (struct gdbarch *gdbarch) | ||
1458 | 1462 | return {}; |
1459 | 1463 | } |
1460 | 1464 | |
1465 | +/* Helper to get the allocation tag from a 64-bit ADDRESS. | |
1466 | + | |
1467 | + Return 0 for success and non-zero otherwise. */ | |
1468 | + | |
1469 | +static int | |
1470 | +aarch64_linux_get_atag (CORE_ADDR address, CORE_ADDR *tag) | |
1471 | +{ | |
1472 | + gdb::byte_vector tags; | |
1473 | + | |
1474 | + /* Attempt to fetch the allocation tag. */ | |
1475 | + if (target_fetch_memtags (address, 0, tags) != 0) | |
1476 | + return 1; | |
1477 | + | |
1478 | + /* Only one tag should've been returned. Make sure we got exactly that. */ | |
1479 | + gdb_assert (tags.size () == 1); | |
1480 | + | |
1481 | + /* Although our tags are 4 bits in size, they are stored in a | |
1482 | + byte. */ | |
1483 | + *tag = tags[0]; | |
1484 | + | |
1485 | + return 0; | |
1486 | +} | |
1487 | + | |
1488 | +/* Implement the tagged_address_p gdbarch method. */ | |
1489 | + | |
1490 | +static bool | |
1491 | +aarch64_linux_tagged_address_p (struct gdbarch *gdbarch, struct value *address) | |
1492 | +{ | |
1493 | + gdb_assert (address != nullptr); | |
1494 | + | |
1495 | + CORE_ADDR addr = value_as_address (address); | |
1496 | + | |
1497 | + /* Remove the top byte for the memory range check. */ | |
1498 | + addr = address_significant (gdbarch, addr); | |
1499 | + | |
1500 | + /* Check if the page that contains ADDRESS is mapped with PROT_MTE. */ | |
1501 | + if (!linux_address_in_memtag_page (addr)) | |
1502 | + return false; | |
1503 | + | |
1504 | + /* We have a valid tag in the top byte of the 64-bit address. */ | |
1505 | + return true; | |
1506 | +} | |
1507 | + | |
1508 | +/* Implement the memtag_mismatch_p gdbarch method. */ | |
1509 | + | |
1510 | +static bool | |
1511 | +aarch64_linux_memtag_mismatch_p (struct gdbarch *gdbarch, | |
1512 | + struct value *address) | |
1513 | +{ | |
1514 | + gdb_assert (address != nullptr); | |
1515 | + | |
1516 | + /* Make sure we are dealing with a tagged address to begin with. */ | |
1517 | + if (!aarch64_linux_tagged_address_p (gdbarch, address)) | |
1518 | + return false; | |
1519 | + | |
1520 | + CORE_ADDR addr = value_as_address (address); | |
1521 | + | |
1522 | + /* Fetch the allocation tag for ADDRESS. */ | |
1523 | + CORE_ADDR atag = 0; | |
1524 | + | |
1525 | + if (aarch64_linux_get_atag (addr, &atag) != 0) | |
1526 | + return false; | |
1527 | + | |
1528 | + /* Fetch the logical tag for ADDRESS. */ | |
1529 | + gdb_byte ltag = aarch64_linux_get_ltag (addr); | |
1530 | + | |
1531 | + /* Are the tags the same? */ | |
1532 | + if (ltag == atag) | |
1533 | + return false; | |
1534 | + | |
1535 | + return true; | |
1536 | +} | |
1537 | + | |
1538 | +/* Implement the set_memtags gdbarch method. */ | |
1539 | + | |
1540 | +static int | |
1541 | +aarch64_linux_set_memtags (struct gdbarch *gdbarch, struct value *address, | |
1542 | + size_t length, const gdb::byte_vector &tags, | |
1543 | + enum memtag_type tag_type) | |
1544 | +{ | |
1545 | + gdb_assert (address != nullptr); | |
1546 | + | |
1547 | + CORE_ADDR addr = value_as_address (address); | |
1548 | + | |
1549 | + /* Set the logical tag or the allocation tag. */ | |
1550 | + if (tag_type == tag_logical) | |
1551 | + { | |
1552 | + /* When setting logical tags, we don't care about the length, since | |
1553 | + we are only setting a single logical tag. */ | |
1554 | + addr = aarch64_linux_set_ltag (addr, tags[0]); | |
1555 | + | |
1556 | + /* Update the value's content with the tag. */ | |
1557 | + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); | |
1558 | + gdb_byte *srcbuf = value_contents_raw (address); | |
1559 | + store_unsigned_integer (srcbuf, sizeof (addr), byte_order, addr); | |
1560 | + } | |
1561 | + else | |
1562 | + { | |
1563 | + /* Make sure we are dealing with a tagged address to begin with. */ | |
1564 | + if (!aarch64_linux_tagged_address_p (gdbarch, address)) | |
1565 | + return 1; | |
1566 | + | |
1567 | + /* With G being the number of tag granules and N the number of tags | |
1568 | + passed in, we can have the following cases: | |
1569 | + | |
1570 | + 1 - G == N: Store all the N tags to memory. | |
1571 | + | |
1572 | + 2 - G < N : Warn about having more tags than granules, but write G | |
1573 | + tags. | |
1574 | + | |
1575 | + 3 - G > N : This is a "fill tags" operation. We should use the tags | |
1576 | + as a pattern to fill the granules repeatedly until we have | |
1577 | + written G tags to memory. | |
1578 | + */ | |
1579 | + | |
1580 | + size_t g = get_tag_granules (addr, length, MTE_GRANULE_SIZE); | |
1581 | + size_t n = tags.size (); | |
1582 | + | |
1583 | + if (g < n) | |
1584 | + { | |
1585 | + warning (_("Got more tags than memory granules. Tags will be " | |
1586 | + "truncated.")); | |
1587 | + } | |
1588 | + else if (g > n) | |
1589 | + warning (_("Using tag pattern to fill memory range.")); | |
1590 | + | |
1591 | + if (target_store_memtags (addr, length, tags) != 0) | |
1592 | + return 1; | |
1593 | + } | |
1594 | + return 0; | |
1595 | +} | |
1596 | + | |
1597 | +/* Implement the get_memtag gdbarch method. */ | |
1598 | + | |
1599 | +static struct value * | |
1600 | +aarch64_linux_get_memtag (struct gdbarch *gdbarch, struct value *address, | |
1601 | + enum memtag_type tag_type) | |
1602 | +{ | |
1603 | + gdb_assert (address != nullptr); | |
1604 | + | |
1605 | + CORE_ADDR addr = value_as_address (address); | |
1606 | + CORE_ADDR tag = 0; | |
1607 | + | |
1608 | + /* Get the logical tag or the allocation tag. */ | |
1609 | + if (tag_type == tag_logical) | |
1610 | + tag = aarch64_linux_get_ltag (addr); | |
1611 | + else | |
1612 | + { | |
1613 | + /* Make sure we are dealing with a tagged address to begin with. */ | |
1614 | + if (!aarch64_linux_tagged_address_p (gdbarch, address)) | |
1615 | + return nullptr; | |
1616 | + | |
1617 | + if (aarch64_linux_get_atag (addr, &tag) != 0) | |
1618 | + return nullptr; | |
1619 | + } | |
1620 | + | |
1621 | + /* Convert the tag to a value. */ | |
1622 | + return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int, | |
1623 | + tag); | |
1624 | +} | |
1625 | + | |
1626 | +/* Implement the memtag_to_string gdbarch method. */ | |
1627 | + | |
1628 | +static std::string | |
1629 | +aarch64_linux_memtag_to_string (struct gdbarch *gdbarch, | |
1630 | + struct value *address, | |
1631 | + enum memtag_type tag_type) | |
1632 | +{ | |
1633 | + gdb_assert (address != nullptr); | |
1634 | + | |
1635 | + struct value *v_tag = aarch64_linux_get_memtag (gdbarch, address, tag_type); | |
1636 | + | |
1637 | + if (v_tag == nullptr && tag_allocation) | |
1638 | + error (_("Error getting tag from target")); | |
1639 | + | |
1640 | + CORE_ADDR tag = value_as_address (v_tag); | |
1641 | + | |
1642 | + return string_printf ("0x%s", phex_nz (tag, sizeof (tag))); | |
1643 | +} | |
1644 | + | |
1461 | 1645 | static void |
1462 | 1646 | aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) |
1463 | 1647 | { |
@@ -1515,6 +1699,31 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) | ||
1515 | 1699 | data associated with the address. */ |
1516 | 1700 | set_gdbarch_significant_addr_bit (gdbarch, 56); |
1517 | 1701 | |
1702 | + /* MTE-specific settings and hooks. */ | |
1703 | + if (tdep->has_mte ()) | |
1704 | + { | |
1705 | + /* Register a hook for checking if an address is tagged or not. */ | |
1706 | + set_gdbarch_tagged_address_p (gdbarch, aarch64_linux_tagged_address_p); | |
1707 | + | |
1708 | + /* Register a hook for checking if there is a memory tag mismatch. */ | |
1709 | + set_gdbarch_memtag_mismatch_p (gdbarch, | |
1710 | + aarch64_linux_memtag_mismatch_p); | |
1711 | + | |
1712 | + /* Register a hook for setting the logical/allocation tags for | |
1713 | + a range of addresses. */ | |
1714 | + set_gdbarch_set_memtags (gdbarch, aarch64_linux_set_memtags); | |
1715 | + | |
1716 | + /* Register a hook for extracting the logical/allocation tag from an | |
1717 | + address. */ | |
1718 | + set_gdbarch_get_memtag (gdbarch, aarch64_linux_get_memtag); | |
1719 | + | |
1720 | + /* Set the allocation tag granule size to 16 bytes. */ | |
1721 | + set_gdbarch_memtag_granule_size (gdbarch, MTE_GRANULE_SIZE); | |
1722 | + | |
1723 | + /* Register a hook for converting a memory tag to a string. */ | |
1724 | + set_gdbarch_memtag_to_string (gdbarch, aarch64_linux_memtag_to_string); | |
1725 | + } | |
1726 | + | |
1518 | 1727 | /* Initialize the aarch64_linux_record_tdep. */ |
1519 | 1728 | /* These values are the size of the type that will be used in a system |
1520 | 1729 | call. They are obtained from Linux Kernel source. */ |
@@ -32,3 +32,39 @@ get_tag_granules (CORE_ADDR addr, size_t len, unsigned int granule_size) | ||
32 | 32 | /* We always have at least 1 granule. */ |
33 | 33 | return 1 + (e_addr - s_addr) / granule_size; |
34 | 34 | } |
35 | + | |
36 | +/* See arch/aarch64-mte-linux.h */ | |
37 | + | |
38 | +CORE_ADDR | |
39 | +make_ltag_bits (CORE_ADDR value) | |
40 | +{ | |
41 | + return value & MTE_LOGICAL_MAX_VALUE; | |
42 | +} | |
43 | + | |
44 | +/* See arch/aarch64-mte-linux.h */ | |
45 | + | |
46 | +CORE_ADDR | |
47 | +make_ltag (CORE_ADDR value) | |
48 | +{ | |
49 | + return make_ltag_bits (value) << MTE_LOGICAL_TAG_START_BIT; | |
50 | +} | |
51 | + | |
52 | +/* See arch/aarch64-mte-linux.h */ | |
53 | + | |
54 | +CORE_ADDR | |
55 | +aarch64_linux_set_ltag (CORE_ADDR address, CORE_ADDR tag) | |
56 | +{ | |
57 | + /* Remove the existing tag. */ | |
58 | + address &= ~make_ltag (MTE_LOGICAL_MAX_VALUE); | |
59 | + | |
60 | + /* Return the new tagged address. */ | |
61 | + return address | make_ltag (tag); | |
62 | +} | |
63 | + | |
64 | +/* See arch/aarch64-mte-linux.h */ | |
65 | + | |
66 | +CORE_ADDR | |
67 | +aarch64_linux_get_ltag (CORE_ADDR address) | |
68 | +{ | |
69 | + return make_ltag_bits (address >> MTE_LOGICAL_TAG_START_BIT); | |
70 | +} |
@@ -32,10 +32,29 @@ | ||
32 | 32 | |
33 | 33 | /* We have one tag per 16 bytes of memory. */ |
34 | 34 | #define MTE_GRANULE_SIZE 16 |
35 | +#define MTE_LOGICAL_TAG_START_BIT 56 | |
36 | +#define MTE_LOGICAL_MAX_VALUE 0xf | |
35 | 37 | |
36 | 38 | /* Return the number of tag granules in the memory range |
37 | 39 | [ADDR, ADDR + LEN) given GRANULE_SIZE. */ |
38 | 40 | extern int get_tag_granules (CORE_ADDR addr, size_t len, |
39 | 41 | unsigned int granule_size); |
40 | 42 | |
43 | +/* Return the 4-bit tag made from VALUE. */ | |
44 | +extern CORE_ADDR make_ltag_bits (CORE_ADDR value); | |
45 | + | |
46 | +/* Return the 4-bit tag that can be OR-ed to an address. */ | |
47 | +extern CORE_ADDR make_ltag (CORE_ADDR value); | |
48 | + | |
49 | +/* Helper to set the logical TAG for a 64-bit ADDRESS. | |
50 | + | |
51 | + It is always possible to set the logical tag. */ | |
52 | +extern CORE_ADDR aarch64_linux_set_ltag (CORE_ADDR address, | |
53 | + CORE_ADDR tag); | |
54 | + | |
55 | +/* Helper to get the logical tag from a 64-bit ADDRESS. | |
56 | + | |
57 | + It is always possible to get the logical tag. */ | |
58 | +extern CORE_ADDR aarch64_linux_get_ltag (CORE_ADDR address); | |
59 | + | |
41 | 60 | #endif /* ARCH_AARCH64_LINUX_H */ |