This is an archive of the discontinued LLVM Phabricator instance.

[lldb][AArch64] Add MTE memory tag reading to lldb
ClosedPublic

Authored by DavidSpickett on Jan 28 2021, 1:45 AM.

Details

Summary

This adds GDB client support for the qMemTags packet
which reads memory tags. Following the design
which was recently committed to GDB.

https://sourceware.org/gdb/current/onlinedocs/gdb/General-Query-Packets.html#General-Query-Packets
(look for qMemTags)

lldb commands will use the new Process methods
GetMemoryTagManager and ReadMemoryTags.

The former takes a range and checks that:

  • The current process architecture has an architecture plugin
  • That plugin provides a MemoryTagManager
  • That the range of memory requested lies in a tagged range (it will expand it to granules for you)

If all that was true you get a MemoryTagManager you
can give to ReadMemoryTags.

This two step process is done to allow commands to get the
tag manager without having to read tags as well. For example
you might just want to remove a logical tag, or error early
if a range with tagged addresses is inverted.

Note that getting a MemoryTagManager doesn't mean that the process
or a specific memory range is tagged. Those are seperate checks.
Having a tag manager just means this architecture *could* have
a tagging feature enabled.

An architecture plugin has been added for AArch64 which
will return a MemoryTagManagerAArch64MTE, which was added in a
previous patch.

Diff Detail

Event Timeline

DavidSpickett created this revision.Jan 28 2021, 1:45 AM
DavidSpickett requested review of this revision.Jan 28 2021, 1:45 AM
Herald added a project: Restricted Project. · View Herald TranscriptJan 28 2021, 1:45 AM

Couple of things I wanted to highlight for review.

I've put the tag handler on the architecture plugin, but it could also go on process directly like trace does. I figured tagging extensions are a per arch thing so it made logical sense, but code wise it does add some complexity.

The command's output is very simple and verbose, it could definitely do some things like not showing repeated tags:

(lldb) memory tag read new_buf_ptr new_buf_ptr+<n>
Logical tag: 0x9
Allocation tags:
[0x900fffff7ffa000, 0x900fffff7ffa010): 0x9
<... 9 repeats...>
[<>, <>): 0x0

Or combine runs into the same row,

(lldb) memory tag read new_buf_ptr new_buf_ptr+<n>
Logical tag: 0x9
Allocation tags:
[0x900fffff7ffa000, 0x900fffff7ffa000+<n>): 0x9

So each row is not always 1 granule.

That could be added later as a flag --compact or make that the default and have a flag to be verbose. The command as is does the job well enough for small reads.

The original RFC suggested a separate commands to read the logical tag only, but having written "tag read" and "tag check" I don't think there's a need. That's why "tag read" shows logical and allocation in its output.

DavidSpickett edited reviewers, added: labath; removed: palebedev.Jan 28 2021, 2:04 AM

For anyone reviewing this:

Orignal RFC with all the required documentation insight into Arm Memory Tagging Extension can be found here:

https://lists.llvm.org/pipermail/lldb-dev/2020-August/016402.html

@DavidSpickett this calls for some documentation update in a future patch to have some MTE/Tags read/write commands related information to go into lldb.llvm.org as well may be here https://lldb.llvm.org/use/map.html

This patch also looks quite good. Some minor nits inline and also move gdb* changes into a single patch with both client and server side code.

lldb/include/lldb/Target/Process.h
1717

Can you explain this line a bit? What i understood we dont include start and end address in tag range. right?

1740

I guess you meant to say read tags for?

2814

By remote you mean gdb-remote process? This probably will be generic routine used by other platforms.
in context of lldb we dont have remote rather platforms where gdb-remote is a type of platform.

2838

I guess if there are no restrictions by specs then we should rethink use of int32_t for type may be uint32_t if possible.

