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