Skip to content

Commit 7e22921

Browse files
committed
[lldb][AArch64] Add thread local storage tpidr register
This register is used as the pointer to the current thread local storage block and is read from NT_ARM_TLS on Linux. Though tpidr will be present on all AArch64 Linux, I am soon going to add a second register tpidr2 to this set. tpidr is only present when SME is implemented, therefore the NT_ARM_TLS set will change size. This is why I've added this as a dynamic register set to save changes later. Reviewed By: omjavaid Differential Revision: https://reviews.llvm.org/D152516
1 parent 6f2e92c commit 7e22921

File tree

7 files changed

+215
-1
lines changed

7 files changed

+215
-1
lines changed

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

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ NativeRegisterContextLinux::CreateHostNativeRegisterContextLinux(
8686
if (auxv_at_hwcap2 && (*auxv_at_hwcap2 & HWCAP2_MTE))
8787
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskMTE);
8888

89+
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskTLS);
90+
8991
auto register_info_up =
9092
std::make_unique<RegisterInfoPOSIX_arm64>(target_arch, opt_regsets);
9193
return std::make_unique<NativeRegisterContextLinux_arm64>(
@@ -116,6 +118,7 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
116118
::memset(&m_pac_mask, 0, sizeof(m_pac_mask));
117119

118120
m_mte_ctrl_reg = 0;
121+
m_tls_tpidr_reg = 0;
119122

120123
// 16 is just a maximum value, query hardware for actual watchpoint count
121124
m_max_hwp_supported = 16;
@@ -129,6 +132,7 @@ NativeRegisterContextLinux_arm64::NativeRegisterContextLinux_arm64(
129132
m_sve_header_is_valid = false;
130133
m_pac_mask_is_valid = false;
131134
m_mte_ctrl_is_valid = false;
135+
m_tls_tpidr_is_valid = false;
132136

133137
if (GetRegisterInfo().IsSVEEnabled())
134138
m_sve_state = SVEState::Unknown;
@@ -232,8 +236,15 @@ NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
232236
assert(offset < GetSVEBufferSize());
233237
src = (uint8_t *)GetSVEBuffer() + offset;
234238
}
235-
} else if (IsSVE(reg)) {
239+
} else if (IsTLS(reg)) {
240+
error = ReadTLSTPIDR();
241+
if (error.Fail())
242+
return error;
236243

244+
offset = reg_info->byte_offset - GetRegisterInfo().GetTLSOffset();
245+
assert(offset < GetTLSTPIDRSize());
246+
src = (uint8_t *)GetTLSTPIDR() + offset;
247+
} else if (IsSVE(reg)) {
237248
if (m_sve_state == SVEState::Disabled || m_sve_state == SVEState::Unknown)
238249
return Status("SVE disabled or not supported");
239250

@@ -450,6 +461,17 @@ Status NativeRegisterContextLinux_arm64::WriteRegister(
450461
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
451462

452463
return WriteMTEControl();
464+
} else if (IsTLS(reg)) {
465+
error = ReadTLSTPIDR();
466+
if (error.Fail())
467+
return error;
468+
469+
offset = reg_info->byte_offset - GetRegisterInfo().GetTLSOffset();
470+
assert(offset < GetTLSTPIDRSize());
471+
dst = (uint8_t *)GetTLSTPIDR() + offset;
472+
::memcpy(dst, reg_value.GetBytes(), reg_info->byte_size);
473+
474+
return WriteTLSTPIDR();
453475
}
454476

455477
return Status("Failed to write register value");
@@ -490,6 +512,12 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
490512
return error;
491513
}
492514

515+
// tpidr is always present but there will be more in future.
516+
reg_data_byte_size += GetTLSTPIDRSize();
517+
error = ReadTLSTPIDR();
518+
if (error.Fail())
519+
return error;
520+
493521
data_sp.reset(new DataBufferHeap(reg_data_byte_size, 0));
494522
uint8_t *dst = data_sp->GetBytes();
495523

@@ -507,6 +535,8 @@ Status NativeRegisterContextLinux_arm64::ReadAllRegisterValues(
507535
if (GetRegisterInfo().IsMTEEnabled())
508536
::memcpy(dst, GetMTEControl(), GetMTEControlSize());
509537

538+
::memcpy(dst, GetTLSTPIDR(), GetTLSTPIDRSize());
539+
510540
return error;
511541
}
512542

@@ -641,6 +671,10 @@ bool NativeRegisterContextLinux_arm64::IsMTE(unsigned reg) const {
641671
return GetRegisterInfo().IsMTEReg(reg);
642672
}
643673

