/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 8 | AddressRange &range, Thread &thread, UnwindPlan &unwind_plan) { |
38 | 8 | std::vector<uint8_t> function_text(range.GetByteSize()); |
39 | 8 | ProcessSP process_sp(thread.GetProcess()); |
40 | 8 | if (process_sp) { |
41 | 8 | Status error; |
42 | 8 | const bool force_live_memory = true; |
43 | 8 | if (process_sp->GetTarget().ReadMemory( |
44 | 8 | range.GetBaseAddress(), function_text.data(), range.GetByteSize(), |
45 | 8 | error, force_live_memory) != range.GetByteSize()) { |
46 | 1 | return false; |
47 | 1 | } |
48 | 8 | } |
49 | 7 | return GetNonCallSiteUnwindPlanFromAssembly( |
50 | 7 | range, function_text.data(), function_text.size(), unwind_plan); |
51 | 8 | } |
52 | | |
53 | | bool UnwindAssemblyInstEmulation::GetNonCallSiteUnwindPlanFromAssembly( |
54 | | AddressRange &range, uint8_t *opcode_data, size_t opcode_size, |
55 | 14 | UnwindPlan &unwind_plan) { |
56 | 14 | if (opcode_data == nullptr || opcode_size == 0) |
57 | 0 | return false; |
58 | | |
59 | 14 | if (range.GetByteSize() > 0 && range.GetBaseAddress().IsValid() && |
60 | 14 | m_inst_emulator_up.get()) { |
61 | | |
62 | | // The instruction emulation subclass setup the unwind plan for the first |
63 | | // instruction. |
64 | 14 | 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 | 14 | if (unwind_plan.GetRowCount() == 0) |
69 | 0 | return false; |
70 | | |
71 | 14 | const bool prefer_file_cache = true; |
72 | 14 | DisassemblerSP disasm_sp(Disassembler::DisassembleBytes( |
73 | 14 | m_arch, nullptr, nullptr, range.GetBaseAddress(), opcode_data, |
74 | 14 | opcode_size, 99999, prefer_file_cache)); |
75 | | |
76 | 14 | Log *log = GetLog(LLDBLog::Unwind); |
77 | | |
78 | 14 | if (disasm_sp) { |
79 | | |
80 | 14 | m_range_ptr = ⦥ |
81 | 14 | m_unwind_plan_ptr = &unwind_plan; |
82 | | |
83 | 14 | const uint32_t addr_byte_size = m_arch.GetAddressByteSize(); |
84 | 14 | const bool show_address = true; |
85 | 14 | const bool show_bytes = true; |
86 | 14 | const bool show_control_flow_kind = true; |
87 | 14 | m_cfa_reg_info = *m_inst_emulator_up->GetRegisterInfo( |
88 | 14 | unwind_plan.GetRegisterKind(), unwind_plan.GetInitialCFARegister()); |
89 | 14 | m_fp_is_cfa = false; |
90 | 14 | m_register_values.clear(); |
91 | 14 | 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 | 14 | m_initial_sp = (1ull << ((addr_byte_size * 8) - 1)); |
97 | 14 | RegisterValue cfa_reg_value; |
98 | 14 | cfa_reg_value.SetUInt(m_initial_sp, m_cfa_reg_info.byte_size); |
99 | 14 | SetRegisterValue(m_cfa_reg_info, cfa_reg_value); |
100 | | |
101 | 14 | const InstructionList &inst_list = disasm_sp->GetInstructionList(); |
102 | 14 | const size_t num_instructions = inst_list.GetSize(); |
103 | | |
104 | 14 | if (num_instructions > 0) { |
105 | 14 | Instruction *inst = inst_list.GetInstructionAtIndex(0).get(); |
106 | 14 | 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 | 14 | std::map<lldb::addr_t, std::pair<UnwindPlan::RowSP, RegisterValueMap>> |
115 | 14 | 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 | 14 | UnwindPlan::RowSP last_row = unwind_plan.GetLastRow(); |
120 | 14 | UnwindPlan::Row *newrow = new UnwindPlan::Row; |
121 | 14 | if (last_row.get()) |
122 | 14 | *newrow = *last_row.get(); |
123 | 14 | m_curr_row.reset(newrow); |
124 | | |
125 | | // Add the initial state to the save list with offset 0. |
126 | 14 | saved_unwind_states.insert({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 | 14 | RegisterInfo sp_reg_info = *m_inst_emulator_up->GetRegisterInfo( |
132 | 14 | eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP); |
133 | | |
134 | | // The architecture dependent condition code of the last processed |
135 | | // instruction. |
136 | 14 | EmulateInstruction::InstructionCondition last_condition = |
137 | 14 | EmulateInstruction::UnconditionalCondition; |
138 | 14 | lldb::addr_t condition_block_start_offset = 0; |
139 | | |
140 | 163 | for (size_t idx = 0; idx < num_instructions; ++idx149 ) { |
141 | 149 | m_curr_row_modified = false; |
142 | 149 | m_forward_branch_offset = 0; |
143 | | |
144 | 149 | inst = inst_list.GetInstructionAtIndex(idx).get(); |
145 | 149 | if (inst) { |
146 | 149 | lldb::addr_t current_offset = |
147 | 149 | inst->GetAddress().GetFileAddress() - base_addr; |
148 | 149 | auto it = saved_unwind_states.upper_bound(current_offset); |
149 | 149 | assert(it != saved_unwind_states.begin() && |
150 | 149 | "Unwind row for the function entry missing"); |
151 | 149 | --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 | 149 | if (it->second.first->GetOffset() != m_curr_row->GetOffset()) { |
159 | 2 | UnwindPlan::Row *newrow = new UnwindPlan::Row; |
160 | 2 | *newrow = *it->second.first; |
161 | 2 | m_curr_row.reset(newrow); |
162 | 2 | m_register_values = it->second.second; |
163 | | // re-set the CFA register ivars to match the |
164 | | // new m_curr_row. |
165 | 2 | if (sp_reg_info.name && |
166 | 2 | m_curr_row->GetCFAValue().IsRegisterPlusOffset()) { |
167 | 2 | uint32_t row_cfa_regnum = |
168 | 2 | m_curr_row->GetCFAValue().GetRegisterNumber(); |
169 | 2 | lldb::RegisterKind row_kind = |
170 | 2 | m_unwind_plan_ptr->GetRegisterKind(); |
171 | | // set m_cfa_reg_info to the row's CFA reg. |
172 | 2 | m_cfa_reg_info = *m_inst_emulator_up->GetRegisterInfo( |
173 | 2 | row_kind, row_cfa_regnum); |
174 | | // set m_fp_is_cfa. |
175 | 2 | if (sp_reg_info.kinds[row_kind] == row_cfa_regnum) |
176 | 1 | m_fp_is_cfa = false; |
177 | 1 | else |
178 | 1 | m_fp_is_cfa = true; |
179 | 2 | } |
180 | 2 | } |
181 | | |
182 | 149 | m_inst_emulator_up->SetInstruction(inst->GetOpcode(), |
183 | 149 | inst->GetAddress(), nullptr); |
184 | | |
185 | 149 | if (last_condition != |
186 | 149 | m_inst_emulator_up->GetInstructionCondition()) { |
187 | 0 | if (m_inst_emulator_up->GetInstructionCondition() != |
188 | 0 | EmulateInstruction::UnconditionalCondition && |
189 | 0 | saved_unwind_states.count(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 | 0 | auto new_row = |
194 | 0 | std::make_shared<UnwindPlan::Row>(*m_curr_row.get()); |
195 | 0 | saved_unwind_states.insert( |
196 | 0 | {current_offset, {new_row, m_register_values}}); |
197 | 0 | } |
198 | | |
199 | | // If the last instruction was conditional with a different |
200 | | // condition then the then current condition then restore the |
201 | | // condition. |
202 | 0 | if (last_condition != |
203 | 0 | EmulateInstruction::UnconditionalCondition) { |
204 | 0 | const auto &saved_state = |
205 | 0 | saved_unwind_states.at(condition_block_start_offset); |
206 | 0 | m_curr_row = |
207 | 0 | std::make_shared<UnwindPlan::Row>(*saved_state.first); |
208 | 0 | m_curr_row->SetOffset(current_offset); |
209 | 0 | m_register_values = saved_state.second; |
210 | | // re-set the CFA register ivars to match the |
211 | | // new m_curr_row. |
212 | 0 | if (sp_reg_info.name && |
213 | 0 | m_curr_row->GetCFAValue().IsRegisterPlusOffset()) { |
214 | 0 | uint32_t row_cfa_regnum = |
215 | 0 | m_curr_row->GetCFAValue().GetRegisterNumber(); |
216 | 0 | lldb::RegisterKind row_kind = |
217 | 0 | m_unwind_plan_ptr->GetRegisterKind(); |
218 | | // set m_cfa_reg_info to the row's CFA reg. |
219 | 0 | m_cfa_reg_info = *m_inst_emulator_up->GetRegisterInfo( |
220 | 0 | row_kind, row_cfa_regnum); |
221 | | // set m_fp_is_cfa. |
222 | 0 | if (sp_reg_info.kinds[row_kind] == row_cfa_regnum) |
223 | 0 | m_fp_is_cfa = false; |
224 | 0 | else |
225 | 0 | m_fp_is_cfa = true; |
226 | 0 | } |
227 | 0 | bool replace_existing = |
228 | 0 | true; // The last instruction might already |
229 | | // created a row for this offset and |
230 | | // we want to overwrite it. |
231 | 0 | unwind_plan.InsertRow( |
232 | 0 | std::make_shared<UnwindPlan::Row>(*m_curr_row), |
233 | 0 | replace_existing); |
234 | 0 | } |
235 | | |
236 | | // We are starting a new conditional block at the actual offset |
237 | 0 | condition_block_start_offset = current_offset; |
238 | 0 | } |
239 | | |
240 | 149 | if (log && log->GetVerbose()0 ) { |
241 | 0 | StreamString strm; |
242 | 0 | lldb_private::FormatEntity::Entry format; |
243 | 0 | FormatEntity::Parse("${frame.pc}: ", format); |
244 | 0 | inst->Dump(&strm, inst_list.GetMaxOpcocdeByteSize(), show_address, |
245 | 0 | show_bytes, show_control_flow_kind, nullptr, nullptr, |
246 | 0 | nullptr, &format, 0); |
247 | 0 | log->PutString(strm.GetString()); |
248 | 0 | } |
249 | | |
250 | 149 | last_condition = m_inst_emulator_up->GetInstructionCondition(); |
251 | | |
252 | 149 | m_inst_emulator_up->EvaluateInstruction( |
253 | 149 | 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 | 149 | if (m_forward_branch_offset != 0 && |
258 | 149 | range.ContainsFileAddress(inst->GetAddress().GetFileAddress() + |
259 | 4 | m_forward_branch_offset)) { |
260 | 2 | auto newrow = |
261 | 2 | std::make_shared<UnwindPlan::Row>(*m_curr_row.get()); |
262 | 2 | newrow->SetOffset(current_offset + m_forward_branch_offset); |
263 | 2 | saved_unwind_states.insert( |
264 | 2 | {current_offset + m_forward_branch_offset, |
265 | 2 | {newrow, m_register_values}}); |
266 | 2 | unwind_plan.InsertRow(newrow); |
267 | 2 | } |
268 | | |
269 | | // Were there any changes to the CFI while evaluating this |
270 | | // instruction? |
271 | 149 | 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 | 73 | if (saved_unwind_states.count( |
275 | 73 | current_offset + inst->GetOpcode().GetByteSize()) == 0) { |
276 | 73 | m_curr_row->SetOffset(current_offset + |
277 | 73 | inst->GetOpcode().GetByteSize()); |
278 | 73 | unwind_plan.InsertRow(m_curr_row); |
279 | 73 | saved_unwind_states.insert( |
280 | 73 | {current_offset + inst->GetOpcode().GetByteSize(), |
281 | 73 | {m_curr_row, m_register_values}}); |
282 | | |
283 | | // Allocate a new Row for m_curr_row, copy the current state |
284 | | // into it |
285 | 73 | UnwindPlan::Row *newrow = new UnwindPlan::Row; |
286 | 73 | *newrow = *m_curr_row.get(); |
287 | 73 | m_curr_row.reset(newrow); |
288 | 73 | } |
289 | 73 | } |
290 | 149 | } |
291 | 149 | } |
292 | 14 | } |
293 | 14 | } |
294 | | |
295 | 14 | if (log && log->GetVerbose()0 ) { |
296 | 0 | StreamString strm; |
297 | 0 | lldb::addr_t base_addr = range.GetBaseAddress().GetFileAddress(); |
298 | 0 | strm.Printf("Resulting unwind rows for [0x%" PRIx64 " - 0x%" PRIx64 "):", |
299 | 0 | base_addr, base_addr + range.GetByteSize()); |
300 | 0 | unwind_plan.Dump(strm, nullptr, base_addr); |
301 | 0 | log->PutString(strm.GetString()); |
302 | 0 | } |
303 | 14 | return unwind_plan.GetRowCount() > 0; |
304 | 14 | } |
305 | 0 | return false; |
306 | 14 | } |
307 | | |
308 | | bool UnwindAssemblyInstEmulation::AugmentUnwindPlanFromCallSite( |
309 | 0 | AddressRange &func, Thread &thread, UnwindPlan &unwind_plan) { |
310 | 0 | return false; |
311 | 0 | } |
312 | | |
313 | | bool UnwindAssemblyInstEmulation::GetFastUnwindPlan(AddressRange &func, |
314 | | Thread &thread, |
315 | 16 | UnwindPlan &unwind_plan) { |
316 | 16 | return false; |
317 | 16 | } |
318 | | |
319 | | bool UnwindAssemblyInstEmulation::FirstNonPrologueInsn( |
320 | | AddressRange &func, const ExecutionContext &exe_ctx, |
321 | 0 | Address &first_non_prologue_insn) { |
322 | 0 | return false; |
323 | 0 | } |
324 | | |
325 | | UnwindAssembly * |
326 | 16.8k | UnwindAssemblyInstEmulation::CreateInstance(const ArchSpec &arch) { |
327 | 16.8k | std::unique_ptr<EmulateInstruction> inst_emulator_up( |
328 | 16.8k | EmulateInstruction::FindPlugin(arch, eInstructionTypePrologueEpilogue, |
329 | 16.8k | nullptr)); |
330 | | // Make sure that all prologue instructions are handled |
331 | 16.8k | if (inst_emulator_up) |
332 | 31 | return new UnwindAssemblyInstEmulation(arch, inst_emulator_up.release()); |
333 | 16.7k | return nullptr; |
334 | 16.8k | } |
335 | | |
336 | 3.92k | void UnwindAssemblyInstEmulation::Initialize() { |
337 | 3.92k | PluginManager::RegisterPlugin(GetPluginNameStatic(), |
338 | 3.92k | GetPluginDescriptionStatic(), CreateInstance); |
339 | 3.92k | } |
340 | | |
341 | 3.92k | void UnwindAssemblyInstEmulation::Terminate() { |
342 | 3.92k | PluginManager::UnregisterPlugin(CreateInstance); |
343 | 3.92k | } |
344 | | |
345 | 3.92k | llvm::StringRef UnwindAssemblyInstEmulation::GetPluginDescriptionStatic() { |
346 | 3.92k | return "Instruction emulation based unwind information."; |
347 | 3.92k | } |
348 | | |
349 | | uint64_t UnwindAssemblyInstEmulation::MakeRegisterKindValuePair( |
350 | 265 | const RegisterInfo ®_info) { |
351 | 265 | lldb::RegisterKind reg_kind; |
352 | 265 | uint32_t reg_num; |
353 | 265 | if (EmulateInstruction::GetBestRegisterKindAndNumber(®_info, reg_kind, |
354 | 265 | reg_num)) |
355 | 265 | return (uint64_t)reg_kind << 24 | reg_num; |
356 | 0 | return 0ull; |
357 | 265 | } |
358 | | |
359 | | void UnwindAssemblyInstEmulation::SetRegisterValue( |
360 | 119 | const RegisterInfo ®_info, const RegisterValue ®_value) { |
361 | 119 | m_register_values[MakeRegisterKindValuePair(reg_info)] = reg_value; |
362 | 119 | } |
363 | | |
364 | | bool UnwindAssemblyInstEmulation::GetRegisterValue(const RegisterInfo ®_info, |
365 | 146 | RegisterValue ®_value) { |
366 | 146 | const uint64_t reg_id = MakeRegisterKindValuePair(reg_info); |
367 | 146 | RegisterValueMap::const_iterator pos = m_register_values.find(reg_id); |
368 | 146 | if (pos != m_register_values.end()) { |
369 | 90 | reg_value = pos->second; |
370 | 90 | return true; // We had a real value that comes from an opcode that wrote |
371 | | // to it... |
372 | 90 | } |
373 | | // We are making up a value that is recognizable... |
374 | 56 | reg_value.SetUInt(reg_id, reg_info.byte_size); |
375 | 56 | return false; |
376 | 146 | } |
377 | | |
378 | | size_t UnwindAssemblyInstEmulation::ReadMemory( |
379 | | EmulateInstruction *instruction, void *baton, |
380 | | const EmulateInstruction::Context &context, lldb::addr_t addr, void *dst, |
381 | 50 | size_t dst_len) { |
382 | 50 | Log *log = GetLog(LLDBLog::Unwind); |
383 | | |
384 | 50 | if (log && log->GetVerbose()0 ) { |
385 | 0 | StreamString strm; |
386 | 0 | strm.Printf( |
387 | 0 | "UnwindAssemblyInstEmulation::ReadMemory (addr = 0x%16.16" PRIx64 |
388 | 0 | ", dst = %p, dst_len = %" PRIu64 ", context = ", |
389 | 0 | addr, dst, (uint64_t)dst_len); |
390 | 0 | context.Dump(strm, instruction); |
391 | 0 | log->PutString(strm.GetString()); |
392 | 0 | } |
393 | 50 | memset(dst, 0, dst_len); |
394 | 50 | return dst_len; |
395 | 50 | } |
396 | | |
397 | | size_t UnwindAssemblyInstEmulation::WriteMemory( |
398 | | EmulateInstruction *instruction, void *baton, |
399 | | const EmulateInstruction::Context &context, lldb::addr_t addr, |
400 | 49 | const void *dst, size_t dst_len) { |
401 | 49 | if (baton && dst && dst_len) |
402 | 49 | return ((UnwindAssemblyInstEmulation *)baton) |
403 | 49 | ->WriteMemory(instruction, context, addr, dst, dst_len); |
404 | 0 | return 0; |
405 | 49 | } |
406 | | |
407 | | size_t UnwindAssemblyInstEmulation::WriteMemory( |
408 | | EmulateInstruction *instruction, const EmulateInstruction::Context &context, |
409 | 49 | lldb::addr_t addr, const void *dst, size_t dst_len) { |
410 | 49 | DataExtractor data(dst, dst_len, |
411 | 49 | instruction->GetArchitecture().GetByteOrder(), |
412 | 49 | instruction->GetArchitecture().GetAddressByteSize()); |
413 | | |
414 | 49 | Log *log = GetLog(LLDBLog::Unwind); |
415 | | |
416 | 49 | if (log && log->GetVerbose()0 ) { |
417 | 0 | StreamString strm; |
418 | |
|
419 | 0 | strm.PutCString("UnwindAssemblyInstEmulation::WriteMemory ("); |
420 | 0 | DumpDataExtractor(data, &strm, 0, eFormatBytes, 1, dst_len, UINT32_MAX, |
421 | 0 | addr, 0, 0); |
422 | 0 | strm.PutCString(", context = "); |
423 | 0 | context.Dump(strm, instruction); |
424 | 0 | log->PutString(strm.GetString()); |
425 | 0 | } |
426 | | |
427 | 49 | const bool cant_replace = false; |
428 | | |
429 | 49 | switch (context.type) { |
430 | 0 | default: |
431 | 0 | case EmulateInstruction::eContextInvalid: |
432 | 0 | case EmulateInstruction::eContextReadOpcode: |
433 | 0 | case EmulateInstruction::eContextImmediate: |
434 | 0 | case EmulateInstruction::eContextAdjustBaseRegister: |
435 | 0 | case EmulateInstruction::eContextRegisterPlusOffset: |
436 | 0 | case EmulateInstruction::eContextAdjustPC: |
437 | 3 | case EmulateInstruction::eContextRegisterStore: |
438 | 3 | case EmulateInstruction::eContextRegisterLoad: |
439 | 3 | case EmulateInstruction::eContextRelativeBranchImmediate: |
440 | 3 | case EmulateInstruction::eContextAbsoluteBranchRegister: |
441 | 3 | case EmulateInstruction::eContextSupervisorCall: |
442 | 3 | case EmulateInstruction::eContextTableBranchReadMemory: |
443 | 3 | case EmulateInstruction::eContextWriteRegisterRandomBits: |
444 | 3 | case EmulateInstruction::eContextWriteMemoryRandomBits: |
445 | 3 | case EmulateInstruction::eContextArithmetic: |
446 | 3 | case EmulateInstruction::eContextAdvancePC: |
447 | 3 | case EmulateInstruction::eContextReturnFromException: |
448 | 3 | case EmulateInstruction::eContextPopRegisterOffStack: |
449 | 3 | case EmulateInstruction::eContextAdjustStackPointer: |
450 | 3 | break; |
451 | | |
452 | 46 | case EmulateInstruction::eContextPushRegisterOnStack: { |
453 | 46 | uint32_t reg_num = LLDB_INVALID_REGNUM; |
454 | 46 | uint32_t generic_regnum = LLDB_INVALID_REGNUM; |
455 | 46 | assert(context.GetInfoType() == |
456 | 46 | EmulateInstruction::eInfoTypeRegisterToRegisterPlusOffset && |
457 | 46 | "unhandled case, add code to handle this!"); |
458 | 46 | const uint32_t unwind_reg_kind = m_unwind_plan_ptr->GetRegisterKind(); |
459 | 46 | reg_num = context.info.RegisterToRegisterPlusOffset.data_reg |
460 | 46 | .kinds[unwind_reg_kind]; |
461 | 46 | generic_regnum = context.info.RegisterToRegisterPlusOffset.data_reg |
462 | 46 | .kinds[eRegisterKindGeneric]; |
463 | | |
464 | 46 | if (reg_num != LLDB_INVALID_REGNUM && |
465 | 46 | generic_regnum != LLDB_REGNUM_GENERIC_SP) { |
466 | 46 | if (m_pushed_regs.find(reg_num) == m_pushed_regs.end()) { |
467 | 44 | m_pushed_regs[reg_num] = addr; |
468 | 44 | const int32_t offset = addr - m_initial_sp; |
469 | 44 | m_curr_row->SetRegisterLocationToAtCFAPlusOffset(reg_num, offset, |
470 | 44 | cant_replace); |
471 | 44 | m_curr_row_modified = true; |
472 | 44 | } |
473 | 46 | } |
474 | 46 | } break; |
475 | 49 | } |
476 | | |
477 | 49 | return dst_len; |
478 | 49 | } |
479 | | |
480 | | bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction, |
481 | | void *baton, |
482 | | const RegisterInfo *reg_info, |
483 | 144 | RegisterValue ®_value) { |
484 | | |
485 | 144 | if (baton && reg_info) |
486 | 144 | return ((UnwindAssemblyInstEmulation *)baton) |
487 | 144 | ->ReadRegister(instruction, reg_info, reg_value); |
488 | 0 | return false; |
489 | 144 | } |
490 | | bool UnwindAssemblyInstEmulation::ReadRegister(EmulateInstruction *instruction, |
491 | | const RegisterInfo *reg_info, |
492 | 144 | RegisterValue ®_value) { |
493 | 144 | bool synthetic = GetRegisterValue(*reg_info, reg_value); |
494 | | |
495 | 144 | Log *log = GetLog(LLDBLog::Unwind); |
496 | | |
497 | 144 | if (log && log->GetVerbose()0 ) { |
498 | |
|
499 | 0 | StreamString strm; |
500 | 0 | strm.Printf("UnwindAssemblyInstEmulation::ReadRegister (name = \"%s\") => " |
501 | 0 | "synthetic_value = %i, value = ", |
502 | 0 | reg_info->name, synthetic); |
503 | 0 | DumpRegisterValue(reg_value, strm, *reg_info, false, false, eFormatDefault); |
504 | 0 | log->PutString(strm.GetString()); |
505 | 0 | } |
506 | 144 | return true; |
507 | 144 | } |
508 | | |
509 | | bool UnwindAssemblyInstEmulation::WriteRegister( |
510 | | EmulateInstruction *instruction, void *baton, |
511 | | const EmulateInstruction::Context &context, const RegisterInfo *reg_info, |
512 | 105 | const RegisterValue ®_value) { |
513 | 105 | if (baton && reg_info) |
514 | 105 | return ((UnwindAssemblyInstEmulation *)baton) |
515 | 105 | ->WriteRegister(instruction, context, reg_info, reg_value); |
516 | 0 | return false; |
517 | 105 | } |
518 | | bool UnwindAssemblyInstEmulation::WriteRegister( |
519 | | EmulateInstruction *instruction, const EmulateInstruction::Context &context, |
520 | 105 | const RegisterInfo *reg_info, const RegisterValue ®_value) { |
521 | 105 | Log *log = GetLog(LLDBLog::Unwind); |
522 | | |
523 | 105 | if (log && log->GetVerbose()0 ) { |
524 | |
|
525 | 0 | StreamString strm; |
526 | 0 | strm.Printf( |
527 | 0 | "UnwindAssemblyInstEmulation::WriteRegister (name = \"%s\", value = ", |
528 | 0 | reg_info->name); |
529 | 0 | DumpRegisterValue(reg_value, strm, *reg_info, false, false, eFormatDefault); |
530 | 0 | strm.PutCString(", context = "); |
531 | 0 | context.Dump(strm, instruction); |
532 | 0 | log->PutString(strm.GetString()); |
533 | 0 | } |
534 | | |
535 | 105 | SetRegisterValue(*reg_info, reg_value); |
536 | | |
537 | 105 | switch (context.type) { |
538 | 0 | case EmulateInstruction::eContextInvalid: |
539 | 0 | case EmulateInstruction::eContextReadOpcode: |
540 | 2 | case EmulateInstruction::eContextImmediate: |
541 | 2 | case EmulateInstruction::eContextAdjustBaseRegister: |
542 | 3 | case EmulateInstruction::eContextRegisterPlusOffset: |
543 | 3 | case EmulateInstruction::eContextAdjustPC: |
544 | 3 | case EmulateInstruction::eContextRegisterStore: |
545 | 3 | case EmulateInstruction::eContextSupervisorCall: |
546 | 3 | case EmulateInstruction::eContextTableBranchReadMemory: |
547 | 3 | case EmulateInstruction::eContextWriteRegisterRandomBits: |
548 | 3 | case EmulateInstruction::eContextWriteMemoryRandomBits: |
549 | 3 | case EmulateInstruction::eContextAdvancePC: |
550 | 3 | case EmulateInstruction::eContextReturnFromException: |
551 | 3 | case EmulateInstruction::eContextPushRegisterOnStack: |
552 | 3 | 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 | 3 | break; |
567 | | |
568 | 0 | 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 | 0 | lldb::RegisterKind kind = m_unwind_plan_ptr->GetRegisterKind(); |
573 | 0 | if (m_fp_is_cfa && reg_info->kinds[kind] == m_cfa_reg_info.kinds[kind] && |
574 | 0 | context.GetInfoType() == |
575 | 0 | EmulateInstruction::eInfoTypeRegisterPlusOffset && |
576 | 0 | context.info.RegisterPlusOffset.reg.kinds[kind] == |
577 | 0 | m_cfa_reg_info.kinds[kind]) { |
578 | 0 | const int64_t offset = context.info.RegisterPlusOffset.signed_offset; |
579 | 0 | m_curr_row->GetCFAValue().IncOffset(-1 * offset); |
580 | 0 | m_curr_row_modified = true; |
581 | 0 | } |
582 | 0 | } break; |
583 | | |
584 | 0 | case EmulateInstruction::eContextAbsoluteBranchRegister: |
585 | 6 | case EmulateInstruction::eContextRelativeBranchImmediate: { |
586 | 6 | if (context.GetInfoType() == EmulateInstruction::eInfoTypeISAAndImmediate && |
587 | 6 | context.info.ISAAndImmediate.unsigned_data32 > 00 ) { |
588 | 0 | m_forward_branch_offset = |
589 | 0 | context.info.ISAAndImmediateSigned.signed_data32; |
590 | 6 | } else if (context.GetInfoType() == |
591 | 6 | EmulateInstruction::eInfoTypeISAAndImmediateSigned && |
592 | 6 | context.info.ISAAndImmediateSigned.signed_data32 > 02 ) { |
593 | 2 | m_forward_branch_offset = context.info.ISAAndImmediate.unsigned_data32; |
594 | 4 | } else if (context.GetInfoType() == |
595 | 4 | EmulateInstruction::eInfoTypeImmediate && |
596 | 4 | context.info.unsigned_immediate > 00 ) { |
597 | 0 | m_forward_branch_offset = context.info.unsigned_immediate; |
598 | 4 | } else if (context.GetInfoType() == |
599 | 4 | EmulateInstruction::eInfoTypeImmediateSigned && |
600 | 4 | context.info.signed_immediate > 0) { |
601 | 3 | m_forward_branch_offset = context.info.signed_immediate; |
602 | 3 | } |
603 | 6 | } break; |
604 | | |
605 | 51 | case EmulateInstruction::eContextPopRegisterOffStack: { |
606 | 51 | const uint32_t reg_num = |
607 | 51 | reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; |
608 | 51 | const uint32_t generic_regnum = reg_info->kinds[eRegisterKindGeneric]; |
609 | 51 | if (reg_num != LLDB_INVALID_REGNUM && |
610 | 51 | generic_regnum != LLDB_REGNUM_GENERIC_SP) { |
611 | 51 | switch (context.GetInfoType()) { |
612 | 49 | case EmulateInstruction::eInfoTypeAddress: |
613 | 49 | if (m_pushed_regs.find(reg_num) != m_pushed_regs.end() && |
614 | 49 | context.info.address == m_pushed_regs[reg_num]48 ) { |
615 | 46 | m_curr_row->SetRegisterLocationToSame(reg_num, |
616 | 46 | false /*must_replace*/); |
617 | 46 | 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 | 46 | if (m_fp_is_cfa) { |
622 | 2 | m_fp_is_cfa = false; |
623 | 2 | lldb::RegisterKind sp_reg_kind = eRegisterKindGeneric; |
624 | 2 | uint32_t sp_reg_num = LLDB_REGNUM_GENERIC_SP; |
625 | 2 | RegisterInfo sp_reg_info = |
626 | 2 | *m_inst_emulator_up->GetRegisterInfo(sp_reg_kind, sp_reg_num); |
627 | 2 | RegisterValue sp_reg_val; |
628 | 2 | if (GetRegisterValue(sp_reg_info, sp_reg_val)) { |
629 | 2 | m_cfa_reg_info = sp_reg_info; |
630 | 2 | const uint32_t cfa_reg_num = |
631 | 2 | sp_reg_info.kinds[m_unwind_plan_ptr->GetRegisterKind()]; |
632 | 2 | assert(cfa_reg_num != LLDB_INVALID_REGNUM); |
633 | 2 | m_curr_row->GetCFAValue().SetIsRegisterPlusOffset( |
634 | 2 | cfa_reg_num, m_initial_sp - sp_reg_val.GetAsUInt64()); |
635 | 2 | } |
636 | 2 | } |
637 | 46 | } |
638 | 49 | break; |
639 | 49 | case EmulateInstruction::eInfoTypeISA: |
640 | 2 | assert( |
641 | 2 | (generic_regnum == LLDB_REGNUM_GENERIC_PC || |
642 | 2 | generic_regnum == LLDB_REGNUM_GENERIC_FLAGS) && |
643 | 2 | "eInfoTypeISA used for popping a register other the PC/FLAGS"); |
644 | 2 | if (generic_regnum != LLDB_REGNUM_GENERIC_FLAGS) { |
645 | 1 | m_curr_row->SetRegisterLocationToSame(reg_num, |
646 | 1 | false /*must_replace*/); |
647 | 1 | m_curr_row_modified = true; |
648 | 1 | } |
649 | 2 | break; |
650 | 0 | default: |
651 | 0 | assert(false && "unhandled case, add code to handle this!"); |
652 | 0 | break; |
653 | 51 | } |
654 | 51 | } |
655 | 51 | } break; |
656 | | |
657 | 51 | case EmulateInstruction::eContextSetFramePointer: |
658 | 7 | if (!m_fp_is_cfa) { |
659 | 7 | m_fp_is_cfa = true; |
660 | 7 | m_cfa_reg_info = *reg_info; |
661 | 7 | const uint32_t cfa_reg_num = |
662 | 7 | reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; |
663 | 7 | assert(cfa_reg_num != LLDB_INVALID_REGNUM); |
664 | 7 | m_curr_row->GetCFAValue().SetIsRegisterPlusOffset( |
665 | 7 | cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64()); |
666 | 7 | m_curr_row_modified = true; |
667 | 7 | } |
668 | 7 | break; |
669 | | |
670 | 7 | case EmulateInstruction::eContextRestoreStackPointer: |
671 | 6 | if (m_fp_is_cfa) { |
672 | 6 | m_fp_is_cfa = false; |
673 | 6 | m_cfa_reg_info = *reg_info; |
674 | 6 | const uint32_t cfa_reg_num = |
675 | 6 | reg_info->kinds[m_unwind_plan_ptr->GetRegisterKind()]; |
676 | 6 | assert(cfa_reg_num != LLDB_INVALID_REGNUM); |
677 | 6 | m_curr_row->GetCFAValue().SetIsRegisterPlusOffset( |
678 | 6 | cfa_reg_num, m_initial_sp - reg_value.GetAsUInt64()); |
679 | 6 | m_curr_row_modified = true; |
680 | 6 | } |
681 | 6 | break; |
682 | | |
683 | 32 | 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 | 32 | if (!m_fp_is_cfa) { |
687 | 24 | m_curr_row->GetCFAValue().SetIsRegisterPlusOffset( |
688 | 24 | m_curr_row->GetCFAValue().GetRegisterNumber(), |
689 | 24 | m_initial_sp - reg_value.GetAsUInt64()); |
690 | 24 | m_curr_row_modified = true; |
691 | 24 | } |
692 | 32 | break; |
693 | 105 | } |
694 | 105 | return true; |
695 | 105 | } |