1 | //===-- ArmUnwindInfo.cpp -------------------------------------------------===// |
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 <vector> |
10 | |
11 | #include "Utility/ARM_DWARF_Registers.h" |
12 | #include "lldb/Core/Module.h" |
13 | #include "lldb/Core/Section.h" |
14 | #include "lldb/Symbol/ArmUnwindInfo.h" |
15 | #include "lldb/Symbol/SymbolVendor.h" |
16 | #include "lldb/Symbol/UnwindPlan.h" |
17 | #include "lldb/Utility/Endian.h" |
18 | |
19 | /* |
20 | * Unwind information reader and parser for the ARM exception handling ABI |
21 | * |
22 | * Implemented based on: |
23 | * Exception Handling ABI for the ARM Architecture |
24 | * Document number: ARM IHI 0038A (current through ABI r2.09) |
25 | * Date of Issue: 25th January 2007, reissued 30th November 2012 |
26 | * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf |
27 | */ |
28 | |
29 | using namespace lldb; |
30 | using namespace lldb_private; |
31 | |
32 | // Converts a prel31 value to lldb::addr_t with sign extension |
33 | static addr_t Prel31ToAddr(uint32_t prel31) { |
34 | addr_t res = prel31; |
35 | if (prel31 & (1 << 30)) |
36 | res |= 0xffffffff80000000ULL; |
37 | return res; |
38 | } |
39 | |
40 | ArmUnwindInfo::ArmExidxEntry::ArmExidxEntry(uint32_t f, lldb::addr_t a, |
41 | uint32_t d) |
42 | : file_address(f), address(a), data(d) {} |
43 | |
44 | bool ArmUnwindInfo::ArmExidxEntry::operator<(const ArmExidxEntry &other) const { |
45 | return address < other.address; |
46 | } |
47 | |
48 | ArmUnwindInfo::ArmUnwindInfo(ObjectFile &objfile, SectionSP &arm_exidx, |
49 | SectionSP &arm_extab) |
50 | : m_byte_order(objfile.GetByteOrder()), m_arm_exidx_sp(arm_exidx), |
51 | m_arm_extab_sp(arm_extab) { |
52 | objfile.ReadSectionData(section: arm_exidx.get(), section_data&: m_arm_exidx_data); |
53 | objfile.ReadSectionData(section: arm_extab.get(), section_data&: m_arm_extab_data); |
54 | |
55 | addr_t exidx_base_addr = m_arm_exidx_sp->GetFileAddress(); |
56 | |
57 | offset_t offset = 0; |
58 | while (m_arm_exidx_data.ValidOffset(offset)) { |
59 | lldb::addr_t file_addr = exidx_base_addr + offset; |
60 | lldb::addr_t addr = exidx_base_addr + (addr_t)offset + |
61 | Prel31ToAddr(prel31: m_arm_exidx_data.GetU32(offset_ptr: &offset)); |
62 | uint32_t data = m_arm_exidx_data.GetU32(offset_ptr: &offset); |
63 | m_exidx_entries.emplace_back(args&: file_addr, args&: addr, args&: data); |
64 | } |
65 | |
66 | // Sort the entries in the exidx section. The entries should be sorted inside |
67 | // the section but some old compiler isn't sorted them. |
68 | llvm::sort(C&: m_exidx_entries); |
69 | } |
70 | |
71 | ArmUnwindInfo::~ArmUnwindInfo() = default; |
72 | |
73 | // Read a byte from the unwind instruction stream with the given offset. Custom |
74 | // function is required because have to red in order of significance within |
75 | // their containing word (most significant byte first) and in increasing word |
76 | // address order. |
77 | uint8_t ArmUnwindInfo::GetByteAtOffset(const uint32_t *data, |
78 | uint16_t offset) const { |
79 | uint32_t value = data[offset / 4]; |
80 | if (m_byte_order != endian::InlHostByteOrder()) |
81 | value = llvm::byteswap<uint32_t>(V: value); |
82 | return (value >> ((3 - (offset % 4)) * 8)) & 0xff; |
83 | } |
84 | |
85 | uint64_t ArmUnwindInfo::GetULEB128(const uint32_t *data, uint16_t &offset, |
86 | uint16_t max_offset) const { |
87 | uint64_t result = 0; |
88 | uint8_t shift = 0; |
89 | while (offset < max_offset) { |
90 | uint8_t byte = GetByteAtOffset(data, offset: offset++); |
91 | result |= (uint64_t)(byte & 0x7f) << shift; |
92 | if ((byte & 0x80) == 0) |
93 | break; |
94 | shift += 7; |
95 | } |
96 | return result; |
97 | } |
98 | |
99 | bool ArmUnwindInfo::GetUnwindPlan(Target &target, const Address &addr, |
100 | UnwindPlan &unwind_plan) { |
101 | const uint32_t *data = (const uint32_t *)GetExceptionHandlingTableEntry(addr); |
102 | if (data == nullptr) |
103 | return false; // No unwind information for the function |
104 | |
105 | if (data[0] == 0x1) |
106 | return false; // EXIDX_CANTUNWIND |
107 | |
108 | uint16_t byte_count = 0; |
109 | uint16_t byte_offset = 0; |
110 | if (data[0] & 0x80000000) { |
111 | switch ((data[0] >> 24) & 0x0f) { |
112 | case 0: |
113 | byte_count = 4; |
114 | byte_offset = 1; |
115 | break; |
116 | case 1: |
117 | case 2: |
118 | byte_count = 4 * ((data[0] >> 16) & 0xff) + 4; |
119 | byte_offset = 2; |
120 | break; |
121 | default: |
122 | // Unhandled personality routine index |
123 | return false; |
124 | } |
125 | } else { |
126 | byte_count = 4 * ((data[1] >> 24) & 0xff) + 8; |
127 | byte_offset = 5; |
128 | } |
129 | |
130 | uint8_t vsp_reg = dwarf_sp; |
131 | int32_t vsp = 0; |
132 | std::vector<std::pair<uint32_t, int32_t>> |
133 | register_offsets; // register -> (offset from vsp_reg) |
134 | |
135 | while (byte_offset < byte_count) { |
136 | uint8_t byte1 = GetByteAtOffset(data, offset: byte_offset++); |
137 | if ((byte1 & 0xc0) == 0x00) { |
138 | // 00xxxxxx |
139 | // vsp = vsp + (xxxxxx << 2) + 4. Covers range 0x04-0x100 inclusive |
140 | vsp += ((byte1 & 0x3f) << 2) + 4; |
141 | } else if ((byte1 & 0xc0) == 0x40) { |
142 | // 01xxxxxx |
143 | // vsp = vsp – (xxxxxx << 2) - 4. Covers range 0x04-0x100 inclusive |
144 | vsp -= ((byte1 & 0x3f) << 2) + 4; |
145 | } else if ((byte1 & 0xf0) == 0x80) { |
146 | if (byte_offset >= byte_count) |
147 | return false; |
148 | |
149 | uint8_t byte2 = GetByteAtOffset(data, offset: byte_offset++); |
150 | if (byte1 == 0x80 && byte2 == 0) { |
151 | // 10000000 00000000 |
152 | // Refuse to unwind (for example, out of a cleanup) (see remark a) |
153 | return false; |
154 | } else { |
155 | // 1000iiii iiiiiiii (i not all 0) |
156 | // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4} (see |
157 | // remark b) |
158 | uint16_t regs = ((byte1 & 0x0f) << 8) | byte2; |
159 | for (uint8_t i = 0; i < 12; ++i) { |
160 | if (regs & (1 << i)) { |
161 | register_offsets.emplace_back(args: dwarf_r4 + i, args&: vsp); |
162 | vsp += 4; |
163 | } |
164 | } |
165 | } |
166 | } else if ((byte1 & 0xff) == 0x9d) { |
167 | // 10011101 |
168 | // Reserved as prefix for ARM register to register moves |
169 | return false; |
170 | } else if ((byte1 & 0xff) == 0x9f) { |
171 | // 10011111 |
172 | // Reserved as prefix for Intel Wireless MMX register to register moves |
173 | return false; |
174 | } else if ((byte1 & 0xf0) == 0x90) { |
175 | // 1001nnnn (nnnn != 13,15) |
176 | // Set vsp = r[nnnn] |
177 | vsp_reg = dwarf_r0 + (byte1 & 0x0f); |
178 | } else if ((byte1 & 0xf8) == 0xa0) { |
179 | // 10100nnn |
180 | // Pop r4-r[4+nnn] |
181 | uint8_t n = byte1 & 0x7; |
182 | for (uint8_t i = 0; i <= n; ++i) { |
183 | register_offsets.emplace_back(args: dwarf_r4 + i, args&: vsp); |
184 | vsp += 4; |
185 | } |
186 | } else if ((byte1 & 0xf8) == 0xa8) { |
187 | // 10101nnn |
188 | // Pop r4-r[4+nnn], r14 |
189 | uint8_t n = byte1 & 0x7; |
190 | for (uint8_t i = 0; i <= n; ++i) { |
191 | register_offsets.emplace_back(args: dwarf_r4 + i, args&: vsp); |
192 | vsp += 4; |
193 | } |
194 | |
195 | register_offsets.emplace_back(args: dwarf_lr, args&: vsp); |
196 | vsp += 4; |
197 | } else if ((byte1 & 0xff) == 0xb0) { |
198 | // 10110000 |
199 | // Finish (see remark c) |
200 | break; |
201 | } else if ((byte1 & 0xff) == 0xb1) { |
202 | if (byte_offset >= byte_count) |
203 | return false; |
204 | |
205 | uint8_t byte2 = GetByteAtOffset(data, offset: byte_offset++); |
206 | if ((byte2 & 0xff) == 0x00) { |
207 | // 10110001 00000000 |
208 | // Spare (see remark f) |
209 | return false; |
210 | } else if ((byte2 & 0xf0) == 0x00) { |
211 | // 10110001 0000iiii (i not all 0) |
212 | // Pop integer registers under mask {r3, r2, r1, r0} |
213 | for (uint8_t i = 0; i < 4; ++i) { |
214 | if (byte2 & (1 << i)) { |
215 | register_offsets.emplace_back(args: dwarf_r0 + i, args&: vsp); |
216 | vsp += 4; |
217 | } |
218 | } |
219 | } else { |
220 | // 10110001 xxxxyyyy |
221 | // Spare (xxxx != 0000) |
222 | return false; |
223 | } |
224 | } else if ((byte1 & 0xff) == 0xb2) { |
225 | // 10110010 uleb128 |
226 | // vsp = vsp + 0x204+ (uleb128 << 2) |
227 | uint64_t uleb128 = GetULEB128(data, offset&: byte_offset, max_offset: byte_count); |
228 | vsp += 0x204 + (uleb128 << 2); |
229 | } else if ((byte1 & 0xff) == 0xb3) { |
230 | // 10110011 sssscccc |
231 | // Pop VFP double-precision registers D[ssss]-D[ssss+cccc] saved (as if) |
232 | // by FSTMFDX (see remark d) |
233 | if (byte_offset >= byte_count) |
234 | return false; |
235 | |
236 | uint8_t byte2 = GetByteAtOffset(data, offset: byte_offset++); |
237 | uint8_t s = (byte2 & 0xf0) >> 4; |
238 | uint8_t c = (byte2 & 0x0f) >> 0; |
239 | for (uint8_t i = 0; i <= c; ++i) { |
240 | register_offsets.emplace_back(args: dwarf_d0 + s + i, args&: vsp); |
241 | vsp += 8; |
242 | } |
243 | vsp += 4; |
244 | } else if ((byte1 & 0xfc) == 0xb4) { |
245 | // 101101nn |
246 | // Spare (was Pop FPA) |
247 | return false; |
248 | } else if ((byte1 & 0xf8) == 0xb8) { |
249 | // 10111nnn |
250 | // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by |
251 | // FSTMFDX (see remark d) |
252 | uint8_t n = byte1 & 0x07; |
253 | for (uint8_t i = 0; i <= n; ++i) { |
254 | register_offsets.emplace_back(args: dwarf_d8 + i, args&: vsp); |
255 | vsp += 8; |
256 | } |
257 | vsp += 4; |
258 | } else if ((byte1 & 0xf8) == 0xc0) { |
259 | // 11000nnn (nnn != 6,7) |
260 | // Intel Wireless MMX pop wR[10]-wR[10+nnn] |
261 | |
262 | // 11000110 sssscccc |
263 | // Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc] (see remark e) |
264 | |
265 | // 11000111 00000000 |
266 | // Spare |
267 | |
268 | // 11000111 0000iiii |
269 | // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0} |
270 | |
271 | // 11000111 xxxxyyyy |
272 | // Spare (xxxx != 0000) |
273 | |
274 | return false; |
275 | } else if ((byte1 & 0xff) == 0xc8) { |
276 | // 11001000 sssscccc |
277 | // Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] saved |
278 | // (as if) by FSTMFDD (see remarks d,e) |
279 | if (byte_offset >= byte_count) |
280 | return false; |
281 | |
282 | uint8_t byte2 = GetByteAtOffset(data, offset: byte_offset++); |
283 | uint8_t s = (byte2 & 0xf0) >> 4; |
284 | uint8_t c = (byte2 & 0x0f) >> 0; |
285 | for (uint8_t i = 0; i <= c; ++i) { |
286 | register_offsets.emplace_back(args: dwarf_d16 + s + i, args&: vsp); |
287 | vsp += 8; |
288 | } |
289 | } else if ((byte1 & 0xff) == 0xc9) { |
290 | // 11001001 sssscccc |
291 | // Pop VFP double precision registers D[ssss]-D[ssss+cccc] saved (as if) |
292 | // by FSTMFDD (see remark d) |
293 | if (byte_offset >= byte_count) |
294 | return false; |
295 | |
296 | uint8_t byte2 = GetByteAtOffset(data, offset: byte_offset++); |
297 | uint8_t s = (byte2 & 0xf0) >> 4; |
298 | uint8_t c = (byte2 & 0x0f) >> 0; |
299 | for (uint8_t i = 0; i <= c; ++i) { |
300 | register_offsets.emplace_back(args: dwarf_d0 + s + i, args&: vsp); |
301 | vsp += 8; |
302 | } |
303 | } else if ((byte1 & 0xf8) == 0xc8) { |
304 | // 11001yyy |
305 | // Spare (yyy != 000, 001) |
306 | return false; |
307 | } else if ((byte1 & 0xf8) == 0xd0) { |
308 | // 11010nnn |
309 | // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by |
310 | // FSTMFDD (see remark d) |
311 | uint8_t n = byte1 & 0x07; |
312 | for (uint8_t i = 0; i <= n; ++i) { |
313 | register_offsets.emplace_back(args: dwarf_d8 + i, args&: vsp); |
314 | vsp += 8; |
315 | } |
316 | } else if ((byte1 & 0xc0) == 0xc0) { |
317 | // 11xxxyyy Spare (xxx != 000, 001, 010) |
318 | return false; |
319 | } else { |
320 | return false; |
321 | } |
322 | } |
323 | |
324 | UnwindPlan::RowSP row = std::make_shared<UnwindPlan::Row>(); |
325 | row->SetOffset(0); |
326 | row->GetCFAValue().SetIsRegisterPlusOffset(reg_num: vsp_reg, offset: vsp); |
327 | |
328 | bool have_location_for_pc = false; |
329 | for (const auto &offset : register_offsets) { |
330 | have_location_for_pc |= offset.first == dwarf_pc; |
331 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: offset.first, offset: offset.second - vsp, |
332 | can_replace: true); |
333 | } |
334 | |
335 | if (!have_location_for_pc) { |
336 | UnwindPlan::Row::RegisterLocation lr_location; |
337 | if (row->GetRegisterInfo(reg_num: dwarf_lr, register_location&: lr_location)) |
338 | row->SetRegisterInfo(reg_num: dwarf_pc, register_location: lr_location); |
339 | else |
340 | row->SetRegisterLocationToRegister(reg_num: dwarf_pc, other_reg_num: dwarf_lr, can_replace: false); |
341 | } |
342 | |
343 | unwind_plan.AppendRow(row_sp: row); |
344 | unwind_plan.SetSourceName("ARM.exidx unwind info" ); |
345 | unwind_plan.SetSourcedFromCompiler(eLazyBoolYes); |
346 | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
347 | unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); |
348 | unwind_plan.SetRegisterKind(eRegisterKindDWARF); |
349 | |
350 | return true; |
351 | } |
352 | |
353 | const uint8_t * |
354 | ArmUnwindInfo::GetExceptionHandlingTableEntry(const Address &addr) { |
355 | auto it = llvm::upper_bound(Range&: m_exidx_entries, |
356 | Value: ArmExidxEntry{0, addr.GetFileAddress(), 0}); |
357 | if (it == m_exidx_entries.begin()) |
358 | return nullptr; |
359 | --it; |
360 | |
361 | if (it->data == 0x1) |
362 | return nullptr; // EXIDX_CANTUNWIND |
363 | |
364 | if (it->data & 0x80000000) |
365 | return (const uint8_t *)&it->data; |
366 | |
367 | addr_t data_file_addr = it->file_address + 4 + Prel31ToAddr(prel31: it->data); |
368 | return m_arm_extab_data.GetDataStart() + |
369 | (data_file_addr - m_arm_extab_sp->GetFileAddress()); |
370 | } |
371 | |