Skip to content

Commit da2e614

Browse files
committed
[lldb][AArch64] Add memory tag reading to lldb-server
This adds memory tag reading using the new "qMemTags" packet and ptrace on AArch64 Linux. This new packet is following the one used by GDB. (https://sourceware.org/gdb/current/onlinedocs/gdb/General-Query-Packets.html) On AArch64 Linux we use ptrace's PEEKMTETAGS to read tags and we assume that lldb has already checked that the memory region actually has tagging enabled. We do not assume that lldb has expanded the requested range to granules and expand it again to be sure. (although lldb will be sending aligned ranges because it happens to need them client side anyway) Also we don't assume untagged addresses. So for AArch64 we'll remove the top byte before using them. (the top byte includes MTE and other non address data) To do the ptrace read NativeProcessLinux will ask the native register context for a memory tag manager based on the type in the packet. This also gives you the ptrace numbers you need. (it's called a register context but it also has non register data, so it saves adding another per platform sub class) The only supported platform for this is AArch64 Linux and the only supported tag type is MTE allocation tags. Anything else will error. Ptrace can return a partial result but for lldb-server we will be treating that as an error. To succeed we need to get all the tags we expect. (Note that the protocol leaves room for logical tags to be read via qMemTags but this is not going to be implemented for lldb at this time.) Reviewed By: omjavaid Differential Revision: https://reviews.llvm.org/D95601
1 parent f6ba845 commit da2e614

15 files changed

+358
-0
lines changed

lldb/include/lldb/Host/common/NativeProcessProtocol.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ class NativeProcessProtocol {
8787
Status ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size,
8888
size_t &bytes_read);
8989

90+
virtual Status ReadMemoryTags(int32_t type, lldb::addr_t addr, size_t len,
91+
std::vector<uint8_t> &tags);
92+
9093
/// Reads a null terminated string from memory.
9194
///
9295
/// Reads up to \p max_size bytes of memory until it finds a '\0'.

lldb/include/lldb/Host/linux/Ptrace.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ typedef int __ptrace_request;
5050
#define ARCH_GET_FS 0x1003
5151
#define ARCH_GET_GS 0x1004
5252
#endif
53+
#ifndef PTRACE_PEEKMTETAGS
54+
#define PTRACE_PEEKMTETAGS 33
55+
#endif
56+
#ifndef PTRACE_POKEMTETAGS
57+
#define PTRACE_POKEMTETAGS 34
58+
#endif
5359

5460
#define LLDB_PTRACE_NT_ARM_TLS 0x401 // ARM TLS register
5561

lldb/include/lldb/Utility/StringExtractorGDBRemote.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ class StringExtractorGDBRemote : public StringExtractor {
167167
eServerPacketType_jLLDBTraceStop,
168168
eServerPacketType_jLLDBTraceGetState,
169169
eServerPacketType_jLLDBTraceGetBinaryData,
170+
171+
eServerPacketType_qMemTags,
170172
};
171173

172174
ServerPacketType GetServerPacketType() const;

lldb/source/Host/common/NativeProcessProtocol.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ NativeProcessProtocol::GetMemoryRegionInfo(lldb::addr_t load_addr,
5252
return Status("not implemented");
5353
}
5454

55+
lldb_private::Status
56+
NativeProcessProtocol::ReadMemoryTags(int32_t type, lldb::addr_t addr,
57+
size_t len, std::vector<uint8_t> &tags) {
58+
return Status("not implemented");
59+
}
60+
5561
llvm::Optional<WaitStatus> NativeProcessProtocol::GetExitStatus() {
5662
if (m_state == lldb::eStateExited)
5763
return m_exit_status;

lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,6 +1431,61 @@ llvm::Error NativeProcessLinux::DeallocateMemory(lldb::addr_t addr) {
14311431
return llvm::Error::success();
14321432
}
14331433

1434+
Status NativeProcessLinux::ReadMemoryTags(int32_t type, lldb::addr_t addr,
1435+
size_t len,
1436+
std::vector<uint8_t> &tags) {
1437+
llvm::Expected<NativeRegisterContextLinux::MemoryTaggingDetails> details =
1438+
GetCurrentThread()->GetRegisterContext().GetMemoryTaggingDetails(type);
1439+
if (!details)
1440+
return Status(details.takeError());
1441+
1442+
// Ignore 0 length read
1443+
if (!len)
1444+
return Status();
1445+
1446+
// lldb will align the range it requests but it is not required to by
1447+
// the protocol so we'll do it again just in case.
1448+
// Remove non address bits too. Ptrace calls may work regardless but that
1449+
// is not a guarantee.
1450+
MemoryTagManager::TagRange range(details->manager->RemoveNonAddressBits(addr),
1451+
len);
1452+
range = details->manager->ExpandToGranule(range);
1453+
1454+
// Allocate enough space for all tags to be read
1455+
size_t num_tags = range.GetByteSize() / details->manager->GetGranuleSize();
1456+
tags.resize(num_tags * details->manager->GetTagSizeInBytes());
1457+
1458+
struct iovec tags_iovec;
1459+
uint8_t *dest = tags.data();
1460+
lldb::addr_t read_addr = range.GetRangeBase();
1461+
1462+
// This call can return partial data so loop until we error or
1463+
// get all tags back.
1464+
while (num_tags) {
1465+
tags_iovec.iov_base = dest;
1466+
tags_iovec.iov_len = num_tags;
1467+
1468+
Status error = NativeProcessLinux::PtraceWrapper(
1469+
details->ptrace_read_req, GetID(), reinterpret_cast<void *>(read_addr),
1470+
static_cast<void *>(&tags_iovec), 0, nullptr);
1471+
1472+
if (error.Fail()) {
1473+
// Discard partial reads
1474+
tags.resize(0);
1475+
return error;
1476+
}
1477+
1478+
size_t tags_read = tags_iovec.iov_len;
1479+
assert(tags_read && (tags_read <= num_tags));
1480+
1481+
dest += tags_read * details->manager->GetTagSizeInBytes();
1482+
read_addr += details->manager->GetGranuleSize() * tags_read;
1483+
num_tags -= tags_read;
1484+
}
1485+
1486+
return Status();
1487+
}
1488+
14341489
size_t NativeProcessLinux::UpdateThreads() {
14351490
// The NativeProcessLinux monitoring threads are always up to date with
14361491
// respect to thread state and they keep the thread list populated properly.

lldb/source/Plugins/Process/Linux/NativeProcessLinux.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ class NativeProcessLinux : public NativeProcessELF,
8080

8181
llvm::Error DeallocateMemory(lldb::addr_t addr) override;
8282

83+
Status ReadMemoryTags(int32_t type, lldb::addr_t addr, size_t len,
84+
std::vector<uint8_t> &tags) override;
85+
8386
size_t UpdateThreads() override;
8487

8588
const ArchSpec &GetArchitecture() const override { return m_arch; }

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
#include "Plugins/Process/Utility/NativeRegisterContextRegisterInfo.h"
1313
#include "lldb/Host/common/NativeThreadProtocol.h"
14+
#include "lldb/Target/MemoryTagManager.h"
15+
#include "llvm/Support/Error.h"
1416

1517
namespace lldb_private {
1618
namespace process_linux {
@@ -57,6 +59,22 @@ class NativeRegisterContextLinux
5759
/// they are supported.
5860
virtual llvm::Optional<MmapData> GetMmapData() { return llvm::None; }
5961

62+
struct MemoryTaggingDetails {
63+
/// Object with tag handling utilities. If the function below returns
64+
/// a valid structure, you can assume that this pointer is valid.
65+
std::unique_ptr<MemoryTagManager> manager;
66+
int ptrace_read_req; /// ptrace operation number for memory tag read
67+
int ptrace_write_req; /// ptrace operation number for memory tag write
68+
};
69+
/// Return architecture specific data needed to use memory tags,
70+
/// if they are supported.
71+
virtual llvm::Expected<MemoryTaggingDetails>
72+
GetMemoryTaggingDetails(int32_t type) {
73+
return llvm::createStringError(
74+
llvm::inconvertibleErrorCode(),
75+
"Architecture does not support memory tagging");
76+
}
77+
6078
protected:
6179
// NB: This constructor is here only because gcc<=6.5 requires a virtual base
6280
// class initializer on abstract class (even though it is never used). It can

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414

1515
#include "lldb/Host/common/NativeProcessProtocol.h"
16+
#include "lldb/Host/linux/Ptrace.h"
1617
#include "lldb/Utility/DataBufferHeap.h"
1718
#include "lldb/Utility/Log.h"
1819
#include "lldb/Utility/RegisterValue.h"
@@ -21,6 +22,7 @@
2122
#include "Plugins/Process/Linux/NativeProcessLinux.h"
2223
#include "Plugins/Process/Linux/Procfs.h"
2324
#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
25+
#include "Plugins/Process/Utility/MemoryTagManagerAArch64MTE.h"
2426
#include "Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h"
2527

2628
// System includes - They have to be included after framework includes because
@@ -877,4 +879,15 @@ std::vector<uint32_t> NativeRegisterContextLinux_arm64::GetExpeditedRegisters(
877879
return expedited_reg_nums;
878880
}
879881

882+
llvm::Expected<NativeRegisterContextLinux::MemoryTaggingDetails>
883+
NativeRegisterContextLinux_arm64::GetMemoryTaggingDetails(int32_t type) {
884+
if (type == MemoryTagManagerAArch64MTE::eMTE_allocation) {
885+
return MemoryTaggingDetails{std::make_unique<MemoryTagManagerAArch64MTE>(),
886+
PTRACE_PEEKMTETAGS, PTRACE_POKEMTETAGS};
887+
}
888+
889+
return llvm::createStringError(llvm::inconvertibleErrorCode(),
890+
"Unknown AArch64 memory tag type %d", type);
891+
}
892+
880893
#endif // defined (__arm64__) || defined (__aarch64__)

lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ class NativeRegisterContextLinux_arm64
5353

5454
bool RegisterOffsetIsDynamic() const override { return true; }
5555

56+
llvm::Expected<MemoryTaggingDetails>
57+
GetMemoryTaggingDetails(int32_t type) override;
58+
5659
protected:
5760
Status ReadGPR() override;
5861

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <chrono>
1515
#include <cstring>
16+
#include <limits>
1617
#include <thread>
1718

1819
#include "GDBRemoteCommunicationServerLLGS.h"
@@ -211,6 +212,10 @@ void GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
211212
RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g,
212213
&GDBRemoteCommunicationServerLLGS::Handle_g);
213214

215+
RegisterMemberFunctionHandler(
216+
StringExtractorGDBRemote::eServerPacketType_qMemTags,
217+
&GDBRemoteCommunicationServerLLGS::Handle_qMemTags);
218+
214219
RegisterPacketHandler(StringExtractorGDBRemote::eServerPacketType_k,
215220
[this](StringExtractorGDBRemote packet, Status &error,
216221
bool &interrupt, bool &quit) {
@@ -3414,6 +3419,71 @@ GDBRemoteCommunicationServerLLGS::Handle_QPassSignals(
34143419
return SendOKResponse();
34153420
}
34163421

3422+
GDBRemoteCommunication::PacketResult
3423+
GDBRemoteCommunicationServerLLGS::Handle_qMemTags(
3424+
StringExtractorGDBRemote &packet) {
3425+
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
3426+
3427+
// Ensure we have a process.
3428+
if (!m_current_process ||
3429+
(m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) {
3430+
LLDB_LOGF(
3431+
log,
3432+
"GDBRemoteCommunicationServerLLGS::%s failed, no process available",
3433+
__FUNCTION__);
3434+
return SendErrorResponse(1);
3435+
}
3436+
3437+
// We are expecting
3438+
// qMemTags:<hex address>,<hex length>:<hex type>
3439+
3440+
// Address
3441+
packet.SetFilePos(strlen("qMemTags:"));
3442+
const char *current_char = packet.Peek();
3443+
if (!current_char || *current_char == ',')
3444+
return SendIllFormedResponse(packet, "Missing address in qMemTags packet");
3445+
const lldb::addr_t addr = packet.GetHexMaxU64(/*little_endian=*/false, 0);
3446+
3447+
// Length
3448+
char previous_char = packet.GetChar();
3449+
current_char = packet.Peek();
3450+
// If we don't have a separator or the length field is empty
3451+
if (previous_char != ',' || (current_char && *current_char == ':'))
3452+
return SendIllFormedResponse(packet,
3453+
"Invalid addr,length pair in qMemTags packet");
3454+
3455+
if (packet.GetBytesLeft() < 1)
3456+
return SendIllFormedResponse(
3457+
packet, "Too short qMemtags: packet (looking for length)");
3458+
const size_t length = packet.GetHexMaxU64(/*little_endian=*/false, 0);
3459+
3460+
// Type
3461+
const char *invalid_type_err = "Invalid type field in qMemTags: packet";
3462+
if (packet.GetBytesLeft() < 1 || packet.GetChar() != ':')
3463+
return SendIllFormedResponse(packet, invalid_type_err);
3464+
3465+
int32_t type =
3466+
packet.GetS32(std::numeric_limits<int32_t>::max(), /*base=*/16);
3467+
if (type == std::numeric_limits<int32_t>::max() ||
3468+
// To catch inputs like "123aardvark" that will parse but clearly aren't
3469+
// valid in this case.
3470+
packet.GetBytesLeft()) {
3471+
return SendIllFormedResponse(packet, invalid_type_err);
3472+
}
3473+
3474+
StreamGDBRemote response;
3475+
std::vector<uint8_t> tags;
3476+
Status error = m_current_process->ReadMemoryTags(type, addr, length, tags);
3477+
if (error.Fail())
3478+
return SendErrorResponse(1);
3479+
3480+
// This m is here in case we want to support multi part replies in the future.
3481+
// In the same manner as qfThreadInfo/qsThreadInfo.
3482+
response.PutChar('m');
3483+
response.PutBytesAsRawHex8(tags.data(), tags.size());
3484+
return SendPacketNoLock(response.GetString());
3485+
}
3486+
34173487
void GDBRemoteCommunicationServerLLGS::MaybeCloseInferiorTerminalConnection() {
34183488
Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
34193489

lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ class GDBRemoteCommunicationServerLLGS
216216

217217
PacketResult Handle_g(StringExtractorGDBRemote &packet);
218218

219+
PacketResult Handle_qMemTags(StringExtractorGDBRemote &packet);
220+
219221
void SetCurrentThreadID(lldb::tid_t tid);
220222

221223
lldb::tid_t GetCurrentThreadID() const;

lldb/source/Utility/StringExtractorGDBRemote.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
223223
return eServerPacketType_qMemoryRegionInfoSupported;
224224
if (PACKET_STARTS_WITH("qModuleInfo:"))
225225
return eServerPacketType_qModuleInfo;
226+
if (PACKET_STARTS_WITH("qMemTags:"))
227+
return eServerPacketType_qMemTags;
226228
break;
227229

228230
case 'P':
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
C_SOURCES := main.c
2+
CFLAGS_EXTRAS := -march=armv8.5-a+memtag
3+
4+
include Makefile.rules

0 commit comments

Comments
 (0)