1 | //===-- UnwindAssemblyInstEmulation.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 "UnwindAssemblyInstEmulation.h" |
10 | |
11 | #include "lldb/Core/Address.h" |
12 | #include "lldb/Core/Disassembler.h" |
13 | #include "lldb/Core/DumpDataExtractor.h" |
14 | #include "lldb/Core/DumpRegisterValue.h" |
15 | #include "lldb/Core/FormatEntity.h" |
16 | #include "lldb/Core/PluginManager.h" |
17 | #include "lldb/Target/ExecutionContext.h" |
18 | #include "lldb/Target/Process.h" |
19 | #include "lldb/Target/Target.h" |
20 | #include "lldb/Target/Thread.h" |
21 | #include "lldb/Utility/ArchSpec.h" |
22 | #include "lldb/Utility/DataBufferHeap.h" |
23 | #include "lldb/Utility/DataExtractor.h" |
24 | #include "lldb/Utility/LLDBLog.h" |
25 | #include "lldb/Utility/Log.h" |
26 | #include "lldb/Utility/Status.h" |
27 | #include "lldb/Utility/StreamString.h" |
28 | |
29 | using namespace lldb; |
30 | using namespace lldb_private; |
31 | |
32 | LLDB_PLUGIN_DEFINE(UnwindAssemblyInstEmulation) |
33 | |
34 | // UnwindAssemblyInstEmulation method definitions |
35 | |
36 | bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( |
37 | AddressRange &range, Thread &thread, UnwindPlan &unwind_plan) { |
38 | std::vector<uint8_t> function_text(range.GetByteSize()); |
39 | ProcessSP process_sp(thread.GetProcess()); |
40 | if (process_sp) { |
41 | Status error; |
42 | const bool force_live_memory = true; |
43 | if (process_sp->GetTarget().ReadMemory( |
44 | addr: range.GetBaseAddress(), dst: function_text.data(), dst_len: range.GetByteSize(), |
45 | error, force_live_memory) != range.GetByteSize()) { |
46 | return false; |
47 | } |
48 | } |
49 | return GetNonCallSiteUnwindPlanFromAssembly( |
50 | func&: range, opcode_data: function_text.data(), opcode_size: function_text.size(), unwind_plan); |
51 | } |
52 | |
53 | bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( |
54 | AddressRange &range, uint8_t *opcode_data, size_t opcode_size, |
55 | UnwindPlan &unwind_plan) { |
56 | if (opcode_data == nullptr || opcode_size == 0) |
57 | return false; |
58 | |
59 | if (range.GetByteSize() == 0 || !range.GetBaseAddress().IsValid() || |
60 | !m_inst_emulator_up) |
61 | return false; |
62 | |
63 | // The instruction emulation subclass setup the unwind plan for the first |
64 | // instruction. |
65 | m_inst_emulator_up->CreateFunctionEntryUnwind(unwind_plan); |
66 | |
67 | // CreateFunctionEntryUnwind should have created the first row. If it doesn't, |
68 | // then we are done. |
69 | if (unwind_plan.GetRowCount() == 0) |
70 | return false; |
71 | |
72 | const bool prefer_file_cache = true; |
73 | DisassemblerSP disasm_sp(Disassembler::DisassembleBytes( |
74 | arch: m_arch, plugin_name: nullptr, flavor: nullptr, cpu: nullptr, features: nullptr, start: range.GetBaseAddress(), |
75 | bytes: opcode_data, length: opcode_size, max_num_instructions: 99999, data_from_file: prefer_file_cache)); |
76 | |
77 | if (!disasm_sp) |
78 | return false; |
79 | |
80 | Log *log = GetLog(mask: LLDBLog::Unwind); |
81 | |
82 | m_range_ptr = ⦥ |
83 | m_unwind_plan_ptr = &unwind_plan; |
84 | |
85 | const uint32_t addr_byte_size = m_arch.GetAddressByteSize(); |
86 | const bool show_address = true; |
87 | const bool show_bytes = true; |
88 | const bool show_control_flow_kind = false; |
89 | |
90 | m_state.cfa_reg_info = *m_inst_emulator_up->GetRegisterInfo( |
91 | reg_kind: unwind_plan.GetRegisterKind(), reg_num: unwind_plan.GetInitialCFARegister()); |
92 | m_state.fp_is_cfa = false; |
93 | m_state.register_values.clear(); |
94 | |
95 | m_pushed_regs.clear(); |
96 | |
97 | // Initialize the CFA with a known value. In the 32 bit case it will be |
98 | // 0x80000000, and in the 64 bit case 0x8000000000000000. We use the address |
99 | // byte size to be safe for any future address sizes |
100 | m_initial_sp = (1ull << ((addr_byte_size * 8) - 1)); |
101 | RegisterValue cfa_reg_value; |
102 | cfa_reg_value.SetUInt(uint: m_initial_sp, byte_size: m_state.cfa_reg_info.byte_size); |
103 | SetRegisterValue(reg_info: m_state.cfa_reg_info, reg_value: cfa_reg_value); |
104 | |
105 | const InstructionList &inst_list = disasm_sp->GetInstructionList(); |
106 | const size_t num_instructions = inst_list.GetSize(); |
107 | |
108 | if (num_instructions > 0) { |
109 | Instruction *inst = inst_list.GetInstructionAtIndex(idx: 0).get(); |
110 | const lldb::addr_t base_addr = inst->GetAddress().GetFileAddress(); |
111 | |
112 | // Map for storing the unwind state at a given offset. When we see a forward |
113 | // branch we add a new entry to this map with the actual unwind plan row and |
114 | // register context for the target address of the branch as the current data |
115 | // have to be valid for the target address of the branch too if we are in |
116 | // the same function. |
117 | std::map<lldb::addr_t, UnwindState> saved_unwind_states; |
118 | |
119 | // Make a copy of the current instruction Row and save it in m_state so |
120 | // we can add updates as we process the instructions. |
121 | m_state.row = *unwind_plan.GetLastRow(); |
122 | |
123 | // Add the initial state to the save list with offset 0. |
124 | auto condition_block_start_state = |
125 | saved_unwind_states.emplace(args: 0, args&: m_state).first; |
126 | |
127 | // The architecture dependent condition code of the last processed |
128 | // instruction. |
129 | EmulateInstruction::InstructionCondition last_condition = |
130 | EmulateInstruction::UnconditionalCondition; |
131 | |
132 | for (size_t idx = 0; idx < num_instructions; ++idx) { |
133 | m_curr_row_modified = false; |
134 | m_forward_branch_offset = 0; |
135 | |
136 | inst = inst_list.GetInstructionAtIndex(idx).get(); |
137 | if (!inst) |
138 | continue; |
139 | |
140 | lldb::addr_t current_offset = |
141 | inst->GetAddress().GetFileAddress() - base_addr; |
142 | auto it = saved_unwind_states.upper_bound(x: current_offset); |
143 | assert(it != saved_unwind_states.begin() && |
144 | "Unwind row for the function entry missing" ); |
145 | --it; // Move it to the row corresponding to the current offset |
146 | |
147 | // If the offset of m_curr_row don't match with the offset we see in |
148 | // saved_unwind_states then we have to update current unwind state to |
149 | // the saved values. It is happening after we processed an epilogue and a |
150 | // return to caller instruction. |
151 | if (it->second.row.GetOffset() != m_state.row.GetOffset()) |
152 | m_state = it->second; |
153 | |
154 | m_inst_emulator_up->SetInstruction(insn_opcode: inst->GetOpcode(), inst_addr: inst->GetAddress(), |
155 | target: nullptr); |
156 | |
157 | if (last_condition != m_inst_emulator_up->GetInstructionCondition()) { |
158 | // If the last instruction was conditional with a different condition |
159 | // than the current condition then restore the state. |
160 | if (last_condition != EmulateInstruction::UnconditionalCondition) { |
161 | m_state = condition_block_start_state->second; |
162 | m_state.row.SetOffset(current_offset); |
163 | // The last instruction might already created a row for this offset |
164 | // and we want to overwrite it. |
165 | saved_unwind_states.insert_or_assign(k: current_offset, obj&: m_state); |
166 | } |
167 | |
168 | // We are starting a new conditional block at the actual offset |
169 | condition_block_start_state = it; |
170 | } |
171 | |
172 | if (log && log->GetVerbose()) { |
173 | StreamString strm; |
174 | lldb_private::FormatEntity::Entry format; |
175 | FormatEntity::Parse(format: "${frame.pc}: " , entry&: format); |
176 | inst->Dump(s: &strm, max_opcode_byte_size: inst_list.GetMaxOpcocdeByteSize(), show_address, |
177 | show_bytes, show_control_flow_kind, exe_ctx: nullptr, sym_ctx: nullptr, |
178 | prev_sym_ctx: nullptr, disassembly_addr_format: &format, max_address_text_size: 0); |
179 | log->PutString(str: strm.GetString()); |
180 | } |
181 | |
182 | last_condition = m_inst_emulator_up->GetInstructionCondition(); |
183 | |
184 | m_inst_emulator_up->EvaluateInstruction( |
185 | evaluate_options: eEmulateInstructionOptionIgnoreConditions); |
186 | |
187 | // If the current instruction is a branch forward then save the current |
188 | // CFI information for the offset where we are branching. |
189 | if (m_forward_branch_offset != 0 && |
190 | range.ContainsFileAddress(file_addr: inst->GetAddress().GetFileAddress() + |
191 | m_forward_branch_offset)) { |
192 | if (auto [it, inserted] = saved_unwind_states.emplace( |
193 | args: current_offset + m_forward_branch_offset, args&: m_state); |
194 | inserted) |
195 | it->second.row.SetOffset(current_offset + m_forward_branch_offset); |
196 | } |
197 | |
198 | // Were there any changes to the CFI while evaluating this instruction? |
199 | if (m_curr_row_modified) { |
200 | // Save the modified row if we don't already have a CFI row in the |
201 | // current address |
202 | if (saved_unwind_states.count(x: current_offset + |
203 | inst->GetOpcode().GetByteSize()) == 0) { |
204 | m_state.row.SetOffset(current_offset + |
205 | inst->GetOpcode().GetByteSize()); |
206 | saved_unwind_states.emplace( |
207 | args: current_offset + inst->GetOpcode().GetByteSize(), args&: m_state); |
208 | } |
209 | } |
210 | } |
211 | for (auto &[_, state] : saved_unwind_states) { |
212 | unwind_plan.InsertRow(row: std::move(state.row), |
213 | /*replace_existing=*/true); |
214 | } |
215 | } |
216 | |
217 | if (log && log->GetVerbose()) { |
218 | StreamString strm; |
219 | lldb::addr_t base_addr = range.GetBaseAddress().GetFileAddress(); |
220 | strm.Printf(format: "Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):" , |
221 | base_addr, base_addr + range.GetByteSize()); |
222 | unwind_plan.Dump(s&: strm, thread: nullptr, base_addr); |
223 | log->PutString(str: strm.GetString()); |
224 | } |
225 | return unwind_plan.GetRowCount() > 0; |
226 | } |
227 | |
228 | bool UnwindAssemblyInstEmulation::AugmentUnwindPlanFromCallSite( |
229 | AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) { |
230 | return false; |
231 | } |
232 | |
233 | bool UnwindAssemblyInstEmulation::GetFastUnwindPlan(AddressRange &func, |
234 | Thread &thread, |
235 | UnwindPlan &unwind_plan) { |
236 | return false; |
237 | } |
238 | |
239 | bool UnwindAssemblyInstEmulation::FirstNonPrologueInsn( |
240 | AddressRange &func, const ExecutionContext &exe_ctx, |
241 | Address &first_non_prologue_insn) { |
242 | return false; |
243 | } |
244 | |
245 | UnwindAssembly * |
246 | UnwindAssemblyInstEmulation::CreateInstance(const ArchSpec &arch) { |
247 | std::unique_ptr<EmulateInstruction> inst_emulator_up( |
248 | EmulateInstruction::FindPlugin(arch, supported_inst_type: eInstructionTypePrologueEpilogue, |
249 | plugin_name: nullptr)); |
250 | // Make sure that all prologue instructions are handled |
251 | if (inst_emulator_up) |
252 | return new UnwindAssemblyInstEmulation(arch, inst_emulator_up.release()); |
253 | return nullptr; |
254 | } |
255 | |
256 | void UnwindAssemblyInstEmulation::Initialize() { |
257 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
258 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance); |
259 | } |
260 | |
261 | void UnwindAssemblyInstEmulation::Terminate() { |
262 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
263 | } |
264 | |
265 | llvm::StringRef UnwindAssemblyInstEmulation::GetPluginDescriptionStatic() { |
266 | return "Instruction emulation based unwind information." ; |
267 | } |
268 | |
269 | uint64_t UnwindAssemblyInstEmulation::MakeRegisterKindValuePair( |
270 | const RegisterInfo ®_info) { |
271 | lldb::RegisterKind reg_kind; |
272 | uint32_t reg_num; |
273 | if (EmulateInstruction::GetBestRegisterKindAndNumber(reg_info: ®_info, reg_kind, |
274 | reg_num)) |
275 | return (uint64_t)reg_kind << 24 | reg_num; |
276 | return 0ull; |
277 | } |
278 | |
279 | void UnwindAssemblyInstEmulation::SetRegisterValue( |
280 | const RegisterInfo ®_info, const RegisterValue ®_value) { |
281 | m_state.register_values[MakeRegisterKindValuePair(reg_info)] = reg_value; |
282 | } |
283 | |
284 | bool UnwindAssemblyInstEmulation::GetRegisterValue(const RegisterInfo ®_info, |
285 | RegisterValue ®_value) { |
286 | const uint64_t reg_id = MakeRegisterKindValuePair(reg_info); |
287 | RegisterValueMap::const_iterator pos = m_state.register_values.find(x: reg_id); |
288 | if (pos != m_state.register_values.end()) { |
289 | reg_value = pos->second; |
290 | return true; // We had a real value that comes from an opcode that wrote |
291 | // to it... |
292 | } |
293 | // We are making up a value that is recognizable... |
294 | reg_value.SetUInt(uint: reg_id, byte_size: reg_info.byte_size); |
295 | return false; |
296 | } |
297 | |
298 | size_t UnwindAssemblyInstEmulation::ReadMemory( |
299 | EmulateInstruction *instruction, void *baton, |
300 | const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst, |
301 | size_t dst_len) { |
302 | Log *log = GetLog(mask: LLDBLog::Unwind); |
303 | |
304 | if (log && log->GetVerbose()) { |
305 | StreamString strm; |
306 | strm.Printf( |
307 | format: "UnwindAssemblyInstEmulation::ReadMemory (addr = 0x%16.16" PRIx64 |
308 | ", dst = %p, dst_len = %" PRIu64 ", context = " , |
309 | addr, dst, (uint64_t)dst_len); |
310 | context.Dump(s&: strm, instruction); |
311 | log->PutString(str: strm.GetString()); |
312 | } |
313 | memset(s: dst, c: 0, n: dst_len); |
314 | return dst_len; |
315 | } |
316 | |
317 | size_t UnwindAssemblyInstEmulation::WriteMemory( |
318 | EmulateInstruction *instruction, void *baton, |
319 | const EmulateInstruction::Context &context, lldb::addr_t addr, |
320 | const void *dst, size_t dst_len) { |
321 | if (baton && dst && dst_len) |
322 | return ((UnwindAssemblyInstEmulation *)baton) |
323 | ->WriteMemory(instruction, context, addr, dst, length: dst_len); |
324 | return 0; |
325 | } |
326 | |
327 | size_t UnwindAssemblyInstEmulation::WriteMemory( |
328 | EmulateInstruction *instruction, const EmulateInstruction::Context &context, |
329 | lldb::addr_t addr, const void *dst, size_t dst_len) { |
330 | DataExtractor data(dst, dst_len, |
331 | instruction->GetArchitecture().GetByteOrder(), |
332 | instruction->GetArchitecture().GetAddressByteSize()); |
333 | |
334 | Log *log = GetLog(mask: LLDBLog::Unwind); |
335 | |
336 | if (log && log->GetVerbose()) { |
337 | StreamString strm; |
338 | |
339 | strm.PutCString(cstr: "UnwindAssemblyInstEmulation::WriteMemory (" ); |
340 | DumpDataExtractor(DE: data, s: &strm, offset: 0, item_format: eFormatBytes, item_byte_size: 1, item_count: dst_len, UINT32_MAX, |
341 | base_addr: addr, item_bit_size: 0, item_bit_offset: 0); |
342 | strm.PutCString(cstr: ", context = " ); |
343 | context.Dump(s&: strm, instruction); |
344 | log->PutString(str: strm.GetString()); |
345 | } |
346 | |
347 | switch (context.type) { |
348 | default: |
349 | case EmulateInstruction::eContextInvalid: |
350 | case EmulateInstruction::eContextReadOpcode: |
351 | case EmulateInstruction::eContextImmediate: |
352 | case EmulateInstruction::eContextAdjustBaseRegister: |
353 | case EmulateInstruction::eContextRegisterPlusOffset: |
354 | case EmulateInstruction::eContextAdjustPC: |
355 | case EmulateInstruction::eContextRegisterStore: |
356 | case EmulateInstruction::eContextRegisterLoad: |
357 | case EmulateInstruction::eContextRelativeBranchImmediate: |
358 | case EmulateInstruction::eContextAbsoluteBranchRegister: |
359 | case EmulateInstruction::eContextSupervisorCall: |
360 | case EmulateInstruction::eContextTableBranchReadMemory: |
361 | case EmulateInstruction::eContextWriteRegisterRandomBits: |
362 | case EmulateInstruction::eContextWriteMemoryRandomBits: |
363 | case EmulateInstruction::eContextArithmetic: |
364 | case EmulateInstruction::eContextAdvancePC: |
365 | case EmulateInstruction::eContextReturnFromException: |
366 | case EmulateInstruction::eContextPopRegisterOffStack: |
367 | case EmulateInstruction::eContextAdjustStackPointer: |
368 | break; |
369 | |
370 | case EmulateInstruction::eContextPushRegisterOnStack: { |
371 | uint32_t reg_num = LLDB_INVALID_REGNUM; |
372 | uint32_t generic_regnum = LLDB_INVALID_REGNUM; |
373 | assert(context.GetInfoType() == |
374 | EmulateInstruction::eInfoTypeRegisterToRegisterPlusOffset && |
375 | "unhandled case, add code to handle this!" ); |
376 | const uint32_t unwind_reg_kind = m_unwind_plan_ptr->GetRegisterKind(); |
377 | reg_num = context.info.RegisterToRegisterPlusOffset.data_reg |
378 | .kinds[unwind_reg_kind]; |
379 | generic_regnum = context.info.RegisterToRegisterPlusOffset.data_reg |
380 | .kinds[eRegisterKindGeneric]; |
381 | |
382 | if (reg_num != LLDB_INVALID_REGNUM && |
383 | generic_regnum != LLDB_REGNUM_GENERIC_SP) { |
384 | if (m_pushed_regs.try_emplace(k: reg_num, args&: addr).second) { |
385 | const int32_t offset = addr - m_initial_sp; |
386 | m_state.row.SetRegisterLocationToAtCFAPlusOffset(reg_num, offset, |
387 | /*can_replace=*/true); |
388 | m_curr_row_modified = true; |
389 | } |
390 | } |
391 | } break; |
392 | } |
393 | |
394 | return dst_len; |
395 | } |
396 | |
397 | bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction, |
398 | void *baton, |
399 | const RegisterInfo *reg_info, |
400 | RegisterValue ®_value) { |
401 | |
402 | if (baton && reg_info) |
403 | return ((UnwindAssemblyInstEmulation *)baton) |
404 | ->ReadRegister(instruction, reg_info, reg_value); |
405 | return false; |
406 | } |
407 | bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction, |
408 | const RegisterInfo *reg_info, |
409 | RegisterValue ®_value) { |
410 | bool synthetic = GetRegisterValue(reg_info: *reg_info, reg_value); |
411 | |
412 | Log *log = GetLog(mask: LLDBLog::Unwind); |
413 | |
414 | if (log && log->GetVerbose()) { |
415 | |
416 | StreamString strm; |
417 | strm.Printf(format: "UnwindAssemblyInstEmulation::ReadRegister (name = \"%s\") => " |
418 | "synthetic_value = %i, value = " , |
419 | reg_info->name, synthetic); |
420 | DumpRegisterValue(reg_val: reg_value, s&: strm, reg_info: *reg_info, prefix_with_name: false, prefix_with_alt_name: false, format: eFormatDefault); |
421 | log->PutString(str: strm.GetString()); |
422 | } |
423 | return true; |
424 | } |
425 | |
426 | bool UnwindAssemblyInstEmulation::WriteRegister( |
427 | EmulateInstruction *instruction, void *baton, |
428 | const EmulateInstruction::Context &context, const RegisterInfo *reg_info, |
429 | const RegisterValue ®_value) { |
430 | if (baton && reg_info) |
431 | return ((UnwindAssemblyInstEmulation *)baton) |
432 | ->WriteRegister(instruction, context, reg_info, reg_value); |
433 | return false; |
434 | } |
435 | bool UnwindAssemblyInstEmulation::WriteRegister( |
436 | EmulateInstruction *instruction, const EmulateInstruction::Context &context, |
437 | const RegisterInfo *reg_info, const RegisterValue ®_value) { |
438 | Log *log = GetLog(mask: LLDBLog::Unwind); |
439 | |
440 | if (log && log->GetVerbose()) { |
441 | |
442 | StreamString strm; |
443 | strm.Printf( |
444 | format: "UnwindAssemblyInstEmulation::WriteRegister (name = \"%s\", value = " , |
445 | reg_info->name); |
446 | DumpRegisterValue(reg_val: reg_value, s&: strm, reg_info: *reg_info, prefix_with_name: false, prefix_with_alt_name: false, format: eFormatDefault); |
447 | strm.PutCString(cstr: ", context = " ); |
448 | context.Dump(s&: strm, instruction); |
449 | log->PutString(str: strm.GetString()); |
450 | } |
451 | |
452 | SetRegisterValue(reg_info: *reg_info, reg_value); |
453 | |
454 | switch (context.type) { |
455 | case EmulateInstruction::eContextInvalid: |
456 | case EmulateInstruction::eContextReadOpcode: |
457 | case EmulateInstruction::eContextImmediate: |
458 | case EmulateInstruction::eContextAdjustBaseRegister: |
459 | case EmulateInstruction::eContextRegisterPlusOffset: |
460 | case EmulateInstruction::eContextAdjustPC: |
461 | case EmulateInstruction::eContextRegisterStore: |
462 | case EmulateInstruction::eContextSupervisorCall: |
463 | case EmulateInstruction::eContextTableBranchReadMemory: |
464 | case EmulateInstruction::eContextWriteRegisterRandomBits: |
465 | case EmulateInstruction::eContextWriteMemoryRandomBits: |
466 | case EmulateInstruction::eContextAdvancePC: |
467 | case EmulateInstruction::eContextReturnFromException: |
468 | case EmulateInstruction::eContextPushRegisterOnStack: |
469 | case EmulateInstruction::eContextRegisterLoad: |
470 | // { |
471 | // const uint32_t reg_num = |
472 | // reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; |
473 | // if (reg_num != LLDB_INVALID_REGNUM) |
474 | // { |
475 | // const bool can_replace_only_if_unspecified = true; |
476 | // |
477 | // m_curr_row.SetRegisterLocationToUndefined (reg_num, |
478 | // can_replace_only_if_unspecified, |
479 | // can_replace_only_if_unspecified); |
480 | // m_curr_row_modified = true; |
481 | // } |
482 | // } |
483 | break; |
484 | |
485 | case EmulateInstruction::eContextArithmetic: { |
486 | // If we adjusted the current frame pointer by a constant then adjust the |
487 | // CFA offset |
488 | // with the same amount. |
489 | lldb::RegisterKind kind = m_unwind_plan_ptr->GetRegisterKind(); |
490 | if (m_state.fp_is_cfa && |
491 | reg_info->kinds[kind] == m_state.cfa_reg_info.kinds[kind] && |
492 | context.GetInfoType() == |
493 | EmulateInstruction::eInfoTypeRegisterPlusOffset && |
494 | context.info.RegisterPlusOffset.reg.kinds[kind] == |
495 | m_state.cfa_reg_info.kinds[kind]) { |
496 | const int64_t offset = context.info.RegisterPlusOffset.signed_offset; |
497 | m_state.row.GetCFAValue().IncOffset(delta: -1 * offset); |
498 | m_curr_row_modified = true; |
499 | } |
500 | } break; |
501 | |
502 | case EmulateInstruction::eContextAbsoluteBranchRegister: |
503 | case EmulateInstruction::eContextRelativeBranchImmediate: { |
504 | if (context.GetInfoType() == EmulateInstruction::eInfoTypeISAAndImmediate && |
505 | context.info.ISAAndImmediate.unsigned_data32 > 0) { |
506 | m_forward_branch_offset = |
507 | context.info.ISAAndImmediateSigned.signed_data32; |
508 | } else if (context.GetInfoType() == |
509 | EmulateInstruction::eInfoTypeISAAndImmediateSigned && |
510 | context.info.ISAAndImmediateSigned.signed_data32 > 0) { |
511 | m_forward_branch_offset = context.info.ISAAndImmediate.unsigned_data32; |
512 | } else if (context.GetInfoType() == |
513 | EmulateInstruction::eInfoTypeImmediate && |
514 | context.info.unsigned_immediate > 0) { |
515 | m_forward_branch_offset = context.info.unsigned_immediate; |
516 | } else if (context.GetInfoType() == |
517 | EmulateInstruction::eInfoTypeImmediateSigned && |
518 | context.info.signed_immediate > 0) { |
519 | m_forward_branch_offset = context.info.signed_immediate; |
520 | } |
521 | } break; |
522 | |
523 | case EmulateInstruction::eContextPopRegisterOffStack: { |
524 | const uint32_t reg_num = |
525 | reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; |
526 | const uint32_t generic_regnum = reg_info->kinds[eRegisterKindGeneric]; |
527 | if (reg_num != LLDB_INVALID_REGNUM && |
528 | generic_regnum != LLDB_REGNUM_GENERIC_SP) { |
529 | switch (context.GetInfoType()) { |
530 | case EmulateInstruction::eInfoTypeAddress: |
531 | if (auto it = m_pushed_regs.find(x: reg_num); |
532 | it != m_pushed_regs.end() && context.info.address == it->second) { |
533 | m_state.row.SetRegisterLocationToSame(reg_num, |
534 | must_replace: false /*must_replace*/); |
535 | m_curr_row_modified = true; |
536 | |
537 | // FP has been restored to its original value, we are back |
538 | // to using SP to calculate the CFA. |
539 | if (m_state.fp_is_cfa) { |
540 | m_state.fp_is_cfa = false; |
541 | lldb::RegisterKind sp_reg_kind = eRegisterKindGeneric; |
542 | uint32_t sp_reg_num = LLDB_REGNUM_GENERIC_SP; |
543 | RegisterInfo sp_reg_info = |
544 | *m_inst_emulator_up->GetRegisterInfo(reg_kind: sp_reg_kind, reg_num: sp_reg_num); |
545 | RegisterValue sp_reg_val; |
546 | if (GetRegisterValue(reg_info: sp_reg_info, reg_value&: sp_reg_val)) { |
547 | m_state.cfa_reg_info = sp_reg_info; |
548 | const uint32_t cfa_reg_num = |
549 | sp_reg_info.kinds[m_unwind_plan_ptr->GetRegisterKind()]; |
550 | assert(cfa_reg_num != LLDB_INVALID_REGNUM); |
551 | m_state.row.GetCFAValue().SetIsRegisterPlusOffset( |
552 | reg_num: cfa_reg_num, offset: m_initial_sp - sp_reg_val.GetAsUInt64()); |
553 | } |
554 | } |
555 | } |
556 | break; |
557 | case EmulateInstruction::eInfoTypeISA: |
558 | assert( |
559 | (generic_regnum == LLDB_REGNUM_GENERIC_PC || |
560 | generic_regnum == LLDB_REGNUM_GENERIC_FLAGS) && |
561 | "eInfoTypeISA used for popping a register other the PC/FLAGS" ); |
562 | if (generic_regnum != LLDB_REGNUM_GENERIC_FLAGS) { |
563 | m_state.row.SetRegisterLocationToSame(reg_num, |
564 | must_replace: false /*must_replace*/); |
565 | m_curr_row_modified = true; |
566 | } |
567 | break; |
568 | default: |
569 | assert(false && "unhandled case, add code to handle this!" ); |
570 | break; |
571 | } |
572 | } |
573 | } break; |
574 | |
575 | case EmulateInstruction::eContextSetFramePointer: |
576 | if (!m_state.fp_is_cfa) { |
577 | m_state.fp_is_cfa = true; |
578 | m_state.cfa_reg_info = *reg_info; |
579 | const uint32_t cfa_reg_num = |
580 | reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; |
581 | assert(cfa_reg_num != LLDB_INVALID_REGNUM); |
582 | m_state.row.GetCFAValue().SetIsRegisterPlusOffset( |
583 | reg_num: cfa_reg_num, offset: m_initial_sp - reg_value.GetAsUInt64()); |
584 | m_curr_row_modified = true; |
585 | } |
586 | break; |
587 | |
588 | case EmulateInstruction::eContextRestoreStackPointer: |
589 | if (m_state.fp_is_cfa) { |
590 | m_state.fp_is_cfa = false; |
591 | m_state.cfa_reg_info = *reg_info; |
592 | const uint32_t cfa_reg_num = |
593 | reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; |
594 | assert(cfa_reg_num != LLDB_INVALID_REGNUM); |
595 | m_state.row.GetCFAValue().SetIsRegisterPlusOffset( |
596 | reg_num: cfa_reg_num, offset: m_initial_sp - reg_value.GetAsUInt64()); |
597 | m_curr_row_modified = true; |
598 | } |
599 | break; |
600 | |
601 | case EmulateInstruction::eContextAdjustStackPointer: |
602 | // If we have created a frame using the frame pointer, don't follow |
603 | // subsequent adjustments to the stack pointer. |
604 | if (!m_state.fp_is_cfa) { |
605 | m_state.row.GetCFAValue().SetIsRegisterPlusOffset( |
606 | reg_num: m_state.row.GetCFAValue().GetRegisterNumber(), |
607 | offset: m_initial_sp - reg_value.GetAsUInt64()); |
608 | m_curr_row_modified = true; |
609 | } |
610 | break; |
611 | } |
612 | return true; |
613 | } |
614 | |