1//===-- AArch66.h ---------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/lldb-types.h"
10
11#include "ABIAArch64.h"
12#include "ABIMacOSX_arm64.h"
13#include "ABISysV_arm64.h"
14#include "Utility/ARM64_DWARF_Registers.h"
15#include "Utility/ARM64_ehframe_Registers.h"
16#include "lldb/Core/PluginManager.h"
17#include "lldb/Target/Process.h"
18
19#include <bitset>
20#include <optional>
21
22using namespace lldb;
23using namespace lldb_private;
24
25LLDB_PLUGIN_DEFINE(ABIAArch64)
26
27void ABIAArch64::Initialize() {
28 ABISysV_arm64::Initialize();
29 ABIMacOSX_arm64::Initialize();
30}
31
32void ABIAArch64::Terminate() {
33 ABISysV_arm64::Terminate();
34 ABIMacOSX_arm64::Terminate();
35}
36
37lldb::addr_t ABIAArch64::FixCodeAddress(lldb::addr_t pc) {
38 if (lldb::ProcessSP process_sp = GetProcessSP()) {
39 // b55 is the highest bit outside TBI (if it's enabled), use
40 // it to determine if the high bits are set to 0 or 1.
41 const addr_t pac_sign_extension = 0x0080000000000000ULL;
42 addr_t mask = process_sp->GetCodeAddressMask();
43 // Test if the high memory mask has been overriden separately
44 if (pc & pac_sign_extension &&
45 process_sp->GetHighmemCodeAddressMask() != LLDB_INVALID_ADDRESS_MASK)
46 mask = process_sp->GetHighmemCodeAddressMask();
47
48 if (mask != LLDB_INVALID_ADDRESS_MASK)
49 return FixAddress(pc, mask);
50 }
51 return pc;
52}
53
54lldb::addr_t ABIAArch64::FixDataAddress(lldb::addr_t pc) {
55 if (lldb::ProcessSP process_sp = GetProcessSP()) {
56 // b55 is the highest bit outside TBI (if it's enabled), use
57 // it to determine if the high bits are set to 0 or 1.
58 const addr_t pac_sign_extension = 0x0080000000000000ULL;
59 addr_t mask = process_sp->GetDataAddressMask();
60 // Test if the high memory mask has been overriden separately
61 if (pc & pac_sign_extension &&
62 process_sp->GetHighmemDataAddressMask() != LLDB_INVALID_ADDRESS_MASK)
63 mask = process_sp->GetHighmemDataAddressMask();
64 if (mask != LLDB_INVALID_ADDRESS_MASK)
65 return FixAddress(pc, mask);
66 }
67 return pc;
68}
69
70std::pair<uint32_t, uint32_t>
71ABIAArch64::GetEHAndDWARFNums(llvm::StringRef name) {
72 if (name == "pc")
73 return {arm64_ehframe::pc, arm64_dwarf::pc};
74 if (name == "cpsr")
75 return {arm64_ehframe::cpsr, arm64_dwarf::cpsr};
76 return MCBasedABI::GetEHAndDWARFNums(reg: name);
77}
78
79std::string ABIAArch64::GetMCName(std::string reg) {
80 MapRegisterName(reg, from_prefix: "v", to_prefix: "q");
81 MapRegisterName(reg, from_prefix: "x29", to_prefix: "fp");
82 MapRegisterName(reg, from_prefix: "x30", to_prefix: "lr");
83 return reg;
84}
85
86uint32_t ABIAArch64::GetGenericNum(llvm::StringRef name) {
87 return llvm::StringSwitch<uint32_t>(name)
88 .Case(S: "pc", LLDB_REGNUM_GENERIC_PC)
89 .Cases(S0: "lr", S1: "x30", LLDB_REGNUM_GENERIC_RA)
90 .Cases(S0: "sp", S1: "x31", LLDB_REGNUM_GENERIC_SP)
91 .Cases(S0: "fp", S1: "x29", LLDB_REGNUM_GENERIC_FP)
92 .Case(S: "cpsr", LLDB_REGNUM_GENERIC_FLAGS)
93 .Case(S: "x0", LLDB_REGNUM_GENERIC_ARG1)
94 .Case(S: "x1", LLDB_REGNUM_GENERIC_ARG2)
95 .Case(S: "x2", LLDB_REGNUM_GENERIC_ARG3)
96 .Case(S: "x3", LLDB_REGNUM_GENERIC_ARG4)
97 .Case(S: "x4", LLDB_REGNUM_GENERIC_ARG5)
98 .Case(S: "x5", LLDB_REGNUM_GENERIC_ARG6)
99 .Case(S: "x6", LLDB_REGNUM_GENERIC_ARG7)
100 .Case(S: "x7", LLDB_REGNUM_GENERIC_ARG8)
101 .Default(LLDB_INVALID_REGNUM);
102}
103
104static void addPartialRegisters(
105 std::vector<lldb_private::DynamicRegisterInfo::Register> &regs,
106 llvm::ArrayRef<std::optional<uint32_t>> full_reg_indices,
107 uint32_t full_reg_size, const char *partial_reg_format,
108 uint32_t partial_reg_size, lldb::Encoding encoding, lldb::Format format) {
109 for (auto it : llvm::enumerate(First&: full_reg_indices)) {
110 std::optional<uint32_t> full_reg_index = it.value();
111 if (!full_reg_index || regs[*full_reg_index].byte_size != full_reg_size)
112 return;
113
114 lldb_private::DynamicRegisterInfo::Register partial_reg{
115 .name: lldb_private::ConstString(
116 llvm::formatv(Fmt: partial_reg_format, Vals: it.index()).str()),
117 .alt_name: lldb_private::ConstString(),
118 .set_name: lldb_private::ConstString("supplementary registers"),
119 .byte_size: partial_reg_size,
120 LLDB_INVALID_INDEX32,
121 .encoding: encoding,
122 .format: format,
123 LLDB_INVALID_REGNUM,
124 LLDB_INVALID_REGNUM,
125 LLDB_INVALID_REGNUM,
126 LLDB_INVALID_REGNUM,
127 .value_regs: {*full_reg_index},
128 .invalidate_regs: {}};
129 addSupplementaryRegister(regs, new_reg_info: partial_reg);
130 }
131}
132
133void ABIAArch64::AugmentRegisterInfo(
134 std::vector<lldb_private::DynamicRegisterInfo::Register> &regs) {
135 lldb_private::MCBasedABI::AugmentRegisterInfo(regs);
136
137 lldb_private::ConstString sp_string{"sp"};
138
139 std::array<std::optional<uint32_t>, 32> x_regs;
140 std::array<std::optional<uint32_t>, 32> v_regs;
141 std::array<std::optional<uint32_t>, 32> z_regs;
142 std::optional<uint32_t> z_byte_size;
143
144 for (auto it : llvm::enumerate(First&: regs)) {
145 lldb_private::DynamicRegisterInfo::Register &info = it.value();
146 // GDB sends x31 as "sp". Add the "x31" alt_name for convenience.
147 if (info.name == sp_string && !info.alt_name)
148 info.alt_name.SetCString("x31");
149
150 unsigned int reg_num;
151 auto get_reg = [&info, &reg_num](const char *prefix) {
152 llvm::StringRef reg_name = info.name.GetStringRef();
153 llvm::StringRef alt_name = info.alt_name.GetStringRef();
154 return (reg_name.consume_front(Prefix: prefix) &&
155 llvm::to_integer(S: reg_name, Num&: reg_num, Base: 10) && reg_num < 32) ||
156 (alt_name.consume_front(Prefix: prefix) &&
157 llvm::to_integer(S: alt_name, Num&: reg_num, Base: 10) && reg_num < 32);
158 };
159
160 if (get_reg("x"))
161 x_regs[reg_num] = it.index();
162 else if (get_reg("v"))
163 v_regs[reg_num] = it.index();
164 else if (get_reg("z")) {
165 z_regs[reg_num] = it.index();
166 if (!z_byte_size)
167 z_byte_size = info.byte_size;
168 }
169 // if we have at least one subregister, abort
170 else if (get_reg("w") || get_reg("s") || get_reg("d"))
171 return;
172 }
173
174 // Create aliases for partial registers.
175
176 // Wn for Xn.
177 addPartialRegisters(regs, full_reg_indices: x_regs, full_reg_size: 8, partial_reg_format: "w{0}", partial_reg_size: 4, encoding: lldb::eEncodingUint,
178 format: lldb::eFormatHex);
179
180 auto bool_predicate = [](const auto &reg_num) { return bool(reg_num); };
181 bool saw_v_regs = llvm::any_of(Range&: v_regs, P: bool_predicate);
182 bool saw_z_regs = llvm::any_of(Range&: z_regs, P: bool_predicate);
183
184 // Sn/Dn for Vn.
185 if (saw_v_regs) {
186 addPartialRegisters(regs, full_reg_indices: v_regs, full_reg_size: 16, partial_reg_format: "s{0}", partial_reg_size: 4, encoding: lldb::eEncodingIEEE754,
187 format: lldb::eFormatFloat);
188 addPartialRegisters(regs, full_reg_indices: v_regs, full_reg_size: 16, partial_reg_format: "d{0}", partial_reg_size: 8, encoding: lldb::eEncodingIEEE754,
189 format: lldb::eFormatFloat);
190 } else if (saw_z_regs && z_byte_size) {
191 // When SVE is enabled, some debug stubs will not describe the Neon V
192 // registers because they can be read from the bottom 128 bits of the SVE
193 // registers.
194
195 // The size used here is the one sent by the debug server. This only needs
196 // to be correct right now. Later we will rely on the value of vg instead.
197 addPartialRegisters(regs, full_reg_indices: z_regs, full_reg_size: *z_byte_size, partial_reg_format: "v{0}", partial_reg_size: 16,
198 encoding: lldb::eEncodingVector, format: lldb::eFormatVectorOfUInt8);
199 addPartialRegisters(regs, full_reg_indices: z_regs, full_reg_size: *z_byte_size, partial_reg_format: "s{0}", partial_reg_size: 4,
200 encoding: lldb::eEncodingIEEE754, format: lldb::eFormatFloat);
201 addPartialRegisters(regs, full_reg_indices: z_regs, full_reg_size: *z_byte_size, partial_reg_format: "d{0}", partial_reg_size: 8,
202 encoding: lldb::eEncodingIEEE754, format: lldb::eFormatFloat);
203 }
204}
205
206UnwindPlanSP ABIAArch64::CreateFunctionEntryUnwindPlan() {
207 UnwindPlan::Row row;
208
209 // Our previous Call Frame Address is the stack pointer
210 row.GetCFAValue().SetIsRegisterPlusOffset(LLDB_REGNUM_GENERIC_SP, offset: 0);
211
212 // Our previous PC is in the LR, all other registers are the same.
213 row.SetRegisterLocationToRegister(LLDB_REGNUM_GENERIC_PC,
214 LLDB_REGNUM_GENERIC_RA, can_replace: true);
215
216 auto plan_sp = std::make_shared<UnwindPlan>(args: eRegisterKindGeneric);
217 plan_sp->AppendRow(row: std::move(row));
218 plan_sp->SetSourceName("arm64 at-func-entry default");
219 plan_sp->SetSourcedFromCompiler(eLazyBoolNo);
220 plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
221 plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo);
222 return plan_sp;
223}
224
225UnwindPlanSP ABIAArch64::CreateDefaultUnwindPlan() {
226 UnwindPlan::Row row;
227 const int32_t ptr_size = 8;
228
229 row.GetCFAValue().SetIsRegisterPlusOffset(LLDB_REGNUM_GENERIC_FP,
230 offset: 2 * ptr_size);
231 row.SetUnspecifiedRegistersAreUndefined(true);
232
233 row.SetRegisterLocationToAtCFAPlusOffset(LLDB_REGNUM_GENERIC_FP,
234 offset: ptr_size * -2, can_replace: true);
235 row.SetRegisterLocationToAtCFAPlusOffset(LLDB_REGNUM_GENERIC_PC,
236 offset: ptr_size * -1, can_replace: true);
237
238 auto plan_sp = std::make_shared<UnwindPlan>(args: eRegisterKindGeneric);
239 plan_sp->AppendRow(row: std::move(row));
240 plan_sp->SetSourceName("arm64 default unwind plan");
241 plan_sp->SetSourcedFromCompiler(eLazyBoolNo);
242 plan_sp->SetUnwindPlanValidAtAllInstructions(eLazyBoolNo);
243 plan_sp->SetUnwindPlanForSignalTrap(eLazyBoolNo);
244 return plan_sp;
245}
246

source code of lldb/source/Plugins/ABI/AArch64/ABIAArch64.cpp