lldb/packages/Python/lldbsuite/test/lldbtest.py
1272 ↗(On Diff #319792)

This change look good and can be committed outside this patch.

1272 ↗(On Diff #319792)

This change looks fine commit it separately.

lldb/source/Target/Process.cpp
6073

'arch' may be nullptr so call to GetMemoryTagHandler is not safe.

6100

Can there be multiple 'granules' defined for an implementation ? if not this func may be renamed (AlignToGranule) to reflect that

lldb/test/API/linux/aarch64/mte_tag_read/main.c
44 ↗(On Diff #319792)

May be add a line or two of comment about mte intrinsics for any future readers of these test code.

This patch also looks quite good. Some minor nits inline and also move gdb* changes into a single patch with both client and server side code.

Cool, I wasn't sure how to split while keeping it readable but that sounds good.

lldb/include/lldb/Target/Process.h
1717

Does the description of end_addr answer your question?

GetMemoryTagHandler(10, 21) would check a range from 10-20.

2838

GDB is using a signed int, though we don't have need for negative numbers at this time. I'll cite the GDB design for this.

We could say well, for the moment it might as well be unsigned but I don't want to introduce a situation where in future we mix client/servers and lldb falls over on a "-".

lldb/packages/Python/lldbsuite/test/lldbtest.py
1272 ↗(On Diff #319792)

Yeah your registers patch does the same thing so depending on timing I might end up using that.

lldb/source/Target/Process.cpp
6073

Sure, that's why I check it first with the arch ?. Happy to refactor if it could be clearer.

I should say that some of this boilerplate looking for the tag handler is subject to change once I've written more commands. It's repetitive now but later I should be able to refactor with the context of how all the commands use it.

6100

MTE only has one granule size, Sparc ADI uses cache lines so I assume theirs is just the cache line size.

If I read "align to granule" singular I think it's align to a single granule, probably up. Since this aligns up and down granules plural made more sense to me.

That said, it's never going to shrink the range, so ExpandToGranule would be more descriptive.

Split into smaller changes. Tag read command is now a further change.

DavidSpickett edited the summary of this revision. (Show Details)
DavidSpickett retitled this revision from [lldb][AArch64] Add MTE memory tag reading for lldb to [lldb][AArch64] Add MTE memory tag reading to lldb.

I guess we also need LLDB API interface for reading/writing memory tags. So that we can do something like process.ReadMemoryTags() from python. Do you plan on adding it in a later patch?

I guess we also need LLDB API interface for reading/writing memory tags. So that we can do something like process.ReadMemoryTags() from python. Do you plan on adding it in a later patch?

Yes but I had forgotten about it so thanks for the reminder. I want to get the read/write commands done first so I had a better idea of what details you'd want access to. Plus, would we want to extend the existing memory API or have an additional call to get tags. (the latter most likely)

omjavaid added inline comments.Mar 3 2021, 2:59 AM
lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.cpp
37

Do you think we should consider aarch64_32 as AArch64 architecture? aarch64_32 AFAIK is used where 64bit kernel is running and 32bit executable. Does MTE also apply on 32 bit executables running on 64bit kernel.

DavidSpickett added inline comments.Mar 3 2021, 3:43 AM
lldb/source/Plugins/Architecture/AArch64/ArchitectureAArch64.cpp
37

For MTE no but the rest of lldb considers it AArch64 for things like breakpoints, instruction emulation etc.

Though this Arch plugin only does MTE, in general they can control stepping behavior, things like that. So it makes sense to count it.

Plus for MTE we have to lookup the memory region via the kernel too. So if you were on aarch64_32 and you couldn't allocate MTE anyway, the debugger wouldn't be able to do anything.

DavidSpickett updated this revision to Diff 329029.EditedMar 8 2021, 8:28 AM
  • Use RemoveNonAddressBits instead of RemoveLogicalTag

Note that the tests don't have any changes because the GDB client layer doesn't have any requirement to remove tags. Only the commands
need to do that.

The parts of this that use RemoveNonAddressBits will be
tested in the "memory tag read" patch.

Update after changing parent

Account for a ranges being split across multiple mappings
that are next to each other.
(tested in the tag read command patch)

DavidSpickett marked 2 inline comments as done.Mar 16 2021, 8:16 AM
DavidSpickett added inline comments.
lldb/include/lldb/Target/Process.h
2814

I forgot to say, I changed this to "process", I think that's ok.

DavidSpickett marked an inline comment as done.

Correct typo in function comment. "to read tags for"

  • Use PRIx64 to properly print 64 bit addresses in invalid range error message.
DavidSpickett added inline comments.Apr 7 2021, 8:29 AM
lldb/source/Target/Process.cpp
6070

While writing "memory tag write" I realised it saves some effort if you return the manager and the checked range here. However it's way more useful for tag write so I'll leave this as is for now unless you really want to see it in this change.

@omjavaid Any more comments on this?

lldb/include/lldb/Core/Architecture.h
110

I also had the thought that we could just return instances of MemoryTagManager, however we rely on virtual functions for the implementation. (incredibly obvious once you try it, but would have been nice to remove a bunch of pointer indirection)

DavidSpickett updated this revision to Diff 337467.EditedApr 14 2021, 8:27 AM
  • Rebase onto main

Also, GDB support has landed so https://sourceware.org/gdb/current/onlinedocs/gdb/General-Query-Packets.html#General-Query-Packets has the packet formats now.

DavidSpickett edited the summary of this revision. (Show Details)Apr 14 2021, 8:28 AM

Rebase onto main.

omjavaid accepted this revision.May 5 2021, 10:41 AM
This revision is now accepted and ready to land.May 5 2021, 10:41 AM

Rebase, fix use of SendPacketAndWaitForResponse.

pcc added a subscriber: pcc.May 25 2021, 5:03 PM
pcc added inline comments.
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
635

Rebase, fix up SendPacketAndWaitForResponse use.

DavidSpickett marked an inline comment as done.May 26 2021, 3:52 AM

Rebase, now ready to land.

This revision was landed with ongoing or failed builds.Jun 24 2021, 9:17 AM
This revision was automatically updated to reflect the committed changes.
fhahn added a subscriber: fhahn.Jun 24 2021, 12:47 PM

It looks like this is breaking building LLDB on Green Dragon: https://smooshbase.apple.com/ci/job/am_github_build_tester/63476/console

please take a look and consider reverting if it takes longer to fix the issue

It looks like this is breaking building LLDB on Green Dragon: https://smooshbase.apple.com/ci/job/am_github_build_tester/63476/console

please take a look and consider reverting if it takes longer to fix the issue

I think that's the correct public CI that shows the failure: https://green.lab.llvm.org/green/view/LLDB/job/lldb-cmake/33057/console

/Users/buildslave/jenkins/workspace/lldb-cmake/llvm-project/lldb/source/Target/Process.cpp:6098:44: error: no viable conversion from 'Range<[...], lldb::addr_t>' to 'Range<[...], size_t>'
  tag_range = tag_manager->ExpandToGranule(tag_range);
                                           ^~~~~~~~~
/Users/buildslave/jenkins/workspace/lldb-cmake/llvm-project/lldb/include/lldb/Utility/RangeMap.h:29:42: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'MemoryRegionInfo::RangeType' (aka 'Range<unsigned long long, unsigned long long>') to 'const lldb_private::Range<unsigned long long, unsigned long> &' for 1st argument
template <typename B, typename S> struct Range {
                                         ^
/Users/buildslave/jenkins/workspace/lldb-cmake/llvm-project/lldb/include/lldb/Utility/RangeMap.h:29:42: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'MemoryRegionInfo::RangeType' (aka 'Range<unsigned long long, unsigned long long>') to 'lldb_private::Range<unsigned long long, unsigned long> &&' for 1st argument
/Users/buildslave/jenkins/workspace/lldb-cmake/llvm-project/lldb/include/lldb/Target/MemoryTagManager.h:58:45: note: passing argument to parameter 'range' here
  virtual TagRange ExpandToGranule(TagRange range) const = 0;
                                            ^
/Users/buildslave/jenkins/workspace/lldb-cmake/llvm-project/lldb/source/Target/Process.cpp:6098:13: error: no viable overloaded '='
  tag_range = tag_manager->ExpandToGranule(tag_range);
  ~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/buildslave/jenkins/workspace/lldb-cmake/llvm-project/lldb/include/lldb/Utility/RangeMap.h:29:42: note: candidate function (the implicit copy assignment operator) not viable: no known conversion from 'Range<[...], size_t>' to 'const Range<[...], unsigned long long>' for 1st argument
template <typename B, typename S> struct Range {
                                         ^
/Users/buildslave/jenkins/workspace/lldb-cmake/llvm-project/lldb/include/lldb/Utility/RangeMap.h:29:42: note: candidate function (the implicit move assignment operator) not viable: no known conversion from 'Range<[...], size_t>' to 'Range<[...], unsigned long long>' for 1st argument
2 errors generated.

I think that's the correct public CI that shows the failure: https://green.lab.llvm.org/green/view/LLDB/job/lldb-cmake/33057/console

This is the correct link, thanks!