summaryrefslogtreecommitdiff
path: root/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
diff options
context:
space:
mode:
authorDavid Spickett <david.spickett@linaro.org>2021-03-31 14:02:34 +0100
committerDavid Spickett <david.spickett@linaro.org>2021-07-27 12:02:17 +0100
commit7d27230de3336b8c79bfdc90f59858f6dad28fa5 (patch)
treedf523622b95d310a6e102d2e9aeb9e9322719d03 /lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
parentd7dd12aee399a19e890143604b6993f02232ca24 (diff)
[lldb][AArch64] Add memory tag writing to lldb-server
This is implemented using the QMemTags packet, as specified by GDB in: https://sourceware.org/gdb/current/onlinedocs/gdb/General-Query-Packets.html#General-Query-Packets (recall that qMemTags was previously added to read tags) On receipt of a valid packet lldb-server will: * align the given address and length to granules (most of the time lldb will have already done this but the specification doesn't guarantee it) * Repeat the supplied tags as many times as needed to cover the range. (if tags > range we just use as many as needed) * Call ptrace POKEMTETAGS to write the tags. The ptrace step will loop just like the tag read does, until all tags are written or we get an error. Meaning that if ptrace succeeds it could be a partial write. So we call it again and if we then get an error, return an error to lldb. We are not going to attempt to restore tags after a partial write followed by an error. This matches the behaviour of the existing memory writes. The lldb-server tests have been extended to include read and write in the same test file. With some updated function names since "qMemTags" vs "QMemTags" isn't very clear when they're next to each other. Reviewed By: omjavaid Differential Revision: https://reviews.llvm.org/D105180
Diffstat (limited to 'lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp')
-rw-r--r--lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp71
1 files changed, 71 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index b816d945181c..8c45796ae0b3 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -1486,6 +1486,77 @@ Status NativeProcessLinux::ReadMemoryTags(int32_t type, lldb::addr_t addr,
return Status();
}
+Status NativeProcessLinux::WriteMemoryTags(int32_t type, lldb::addr_t addr,
+ size_t len,
+ const std::vector<uint8_t> &tags) {
+ llvm::Expected<NativeRegisterContextLinux::MemoryTaggingDetails> details =
+ GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type);
+ if (!details)
+ return Status(details.takeError());
+
+ // Ignore 0 length write
+ if (!len)
+ return Status();
+
+ // lldb will align the range it requests but it is not required to by
+ // the protocol so we'll do it again just in case.
+ // Remove non address bits too. Ptrace calls may work regardless but that
+ // is not a guarantee.
+ MemoryTagManager::TagRange range(details->manager->RemoveNonAddressBits(addr),
+ len);
+ range = details->manager->ExpandToGranule(range);
+
+ // Not checking number of tags here, we may repeat them below
+ llvm::Expected<std::vector<lldb::addr_t>> unpacked_tags_or_err =
+ details->manager->UnpackTagsData(tags);
+ if (!unpacked_tags_or_err)
+ return Status(unpacked_tags_or_err.takeError());
+
+ llvm::Expected<std::vector<lldb::addr_t>> repeated_tags_or_err =
+ details->manager->RepeatTagsForRange(*unpacked_tags_or_err, range);
+ if (!repeated_tags_or_err)
+ return Status(repeated_tags_or_err.takeError());
+
+ // Repack them for ptrace to use
+ llvm::Expected<std::vector<uint8_t>> final_tag_data =
+ details->manager->PackTags(*repeated_tags_or_err);
+ if (!final_tag_data)
+ return Status(final_tag_data.takeError());
+
+ struct iovec tags_vec;
+ uint8_t *src = final_tag_data->data();
+ lldb::addr_t write_addr = range.GetRangeBase();
+ // unpacked tags size because the number of bytes per tag might not be 1
+ size_t num_tags = repeated_tags_or_err->size();
+
+ // This call can partially write tags, so we loop until we
+ // error or all tags have been written.
+ while (num_tags > 0) {
+ tags_vec.iov_base = src;
+ tags_vec.iov_len = num_tags;
+
+ Status error = NativeProcessLinux::PtraceWrapper(
+ details->ptrace_write_req, GetID(),
+ reinterpret_cast<void *>(write_addr), static_cast<void *>(&tags_vec), 0,
+ nullptr);
+
+ if (error.Fail()) {
+ // Don't attempt to restore the original values in the case of a partial
+ // write
+ return error;
+ }
+
+ size_t tags_written = tags_vec.iov_len;
+ assert(tags_written && (tags_written <= num_tags));
+
+ src += tags_written * details->manager->GetTagSizeInBytes();
+ write_addr += details->manager->GetGranuleSize() * tags_written;
+ num_tags -= tags_written;
+ }
+
+ return Status();
+}
+
size_t NativeProcessLinux::UpdateThreads() {
// The NativeProcessLinux monitoring threads are always up to date with
// respect to thread state and they keep the thread list populated properly.