674+
bool NativeRegisterContextLinux_arm64::IsTLS(unsigned reg) const {
675+
return GetRegisterInfo().IsTLSReg(reg);
676+
}
677+
644678
llvm::Error NativeRegisterContextLinux_arm64::ReadHardwareDebugInfo() {
645679
if (!m_refresh_hwdebug_info) {
646680
return llvm::Error::success();
@@ -784,6 +818,7 @@ void NativeRegisterContextLinux_arm64::InvalidateAllRegisters() {
784818
m_sve_header_is_valid = false;
785819
m_pac_mask_is_valid = false;
786820
m_mte_ctrl_is_valid = false;
821+
m_tls_tpidr_is_valid = false;
787822

788823
// Update SVE registers in case there is change in configuration.
789824
ConfigureRegisterContext();
@@ -914,6 +949,40 @@ Status NativeRegisterContextLinux_arm64::WriteMTEControl() {
914949
return WriteRegisterSet(&ioVec, GetMTEControlSize(), NT_ARM_TAGGED_ADDR_CTRL);
915950
}
916951

952+
Status NativeRegisterContextLinux_arm64::ReadTLSTPIDR() {
953+
Status error;
954+
955+
if (m_tls_tpidr_is_valid)
956+
return error;
957+
958+
struct iovec ioVec;
959+
ioVec.iov_base = GetTLSTPIDR();
960+
ioVec.iov_len = GetTLSTPIDRSize();
961+
962+
error = ReadRegisterSet(&ioVec, GetTLSTPIDRSize(), NT_ARM_TLS);
963+
964+
if (error.Success())
965+
m_tls_tpidr_is_valid = true;
966+
967+
return error;
968+
}
969+
970+
Status NativeRegisterContextLinux_arm64::WriteTLSTPIDR() {
971+
Status error;
972+
973+
error = ReadTLSTPIDR();
974+
if (error.Fail())
975+
return error;
976+
977+
struct iovec ioVec;
978+
ioVec.iov_base = GetTLSTPIDR();
979+
ioVec.iov_len = GetTLSTPIDRSize();
980+
981+
m_tls_tpidr_is_valid = false;
982+
983+
return WriteRegisterSet(&ioVec, GetTLSTPIDRSize(), NT_ARM_TLS);
984+
}
985+
917986
void NativeRegisterContextLinux_arm64::ConfigureRegisterContext() {
918987
// ConfigureRegisterContext gets called from InvalidateAllRegisters
919988
// on every stop and configures SVE vector length.

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class NativeRegisterContextLinux_arm64
8383
bool m_fpu_is_valid;
8484
bool m_sve_buffer_is_valid;
8585
bool m_mte_ctrl_is_valid;
86+
bool m_tls_tpidr_is_valid;
8687

8788
bool m_sve_header_is_valid;
8889
bool m_pac_mask_is_valid;
@@ -107,6 +108,8 @@ class NativeRegisterContextLinux_arm64
107108

108109
uint64_t m_mte_ctrl_reg;
109110

111+
uint64_t m_tls_tpidr_reg;
112+
110113
bool IsGPR(unsigned reg) const;
111114

112115
bool IsFPR(unsigned reg) const;
@@ -125,9 +128,14 @@ class NativeRegisterContextLinux_arm64
125128

126129
Status WriteMTEControl();
127130

131+
Status ReadTLSTPIDR();
132+
133+
Status WriteTLSTPIDR();
134+
128135
bool IsSVE(unsigned reg) const;
129136
bool IsPAuth(unsigned reg) const;
130137
bool IsMTE(unsigned reg) const;
138+
bool IsTLS(unsigned reg) const;
131139

132140
uint64_t GetSVERegVG() { return m_sve_header.vl / 8; }
133141

@@ -139,6 +147,8 @@ class NativeRegisterContextLinux_arm64
139147

140148
void *GetMTEControl() { return &m_mte_ctrl_reg; }
141149

150+
void *GetTLSTPIDR() { return &m_tls_tpidr_reg; }
151+
142152
void *GetSVEBuffer() { return m_sve_ptrace_payload.data(); };
143153

144154
size_t GetSVEHeaderSize() { return sizeof(m_sve_header); }
@@ -149,6 +159,8 @@ class NativeRegisterContextLinux_arm64
149159

150160
size_t GetMTEControlSize() { return sizeof(m_mte_ctrl_reg); }
151161

162+
size_t GetTLSTPIDRSize() { return sizeof(m_tls_tpidr_reg); }
163+
152164
llvm::Error ReadHardwareDebugInfo() override;
153165

154166
llvm::Error WriteHardwareDebugRegs(DREGType hwbType) override;

lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,16 @@ static lldb_private::RegisterInfo g_register_infos_pauth[] = {
7878
static lldb_private::RegisterInfo g_register_infos_mte[] = {
7979
DEFINE_EXTENSION_REG(mte_ctrl)};
8080

81+
static lldb_private::RegisterInfo g_register_infos_tls[] = {
82+
DEFINE_EXTENSION_REG(tpidr)};
83+
8184
// Number of register sets provided by this context.
8285
enum {
8386
k_num_gpr_registers = gpr_w28 - gpr_x0 + 1,
8487
k_num_fpr_registers = fpu_fpcr - fpu_v0 + 1,
8588
k_num_sve_registers = sve_ffr - sve_vg + 1,
8689
k_num_mte_register = 1,
90+
k_num_tls_register = 1,
8791
k_num_pauth_register = 2,
8892
k_num_register_sets_default = 2,
8993
k_num_register_sets = 3
@@ -189,6 +193,9 @@ static const lldb_private::RegisterSet g_reg_set_pauth_arm64 = {
189193
static const lldb_private::RegisterSet g_reg_set_mte_arm64 = {
190194
"MTE Control Register", "mte", k_num_mte_register, nullptr};
191195

196+
static const lldb_private::RegisterSet g_reg_set_tls_arm64 = {
197+
"Thread Local Storage Registers", "tls", k_num_tls_register, nullptr};
198+
192199
RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
193200
const lldb_private::ArchSpec &target_arch, lldb_private::Flags opt_regsets)
194201
: lldb_private::RegisterInfoAndSetInterface(target_arch),
@@ -229,6 +236,10 @@ RegisterInfoPOSIX_arm64::RegisterInfoPOSIX_arm64(
229236
if (m_opt_regsets.AllSet(eRegsetMaskMTE))
230237
AddRegSetMTE();
231238

239+
// tpidr is always present, but in future there will be others so this is
240+
// done as a dynamic set.
241+
AddRegSetTLS();
242+
232243
m_register_info_count = m_dynamic_reg_infos.size();
233244
m_register_info_p = m_dynamic_reg_infos.data();
234245
m_register_set_p = m_dynamic_reg_sets.data();
@@ -312,6 +323,21 @@ void RegisterInfoPOSIX_arm64::AddRegSetMTE() {
312323
m_dynamic_reg_sets.back().registers = m_mte_regnum_collection.data();
313324
}
314325

326+
void RegisterInfoPOSIX_arm64::AddRegSetTLS() {
327+
uint32_t tls_regnum = m_dynamic_reg_infos.size();
328+
m_tls_regnum_collection.push_back(tls_regnum);
329+
m_dynamic_reg_infos.push_back(g_register_infos_tls[0]);
330+
m_dynamic_reg_infos[tls_regnum].byte_offset =
331+
m_dynamic_reg_infos[tls_regnum - 1].byte_offset +
332+
m_dynamic_reg_infos[tls_regnum - 1].byte_size;
333+
m_dynamic_reg_infos[tls_regnum].kinds[lldb::eRegisterKindLLDB] = tls_regnum;
334+
335+
m_per_regset_regnum_range[m_register_set_count] =
336+
std::make_pair(tls_regnum, tls_regnum + 1);
337+
m_dynamic_reg_sets.push_back(g_reg_set_tls_arm64);
338+
m_dynamic_reg_sets.back().registers = m_tls_regnum_collection.data();
339+
}
340+
315341
uint32_t RegisterInfoPOSIX_arm64::ConfigureVectorLength(uint32_t sve_vq) {
316342
// sve_vq contains SVE Quad vector length in context of AArch64 SVE.
317343
// SVE register infos if enabled cannot be disabled by selecting sve_vq = 0.
@@ -403,6 +429,10 @@ bool RegisterInfoPOSIX_arm64::IsMTEReg(unsigned reg) const {
403429
return llvm::is_contained(m_mte_regnum_collection, reg);
404430
}
405431

432+
bool RegisterInfoPOSIX_arm64::IsTLSReg(unsigned reg) const {
433+
return llvm::is_contained(m_tls_regnum_collection, reg);
434+
}
435+
406436
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEZ0() const { return sve_z0; }
407437

408438
uint32_t RegisterInfoPOSIX_arm64::GetRegNumSVEFFR() const { return sve_ffr; }
@@ -420,3 +450,7 @@ uint32_t RegisterInfoPOSIX_arm64::GetPAuthOffset() const {
420450
uint32_t RegisterInfoPOSIX_arm64::GetMTEOffset() const {
421451
return m_register_info_p[m_mte_regnum_collection[0]].byte_offset;
422452
}
453+
454+
uint32_t RegisterInfoPOSIX_arm64::GetTLSOffset() const {
455+
return m_register_info_p[m_tls_regnum_collection[0]].byte_offset;
456+
}

lldb/source/Plugins/Process/Utility/RegisterInfoPOSIX_arm64.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class RegisterInfoPOSIX_arm64
2828
eRegsetMaskSVE = 1,
2929
eRegsetMaskPAuth = 2,
3030
eRegsetMaskMTE = 4,
31+
eRegsetMaskTLS = 8,
3132
eRegsetMaskDynamic = ~1,
3233
};
3334

@@ -102,6 +103,8 @@ class RegisterInfoPOSIX_arm64
102103

103104
void AddRegSetMTE();
104105

106+
void AddRegSetTLS();
107+
105108
uint32_t ConfigureVectorLength(uint32_t sve_vq);
106109

107110
bool VectorSizeIsValid(uint32_t vq) {
@@ -121,6 +124,7 @@ class RegisterInfoPOSIX_arm64
121124
bool IsSVERegVG(unsigned reg) const;
122125
bool IsPAuthReg(unsigned reg) const;
123126
bool IsMTEReg(unsigned reg) const;
127+
bool IsTLSReg(unsigned reg) const;
124128

125129
uint32_t GetRegNumSVEZ0() const;
126130
uint32_t GetRegNumSVEFFR() const;
@@ -129,6 +133,7 @@ class RegisterInfoPOSIX_arm64
129133
uint32_t GetRegNumSVEVG() const;
130134
uint32_t GetPAuthOffset() const;
131135
uint32_t GetMTEOffset() const;
136+
uint32_t GetTLSOffset() const;
132137

133138
private:
134139
typedef std::map<uint32_t, std::vector<lldb_private::RegisterInfo>>
@@ -155,6 +160,7 @@ class RegisterInfoPOSIX_arm64
155160

156161
std::vector<uint32_t> pauth_regnum_collection;
157162
std::vector<uint32_t> m_mte_regnum_collection;
163+
std::vector<uint32_t> m_tls_regnum_collection;
158164
};
159165

160166
#endif
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""
2+
Test lldb's ability to read and write the AArch64 TLS register tpidr.
3+
"""
4+
5+
import lldb
6+
from lldbsuite.test.decorators import *
7+
from lldbsuite.test.lldbtest import *
8+
from lldbsuite.test import lldbutil
9+
10+
11+
class AArch64LinuxTLSRegister(TestBase):
12+
NO_DEBUG_INFO_TESTCASE = True
13+
14+
@skipUnlessArch("aarch64")
15+
@skipUnlessPlatform(["linux"])
16+
def test_tls(self):
17+
self.build()
18+
self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
19+
20+
lldbutil.run_break_set_by_file_and_line(
21+
self,
22+
"main.c",
23+
line_number("main.c", "// Set break point at this line."),
24+
num_expected_locations=1,
25+
)
26+
27+
lldbutil.run_break_set_by_file_and_line(
28+
self,
29+
"main.c",
30+
line_number("main.c", "// Set break point 2 at this line."),
31+
num_expected_locations=1,
32+
)
33+
34+
self.runCmd("run", RUN_SUCCEEDED)
35+
36+
if self.process().GetState() == lldb.eStateExited:
37+
self.fail("Test program failed to run.")
38+
39+
self.expect(
40+
"thread list",
41+
STOPPED_DUE_TO_BREAKPOINT,
42+
substrs=["stopped", "stop reason = breakpoint"],
43+
)
44+
45+
# Since we can't predict what the value will be, the program has set
46+
# a target value for us to find.
47+
48+
regs = self.thread().GetSelectedFrame().GetRegisters()
49+
tls_regs = regs.GetFirstValueByName("Thread Local Storage Registers")
50+
self.assertTrue(tls_regs.IsValid(), "No TLS registers found.")
51+
tpidr = tls_regs.GetChildMemberWithName("tpidr")
52+
self.assertTrue(tpidr.IsValid(), "No tpidr register found.")
53+
54+
self.assertEqual(tpidr.GetValueAsUnsigned(), 0x1122334455667788)
55+
56+
# Set our own value for the program to find.
57+
self.expect("register write tpidr 0x{:x}".format(0x8877665544332211))
58+
self.expect("continue")
59+
60+
self.expect(
61+
"thread list",
62+
STOPPED_DUE_TO_BREAKPOINT,
63+
substrs=["stopped", "stop reason = breakpoint"],
64+
)
65+
66+
self.expect("p tpidr_was_set", substrs=["true"])

0 commit comments

Comments
 (0)