From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-qt1-x835.google.com (mail-qt1-x835.google.com [IPv6:2607:f8b0:4864:20::835]) by sourceware.org (Postfix) with ESMTPS id 9AFE1386FC37 for ; Wed, 19 May 2021 11:12:04 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 9AFE1386FC37 Received: by mail-qt1-x835.google.com with SMTP id v4so9774163qtp.1 for ; Wed, 19 May 2021 04:12:04 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:subject:to:cc:references:from:message-id:date :user-agent:mime-version:in-reply-to:content-language :content-transfer-encoding; bh=W/uxmqwqH/EeyQ6dX/Fx8q9kLNYt5LPCaZtrcr2GbZA=; b=jGLxN+z0kJ597wyQejjwSJlwFpdZlLUxxNsgq38R9FXVT3OhIn8k3DRKFgqMdnAOwg jZ6lZjiFn3wZGP2EErgWmPPVgO6nXXQCSQ2p3QljHIh4y7HAa+QGe0hDqdK2iV1VZjEr yAkNuhrzLf3y3I0wHsbHeX9zrjytm+haZ40AqlnFp7so9Gp9WdwCfCM/lJChNprEKHXk 8jkdX1JZyyHYpWmcSVPe18mrN+rxcOPjKDYPc7u7pcuwTTn6N6ja7h9K0ViexfyPB/DV yI1O6tjJfOL4SpQSVw6A1Q8XQ1O6BeqUQjECXPxRSOPdRXLxxYt/6CPNr2DAx5WGjPc+ hxxA== X-Gm-Message-State: AOAM532Nk/tvpH4lxxnaiQOjTF4HBKaZi+bRnRJQA9clP7kVWrlmLyre r+op2R1rZPP+peW3Z+kRgK8GfA== X-Google-Smtp-Source: ABdhPJxzX5VFy9ktmCNex6n/OchdBAnR6UfAC4PlJYG8c4mAXepB4gRzirxPo39cS1HFwnN7/1nkrg== X-Received: by 2002:ac8:47da:: with SMTP id d26mr10479380qtr.292.1621422723690; Wed, 19 May 2021 04:12:03 -0700 (PDT) Received: from ?IPv6:2804:7f0:4841:40ad:9dc9:85ce:6858:530f? ([2804:7f0:4841:40ad:9dc9:85ce:6858:530f]) by smtp.gmail.com with ESMTPSA id z4sm14837473qtq.34.2021.05.19.04.12.01 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 19 May 2021 04:12:03 -0700 (PDT) Subject: Re: [PATCH] [AArch64] MTE corefile support To: David Spickett Cc: gdb-patches@sourceware.org, alan.hayward@arm.com, John Baldwin , Catalin Marinas References: <20210518202047.3492211-1-luis.machado@linaro.org> From: Luis Machado Message-ID: Date: Wed, 19 May 2021 08:11:59 -0300 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.8.1 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8; format=flowed Content-Language: en-US Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-12.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, NICE_REPLY_A, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 19 May 2021 11:12:09 -0000 On 5/19/21 7:01 AM, David Spickett wrote: >> +/* Maximum number of tags to request. */ >> +#define MAX_TAGS_TO_TRANSFER 1024 > > Is this 1024 derived from something? Random guess the max note size > minus the size of the note header. No. Just a fixed number so we can transfer tags in chunks as opposed to transferring a lot of them at once. ptrace won't let us do it in one go anyway. > >> +/* AArch64 Linux implementation of the decode_memtag_note gdbarch >> + hook. Decode a memory tag note and return the request tags. */ >> + >> +static gdb::byte_vector >> +aarch64_linux_decode_memtag_note (struct gdbarch *gdbarch, >> + gdb::array_view note, >> + CORE_ADDR address, size_t length) > > Would be worth commenting that this assumes that address is within the > range of the note given. (I see later that it's only called if that's > true) I'll add a comment. > > On Tue, 18 May 2021 at 21:20, Luis Machado wrote: >> >> Teach GDB how to dump memory tags when using the gcore command and how >> to read them back from a core file generated via gcore or the kernel. >> >> Each tagged memory range (listed in /proc//smaps) gets dumped to its >> own NT_MEMTAG note. A section named ".memtag" is created for each of those >> when reading the core file back. >> >> Dumping memory tags >> - >> >> When using the gcore command to dump a core file, GDB will go through the maps >> in /proc//smaps looking for tagged ranges. Each of those entries gets >> passed to an arch-specific gdbarch hook that generates a vector of blobs of >> memory tag data that are blindly put into a NT_MEMTAG note. >> >> The vector is used because we may have, in the future, multiple tag types for >> a particular memory range. >> >> Each of the NT_MEMTAG notes have a generic header and a arch-specific header, >> like so: >> >> struct tag_dump_header >> { >> uint16_t format; // Only NT_MEMTAG_TYPE_AARCH_MTE at present >> uint64_t start_vma; >> uint64_t end_vma; >> }; >> >> struct tag_dump_mte >> { >> uint16_t granule_byte_size; >> uint16_t tag_bit_size; >> uint16_t __unused; >> }; >> >> The only bits meant to be generic are the tag_dump_format, start_vma and >> end_vma fields. >> >> The format-specific data is supposed to be opaque and only useful for the >> arch-specific code. >> >> We can extend the format in the future to make room for other memory tag >> layouts. >> >> Reading memory tags >> - >> >> When reading a core file that contains NT_MEMTAG entries, GDB will use >> a different approach to check for tagged memory range. Rather than looking >> at /proc//smaps, it will now look for ".memtag" sections with the right >> memory range. >> >> When reading tags, GDB will now use the core target's implementation of >> fetch_memtags (store_memtags doesn't exist for core targets). Then the data >> is fed into an arch-specific hook that will decode the memory tag format and >> return a vector of tags. >> >> I've added a test to exercise writing and reading of memory tags in core >> files. >> >> gdb/ChangeLog: >> >> YYYY-MM-DD Luis Machado >> >> * aarch64-linux-tdep.c: Include elf/common.h. >> (MAX_TAGS_TO_TRANSFER): New constant. >> (aarch64_linux_create_memtag_notes_from_range): New function. >> (aarch64_linux_decode_memtag_note): Likewise. >> (aarch64_linux_init_abi): Register new core file hooks. >> * arch/aarch64-mte-linux.h (AARCH64_MTE_TAG_BIT_SIZE): New constant. >> (struct tag_dump_header): New struct. >> (struct tag_dump_mte): New struct. >> (MEMTAG_NOTE_HEADER_SIZE): New constant. >> * corelow.c (core_target) : New >> method overrides. >> * gdbarch.c: Regenerate. >> * gdbarch.h: Likewise. >> * gdbarch.sh (create_memtag_notes_from_range): New hook. >> (decode_memtag_note): Likewise. >> * linux-tdep.c (linux_address_in_memtag_page): Renamed to... >> (linux_process_address_in_memtag_page): ... this. >> (linux_core_file_address_in_memtag_page): New function. >> (linux_address_in_memtag_page): Likewise. >> (linux_make_memtag_corefile_notes): Likewise. >> (linux_make_corefile_notes): Handle memory tag notes. >> * NEWS: Mention core file support for memory tagging. >> >> gdb/doc/ChangeLog: >> >> YYYY-MM-DD Luis Machado >> >> * gdb.texinfo (AArch64 Memory Tagging Extension): Mention support >> for memory tagging in core files. >> >> gdb/testsuite/ChangeLog: >> >> YYYY-MM-DD Luis Machado >> >> * gdb.arch/aarch64-mte-gcore.c: New file. >> * gdb.arch/aarch64-mte-gcore.exp: New file. >> --- >> gdb/NEWS | 4 + >> gdb/aarch64-linux-tdep.c | 179 +++++++++++++++++++ >> gdb/arch/aarch64-mte-linux.h | 29 +++ >> gdb/corelow.c | 88 +++++++++ >> gdb/doc/gdb.texinfo | 4 + >> gdb/gdbarch.c | 64 +++++++ >> gdb/gdbarch.h | 16 ++ >> gdb/gdbarch.sh | 6 + >> gdb/linux-tdep.c | 141 ++++++++++++++- >> gdb/testsuite/gdb.arch/aarch64-mte-gcore.c | 93 ++++++++++ >> gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 115 ++++++++++++ >> 11 files changed, 736 insertions(+), 3 deletions(-) >> create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c >> create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp >> >> diff --git a/gdb/NEWS b/gdb/NEWS >> index ab678acec8b..58b9f739d4f 100644 >> --- a/gdb/NEWS >> +++ b/gdb/NEWS >> @@ -3,6 +3,10 @@ >> >> *** Changes since GDB 10 >> >> +* GDB now supports dumping memory tag data for AArch64 MTE. It also supports >> + reading memory tag data for AArch64 MTE from core files generated by >> + the gcore command or the Linux kernel. >> + >> * 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. >> diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c >> index e9761ed2189..663d0e1a215 100644 >> --- a/gdb/aarch64-linux-tdep.c >> +++ b/gdb/aarch64-linux-tdep.c >> @@ -53,6 +53,8 @@ >> >> #include "gdbsupport/selftest.h" >> >> +#include "elf/common.h" >> + >> /* Signal frame handling. >> >> +------------+ ^ >> @@ -1779,6 +1781,172 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch, >> } >> } >> >> +/* Maximum number of tags to request. */ >> +#define MAX_TAGS_TO_TRANSFER 1024 >> + >> +/* AArch64 Linux implementation of the aarch64_create_memtag_notes_from_range >> + gdbarch hook. Create core file notes for memory tags. */ >> + >> +static std::vector >> +aarch64_linux_create_memtag_notes_from_range (struct gdbarch *gdbarch, >> + CORE_ADDR start_address, >> + CORE_ADDR end_address) >> +{ >> + /* We only handle MTE tags for now. */ >> + >> + /* Figure out how many tags we need to store in this memory range. */ >> + int granules = aarch64_mte_get_tag_granules (start_address, >> + end_address - start_address, >> + AARCH64_MTE_GRANULE_SIZE); >> + >> + /* Vector of memory tag notes. Add the MTE note (we only have MTE tags >> + at the moment). */ >> + std::vector notes (1); >> + >> + /* If there are no tag granules to fetch, just return. */ >> + if (granules == 0) >> + return notes; >> + >> + /* Adjust the MTE note size to hold the header + tags. */ >> + notes[0].resize (MEMTAG_NOTE_HEADER_SIZE + granules); >> + >> + CORE_ADDR address = start_address; >> + /* Vector of tags. */ >> + gdb::byte_vector tags; >> + >> + while (granules > 0) >> + { >> + /* Transfer tags in chunks. */ >> + gdb::byte_vector tags_read; >> + size_t xfer_len >> + = (granules >= MAX_TAGS_TO_TRANSFER)? >> + MAX_TAGS_TO_TRANSFER * AARCH64_MTE_GRANULE_SIZE : >> + granules * AARCH64_MTE_GRANULE_SIZE; >> + >> + if (!target_fetch_memtags (address, xfer_len, tags_read, >> + static_cast (memtag_type::allocation))) >> + { >> + warning (_("Failed to read MTE tags from memory range [%s,%s]."), >> + phex_nz (start_address, sizeof (start_address)), >> + phex_nz (end_address, sizeof (end_address))); >> + notes.resize (0); >> + return notes; >> + } >> + >> + /* Transfer over the tags that have been read. */ >> + tags.insert (tags.end (), tags_read.begin (), tags_read.end ()); >> + >> + /* Adjust the remaining granules and starting address. */ >> + granules -= tags_read.size (); >> + address += tags_read.size () * AARCH64_MTE_GRANULE_SIZE; >> + } >> + >> + /* Create the header. */ >> + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); >> + gdb_byte *buf = notes[0].data (); >> + >> + /* Generic header. */ >> + /* Tag dump format */ >> + store_unsigned_integer (buf, sizeof (uint16_t), byte_order, >> + NT_MEMTAG_TYPE_AARCH_MTE); >> + buf += sizeof (uint16_t); >> + >> + /* Start address */ >> + store_unsigned_integer (buf, sizeof (uint64_t), byte_order, start_address); >> + buf += sizeof (uint64_t); >> + >> + /* End address */ >> + store_unsigned_integer (buf, sizeof (uint64_t), byte_order, end_address); >> + buf += sizeof (uint64_t); >> + >> + /* MTE-specific header. */ >> + /* Granule byte size */ >> + store_unsigned_integer (buf, sizeof (uint16_t), byte_order, >> + AARCH64_MTE_GRANULE_SIZE); >> + buf += sizeof (uint16_t); >> + >> + /* Tag bit size */ >> + store_unsigned_integer (buf, sizeof (uint16_t), byte_order, >> + AARCH64_MTE_TAG_BIT_SIZE); >> + buf += sizeof (uint16_t); >> + >> + /* Unused value */ >> + store_unsigned_integer (buf, sizeof (uint16_t), byte_order, 0); >> + >> + /* Store the tags. */ >> + memcpy (notes[0].data () + MEMTAG_NOTE_HEADER_SIZE, tags.data (), >> + tags.size ()); >> + >> + return notes; >> +} >> + >> +/* AArch64 Linux implementation of the decode_memtag_note gdbarch >> + hook. Decode a memory tag note and return the request tags. */ >> + >> +static gdb::byte_vector >> +aarch64_linux_decode_memtag_note (struct gdbarch *gdbarch, >> + gdb::array_view note, >> + CORE_ADDR address, size_t length) >> +{ >> + gdb::byte_vector tags; >> + >> + /* Read the generic header. */ >> + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); >> + struct tag_dump_header header; >> + const gdb_byte *buf = note.data (); >> + >> + header.format >> + = extract_unsigned_integer (buf, sizeof (uint16_t), byte_order); >> + buf += sizeof (uint16_t); >> + >> + header.start_vma >> + = extract_unsigned_integer (buf, sizeof (uint64_t), byte_order); >> + buf += sizeof (uint64_t); >> + >> + header.end_vma >> + = extract_unsigned_integer (buf, sizeof (uint64_t), byte_order); >> + buf += sizeof (uint64_t); >> + >> + /* Sanity check */ >> + if (header.format != NT_MEMTAG_TYPE_AARCH_MTE) >> + { >> + warning (_("Unexpected memory tag note format (%x).\n"), header.format); >> + return tags; >> + } >> + >> + /* Calculate how many granules we need to skip to get to the granule of >> + ADDRESS. Align both the start address and the requested address >> + so it is easier to get the number of granules to skip. This way we >> + don't need to consider cases where ADDRESS falls in the middle of a >> + tag granule range. */ >> + CORE_ADDR aligned_start_address >> + = align_down (header.start_vma, AARCH64_MTE_GRANULE_SIZE); >> + CORE_ADDR aligned_address = align_down (address, AARCH64_MTE_GRANULE_SIZE); >> + >> + int skipped_granules >> + = aarch64_mte_get_tag_granules (aligned_start_address, >> + aligned_address - aligned_start_address, >> + AARCH64_MTE_GRANULE_SIZE); >> + >> + /* The amount of memory tag granules we need to fetch. */ >> + int granules >> + = aarch64_mte_get_tag_granules (address, length, AARCH64_MTE_GRANULE_SIZE); >> + >> + /* If there are no tag granules to decode, just return. */ >> + if (granules == 0) >> + return tags; >> + >> + /* Point to the block of data that contains the first granule we are >> + interested in. */ >> + const gdb_byte *tags_data = note.data () + sizeof (header) + skipped_granules; >> + >> + /* Read the tag granules. */ >> + for (unsigned int i = 0; i < granules; i++) >> + tags.push_back (tags_data[i]); >> + >> + return tags; >> +} >> + >> static void >> aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) >> { >> @@ -1862,6 +2030,17 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) >> >> set_gdbarch_report_signal_info (gdbarch, >> aarch64_linux_report_signal_info); >> + >> + /* Core file helpers. */ >> + >> + /* Core file helper to create memory tag notes for a particular range of >> + addresses. */ >> + set_gdbarch_create_memtag_notes_from_range (gdbarch, >> + aarch64_linux_create_memtag_notes_from_range); >> + >> + /* Core file helper to decode a memory tag note. */ >> + set_gdbarch_decode_memtag_note (gdbarch, >> + aarch64_linux_decode_memtag_note); >> } >> >> /* Initialize the aarch64_linux_record_tdep. */ >> diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-mte-linux.h >> index 2aa97eb861a..a4a26628847 100644 >> --- a/gdb/arch/aarch64-mte-linux.h >> +++ b/gdb/arch/aarch64-mte-linux.h >> @@ -32,6 +32,7 @@ >> >> /* We have one tag per 16 bytes of memory. */ >> #define AARCH64_MTE_GRANULE_SIZE 16 >> +#define AARCH64_MTE_TAG_BIT_SIZE 4 >> #define AARCH64_MTE_LOGICAL_TAG_START_BIT 56 >> #define AARCH64_MTE_LOGICAL_MAX_VALUE 0xf >> >> @@ -71,4 +72,32 @@ extern CORE_ADDR aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag); >> It is always possible to get the logical tag. */ >> extern CORE_ADDR aarch64_mte_get_ltag (CORE_ADDR address); >> >> +/* Headers for the NT_MEMTAG notes. */ >> + >> +/* Generic NT_MEMTAG header. */ >> +struct tag_dump_header >> +{ >> + /* Tag format. */ >> + uint16_t format; >> + /* Start address of the tagged range. */ >> + uint64_t start_vma; >> + /* End address of the tagged range. */ >> + uint64_t end_vma; >> +}; >> + >> +/* MTE-specific NT_MEMTAG header. */ >> +struct tag_dump_mte >> +{ >> + /* Size of the tag granule in bytes. */ >> + uint16_t granule_byte_size; >> + /* Size of the tag in bits. */ >> + uint16_t tag_bit_size; >> + /* Reserved field for the future. */ >> + uint16_t __unused; >> +}; >> + >> +/* Memory tag note header size. Includes both the generic and the >> + arch-specific parts. */ >> +#define MEMTAG_NOTE_HEADER_SIZE (2 + 8 + 8 + 2 + 2 + 2) >> + >> #endif /* ARCH_AARCH64_LINUX_H */ >> diff --git a/gdb/corelow.c b/gdb/corelow.c >> index 452b4dd4f9a..33d16b7220a 100644 >> --- a/gdb/corelow.c >> +++ b/gdb/corelow.c >> @@ -100,6 +100,13 @@ class core_target final : public process_stratum_target >> >> bool info_proc (const char *, enum info_proc_what) override; >> >> + bool supports_memory_tagging () override; >> + >> + /* Core file implementation of fetch_memtags. Fetch the memory tags from >> + core file notes. */ >> + bool fetch_memtags (CORE_ADDR address, size_t len, >> + gdb::byte_vector &tags, int type) override; >> + >> /* A few helpers. */ >> >> /* Getter, see variable definition. */ >> @@ -1115,6 +1122,87 @@ core_target::info_proc (const char *args, enum info_proc_what request) >> return true; >> } >> >> +/* Implementation of the "supports_memory_tagging" target_ops method. */ >> + >> +bool >> +core_target::supports_memory_tagging () >> +{ >> + /* Look for memory tag notes. If they exist, that means this core file >> + supports memory tagging. */ >> + if (bfd_get_section_by_name (core_bfd, ".memtag") == nullptr) >> + return false; >> + >> + return true; >> +} >> + >> +/* Implementation of the "fetch_memtags" target_ops method. */ >> + >> +bool >> +core_target::fetch_memtags (CORE_ADDR address, size_t len, >> + gdb::byte_vector &tags, int type) >> +{ >> + struct gdbarch *gdbarch = target_gdbarch (); >> + >> + /* Make sure we have a way to decode the memory tag notes. */ >> + if (!gdbarch_decode_memtag_note_p (gdbarch)) >> + warning (_("gdbarch_decode_memtag_note not implemented for this " >> + "architecture.")); >> + >> + asection *section >> + = bfd_get_section_by_name (core_bfd, ".memtag"); >> + >> + /* Remove the top byte for the memory range check. */ >> + address = address_significant (gdbarch, address); >> + >> + /* Go through all the memtag sections and figure out if ADDRESS >> + falls within one of the memory ranges that contain tags. */ >> + while (section != nullptr) >> + { >> + size_t note_size = bfd_section_size (section); >> + >> + /* If the note is smaller than the size of the header, this core note >> + is malformed. */ >> + if (note_size < 2 * sizeof (uint64_t) + sizeof (uint16_t)) >> + { >> + warning (_("malformed core note - too short for header")); >> + return false; >> + } >> + >> + gdb::byte_vector note (note_size); >> + >> + /* Fetch the contents of this particular memtag note. */ >> + if (!bfd_get_section_contents (core_bfd, section, >> + note.data (), 0, note_size)) >> + { >> + warning (_("could not get core note contents.")); >> + return false; >> + } >> + >> + /* Read the generic header of the note. Thos contains the format, >> + start address and end address. */ >> + uint64_t start_address >> + = bfd_get_64 (core_bfd, note.data () + sizeof (uint16_t)); >> + uint64_t end_address >> + = bfd_get_64 (core_bfd, note.data () + sizeof (uint16_t) >> + + sizeof (uint64_t)); >> + >> + /* Is the address within [start_address, end_address)? */ >> + if (address >= start_address >> + && address < end_address) >> + { >> + /* Decode the memory tag note and return the tags. */ >> + tags = gdbarch_decode_memtag_note (gdbarch, note, address, len); >> + return true; >> + } >> + >> + /* The requested address lies outside this particular memtag note. Keep >> + looking and get the next section. */ >> + section = bfd_get_next_section_by_name (core_bfd, section); >> + } >> + >> + return false; >> +} >> + >> /* Get a pointer to the current core target. If not connected to a >> core target, return NULL. */ >> >> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo >> index 56f37eb2288..738cb3b56eb 100644 >> --- a/gdb/doc/gdb.texinfo >> +++ b/gdb/doc/gdb.texinfo >> @@ -25257,6 +25257,10 @@ 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. >> >> +@value{GDBN} supports dumping memory tag data to core files through the >> +@command{gcore} command and reading memory tag data from core files generated >> +by the @command{gcore} command or the Linux kernel. >> + >> @node i386 >> @subsection x86 Architecture-specific Issues >> >> diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c >> index 208cf4b5aaa..de384da2e9a 100644 >> --- a/gdb/gdbarch.c >> +++ b/gdb/gdbarch.c >> @@ -283,6 +283,8 @@ struct gdbarch >> gdbarch_iterate_over_regset_sections_ftype *iterate_over_regset_sections; >> gdbarch_make_corefile_notes_ftype *make_corefile_notes; >> gdbarch_find_memory_regions_ftype *find_memory_regions; >> + gdbarch_create_memtag_notes_from_range_ftype *create_memtag_notes_from_range; >> + gdbarch_decode_memtag_note_ftype *decode_memtag_note; >> gdbarch_core_xfer_shared_libraries_ftype *core_xfer_shared_libraries; >> gdbarch_core_xfer_shared_libraries_aix_ftype *core_xfer_shared_libraries_aix; >> gdbarch_core_pid_to_str_ftype *core_pid_to_str; >> @@ -667,6 +669,8 @@ verify_gdbarch (struct gdbarch *gdbarch) >> /* Skip verify of iterate_over_regset_sections, has predicate. */ >> /* Skip verify of make_corefile_notes, has predicate. */ >> /* Skip verify of find_memory_regions, has predicate. */ >> + /* Skip verify of create_memtag_notes_from_range, has predicate. */ >> + /* Skip verify of decode_memtag_note, has predicate. */ >> /* Skip verify of core_xfer_shared_libraries, has predicate. */ >> /* Skip verify of core_xfer_shared_libraries_aix, has predicate. */ >> /* Skip verify of core_pid_to_str, has predicate. */ >> @@ -925,6 +929,18 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) >> fprintf_unfiltered (file, >> "gdbarch_dump: core_xfer_siginfo = <%s>\n", >> host_address_to_string (gdbarch->core_xfer_siginfo)); >> + fprintf_unfiltered (file, >> + "gdbarch_dump: gdbarch_create_memtag_notes_from_range_p() = %d\n", >> + gdbarch_create_memtag_notes_from_range_p (gdbarch)); >> + fprintf_unfiltered (file, >> + "gdbarch_dump: create_memtag_notes_from_range = <%s>\n", >> + host_address_to_string (gdbarch->create_memtag_notes_from_range)); >> + fprintf_unfiltered (file, >> + "gdbarch_dump: gdbarch_decode_memtag_note_p() = %d\n", >> + gdbarch_decode_memtag_note_p (gdbarch)); >> + fprintf_unfiltered (file, >> + "gdbarch_dump: decode_memtag_note = <%s>\n", >> + host_address_to_string (gdbarch->decode_memtag_note)); >> fprintf_unfiltered (file, >> "gdbarch_dump: decr_pc_after_break = %s\n", >> core_addr_to_string_nz (gdbarch->decr_pc_after_break)); >> @@ -3898,6 +3914,54 @@ set_gdbarch_find_memory_regions (struct gdbarch *gdbarch, >> gdbarch->find_memory_regions = find_memory_regions; >> } >> >> +bool >> +gdbarch_create_memtag_notes_from_range_p (struct gdbarch *gdbarch) >> +{ >> + gdb_assert (gdbarch != NULL); >> + return gdbarch->create_memtag_notes_from_range != NULL; >> +} >> + >> +std::vector >> +gdbarch_create_memtag_notes_from_range (struct gdbarch *gdbarch, CORE_ADDR start_address, CORE_ADDR end_address) >> +{ >> + gdb_assert (gdbarch != NULL); >> + gdb_assert (gdbarch->create_memtag_notes_from_range != NULL); >> + if (gdbarch_debug >= 2) >> + fprintf_unfiltered (gdb_stdlog, "gdbarch_create_memtag_notes_from_range called\n"); >> + return gdbarch->create_memtag_notes_from_range (gdbarch, start_address, end_address); >> +} >> + >> +void >> +set_gdbarch_create_memtag_notes_from_range (struct gdbarch *gdbarch, >> + gdbarch_create_memtag_notes_from_range_ftype create_memtag_notes_from_range) >> +{ >> + gdbarch->create_memtag_notes_from_range = create_memtag_notes_from_range; >> +} >> + >> +bool >> +gdbarch_decode_memtag_note_p (struct gdbarch *gdbarch) >> +{ >> + gdb_assert (gdbarch != NULL); >> + return gdbarch->decode_memtag_note != NULL; >> +} >> + >> +gdb::byte_vector >> +gdbarch_decode_memtag_note (struct gdbarch *gdbarch, gdb::array_view note, CORE_ADDR address, size_t length) >> +{ >> + gdb_assert (gdbarch != NULL); >> + gdb_assert (gdbarch->decode_memtag_note != NULL); >> + if (gdbarch_debug >= 2) >> + fprintf_unfiltered (gdb_stdlog, "gdbarch_decode_memtag_note called\n"); >> + return gdbarch->decode_memtag_note (gdbarch, note, address, length); >> +} >> + >> +void >> +set_gdbarch_decode_memtag_note (struct gdbarch *gdbarch, >> + gdbarch_decode_memtag_note_ftype decode_memtag_note) >> +{ >> + gdbarch->decode_memtag_note = decode_memtag_note; >> +} >> + >> bool >> gdbarch_core_xfer_shared_libraries_p (struct gdbarch *gdbarch) >> { >> diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h >> index 7157e5596fd..80e244624de 100644 >> --- a/gdb/gdbarch.h >> +++ b/gdb/gdbarch.h >> @@ -980,6 +980,22 @@ typedef int (gdbarch_find_memory_regions_ftype) (struct gdbarch *gdbarch, find_m >> extern int gdbarch_find_memory_regions (struct gdbarch *gdbarch, find_memory_region_ftype func, void *data); >> extern void set_gdbarch_find_memory_regions (struct gdbarch *gdbarch, gdbarch_find_memory_regions_ftype *find_memory_regions); >> >> +/* Create memory tag core file notes given a range of addresses. */ >> + >> +extern bool gdbarch_create_memtag_notes_from_range_p (struct gdbarch *gdbarch); >> + >> +typedef std::vector (gdbarch_create_memtag_notes_from_range_ftype) (struct gdbarch *gdbarch, CORE_ADDR start_address, CORE_ADDR end_address); >> +extern std::vector gdbarch_create_memtag_notes_from_range (struct gdbarch *gdbarch, CORE_ADDR start_address, CORE_ADDR end_address); >> +extern void set_gdbarch_create_memtag_notes_from_range (struct gdbarch *gdbarch, gdbarch_create_memtag_notes_from_range_ftype *create_memtag_notes_from_range); >> + >> +/* Decode a memory tag note and return the tags that it contains. */ >> + >> +extern bool gdbarch_decode_memtag_note_p (struct gdbarch *gdbarch); >> + >> +typedef gdb::byte_vector (gdbarch_decode_memtag_note_ftype) (struct gdbarch *gdbarch, gdb::array_view note, CORE_ADDR address, size_t length); >> +extern gdb::byte_vector gdbarch_decode_memtag_note (struct gdbarch *gdbarch, gdb::array_view note, CORE_ADDR address, size_t length); >> +extern void set_gdbarch_decode_memtag_note (struct gdbarch *gdbarch, gdbarch_decode_memtag_note_ftype *decode_memtag_note); >> + >> /* Read offset OFFSET of TARGET_OBJECT_LIBRARIES formatted shared libraries list from >> core file into buffer READBUF with length LEN. Return the number of bytes read >> (zero indicates failure). >> diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh >> index 43e51341f97..7c5eed0780c 100755 >> --- a/gdb/gdbarch.sh >> +++ b/gdb/gdbarch.sh >> @@ -745,6 +745,12 @@ M;gdb::unique_xmalloc_ptr;make_corefile_notes;bfd *obfd, int *note_size;ob >> # Find core file memory regions >> M;int;find_memory_regions;find_memory_region_ftype func, void *data;func, data >> >> +# Create memory tag core file notes given a range of addresses. >> +M;std::vector;create_memtag_notes_from_range;CORE_ADDR start_address, CORE_ADDR end_address;start_address, end_address >> + >> +# Decode a memory tag note and return the tags that it contains. >> +M;gdb::byte_vector;decode_memtag_note;gdb::array_view note, CORE_ADDR address, size_t length;note, address, length >> + >> # Read offset OFFSET of TARGET_OBJECT_LIBRARIES formatted shared libraries list from >> # core file into buffer READBUF with length LEN. Return the number of bytes read >> # (zero indicates failure). >> diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c >> index 927e69bf1e1..f4ff57dec87 100644 >> --- a/gdb/linux-tdep.c >> +++ b/gdb/linux-tdep.c >> @@ -1438,10 +1438,11 @@ parse_smaps_data (const char *data, >> return smaps; >> } >> >> -/* See linux-tdep.h. */ >> +/* Helper that checks if an address is in a memory tag page for a live >> + process. */ >> >> -bool >> -linux_address_in_memtag_page (CORE_ADDR address) >> +static bool >> +linux_process_address_in_memtag_page (CORE_ADDR address) >> { >> if (current_inferior ()->fake_pid_p) >> return false; >> @@ -1473,6 +1474,137 @@ linux_address_in_memtag_page (CORE_ADDR address) >> return false; >> } >> >> +/* Helper that checks if an address is in a memory tag page for a core file >> + process. */ >> + >> +static bool >> +linux_core_file_address_in_memtag_page (CORE_ADDR address) >> +{ >> + if (core_bfd == nullptr) >> + return false; >> + >> + asection *section >> + = bfd_get_section_by_name (core_bfd, ".memtag"); >> + >> + /* Go through all the memtag sections and figure out if ADDRESS >> + falls within one of the memory ranges that contain tags. */ >> + while (section != nullptr) >> + { >> + size_t note_size = bfd_section_size (section); >> + >> + /* If the note is smaller than the size of the header, this core note >> + is malformed. */ >> + if (note_size < 2 * sizeof (uint64_t) + sizeof (uint16_t)) >> + { >> + warning (_("malformed core note - too short for header")); >> + return false; >> + } >> + >> + gdb::byte_vector note (note_size); >> + >> + /* Fetch the contents of this particular memtag note. */ >> + if (!bfd_get_section_contents (core_bfd, section, >> + note.data (), 0, note_size)) >> + { >> + warning (_("could not get core note contents.")); >> + return false; >> + } >> + >> + /* Read the generic header of the note. Those contain the format, >> + start address and end address. */ >> + uint64_t start_address >> + = bfd_get_64 (core_bfd, note.data () + sizeof (uint16_t)); >> + uint64_t end_address >> + = bfd_get_64 (core_bfd, note.data () + sizeof (uint16_t) >> + + sizeof (uint64_t)); >> + >> + /* Is the address within [start_address, end_address)? */ >> + if (address >= start_address >> + && address < end_address) >> + return true; >> + >> + /* The requested address lies outside this particular memtag note. Keep >> + looking and get the next section. */ >> + section = bfd_get_next_section_by_name (core_bfd, section); >> + } >> + >> + return false; >> +} >> + >> +/* See linux-tdep.h. */ >> + >> +bool >> +linux_address_in_memtag_page (CORE_ADDR address) >> +{ >> + if (!target_has_execution ()) >> + return linux_core_file_address_in_memtag_page (address); >> + >> + return linux_process_address_in_memtag_page (address); >> +} >> + >> +/* For each memory map entry that has memory tagging enabled, create a new >> + core file note that contains all of its memory tags. Save the data to >> + NOTE_DATA and update NOTE_SIZE accordingly. */ >> + >> +static void >> +linux_make_memtag_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, >> + gdb::unique_xmalloc_ptr ¬e_data, >> + int *note_size) >> +{ >> + if (current_inferior ()->fake_pid_p) >> + return; >> + >> + /* If the architecture doesn't have a hook to return memory tag notes, >> + there is nothing left to do. */ >> + if (!gdbarch_create_memtag_notes_from_range_p (gdbarch)) >> + return; >> + >> + pid_t pid = current_inferior ()->pid; >> + >> + std::string smaps_file = string_printf ("/proc/%d/smaps", pid); >> + >> + gdb::unique_xmalloc_ptr data >> + = target_fileio_read_stralloc (NULL, smaps_file.c_str ()); >> + >> + if (data == nullptr) >> + return; >> + >> + std::vector smaps; >> + >> + /* Parse the contents of smaps into a vector. */ >> + smaps = parse_smaps_data (data.get (), smaps_file); >> + >> + for (const smaps_data &map : smaps) >> + { >> + /* Does this mapping have memory tagging enabled? If so, save the >> + memory tags to the core file note. */ >> + if (map.vmflags.memory_tagging == 0) >> + continue; >> + >> + /* Ask the architecture to create (one or more) NT_MEMTAG notes for >> + this particular memory range, including the header. >> + >> + If the notes are too big, we may need to break up the transfer >> + into smaller chunks. >> + >> + If the architecture returns an empty vector, that means there are >> + no memory tag notes to write. */ >> + std::vector memory_tag_notes; >> + memory_tag_notes >> + = gdbarch_create_memtag_notes_from_range (gdbarch, >> + map.start_address, >> + map.end_address); >> + /* Write notes to the core file. */ >> + for (gdb::byte_vector note : memory_tag_notes) >> + { >> + note_data.reset (elfcore_write_note (obfd, note_data.release (), >> + note_size, "CORE", >> + NT_MEMTAG, note.data (), >> + note.size ())); >> + } >> + } >> +} >> + >> /* List memory regions in the inferior for a corefile. */ >> >> static int >> @@ -2051,6 +2183,9 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size) >> return NULL; >> } >> >> + /* Dump the memory tags, if any. */ >> + linux_make_memtag_corefile_notes (gdbarch, obfd, note_data, note_size); >> + >> /* File mappings. */ >> linux_make_mappings_corefile_notes (gdbarch, obfd, note_data, note_size); >> >> diff --git a/gdb/testsuite/gdb.arch/aarch64-mte-gcore.c b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.c >> new file mode 100644 >> index 00000000000..b20ebcff424 >> --- /dev/null >> +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.c >> @@ -0,0 +1,93 @@ >> +/* This test program is part of GDB, the GNU debugger. >> + >> + Copyright 2021 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 . */ >> + >> +/* 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 >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +/* 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_SYNC (1UL << PR_MTE_TCF_SHIFT) >> +#define PR_MTE_TAG_SHIFT 3 >> + >> +void >> +access_memory (unsigned char *tagged_ptr) >> +{ >> + tagged_ptr[0] = 'a'; >> +} >> + >> +int >> +main (int argc, char **argv) >> +{ >> + unsigned char *tagged_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; >> + } >> + >> + /* 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); >> + >> + return 0; >> +} >> diff --git a/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp >> new file mode 100644 >> index 00000000000..bb529a8b369 >> --- /dev/null >> +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp >> @@ -0,0 +1,115 @@ >> +# Copyright (C) 2018-2021 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 . >> + >> +# This file is part of the gdb testsuite. >> + >> +# Test generating and reading a core file with MTE memory tags. >> + >> +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]} { >> + unsupported "memory tagging unsupported" >> + return -1 >> +} >> + >> +gdb_breakpoint "access_memory" >> + >> +if [gdb_continue "access_memory"] { >> + return -1 >> +} >> + >> +# Set each tag granule to a different tag value, from 0x0 to 0xf. >> +set atag_msg "Allocation tag\\(s\\) updated successfully\." >> +for {set i 15} {$i >= 0} {incr i -1} { >> + set index [expr [expr 15 - $i] * 16] >> + set tag [format "%02x" $i] >> + gdb_test "memory-tag set-allocation-tag &tagged_ptr\[$index\] 1 $tag" \ >> + $atag_msg \ >> + "set memory tag of &tagged_ptr\[$index\] to $tag" >> +} >> + >> +# 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" \ >> + "Logical tag $hex\." \ >> + "$hex in access_memory \\(.*\\) at .*" \ >> + ".*tagged_ptr\\\[0\\\] = 'a';"] \ >> + "display tag violation information for live process" >> + >> +# Generate the core file. >> +set core_filename [standard_output_file "$testfile.core"] >> +set core_generated [gdb_gcore_cmd "$core_filename" "generate core file"] >> + >> +if { !$core_generated } { >> + return -1 >> +} >> + >> +clean_restart >> + >> +# Load the program file. >> +set program_filename [standard_output_file $testfile] >> +set program_loaded [gdb_file_cmd $program_filename] >> + >> +if { $program_loaded } { >> + return -1 >> +} >> + >> +# Load the core file and make sure we see the tag violation fault >> +# information. >> +gdb_test "core $core_filename" \ >> + [multi_line \ >> + "Core was generated by.*\." \ >> + "Program terminated with signal SIGSEGV, Segmentation fault" \ >> + "Memory tag violation while accessing address $hex" \ >> + "Allocation tag 0xf" \ >> + "Logical tag 0x0\." \ >> + "#0.*$hex in access_memory \\(.*\\) at .*" \ >> + ".*tagged_ptr\\\[0\\\] = 'a';"] \ >> + "core file shows tag violation information" >> + >> +# Make sure we have the tag_ctl register. >> +gdb_test "info register tag_ctl" \ >> + "tag_ctl.*$hex.*${::decimal}" \ >> + "tag_ctl is available" >> + >> +# Check if the tag granules have the expected values. If they do, that >> +# means the core file saved the tags properly and GDB has read them >> +# correctly. >> +for {set i 15} {$i >= 0} {incr i -1} { >> + set index [expr [expr 15 - $i] * 16] >> + set tag [format "%x" $i] >> + gdb_test "memory-tag print-allocation-tag &tagged_ptr\[$index\]" \ >> + "= 0x$tag" \ >> + "memory tag of &tagged_ptr\[$index\] is correct" >> +} >> -- >> 2.25.1 >>