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
29using namespace lldb;
30using namespace lldb_private;
31
32// Converts a prel31 value to lldb::addr_t with sign extension
33static addr_t Prel31ToAddr(uint32_t prel31) {
34 addr_t res = prel31;
35 if (prel31 & (1 << 30))
36 res |= 0xffffffff80000000ULL;
37 return res;
38}
39
40ArmUnwindInfo::ArmExidxEntry::ArmExidxEntry(uint32_t f, lldb::addr_t a,
41 uint32_t d)
42 : file_address(f), address(a), data(d) {}
43
44bool ArmUnwindInfo::ArmExidxEntry::operator<(const ArmExidxEntry &other) const {
45 return address < other.address;
46}
47
48ArmUnwindInfo::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
71ArmUnwindInfo::~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.
77uint8_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
85uint64_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
99bool 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
353const uint8_t *
354ArmUnwindInfo::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

source code of lldb/source/Symbol/ArmUnwindInfo.cpp