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 | |
22 | using namespace lldb; |
23 | using namespace lldb_private; |
24 | |
25 | LLDB_PLUGIN_DEFINE(ABIAArch64) |
26 | |
27 | void ABIAArch64::Initialize() { |
28 | ABISysV_arm64::Initialize(); |
29 | ABIMacOSX_arm64::Initialize(); |
30 | } |
31 | |
32 | void ABIAArch64::Terminate() { |
33 | ABISysV_arm64::Terminate(); |
34 | ABIMacOSX_arm64::Terminate(); |
35 | } |
36 | |
37 | lldb::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 | |
54 | lldb::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 | |
70 | std::pair<uint32_t, uint32_t> |
71 | ABIAArch64::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 | |
79 | std::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 | |
86 | uint32_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 | |
104 | static void addPartialRegisters( |
105 | std::vector<lldb_private::DynamicRegisterInfo::Register> ®s, |
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 | |
133 | void ABIAArch64::AugmentRegisterInfo( |
134 | std::vector<lldb_private::DynamicRegisterInfo::Register> ®s) { |
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, ®_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 ®_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 | |
206 | UnwindPlanSP 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 | |
225 | UnwindPlanSP 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 | |