1 | //===-- CompactUnwindInfo.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 "lldb/Symbol/CompactUnwindInfo.h" |
10 | #include "lldb/Core/Debugger.h" |
11 | #include "lldb/Core/Module.h" |
12 | #include "lldb/Core/Section.h" |
13 | #include "lldb/Symbol/ObjectFile.h" |
14 | #include "lldb/Symbol/UnwindPlan.h" |
15 | #include "lldb/Target/Process.h" |
16 | #include "lldb/Target/Target.h" |
17 | #include "lldb/Utility/ArchSpec.h" |
18 | #include "lldb/Utility/DataBufferHeap.h" |
19 | #include "lldb/Utility/LLDBLog.h" |
20 | #include "lldb/Utility/Log.h" |
21 | #include "lldb/Utility/StreamString.h" |
22 | |
23 | #include "llvm/Support/MathExtras.h" |
24 | |
25 | #include <algorithm> |
26 | #include <memory> |
27 | |
28 | using namespace lldb; |
29 | using namespace lldb_private; |
30 | |
31 | namespace lldb_private { |
32 | |
33 | // Constants from <mach-o/compact_unwind_encoding.h> |
34 | |
35 | FLAGS_ANONYMOUS_ENUM(){ |
36 | UNWIND_IS_NOT_FUNCTION_START = 0x80000000, UNWIND_HAS_LSDA = 0x40000000, |
37 | UNWIND_PERSONALITY_MASK = 0x30000000, |
38 | }; |
39 | |
40 | FLAGS_ANONYMOUS_ENUM(){ |
41 | UNWIND_X86_MODE_MASK = 0x0F000000, |
42 | UNWIND_X86_MODE_EBP_FRAME = 0x01000000, |
43 | UNWIND_X86_MODE_STACK_IMMD = 0x02000000, |
44 | UNWIND_X86_MODE_STACK_IND = 0x03000000, |
45 | UNWIND_X86_MODE_DWARF = 0x04000000, |
46 | |
47 | UNWIND_X86_EBP_FRAME_REGISTERS = 0x00007FFF, |
48 | UNWIND_X86_EBP_FRAME_OFFSET = 0x00FF0000, |
49 | |
50 | UNWIND_X86_FRAMELESS_STACK_SIZE = 0x00FF0000, |
51 | UNWIND_X86_FRAMELESS_STACK_ADJUST = 0x0000E000, |
52 | UNWIND_X86_FRAMELESS_STACK_REG_COUNT = 0x00001C00, |
53 | UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF, |
54 | |
55 | UNWIND_X86_DWARF_SECTION_OFFSET = 0x00FFFFFF, |
56 | }; |
57 | |
58 | enum { |
59 | UNWIND_X86_REG_NONE = 0, |
60 | UNWIND_X86_REG_EBX = 1, |
61 | UNWIND_X86_REG_ECX = 2, |
62 | UNWIND_X86_REG_EDX = 3, |
63 | UNWIND_X86_REG_EDI = 4, |
64 | UNWIND_X86_REG_ESI = 5, |
65 | UNWIND_X86_REG_EBP = 6, |
66 | }; |
67 | |
68 | FLAGS_ANONYMOUS_ENUM(){ |
69 | UNWIND_X86_64_MODE_MASK = 0x0F000000, |
70 | UNWIND_X86_64_MODE_RBP_FRAME = 0x01000000, |
71 | UNWIND_X86_64_MODE_STACK_IMMD = 0x02000000, |
72 | UNWIND_X86_64_MODE_STACK_IND = 0x03000000, |
73 | UNWIND_X86_64_MODE_DWARF = 0x04000000, |
74 | |
75 | UNWIND_X86_64_RBP_FRAME_REGISTERS = 0x00007FFF, |
76 | UNWIND_X86_64_RBP_FRAME_OFFSET = 0x00FF0000, |
77 | |
78 | UNWIND_X86_64_FRAMELESS_STACK_SIZE = 0x00FF0000, |
79 | UNWIND_X86_64_FRAMELESS_STACK_ADJUST = 0x0000E000, |
80 | UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT = 0x00001C00, |
81 | UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF, |
82 | |
83 | UNWIND_X86_64_DWARF_SECTION_OFFSET = 0x00FFFFFF, |
84 | }; |
85 | |
86 | enum { |
87 | UNWIND_X86_64_REG_NONE = 0, |
88 | UNWIND_X86_64_REG_RBX = 1, |
89 | UNWIND_X86_64_REG_R12 = 2, |
90 | UNWIND_X86_64_REG_R13 = 3, |
91 | UNWIND_X86_64_REG_R14 = 4, |
92 | UNWIND_X86_64_REG_R15 = 5, |
93 | UNWIND_X86_64_REG_RBP = 6, |
94 | }; |
95 | |
96 | FLAGS_ANONYMOUS_ENUM(){ |
97 | UNWIND_ARM64_MODE_MASK = 0x0F000000, |
98 | UNWIND_ARM64_MODE_FRAMELESS = 0x02000000, |
99 | UNWIND_ARM64_MODE_DWARF = 0x03000000, |
100 | UNWIND_ARM64_MODE_FRAME = 0x04000000, |
101 | |
102 | UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001, |
103 | UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002, |
104 | UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004, |
105 | UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008, |
106 | UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010, |
107 | UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100, |
108 | UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200, |
109 | UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400, |
110 | UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800, |
111 | |
112 | UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000, |
113 | UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF, |
114 | }; |
115 | |
116 | FLAGS_ANONYMOUS_ENUM(){ |
117 | UNWIND_ARM_MODE_MASK = 0x0F000000, |
118 | UNWIND_ARM_MODE_FRAME = 0x01000000, |
119 | UNWIND_ARM_MODE_FRAME_D = 0x02000000, |
120 | UNWIND_ARM_MODE_DWARF = 0x04000000, |
121 | |
122 | UNWIND_ARM_FRAME_STACK_ADJUST_MASK = 0x00C00000, |
123 | |
124 | UNWIND_ARM_FRAME_FIRST_PUSH_R4 = 0x00000001, |
125 | UNWIND_ARM_FRAME_FIRST_PUSH_R5 = 0x00000002, |
126 | UNWIND_ARM_FRAME_FIRST_PUSH_R6 = 0x00000004, |
127 | |
128 | UNWIND_ARM_FRAME_SECOND_PUSH_R8 = 0x00000008, |
129 | UNWIND_ARM_FRAME_SECOND_PUSH_R9 = 0x00000010, |
130 | UNWIND_ARM_FRAME_SECOND_PUSH_R10 = 0x00000020, |
131 | UNWIND_ARM_FRAME_SECOND_PUSH_R11 = 0x00000040, |
132 | UNWIND_ARM_FRAME_SECOND_PUSH_R12 = 0x00000080, |
133 | |
134 | UNWIND_ARM_FRAME_D_REG_COUNT_MASK = 0x00000700, |
135 | |
136 | UNWIND_ARM_DWARF_SECTION_OFFSET = 0x00FFFFFF, |
137 | }; |
138 | } |
139 | |
140 | #ifndef UNWIND_SECOND_LEVEL_REGULAR |
141 | #define UNWIND_SECOND_LEVEL_REGULAR 2 |
142 | #endif |
143 | |
144 | #ifndef UNWIND_SECOND_LEVEL_COMPRESSED |
145 | #define UNWIND_SECOND_LEVEL_COMPRESSED 3 |
146 | #endif |
147 | |
148 | #ifndef UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET |
149 | #define UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) (entry & 0x00FFFFFF) |
150 | #endif |
151 | |
152 | #ifndef UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX |
153 | #define UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry) \ |
154 | ((entry >> 24) & 0xFF) |
155 | #endif |
156 | |
157 | #define (value, mask) \ |
158 | ((value >> llvm::countr_zero(static_cast<uint32_t>(mask))) & \ |
159 | (((1 << llvm::popcount(static_cast<uint32_t>(mask)))) - 1)) |
160 | |
161 | // constructor |
162 | |
163 | CompactUnwindInfo::CompactUnwindInfo(ObjectFile &objfile, SectionSP §ion_sp) |
164 | : m_objfile(objfile), m_section_sp(section_sp), |
165 | m_section_contents_if_encrypted(), m_mutex(), m_indexes(), |
166 | m_indexes_computed(eLazyBoolCalculate), m_unwindinfo_data(), |
167 | m_unwindinfo_data_computed(false), m_unwind_header() {} |
168 | |
169 | // destructor |
170 | |
171 | CompactUnwindInfo::~CompactUnwindInfo() = default; |
172 | |
173 | bool CompactUnwindInfo::GetUnwindPlan(Target &target, Address addr, |
174 | UnwindPlan &unwind_plan) { |
175 | if (!IsValid(process_sp: target.GetProcessSP())) { |
176 | return false; |
177 | } |
178 | FunctionInfo function_info; |
179 | if (GetCompactUnwindInfoForFunction(target, address: addr, unwind_info&: function_info)) { |
180 | // shortcut return for functions that have no compact unwind |
181 | if (function_info.encoding == 0) |
182 | return false; |
183 | |
184 | if (ArchSpec arch = m_objfile.GetArchitecture()) { |
185 | |
186 | Log *log = GetLog(mask: LLDBLog::Unwind); |
187 | if (log && log->GetVerbose()) { |
188 | StreamString strm; |
189 | addr.Dump( |
190 | s: &strm, exe_scope: nullptr, |
191 | style: Address::DumpStyle::DumpStyleResolvedDescriptionNoFunctionArguments, |
192 | fallback_style: Address::DumpStyle::DumpStyleFileAddress, |
193 | addr_byte_size: arch.GetAddressByteSize()); |
194 | LLDB_LOGF(log, "Got compact unwind encoding 0x%x for function %s" , |
195 | function_info.encoding, strm.GetData()); |
196 | } |
197 | |
198 | if (function_info.valid_range_offset_start != 0 && |
199 | function_info.valid_range_offset_end != 0) { |
200 | SectionList *sl = m_objfile.GetSectionList(); |
201 | if (sl) { |
202 | addr_t func_range_start_file_addr = |
203 | function_info.valid_range_offset_start + |
204 | m_objfile.GetBaseAddress().GetFileAddress(); |
205 | AddressRange func_range(func_range_start_file_addr, |
206 | function_info.valid_range_offset_end - |
207 | function_info.valid_range_offset_start, |
208 | sl); |
209 | unwind_plan.SetPlanValidAddressRange(func_range); |
210 | } |
211 | } |
212 | |
213 | if (arch.GetTriple().getArch() == llvm::Triple::x86_64) { |
214 | return CreateUnwindPlan_x86_64(target, function_info, unwind_plan, |
215 | pc_or_function_start: addr); |
216 | } |
217 | if (arch.GetTriple().getArch() == llvm::Triple::aarch64 || |
218 | arch.GetTriple().getArch() == llvm::Triple::aarch64_32) { |
219 | return CreateUnwindPlan_arm64(target, function_info, unwind_plan, pc_or_function_start: addr); |
220 | } |
221 | if (arch.GetTriple().getArch() == llvm::Triple::x86) { |
222 | return CreateUnwindPlan_i386(target, function_info, unwind_plan, pc_or_function_start: addr); |
223 | } |
224 | if (arch.GetTriple().getArch() == llvm::Triple::arm || |
225 | arch.GetTriple().getArch() == llvm::Triple::thumb) { |
226 | return CreateUnwindPlan_armv7(target, function_info, unwind_plan, pc_or_function_start: addr); |
227 | } |
228 | } |
229 | } |
230 | return false; |
231 | } |
232 | |
233 | bool CompactUnwindInfo::IsValid(const ProcessSP &process_sp) { |
234 | if (m_section_sp.get() == nullptr) |
235 | return false; |
236 | |
237 | if (m_indexes_computed == eLazyBoolYes && m_unwindinfo_data_computed) |
238 | return true; |
239 | |
240 | ScanIndex(process_sp); |
241 | |
242 | return m_indexes_computed == eLazyBoolYes && m_unwindinfo_data_computed; |
243 | } |
244 | |
245 | void CompactUnwindInfo::ScanIndex(const ProcessSP &process_sp) { |
246 | std::lock_guard<std::mutex> guard(m_mutex); |
247 | if (m_indexes_computed == eLazyBoolYes && m_unwindinfo_data_computed) |
248 | return; |
249 | |
250 | // We can't read the index for some reason. |
251 | if (m_indexes_computed == eLazyBoolNo) { |
252 | return; |
253 | } |
254 | |
255 | Log *log = GetLog(mask: LLDBLog::Unwind); |
256 | if (log) |
257 | m_objfile.GetModule()->LogMessage( |
258 | log, format: "Reading compact unwind first-level indexes" ); |
259 | |
260 | if (!m_unwindinfo_data_computed) { |
261 | if (m_section_sp->IsEncrypted()) { |
262 | // Can't get section contents of a protected/encrypted section until we |
263 | // have a live process and can read them out of memory. |
264 | if (process_sp.get() == nullptr) |
265 | return; |
266 | m_section_contents_if_encrypted = |
267 | std::make_shared<DataBufferHeap>(args: m_section_sp->GetByteSize(), args: 0); |
268 | Status error; |
269 | if (process_sp->ReadMemory( |
270 | vm_addr: m_section_sp->GetLoadBaseAddress(target: &process_sp->GetTarget()), |
271 | buf: m_section_contents_if_encrypted->GetBytes(), |
272 | size: m_section_sp->GetByteSize(), |
273 | error) == m_section_sp->GetByteSize() && |
274 | error.Success()) { |
275 | m_unwindinfo_data.SetAddressByteSize( |
276 | process_sp->GetTarget().GetArchitecture().GetAddressByteSize()); |
277 | m_unwindinfo_data.SetByteOrder( |
278 | process_sp->GetTarget().GetArchitecture().GetByteOrder()); |
279 | m_unwindinfo_data.SetData(data_sp: m_section_contents_if_encrypted, offset: 0); |
280 | } |
281 | } else { |
282 | m_objfile.ReadSectionData(section: m_section_sp.get(), section_data&: m_unwindinfo_data); |
283 | } |
284 | if (m_unwindinfo_data.GetByteSize() != m_section_sp->GetByteSize()) |
285 | return; |
286 | m_unwindinfo_data_computed = true; |
287 | } |
288 | |
289 | if (m_unwindinfo_data.GetByteSize() > 0) { |
290 | offset_t offset = 0; |
291 | |
292 | // struct unwind_info_section_header |
293 | // { |
294 | // uint32_t version; // UNWIND_SECTION_VERSION |
295 | // uint32_t commonEncodingsArraySectionOffset; |
296 | // uint32_t commonEncodingsArrayCount; |
297 | // uint32_t personalityArraySectionOffset; |
298 | // uint32_t personalityArrayCount; |
299 | // uint32_t indexSectionOffset; |
300 | // uint32_t indexCount; |
301 | |
302 | m_unwind_header.version = m_unwindinfo_data.GetU32(offset_ptr: &offset); |
303 | m_unwind_header.common_encodings_array_offset = |
304 | m_unwindinfo_data.GetU32(offset_ptr: &offset); |
305 | m_unwind_header.common_encodings_array_count = |
306 | m_unwindinfo_data.GetU32(offset_ptr: &offset); |
307 | m_unwind_header.personality_array_offset = |
308 | m_unwindinfo_data.GetU32(offset_ptr: &offset); |
309 | m_unwind_header.personality_array_count = m_unwindinfo_data.GetU32(offset_ptr: &offset); |
310 | uint32_t indexSectionOffset = m_unwindinfo_data.GetU32(offset_ptr: &offset); |
311 | |
312 | uint32_t indexCount = m_unwindinfo_data.GetU32(offset_ptr: &offset); |
313 | |
314 | if (m_unwind_header.common_encodings_array_offset > |
315 | m_unwindinfo_data.GetByteSize() || |
316 | m_unwind_header.personality_array_offset > |
317 | m_unwindinfo_data.GetByteSize() || |
318 | indexSectionOffset > m_unwindinfo_data.GetByteSize() || |
319 | offset > m_unwindinfo_data.GetByteSize()) { |
320 | Debugger::ReportError( |
321 | message: "Invalid offset encountered in compact unwind info, skipping" ); |
322 | // don't trust anything from this compact_unwind section if it looks |
323 | // blatantly invalid data in the header. |
324 | m_indexes_computed = eLazyBoolNo; |
325 | return; |
326 | } |
327 | |
328 | // Parse the basic information from the indexes We wait to scan the second |
329 | // level page info until it's needed |
330 | |
331 | // struct unwind_info_section_header_index_entry { |
332 | // uint32_t functionOffset; |
333 | // uint32_t secondLevelPagesSectionOffset; |
334 | // uint32_t lsdaIndexArraySectionOffset; |
335 | // }; |
336 | |
337 | bool clear_address_zeroth_bit = false; |
338 | if (ArchSpec arch = m_objfile.GetArchitecture()) { |
339 | if (arch.GetTriple().getArch() == llvm::Triple::arm || |
340 | arch.GetTriple().getArch() == llvm::Triple::thumb) |
341 | clear_address_zeroth_bit = true; |
342 | } |
343 | |
344 | offset = indexSectionOffset; |
345 | for (uint32_t idx = 0; idx < indexCount; idx++) { |
346 | uint32_t function_offset = |
347 | m_unwindinfo_data.GetU32(offset_ptr: &offset); // functionOffset |
348 | uint32_t second_level_offset = |
349 | m_unwindinfo_data.GetU32(offset_ptr: &offset); // secondLevelPagesSectionOffset |
350 | uint32_t lsda_offset = |
351 | m_unwindinfo_data.GetU32(offset_ptr: &offset); // lsdaIndexArraySectionOffset |
352 | |
353 | if (second_level_offset > m_section_sp->GetByteSize() || |
354 | lsda_offset > m_section_sp->GetByteSize()) { |
355 | m_indexes_computed = eLazyBoolNo; |
356 | } |
357 | |
358 | if (clear_address_zeroth_bit) |
359 | function_offset &= ~1ull; |
360 | |
361 | UnwindIndex this_index; |
362 | this_index.function_offset = function_offset; |
363 | this_index.second_level = second_level_offset; |
364 | this_index.lsda_array_start = lsda_offset; |
365 | |
366 | if (m_indexes.size() > 0) { |
367 | m_indexes[m_indexes.size() - 1].lsda_array_end = lsda_offset; |
368 | } |
369 | |
370 | if (second_level_offset == 0) { |
371 | this_index.sentinal_entry = true; |
372 | } |
373 | |
374 | m_indexes.push_back(x: this_index); |
375 | } |
376 | m_indexes_computed = eLazyBoolYes; |
377 | } else { |
378 | m_indexes_computed = eLazyBoolNo; |
379 | } |
380 | } |
381 | |
382 | uint32_t CompactUnwindInfo::GetLSDAForFunctionOffset(uint32_t lsda_offset, |
383 | uint32_t lsda_count, |
384 | uint32_t function_offset) { |
385 | // struct unwind_info_section_header_lsda_index_entry { |
386 | // uint32_t functionOffset; |
387 | // uint32_t lsdaOffset; |
388 | // }; |
389 | |
390 | offset_t first_entry = lsda_offset; |
391 | uint32_t low = 0; |
392 | uint32_t high = lsda_count; |
393 | while (low < high) { |
394 | uint32_t mid = (low + high) / 2; |
395 | offset_t offset = first_entry + (mid * 8); |
396 | uint32_t mid_func_offset = |
397 | m_unwindinfo_data.GetU32(offset_ptr: &offset); // functionOffset |
398 | uint32_t mid_lsda_offset = m_unwindinfo_data.GetU32(offset_ptr: &offset); // lsdaOffset |
399 | if (mid_func_offset == function_offset) { |
400 | return mid_lsda_offset; |
401 | } |
402 | if (mid_func_offset < function_offset) { |
403 | low = mid + 1; |
404 | } else { |
405 | high = mid; |
406 | } |
407 | } |
408 | return 0; |
409 | } |
410 | |
411 | lldb::offset_t CompactUnwindInfo::BinarySearchRegularSecondPage( |
412 | uint32_t entry_page_offset, uint32_t entry_count, uint32_t function_offset, |
413 | uint32_t *entry_func_start_offset, uint32_t *entry_func_end_offset) { |
414 | // typedef uint32_t compact_unwind_encoding_t; |
415 | // struct unwind_info_regular_second_level_entry { |
416 | // uint32_t functionOffset; |
417 | // compact_unwind_encoding_t encoding; |
418 | |
419 | offset_t first_entry = entry_page_offset; |
420 | |
421 | uint32_t low = 0; |
422 | uint32_t high = entry_count; |
423 | uint32_t last = high - 1; |
424 | while (low < high) { |
425 | uint32_t mid = (low + high) / 2; |
426 | offset_t offset = first_entry + (mid * 8); |
427 | uint32_t mid_func_offset = |
428 | m_unwindinfo_data.GetU32(offset_ptr: &offset); // functionOffset |
429 | uint32_t next_func_offset = 0; |
430 | if (mid < last) { |
431 | offset = first_entry + ((mid + 1) * 8); |
432 | next_func_offset = m_unwindinfo_data.GetU32(offset_ptr: &offset); // functionOffset |
433 | } |
434 | if (mid_func_offset <= function_offset) { |
435 | if (mid == last || (next_func_offset > function_offset)) { |
436 | if (entry_func_start_offset) |
437 | *entry_func_start_offset = mid_func_offset; |
438 | if (mid != last && entry_func_end_offset) |
439 | *entry_func_end_offset = next_func_offset; |
440 | return first_entry + (mid * 8); |
441 | } else { |
442 | low = mid + 1; |
443 | } |
444 | } else { |
445 | high = mid; |
446 | } |
447 | } |
448 | return LLDB_INVALID_OFFSET; |
449 | } |
450 | |
451 | uint32_t CompactUnwindInfo::BinarySearchCompressedSecondPage( |
452 | uint32_t entry_page_offset, uint32_t entry_count, |
453 | uint32_t function_offset_to_find, uint32_t function_offset_base, |
454 | uint32_t *entry_func_start_offset, uint32_t *entry_func_end_offset) { |
455 | offset_t first_entry = entry_page_offset; |
456 | |
457 | uint32_t low = 0; |
458 | uint32_t high = entry_count; |
459 | uint32_t last = high - 1; |
460 | while (low < high) { |
461 | uint32_t mid = (low + high) / 2; |
462 | offset_t offset = first_entry + (mid * 4); |
463 | uint32_t entry = m_unwindinfo_data.GetU32(offset_ptr: &offset); // entry |
464 | uint32_t mid_func_offset = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry); |
465 | mid_func_offset += function_offset_base; |
466 | uint32_t next_func_offset = 0; |
467 | if (mid < last) { |
468 | offset = first_entry + ((mid + 1) * 4); |
469 | uint32_t next_entry = m_unwindinfo_data.GetU32(offset_ptr: &offset); // entry |
470 | next_func_offset = UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(next_entry); |
471 | next_func_offset += function_offset_base; |
472 | } |
473 | if (mid_func_offset <= function_offset_to_find) { |
474 | if (mid == last || (next_func_offset > function_offset_to_find)) { |
475 | if (entry_func_start_offset) |
476 | *entry_func_start_offset = mid_func_offset; |
477 | if (mid != last && entry_func_end_offset) |
478 | *entry_func_end_offset = next_func_offset; |
479 | return UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry); |
480 | } else { |
481 | low = mid + 1; |
482 | } |
483 | } else { |
484 | high = mid; |
485 | } |
486 | } |
487 | |
488 | return UINT32_MAX; |
489 | } |
490 | |
491 | bool CompactUnwindInfo::GetCompactUnwindInfoForFunction( |
492 | Target &target, Address address, FunctionInfo &unwind_info) { |
493 | unwind_info.encoding = 0; |
494 | unwind_info.lsda_address.Clear(); |
495 | unwind_info.personality_ptr_address.Clear(); |
496 | |
497 | if (!IsValid(process_sp: target.GetProcessSP())) |
498 | return false; |
499 | |
500 | addr_t text_section_file_address = LLDB_INVALID_ADDRESS; |
501 | SectionList *sl = m_objfile.GetSectionList(); |
502 | if (sl) { |
503 | SectionSP text_sect = sl->FindSectionByType(sect_type: eSectionTypeCode, check_children: true); |
504 | if (text_sect.get()) { |
505 | text_section_file_address = text_sect->GetFileAddress(); |
506 | } |
507 | } |
508 | if (text_section_file_address == LLDB_INVALID_ADDRESS) |
509 | return false; |
510 | |
511 | addr_t function_offset = |
512 | address.GetFileAddress() - m_objfile.GetBaseAddress().GetFileAddress(); |
513 | |
514 | UnwindIndex key; |
515 | key.function_offset = function_offset; |
516 | |
517 | std::vector<UnwindIndex>::const_iterator it; |
518 | it = llvm::lower_bound(Range&: m_indexes, Value&: key); |
519 | if (it == m_indexes.end()) { |
520 | return false; |
521 | } |
522 | |
523 | if (it->function_offset != key.function_offset) { |
524 | if (it != m_indexes.begin()) |
525 | --it; |
526 | } |
527 | |
528 | if (it->sentinal_entry) { |
529 | return false; |
530 | } |
531 | |
532 | auto next_it = it + 1; |
533 | if (next_it != m_indexes.end()) { |
534 | // initialize the function offset end range to be the start of the next |
535 | // index offset. If we find an entry which is at the end of the index |
536 | // table, this will establish the range end. |
537 | unwind_info.valid_range_offset_end = next_it->function_offset; |
538 | } |
539 | |
540 | offset_t second_page_offset = it->second_level; |
541 | offset_t lsda_array_start = it->lsda_array_start; |
542 | offset_t lsda_array_count = (it->lsda_array_end - it->lsda_array_start) / 8; |
543 | |
544 | offset_t offset = second_page_offset; |
545 | uint32_t kind = m_unwindinfo_data.GetU32( |
546 | offset_ptr: &offset); // UNWIND_SECOND_LEVEL_REGULAR or UNWIND_SECOND_LEVEL_COMPRESSED |
547 | |
548 | if (kind == UNWIND_SECOND_LEVEL_REGULAR) { |
549 | // struct unwind_info_regular_second_level_page_header { |
550 | // uint32_t kind; // UNWIND_SECOND_LEVEL_REGULAR |
551 | // uint16_t entryPageOffset; |
552 | // uint16_t entryCount; |
553 | |
554 | // typedef uint32_t compact_unwind_encoding_t; |
555 | // struct unwind_info_regular_second_level_entry { |
556 | // uint32_t functionOffset; |
557 | // compact_unwind_encoding_t encoding; |
558 | |
559 | uint16_t entry_page_offset = |
560 | m_unwindinfo_data.GetU16(offset_ptr: &offset); // entryPageOffset |
561 | uint16_t entry_count = m_unwindinfo_data.GetU16(offset_ptr: &offset); // entryCount |
562 | |
563 | offset_t entry_offset = BinarySearchRegularSecondPage( |
564 | entry_page_offset: second_page_offset + entry_page_offset, entry_count, function_offset, |
565 | entry_func_start_offset: &unwind_info.valid_range_offset_start, |
566 | entry_func_end_offset: &unwind_info.valid_range_offset_end); |
567 | if (entry_offset == LLDB_INVALID_OFFSET) { |
568 | return false; |
569 | } |
570 | entry_offset += 4; // skip over functionOffset |
571 | unwind_info.encoding = m_unwindinfo_data.GetU32(offset_ptr: &entry_offset); // encoding |
572 | if (unwind_info.encoding & UNWIND_HAS_LSDA) { |
573 | SectionList *sl = m_objfile.GetSectionList(); |
574 | if (sl) { |
575 | uint32_t lsda_offset = GetLSDAForFunctionOffset( |
576 | lsda_offset: lsda_array_start, lsda_count: lsda_array_count, function_offset); |
577 | addr_t objfile_base_address = |
578 | m_objfile.GetBaseAddress().GetFileAddress(); |
579 | unwind_info.lsda_address.ResolveAddressUsingFileSections( |
580 | addr: objfile_base_address + lsda_offset, sections: sl); |
581 | } |
582 | } |
583 | if (unwind_info.encoding & UNWIND_PERSONALITY_MASK) { |
584 | uint32_t personality_index = |
585 | EXTRACT_BITS(unwind_info.encoding, UNWIND_PERSONALITY_MASK); |
586 | |
587 | if (personality_index > 0) { |
588 | personality_index--; |
589 | if (personality_index < m_unwind_header.personality_array_count) { |
590 | offset_t offset = m_unwind_header.personality_array_offset; |
591 | offset += 4 * personality_index; |
592 | SectionList *sl = m_objfile.GetSectionList(); |
593 | if (sl) { |
594 | uint32_t personality_offset = m_unwindinfo_data.GetU32(offset_ptr: &offset); |
595 | addr_t objfile_base_address = |
596 | m_objfile.GetBaseAddress().GetFileAddress(); |
597 | unwind_info.personality_ptr_address.ResolveAddressUsingFileSections( |
598 | addr: objfile_base_address + personality_offset, sections: sl); |
599 | } |
600 | } |
601 | } |
602 | } |
603 | return true; |
604 | } else if (kind == UNWIND_SECOND_LEVEL_COMPRESSED) { |
605 | // struct unwind_info_compressed_second_level_page_header { |
606 | // uint32_t kind; // UNWIND_SECOND_LEVEL_COMPRESSED |
607 | // uint16_t entryPageOffset; // offset from this 2nd lvl page |
608 | // idx to array of entries |
609 | // // (an entry has a function |
610 | // offset and index into the |
611 | // encodings) |
612 | // // NB function offset from the |
613 | // entry in the compressed page |
614 | // // must be added to the index's |
615 | // functionOffset value. |
616 | // uint16_t entryCount; |
617 | // uint16_t encodingsPageOffset; // offset from this 2nd lvl page |
618 | // idx to array of encodings |
619 | // uint16_t encodingsCount; |
620 | |
621 | uint16_t entry_page_offset = |
622 | m_unwindinfo_data.GetU16(offset_ptr: &offset); // entryPageOffset |
623 | uint16_t entry_count = m_unwindinfo_data.GetU16(offset_ptr: &offset); // entryCount |
624 | uint16_t encodings_page_offset = |
625 | m_unwindinfo_data.GetU16(offset_ptr: &offset); // encodingsPageOffset |
626 | uint16_t encodings_count = |
627 | m_unwindinfo_data.GetU16(offset_ptr: &offset); // encodingsCount |
628 | |
629 | uint32_t encoding_index = BinarySearchCompressedSecondPage( |
630 | entry_page_offset: second_page_offset + entry_page_offset, entry_count, function_offset_to_find: function_offset, |
631 | function_offset_base: it->function_offset, entry_func_start_offset: &unwind_info.valid_range_offset_start, |
632 | entry_func_end_offset: &unwind_info.valid_range_offset_end); |
633 | if (encoding_index == UINT32_MAX || |
634 | encoding_index >= |
635 | encodings_count + m_unwind_header.common_encodings_array_count) { |
636 | return false; |
637 | } |
638 | uint32_t encoding = 0; |
639 | if (encoding_index < m_unwind_header.common_encodings_array_count) { |
640 | offset = m_unwind_header.common_encodings_array_offset + |
641 | (encoding_index * sizeof(uint32_t)); |
642 | encoding = m_unwindinfo_data.GetU32( |
643 | offset_ptr: &offset); // encoding entry from the commonEncodingsArray |
644 | } else { |
645 | uint32_t page_specific_entry_index = |
646 | encoding_index - m_unwind_header.common_encodings_array_count; |
647 | offset = second_page_offset + encodings_page_offset + |
648 | (page_specific_entry_index * sizeof(uint32_t)); |
649 | encoding = m_unwindinfo_data.GetU32( |
650 | offset_ptr: &offset); // encoding entry from the page-specific encoding array |
651 | } |
652 | if (encoding == 0) |
653 | return false; |
654 | |
655 | unwind_info.encoding = encoding; |
656 | if (unwind_info.encoding & UNWIND_HAS_LSDA) { |
657 | SectionList *sl = m_objfile.GetSectionList(); |
658 | if (sl) { |
659 | uint32_t lsda_offset = GetLSDAForFunctionOffset( |
660 | lsda_offset: lsda_array_start, lsda_count: lsda_array_count, function_offset); |
661 | addr_t objfile_base_address = |
662 | m_objfile.GetBaseAddress().GetFileAddress(); |
663 | unwind_info.lsda_address.ResolveAddressUsingFileSections( |
664 | addr: objfile_base_address + lsda_offset, sections: sl); |
665 | } |
666 | } |
667 | if (unwind_info.encoding & UNWIND_PERSONALITY_MASK) { |
668 | uint32_t personality_index = |
669 | EXTRACT_BITS(unwind_info.encoding, UNWIND_PERSONALITY_MASK); |
670 | |
671 | if (personality_index > 0) { |
672 | personality_index--; |
673 | if (personality_index < m_unwind_header.personality_array_count) { |
674 | offset_t offset = m_unwind_header.personality_array_offset; |
675 | offset += 4 * personality_index; |
676 | SectionList *sl = m_objfile.GetSectionList(); |
677 | if (sl) { |
678 | uint32_t personality_offset = m_unwindinfo_data.GetU32(offset_ptr: &offset); |
679 | addr_t objfile_base_address = |
680 | m_objfile.GetBaseAddress().GetFileAddress(); |
681 | unwind_info.personality_ptr_address.ResolveAddressUsingFileSections( |
682 | addr: objfile_base_address + personality_offset, sections: sl); |
683 | } |
684 | } |
685 | } |
686 | } |
687 | return true; |
688 | } |
689 | return false; |
690 | } |
691 | |
692 | enum x86_64_eh_regnum { |
693 | rax = 0, |
694 | rdx = 1, |
695 | rcx = 2, |
696 | rbx = 3, |
697 | rsi = 4, |
698 | rdi = 5, |
699 | rbp = 6, |
700 | rsp = 7, |
701 | r8 = 8, |
702 | r9 = 9, |
703 | r10 = 10, |
704 | r11 = 11, |
705 | r12 = 12, |
706 | r13 = 13, |
707 | r14 = 14, |
708 | r15 = 15, |
709 | rip = 16 // this is officially the Return Address register number, but close |
710 | // enough |
711 | }; |
712 | |
713 | // Convert the compact_unwind_info.h register numbering scheme to |
714 | // eRegisterKindEHFrame (eh_frame) register numbering scheme. |
715 | uint32_t translate_to_eh_frame_regnum_x86_64(uint32_t unwind_regno) { |
716 | switch (unwind_regno) { |
717 | case UNWIND_X86_64_REG_RBX: |
718 | return x86_64_eh_regnum::rbx; |
719 | case UNWIND_X86_64_REG_R12: |
720 | return x86_64_eh_regnum::r12; |
721 | case UNWIND_X86_64_REG_R13: |
722 | return x86_64_eh_regnum::r13; |
723 | case UNWIND_X86_64_REG_R14: |
724 | return x86_64_eh_regnum::r14; |
725 | case UNWIND_X86_64_REG_R15: |
726 | return x86_64_eh_regnum::r15; |
727 | case UNWIND_X86_64_REG_RBP: |
728 | return x86_64_eh_regnum::rbp; |
729 | default: |
730 | return LLDB_INVALID_REGNUM; |
731 | } |
732 | } |
733 | |
734 | bool CompactUnwindInfo::CreateUnwindPlan_x86_64(Target &target, |
735 | FunctionInfo &function_info, |
736 | UnwindPlan &unwind_plan, |
737 | Address pc_or_function_start) { |
738 | unwind_plan.SetSourceName("compact unwind info" ); |
739 | unwind_plan.SetSourcedFromCompiler(eLazyBoolYes); |
740 | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
741 | unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); |
742 | unwind_plan.SetRegisterKind(eRegisterKindEHFrame); |
743 | |
744 | unwind_plan.SetLSDAAddress(function_info.lsda_address); |
745 | unwind_plan.SetPersonalityFunctionPtr(function_info.personality_ptr_address); |
746 | |
747 | UnwindPlan::RowSP row(new UnwindPlan::Row); |
748 | |
749 | const int wordsize = 8; |
750 | int mode = function_info.encoding & UNWIND_X86_64_MODE_MASK; |
751 | switch (mode) { |
752 | case UNWIND_X86_64_MODE_RBP_FRAME: { |
753 | row->GetCFAValue().SetIsRegisterPlusOffset( |
754 | reg_num: translate_to_eh_frame_regnum_x86_64(unwind_regno: UNWIND_X86_64_REG_RBP), |
755 | offset: 2 * wordsize); |
756 | row->SetOffset(0); |
757 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: x86_64_eh_regnum::rbp, |
758 | offset: wordsize * -2, can_replace: true); |
759 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: x86_64_eh_regnum::rip, |
760 | offset: wordsize * -1, can_replace: true); |
761 | row->SetRegisterLocationToIsCFAPlusOffset(reg_num: x86_64_eh_regnum::rsp, offset: 0, can_replace: true); |
762 | |
763 | uint32_t saved_registers_offset = |
764 | EXTRACT_BITS(function_info.encoding, UNWIND_X86_64_RBP_FRAME_OFFSET); |
765 | |
766 | uint32_t saved_registers_locations = |
767 | EXTRACT_BITS(function_info.encoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); |
768 | |
769 | saved_registers_offset += 2; |
770 | |
771 | for (int i = 0; i < 5; i++) { |
772 | uint32_t regnum = saved_registers_locations & 0x7; |
773 | switch (regnum) { |
774 | case UNWIND_X86_64_REG_NONE: |
775 | break; |
776 | case UNWIND_X86_64_REG_RBX: |
777 | case UNWIND_X86_64_REG_R12: |
778 | case UNWIND_X86_64_REG_R13: |
779 | case UNWIND_X86_64_REG_R14: |
780 | case UNWIND_X86_64_REG_R15: |
781 | row->SetRegisterLocationToAtCFAPlusOffset( |
782 | reg_num: translate_to_eh_frame_regnum_x86_64(unwind_regno: regnum), |
783 | offset: wordsize * -saved_registers_offset, can_replace: true); |
784 | break; |
785 | } |
786 | saved_registers_offset--; |
787 | saved_registers_locations >>= 3; |
788 | } |
789 | unwind_plan.AppendRow(row_sp: row); |
790 | return true; |
791 | } break; |
792 | |
793 | case UNWIND_X86_64_MODE_STACK_IND: { |
794 | // The clang in Xcode 6 is emitting incorrect compact unwind encodings for |
795 | // this style of unwind. It was fixed in llvm r217020. The clang in Xcode |
796 | // 7 has this fixed. |
797 | return false; |
798 | } break; |
799 | |
800 | case UNWIND_X86_64_MODE_STACK_IMMD: { |
801 | uint32_t stack_size = EXTRACT_BITS(function_info.encoding, |
802 | UNWIND_X86_64_FRAMELESS_STACK_SIZE); |
803 | uint32_t register_count = EXTRACT_BITS( |
804 | function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); |
805 | uint32_t permutation = EXTRACT_BITS( |
806 | function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); |
807 | |
808 | if (mode == UNWIND_X86_64_MODE_STACK_IND && |
809 | function_info.valid_range_offset_start != 0) { |
810 | uint32_t stack_adjust = EXTRACT_BITS( |
811 | function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); |
812 | |
813 | // offset into the function instructions; 0 == beginning of first |
814 | // instruction |
815 | uint32_t offset_to_subl_insn = EXTRACT_BITS( |
816 | function_info.encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); |
817 | |
818 | SectionList *sl = m_objfile.GetSectionList(); |
819 | if (sl) { |
820 | ProcessSP process_sp = target.GetProcessSP(); |
821 | if (process_sp) { |
822 | Address subl_payload_addr(function_info.valid_range_offset_start, sl); |
823 | subl_payload_addr.Slide(offset: offset_to_subl_insn); |
824 | Status error; |
825 | uint64_t large_stack_size = process_sp->ReadUnsignedIntegerFromMemory( |
826 | load_addr: subl_payload_addr.GetLoadAddress(target: &target), byte_size: 4, fail_value: 0, error); |
827 | if (large_stack_size != 0 && error.Success()) { |
828 | // Got the large stack frame size correctly - use it |
829 | stack_size = large_stack_size + (stack_adjust * wordsize); |
830 | } else { |
831 | return false; |
832 | } |
833 | } else { |
834 | return false; |
835 | } |
836 | } else { |
837 | return false; |
838 | } |
839 | } |
840 | |
841 | int32_t offset = mode == UNWIND_X86_64_MODE_STACK_IND |
842 | ? stack_size |
843 | : stack_size * wordsize; |
844 | row->GetCFAValue().SetIsRegisterPlusOffset(reg_num: x86_64_eh_regnum::rsp, offset); |
845 | |
846 | row->SetOffset(0); |
847 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: x86_64_eh_regnum::rip, |
848 | offset: wordsize * -1, can_replace: true); |
849 | row->SetRegisterLocationToIsCFAPlusOffset(reg_num: x86_64_eh_regnum::rsp, offset: 0, can_replace: true); |
850 | |
851 | if (register_count > 0) { |
852 | |
853 | // We need to include (up to) 6 registers in 10 bits. That would be 18 |
854 | // bits if we just used 3 bits per reg to indicate the order they're |
855 | // saved on the stack. |
856 | // |
857 | // This is done with Lehmer code permutation, e.g. see |
858 | // http://stackoverflow.com/questions/1506078/fast-permutation-number- |
859 | // permutation-mapping-algorithms |
860 | int permunreg[6] = {0, 0, 0, 0, 0, 0}; |
861 | |
862 | // This decodes the variable-base number in the 10 bits and gives us the |
863 | // Lehmer code sequence which can then be decoded. |
864 | |
865 | switch (register_count) { |
866 | case 6: |
867 | permunreg[0] = permutation / 120; // 120 == 5! |
868 | permutation -= (permunreg[0] * 120); |
869 | permunreg[1] = permutation / 24; // 24 == 4! |
870 | permutation -= (permunreg[1] * 24); |
871 | permunreg[2] = permutation / 6; // 6 == 3! |
872 | permutation -= (permunreg[2] * 6); |
873 | permunreg[3] = permutation / 2; // 2 == 2! |
874 | permutation -= (permunreg[3] * 2); |
875 | permunreg[4] = permutation; // 1 == 1! |
876 | permunreg[5] = 0; |
877 | break; |
878 | case 5: |
879 | permunreg[0] = permutation / 120; |
880 | permutation -= (permunreg[0] * 120); |
881 | permunreg[1] = permutation / 24; |
882 | permutation -= (permunreg[1] * 24); |
883 | permunreg[2] = permutation / 6; |
884 | permutation -= (permunreg[2] * 6); |
885 | permunreg[3] = permutation / 2; |
886 | permutation -= (permunreg[3] * 2); |
887 | permunreg[4] = permutation; |
888 | break; |
889 | case 4: |
890 | permunreg[0] = permutation / 60; |
891 | permutation -= (permunreg[0] * 60); |
892 | permunreg[1] = permutation / 12; |
893 | permutation -= (permunreg[1] * 12); |
894 | permunreg[2] = permutation / 3; |
895 | permutation -= (permunreg[2] * 3); |
896 | permunreg[3] = permutation; |
897 | break; |
898 | case 3: |
899 | permunreg[0] = permutation / 20; |
900 | permutation -= (permunreg[0] * 20); |
901 | permunreg[1] = permutation / 4; |
902 | permutation -= (permunreg[1] * 4); |
903 | permunreg[2] = permutation; |
904 | break; |
905 | case 2: |
906 | permunreg[0] = permutation / 5; |
907 | permutation -= (permunreg[0] * 5); |
908 | permunreg[1] = permutation; |
909 | break; |
910 | case 1: |
911 | permunreg[0] = permutation; |
912 | break; |
913 | } |
914 | |
915 | // Decode the Lehmer code for this permutation of the registers v. |
916 | // http://en.wikipedia.org/wiki/Lehmer_code |
917 | |
918 | int registers[6] = {UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE, |
919 | UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE, |
920 | UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE}; |
921 | bool used[7] = {false, false, false, false, false, false, false}; |
922 | for (uint32_t i = 0; i < register_count; i++) { |
923 | int renum = 0; |
924 | for (int j = 1; j < 7; j++) { |
925 | if (!used[j]) { |
926 | if (renum == permunreg[i]) { |
927 | registers[i] = j; |
928 | used[j] = true; |
929 | break; |
930 | } |
931 | renum++; |
932 | } |
933 | } |
934 | } |
935 | |
936 | uint32_t saved_registers_offset = 1; |
937 | saved_registers_offset++; |
938 | |
939 | for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) { |
940 | switch (registers[i]) { |
941 | case UNWIND_X86_64_REG_NONE: |
942 | break; |
943 | case UNWIND_X86_64_REG_RBX: |
944 | case UNWIND_X86_64_REG_R12: |
945 | case UNWIND_X86_64_REG_R13: |
946 | case UNWIND_X86_64_REG_R14: |
947 | case UNWIND_X86_64_REG_R15: |
948 | case UNWIND_X86_64_REG_RBP: |
949 | row->SetRegisterLocationToAtCFAPlusOffset( |
950 | reg_num: translate_to_eh_frame_regnum_x86_64(unwind_regno: registers[i]), |
951 | offset: wordsize * -saved_registers_offset, can_replace: true); |
952 | saved_registers_offset++; |
953 | break; |
954 | } |
955 | } |
956 | } |
957 | unwind_plan.AppendRow(row_sp: row); |
958 | return true; |
959 | } break; |
960 | |
961 | case UNWIND_X86_64_MODE_DWARF: { |
962 | return false; |
963 | } break; |
964 | |
965 | case 0: { |
966 | return false; |
967 | } break; |
968 | } |
969 | return false; |
970 | } |
971 | |
972 | enum i386_eh_regnum { |
973 | eax = 0, |
974 | ecx = 1, |
975 | edx = 2, |
976 | ebx = 3, |
977 | ebp = 4, |
978 | esp = 5, |
979 | esi = 6, |
980 | edi = 7, |
981 | eip = 8 // this is officially the Return Address register number, but close |
982 | // enough |
983 | }; |
984 | |
985 | // Convert the compact_unwind_info.h register numbering scheme to |
986 | // eRegisterKindEHFrame (eh_frame) register numbering scheme. |
987 | uint32_t translate_to_eh_frame_regnum_i386(uint32_t unwind_regno) { |
988 | switch (unwind_regno) { |
989 | case UNWIND_X86_REG_EBX: |
990 | return i386_eh_regnum::ebx; |
991 | case UNWIND_X86_REG_ECX: |
992 | return i386_eh_regnum::ecx; |
993 | case UNWIND_X86_REG_EDX: |
994 | return i386_eh_regnum::edx; |
995 | case UNWIND_X86_REG_EDI: |
996 | return i386_eh_regnum::edi; |
997 | case UNWIND_X86_REG_ESI: |
998 | return i386_eh_regnum::esi; |
999 | case UNWIND_X86_REG_EBP: |
1000 | return i386_eh_regnum::ebp; |
1001 | default: |
1002 | return LLDB_INVALID_REGNUM; |
1003 | } |
1004 | } |
1005 | |
1006 | bool CompactUnwindInfo::CreateUnwindPlan_i386(Target &target, |
1007 | FunctionInfo &function_info, |
1008 | UnwindPlan &unwind_plan, |
1009 | Address pc_or_function_start) { |
1010 | unwind_plan.SetSourceName("compact unwind info" ); |
1011 | unwind_plan.SetSourcedFromCompiler(eLazyBoolYes); |
1012 | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
1013 | unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); |
1014 | unwind_plan.SetRegisterKind(eRegisterKindEHFrame); |
1015 | |
1016 | unwind_plan.SetLSDAAddress(function_info.lsda_address); |
1017 | unwind_plan.SetPersonalityFunctionPtr(function_info.personality_ptr_address); |
1018 | |
1019 | UnwindPlan::RowSP row(new UnwindPlan::Row); |
1020 | |
1021 | const int wordsize = 4; |
1022 | int mode = function_info.encoding & UNWIND_X86_MODE_MASK; |
1023 | switch (mode) { |
1024 | case UNWIND_X86_MODE_EBP_FRAME: { |
1025 | row->GetCFAValue().SetIsRegisterPlusOffset( |
1026 | reg_num: translate_to_eh_frame_regnum_i386(unwind_regno: UNWIND_X86_REG_EBP), offset: 2 * wordsize); |
1027 | row->SetOffset(0); |
1028 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: i386_eh_regnum::ebp, |
1029 | offset: wordsize * -2, can_replace: true); |
1030 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: i386_eh_regnum::eip, |
1031 | offset: wordsize * -1, can_replace: true); |
1032 | row->SetRegisterLocationToIsCFAPlusOffset(reg_num: i386_eh_regnum::esp, offset: 0, can_replace: true); |
1033 | |
1034 | uint32_t saved_registers_offset = |
1035 | EXTRACT_BITS(function_info.encoding, UNWIND_X86_EBP_FRAME_OFFSET); |
1036 | |
1037 | uint32_t saved_registers_locations = |
1038 | EXTRACT_BITS(function_info.encoding, UNWIND_X86_EBP_FRAME_REGISTERS); |
1039 | |
1040 | saved_registers_offset += 2; |
1041 | |
1042 | for (int i = 0; i < 5; i++) { |
1043 | uint32_t regnum = saved_registers_locations & 0x7; |
1044 | switch (regnum) { |
1045 | case UNWIND_X86_REG_NONE: |
1046 | break; |
1047 | case UNWIND_X86_REG_EBX: |
1048 | case UNWIND_X86_REG_ECX: |
1049 | case UNWIND_X86_REG_EDX: |
1050 | case UNWIND_X86_REG_EDI: |
1051 | case UNWIND_X86_REG_ESI: |
1052 | row->SetRegisterLocationToAtCFAPlusOffset( |
1053 | reg_num: translate_to_eh_frame_regnum_i386(unwind_regno: regnum), |
1054 | offset: wordsize * -saved_registers_offset, can_replace: true); |
1055 | break; |
1056 | } |
1057 | saved_registers_offset--; |
1058 | saved_registers_locations >>= 3; |
1059 | } |
1060 | unwind_plan.AppendRow(row_sp: row); |
1061 | return true; |
1062 | } break; |
1063 | |
1064 | case UNWIND_X86_MODE_STACK_IND: |
1065 | case UNWIND_X86_MODE_STACK_IMMD: { |
1066 | uint32_t stack_size = |
1067 | EXTRACT_BITS(function_info.encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); |
1068 | uint32_t register_count = EXTRACT_BITS( |
1069 | function_info.encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); |
1070 | uint32_t permutation = EXTRACT_BITS( |
1071 | function_info.encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); |
1072 | |
1073 | if (mode == UNWIND_X86_MODE_STACK_IND && |
1074 | function_info.valid_range_offset_start != 0) { |
1075 | uint32_t stack_adjust = EXTRACT_BITS(function_info.encoding, |
1076 | UNWIND_X86_FRAMELESS_STACK_ADJUST); |
1077 | |
1078 | // offset into the function instructions; 0 == beginning of first |
1079 | // instruction |
1080 | uint32_t offset_to_subl_insn = |
1081 | EXTRACT_BITS(function_info.encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); |
1082 | |
1083 | SectionList *sl = m_objfile.GetSectionList(); |
1084 | if (sl) { |
1085 | ProcessSP process_sp = target.GetProcessSP(); |
1086 | if (process_sp) { |
1087 | Address subl_payload_addr(function_info.valid_range_offset_start, sl); |
1088 | subl_payload_addr.Slide(offset: offset_to_subl_insn); |
1089 | Status error; |
1090 | uint64_t large_stack_size = process_sp->ReadUnsignedIntegerFromMemory( |
1091 | load_addr: subl_payload_addr.GetLoadAddress(target: &target), byte_size: 4, fail_value: 0, error); |
1092 | if (large_stack_size != 0 && error.Success()) { |
1093 | // Got the large stack frame size correctly - use it |
1094 | stack_size = large_stack_size + (stack_adjust * wordsize); |
1095 | } else { |
1096 | return false; |
1097 | } |
1098 | } else { |
1099 | return false; |
1100 | } |
1101 | } else { |
1102 | return false; |
1103 | } |
1104 | } |
1105 | |
1106 | int32_t offset = |
1107 | mode == UNWIND_X86_MODE_STACK_IND ? stack_size : stack_size * wordsize; |
1108 | row->GetCFAValue().SetIsRegisterPlusOffset(reg_num: i386_eh_regnum::esp, offset); |
1109 | row->SetOffset(0); |
1110 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: i386_eh_regnum::eip, |
1111 | offset: wordsize * -1, can_replace: true); |
1112 | row->SetRegisterLocationToIsCFAPlusOffset(reg_num: i386_eh_regnum::esp, offset: 0, can_replace: true); |
1113 | |
1114 | if (register_count > 0) { |
1115 | |
1116 | // We need to include (up to) 6 registers in 10 bits. That would be 18 |
1117 | // bits if we just used 3 bits per reg to indicate the order they're |
1118 | // saved on the stack. |
1119 | // |
1120 | // This is done with Lehmer code permutation, e.g. see |
1121 | // http://stackoverflow.com/questions/1506078/fast-permutation-number- |
1122 | // permutation-mapping-algorithms |
1123 | int permunreg[6] = {0, 0, 0, 0, 0, 0}; |
1124 | |
1125 | // This decodes the variable-base number in the 10 bits and gives us the |
1126 | // Lehmer code sequence which can then be decoded. |
1127 | |
1128 | switch (register_count) { |
1129 | case 6: |
1130 | permunreg[0] = permutation / 120; // 120 == 5! |
1131 | permutation -= (permunreg[0] * 120); |
1132 | permunreg[1] = permutation / 24; // 24 == 4! |
1133 | permutation -= (permunreg[1] * 24); |
1134 | permunreg[2] = permutation / 6; // 6 == 3! |
1135 | permutation -= (permunreg[2] * 6); |
1136 | permunreg[3] = permutation / 2; // 2 == 2! |
1137 | permutation -= (permunreg[3] * 2); |
1138 | permunreg[4] = permutation; // 1 == 1! |
1139 | permunreg[5] = 0; |
1140 | break; |
1141 | case 5: |
1142 | permunreg[0] = permutation / 120; |
1143 | permutation -= (permunreg[0] * 120); |
1144 | permunreg[1] = permutation / 24; |
1145 | permutation -= (permunreg[1] * 24); |
1146 | permunreg[2] = permutation / 6; |
1147 | permutation -= (permunreg[2] * 6); |
1148 | permunreg[3] = permutation / 2; |
1149 | permutation -= (permunreg[3] * 2); |
1150 | permunreg[4] = permutation; |
1151 | break; |
1152 | case 4: |
1153 | permunreg[0] = permutation / 60; |
1154 | permutation -= (permunreg[0] * 60); |
1155 | permunreg[1] = permutation / 12; |
1156 | permutation -= (permunreg[1] * 12); |
1157 | permunreg[2] = permutation / 3; |
1158 | permutation -= (permunreg[2] * 3); |
1159 | permunreg[3] = permutation; |
1160 | break; |
1161 | case 3: |
1162 | permunreg[0] = permutation / 20; |
1163 | permutation -= (permunreg[0] * 20); |
1164 | permunreg[1] = permutation / 4; |
1165 | permutation -= (permunreg[1] * 4); |
1166 | permunreg[2] = permutation; |
1167 | break; |
1168 | case 2: |
1169 | permunreg[0] = permutation / 5; |
1170 | permutation -= (permunreg[0] * 5); |
1171 | permunreg[1] = permutation; |
1172 | break; |
1173 | case 1: |
1174 | permunreg[0] = permutation; |
1175 | break; |
1176 | } |
1177 | |
1178 | // Decode the Lehmer code for this permutation of the registers v. |
1179 | // http://en.wikipedia.org/wiki/Lehmer_code |
1180 | |
1181 | int registers[6] = {UNWIND_X86_REG_NONE, UNWIND_X86_REG_NONE, |
1182 | UNWIND_X86_REG_NONE, UNWIND_X86_REG_NONE, |
1183 | UNWIND_X86_REG_NONE, UNWIND_X86_REG_NONE}; |
1184 | bool used[7] = {false, false, false, false, false, false, false}; |
1185 | for (uint32_t i = 0; i < register_count; i++) { |
1186 | int renum = 0; |
1187 | for (int j = 1; j < 7; j++) { |
1188 | if (!used[j]) { |
1189 | if (renum == permunreg[i]) { |
1190 | registers[i] = j; |
1191 | used[j] = true; |
1192 | break; |
1193 | } |
1194 | renum++; |
1195 | } |
1196 | } |
1197 | } |
1198 | |
1199 | uint32_t saved_registers_offset = 1; |
1200 | saved_registers_offset++; |
1201 | |
1202 | for (int i = (sizeof(registers) / sizeof(int)) - 1; i >= 0; i--) { |
1203 | switch (registers[i]) { |
1204 | case UNWIND_X86_REG_NONE: |
1205 | break; |
1206 | case UNWIND_X86_REG_EBX: |
1207 | case UNWIND_X86_REG_ECX: |
1208 | case UNWIND_X86_REG_EDX: |
1209 | case UNWIND_X86_REG_EDI: |
1210 | case UNWIND_X86_REG_ESI: |
1211 | case UNWIND_X86_REG_EBP: |
1212 | row->SetRegisterLocationToAtCFAPlusOffset( |
1213 | reg_num: translate_to_eh_frame_regnum_i386(unwind_regno: registers[i]), |
1214 | offset: wordsize * -saved_registers_offset, can_replace: true); |
1215 | saved_registers_offset++; |
1216 | break; |
1217 | } |
1218 | } |
1219 | } |
1220 | |
1221 | unwind_plan.AppendRow(row_sp: row); |
1222 | return true; |
1223 | } break; |
1224 | |
1225 | case UNWIND_X86_MODE_DWARF: { |
1226 | return false; |
1227 | } break; |
1228 | } |
1229 | return false; |
1230 | } |
1231 | |
1232 | // DWARF register numbers from "DWARF for the ARM 64-bit Architecture (AArch64)" |
1233 | // doc by ARM |
1234 | |
1235 | enum arm64_eh_regnum { |
1236 | x19 = 19, |
1237 | x20 = 20, |
1238 | x21 = 21, |
1239 | x22 = 22, |
1240 | x23 = 23, |
1241 | x24 = 24, |
1242 | x25 = 25, |
1243 | x26 = 26, |
1244 | x27 = 27, |
1245 | x28 = 28, |
1246 | |
1247 | fp = 29, |
1248 | ra = 30, |
1249 | sp = 31, |
1250 | pc = 32, |
1251 | |
1252 | // Compact unwind encodes d8-d15 but we don't have eh_frame / dwarf reg #'s |
1253 | // for the 64-bit fp regs. Normally in DWARF it's context sensitive - so it |
1254 | // knows it is fetching a 32- or 64-bit quantity from reg v8 to indicate s0 |
1255 | // or d0 - but the unwinder is operating at a lower level and we'd try to |
1256 | // fetch 128 bits if we were told that v8 were stored on the stack... |
1257 | v8 = 72, |
1258 | v9 = 73, |
1259 | v10 = 74, |
1260 | v11 = 75, |
1261 | v12 = 76, |
1262 | v13 = 77, |
1263 | v14 = 78, |
1264 | v15 = 79, |
1265 | }; |
1266 | |
1267 | enum arm_eh_regnum { |
1268 | arm_r0 = 0, |
1269 | arm_r1 = 1, |
1270 | arm_r2 = 2, |
1271 | arm_r3 = 3, |
1272 | arm_r4 = 4, |
1273 | arm_r5 = 5, |
1274 | arm_r6 = 6, |
1275 | arm_r7 = 7, |
1276 | arm_r8 = 8, |
1277 | arm_r9 = 9, |
1278 | arm_r10 = 10, |
1279 | arm_r11 = 11, |
1280 | arm_r12 = 12, |
1281 | |
1282 | arm_sp = 13, |
1283 | arm_lr = 14, |
1284 | arm_pc = 15, |
1285 | |
1286 | arm_d0 = 256, |
1287 | arm_d1 = 257, |
1288 | arm_d2 = 258, |
1289 | arm_d3 = 259, |
1290 | arm_d4 = 260, |
1291 | arm_d5 = 261, |
1292 | arm_d6 = 262, |
1293 | arm_d7 = 263, |
1294 | arm_d8 = 264, |
1295 | arm_d9 = 265, |
1296 | arm_d10 = 266, |
1297 | arm_d11 = 267, |
1298 | arm_d12 = 268, |
1299 | arm_d13 = 269, |
1300 | arm_d14 = 270, |
1301 | }; |
1302 | |
1303 | bool CompactUnwindInfo::CreateUnwindPlan_arm64(Target &target, |
1304 | FunctionInfo &function_info, |
1305 | UnwindPlan &unwind_plan, |
1306 | Address pc_or_function_start) { |
1307 | unwind_plan.SetSourceName("compact unwind info" ); |
1308 | unwind_plan.SetSourcedFromCompiler(eLazyBoolYes); |
1309 | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
1310 | unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); |
1311 | unwind_plan.SetRegisterKind(eRegisterKindEHFrame); |
1312 | |
1313 | unwind_plan.SetLSDAAddress(function_info.lsda_address); |
1314 | unwind_plan.SetPersonalityFunctionPtr(function_info.personality_ptr_address); |
1315 | |
1316 | UnwindPlan::RowSP row(new UnwindPlan::Row); |
1317 | |
1318 | const int wordsize = 8; |
1319 | int mode = function_info.encoding & UNWIND_ARM64_MODE_MASK; |
1320 | |
1321 | if (mode == UNWIND_ARM64_MODE_DWARF) |
1322 | return false; |
1323 | |
1324 | if (mode == UNWIND_ARM64_MODE_FRAMELESS) { |
1325 | row->SetOffset(0); |
1326 | |
1327 | uint32_t stack_size = |
1328 | (EXTRACT_BITS(function_info.encoding, |
1329 | UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)) * |
1330 | 16; |
1331 | |
1332 | // Our previous Call Frame Address is the stack pointer plus the stack size |
1333 | row->GetCFAValue().SetIsRegisterPlusOffset(reg_num: arm64_eh_regnum::sp, offset: stack_size); |
1334 | |
1335 | // Our previous PC is in the LR |
1336 | row->SetRegisterLocationToRegister(reg_num: arm64_eh_regnum::pc, other_reg_num: arm64_eh_regnum::ra, |
1337 | can_replace: true); |
1338 | |
1339 | unwind_plan.AppendRow(row_sp: row); |
1340 | return true; |
1341 | } |
1342 | |
1343 | // Should not be possible |
1344 | if (mode != UNWIND_ARM64_MODE_FRAME) |
1345 | return false; |
1346 | |
1347 | // mode == UNWIND_ARM64_MODE_FRAME |
1348 | |
1349 | row->GetCFAValue().SetIsRegisterPlusOffset(reg_num: arm64_eh_regnum::fp, offset: 2 * wordsize); |
1350 | row->SetOffset(0); |
1351 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_eh_regnum::fp, offset: wordsize * -2, |
1352 | can_replace: true); |
1353 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_eh_regnum::pc, offset: wordsize * -1, |
1354 | can_replace: true); |
1355 | row->SetRegisterLocationToIsCFAPlusOffset(reg_num: arm64_eh_regnum::sp, offset: 0, can_replace: true); |
1356 | |
1357 | int reg_pairs_saved_count = 1; |
1358 | |
1359 | uint32_t saved_register_bits = function_info.encoding & 0xfff; |
1360 | |
1361 | if (saved_register_bits & UNWIND_ARM64_FRAME_X19_X20_PAIR) { |
1362 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
1363 | cfa_offset -= wordsize; |
1364 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_eh_regnum::x19, offset: cfa_offset, |
1365 | can_replace: true); |
1366 | cfa_offset -= wordsize; |
1367 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_eh_regnum::x20, offset: cfa_offset, |
1368 | can_replace: true); |
1369 | reg_pairs_saved_count++; |
1370 | } |
1371 | |
1372 | if (saved_register_bits & UNWIND_ARM64_FRAME_X21_X22_PAIR) { |
1373 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
1374 | cfa_offset -= wordsize; |
1375 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_eh_regnum::x21, offset: cfa_offset, |
1376 | can_replace: true); |
1377 | cfa_offset -= wordsize; |
1378 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_eh_regnum::x22, offset: cfa_offset, |
1379 | can_replace: true); |
1380 | reg_pairs_saved_count++; |
1381 | } |
1382 | |
1383 | if (saved_register_bits & UNWIND_ARM64_FRAME_X23_X24_PAIR) { |
1384 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
1385 | cfa_offset -= wordsize; |
1386 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_eh_regnum::x23, offset: cfa_offset, |
1387 | can_replace: true); |
1388 | cfa_offset -= wordsize; |
1389 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_eh_regnum::x24, offset: cfa_offset, |
1390 | can_replace: true); |
1391 | reg_pairs_saved_count++; |
1392 | } |
1393 | |
1394 | if (saved_register_bits & UNWIND_ARM64_FRAME_X25_X26_PAIR) { |
1395 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
1396 | cfa_offset -= wordsize; |
1397 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_eh_regnum::x25, offset: cfa_offset, |
1398 | can_replace: true); |
1399 | cfa_offset -= wordsize; |
1400 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_eh_regnum::x26, offset: cfa_offset, |
1401 | can_replace: true); |
1402 | reg_pairs_saved_count++; |
1403 | } |
1404 | |
1405 | if (saved_register_bits & UNWIND_ARM64_FRAME_X27_X28_PAIR) { |
1406 | int cfa_offset = reg_pairs_saved_count * -2 * wordsize; |
1407 | cfa_offset -= wordsize; |
1408 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_eh_regnum::x27, offset: cfa_offset, |
1409 | can_replace: true); |
1410 | cfa_offset -= wordsize; |
1411 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm64_eh_regnum::x28, offset: cfa_offset, |
1412 | can_replace: true); |
1413 | reg_pairs_saved_count++; |
1414 | } |
1415 | |
1416 | // If we use the v8-v15 regnums here, the unwinder will try to grab 128 bits |
1417 | // off the stack; |
1418 | // not sure if we have a good way to represent the 64-bitness of these saves. |
1419 | |
1420 | if (saved_register_bits & UNWIND_ARM64_FRAME_D8_D9_PAIR) { |
1421 | reg_pairs_saved_count++; |
1422 | } |
1423 | if (saved_register_bits & UNWIND_ARM64_FRAME_D10_D11_PAIR) { |
1424 | reg_pairs_saved_count++; |
1425 | } |
1426 | if (saved_register_bits & UNWIND_ARM64_FRAME_D12_D13_PAIR) { |
1427 | reg_pairs_saved_count++; |
1428 | } |
1429 | if (saved_register_bits & UNWIND_ARM64_FRAME_D14_D15_PAIR) { |
1430 | reg_pairs_saved_count++; |
1431 | } |
1432 | |
1433 | unwind_plan.AppendRow(row_sp: row); |
1434 | return true; |
1435 | } |
1436 | |
1437 | bool CompactUnwindInfo::CreateUnwindPlan_armv7(Target &target, |
1438 | FunctionInfo &function_info, |
1439 | UnwindPlan &unwind_plan, |
1440 | Address pc_or_function_start) { |
1441 | unwind_plan.SetSourceName("compact unwind info" ); |
1442 | unwind_plan.SetSourcedFromCompiler(eLazyBoolYes); |
1443 | unwind_plan.SetUnwindPlanValidAtAllInstructions(eLazyBoolNo); |
1444 | unwind_plan.SetUnwindPlanForSignalTrap(eLazyBoolNo); |
1445 | unwind_plan.SetRegisterKind(eRegisterKindEHFrame); |
1446 | |
1447 | unwind_plan.SetLSDAAddress(function_info.lsda_address); |
1448 | unwind_plan.SetPersonalityFunctionPtr(function_info.personality_ptr_address); |
1449 | |
1450 | UnwindPlan::RowSP row(new UnwindPlan::Row); |
1451 | |
1452 | const int wordsize = 4; |
1453 | int mode = function_info.encoding & UNWIND_ARM_MODE_MASK; |
1454 | |
1455 | if (mode == UNWIND_ARM_MODE_DWARF) |
1456 | return false; |
1457 | |
1458 | uint32_t stack_adjust = (EXTRACT_BITS(function_info.encoding, |
1459 | UNWIND_ARM_FRAME_STACK_ADJUST_MASK)) * |
1460 | wordsize; |
1461 | |
1462 | row->GetCFAValue().SetIsRegisterPlusOffset(reg_num: arm_r7, |
1463 | offset: (2 * wordsize) + stack_adjust); |
1464 | row->SetOffset(0); |
1465 | row->SetRegisterLocationToAtCFAPlusOffset( |
1466 | reg_num: arm_r7, offset: (wordsize * -2) - stack_adjust, can_replace: true); |
1467 | row->SetRegisterLocationToAtCFAPlusOffset( |
1468 | reg_num: arm_pc, offset: (wordsize * -1) - stack_adjust, can_replace: true); |
1469 | row->SetRegisterLocationToIsCFAPlusOffset(reg_num: arm_sp, offset: 0, can_replace: true); |
1470 | |
1471 | int cfa_offset = -stack_adjust - (2 * wordsize); |
1472 | |
1473 | uint32_t saved_register_bits = function_info.encoding & 0xff; |
1474 | |
1475 | if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R6) { |
1476 | cfa_offset -= wordsize; |
1477 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_r6, offset: cfa_offset, can_replace: true); |
1478 | } |
1479 | |
1480 | if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R5) { |
1481 | cfa_offset -= wordsize; |
1482 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_r5, offset: cfa_offset, can_replace: true); |
1483 | } |
1484 | |
1485 | if (saved_register_bits & UNWIND_ARM_FRAME_FIRST_PUSH_R4) { |
1486 | cfa_offset -= wordsize; |
1487 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_r4, offset: cfa_offset, can_replace: true); |
1488 | } |
1489 | |
1490 | if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R12) { |
1491 | cfa_offset -= wordsize; |
1492 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_r12, offset: cfa_offset, can_replace: true); |
1493 | } |
1494 | |
1495 | if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R11) { |
1496 | cfa_offset -= wordsize; |
1497 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_r11, offset: cfa_offset, can_replace: true); |
1498 | } |
1499 | |
1500 | if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R10) { |
1501 | cfa_offset -= wordsize; |
1502 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_r10, offset: cfa_offset, can_replace: true); |
1503 | } |
1504 | |
1505 | if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R9) { |
1506 | cfa_offset -= wordsize; |
1507 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_r9, offset: cfa_offset, can_replace: true); |
1508 | } |
1509 | |
1510 | if (saved_register_bits & UNWIND_ARM_FRAME_SECOND_PUSH_R8) { |
1511 | cfa_offset -= wordsize; |
1512 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_r8, offset: cfa_offset, can_replace: true); |
1513 | } |
1514 | |
1515 | if (mode == UNWIND_ARM_MODE_FRAME_D) { |
1516 | uint32_t d_reg_bits = |
1517 | EXTRACT_BITS(function_info.encoding, UNWIND_ARM_FRAME_D_REG_COUNT_MASK); |
1518 | switch (d_reg_bits) { |
1519 | case 0: |
1520 | // vpush {d8} |
1521 | cfa_offset -= 8; |
1522 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d8, offset: cfa_offset, can_replace: true); |
1523 | break; |
1524 | case 1: |
1525 | // vpush {d10} |
1526 | // vpush {d8} |
1527 | cfa_offset -= 8; |
1528 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d10, offset: cfa_offset, can_replace: true); |
1529 | cfa_offset -= 8; |
1530 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d8, offset: cfa_offset, can_replace: true); |
1531 | break; |
1532 | case 2: |
1533 | // vpush {d12} |
1534 | // vpush {d10} |
1535 | // vpush {d8} |
1536 | cfa_offset -= 8; |
1537 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d12, offset: cfa_offset, can_replace: true); |
1538 | cfa_offset -= 8; |
1539 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d10, offset: cfa_offset, can_replace: true); |
1540 | cfa_offset -= 8; |
1541 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d8, offset: cfa_offset, can_replace: true); |
1542 | break; |
1543 | case 3: |
1544 | // vpush {d14} |
1545 | // vpush {d12} |
1546 | // vpush {d10} |
1547 | // vpush {d8} |
1548 | cfa_offset -= 8; |
1549 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d14, offset: cfa_offset, can_replace: true); |
1550 | cfa_offset -= 8; |
1551 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d12, offset: cfa_offset, can_replace: true); |
1552 | cfa_offset -= 8; |
1553 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d10, offset: cfa_offset, can_replace: true); |
1554 | cfa_offset -= 8; |
1555 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d8, offset: cfa_offset, can_replace: true); |
1556 | break; |
1557 | case 4: |
1558 | // vpush {d14} |
1559 | // vpush {d12} |
1560 | // sp = (sp - 24) & (-16); |
1561 | // vst {d8, d9, d10} |
1562 | cfa_offset -= 8; |
1563 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d14, offset: cfa_offset, can_replace: true); |
1564 | cfa_offset -= 8; |
1565 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d12, offset: cfa_offset, can_replace: true); |
1566 | |
1567 | // FIXME we don't have a way to represent reg saves at an specific |
1568 | // alignment short of |
1569 | // coming up with some DWARF location description. |
1570 | |
1571 | break; |
1572 | case 5: |
1573 | // vpush {d14} |
1574 | // sp = (sp - 40) & (-16); |
1575 | // vst {d8, d9, d10, d11} |
1576 | // vst {d12} |
1577 | |
1578 | cfa_offset -= 8; |
1579 | row->SetRegisterLocationToAtCFAPlusOffset(reg_num: arm_d14, offset: cfa_offset, can_replace: true); |
1580 | |
1581 | // FIXME we don't have a way to represent reg saves at an specific |
1582 | // alignment short of |
1583 | // coming up with some DWARF location description. |
1584 | |
1585 | break; |
1586 | case 6: |
1587 | // sp = (sp - 56) & (-16); |
1588 | // vst {d8, d9, d10, d11} |
1589 | // vst {d12, d13, d14} |
1590 | |
1591 | // FIXME we don't have a way to represent reg saves at an specific |
1592 | // alignment short of |
1593 | // coming up with some DWARF location description. |
1594 | |
1595 | break; |
1596 | case 7: |
1597 | // sp = (sp - 64) & (-16); |
1598 | // vst {d8, d9, d10, d11} |
1599 | // vst {d12, d13, d14, d15} |
1600 | |
1601 | // FIXME we don't have a way to represent reg saves at an specific |
1602 | // alignment short of |
1603 | // coming up with some DWARF location description. |
1604 | |
1605 | break; |
1606 | } |
1607 | } |
1608 | |
1609 | unwind_plan.AppendRow(row_sp: row); |
1610 | return true; |
1611 | } |
1612 | |