1 | //===-- Disassembler.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/Core/Disassembler.h" |
10 | |
11 | #include "lldb/Core/AddressRange.h" |
12 | #include "lldb/Core/Debugger.h" |
13 | #include "lldb/Core/EmulateInstruction.h" |
14 | #include "lldb/Core/Mangled.h" |
15 | #include "lldb/Core/Module.h" |
16 | #include "lldb/Core/ModuleList.h" |
17 | #include "lldb/Core/PluginManager.h" |
18 | #include "lldb/Core/SourceManager.h" |
19 | #include "lldb/Host/FileSystem.h" |
20 | #include "lldb/Interpreter/OptionValue.h" |
21 | #include "lldb/Interpreter/OptionValueArray.h" |
22 | #include "lldb/Interpreter/OptionValueDictionary.h" |
23 | #include "lldb/Interpreter/OptionValueRegex.h" |
24 | #include "lldb/Interpreter/OptionValueString.h" |
25 | #include "lldb/Interpreter/OptionValueUInt64.h" |
26 | #include "lldb/Symbol/Function.h" |
27 | #include "lldb/Symbol/Symbol.h" |
28 | #include "lldb/Symbol/SymbolContext.h" |
29 | #include "lldb/Target/ExecutionContext.h" |
30 | #include "lldb/Target/SectionLoadList.h" |
31 | #include "lldb/Target/StackFrame.h" |
32 | #include "lldb/Target/Target.h" |
33 | #include "lldb/Target/Thread.h" |
34 | #include "lldb/Utility/DataBufferHeap.h" |
35 | #include "lldb/Utility/DataExtractor.h" |
36 | #include "lldb/Utility/RegularExpression.h" |
37 | #include "lldb/Utility/Status.h" |
38 | #include "lldb/Utility/Stream.h" |
39 | #include "lldb/Utility/StreamString.h" |
40 | #include "lldb/Utility/Timer.h" |
41 | #include "lldb/lldb-private-enumerations.h" |
42 | #include "lldb/lldb-private-interfaces.h" |
43 | #include "lldb/lldb-private-types.h" |
44 | #include "llvm/Support/Compiler.h" |
45 | #include "llvm/TargetParser/Triple.h" |
46 | |
47 | #include <cstdint> |
48 | #include <cstring> |
49 | #include <utility> |
50 | |
51 | #include <cassert> |
52 | |
53 | #define DEFAULT_DISASM_BYTE_SIZE 32 |
54 | |
55 | using namespace lldb; |
56 | using namespace lldb_private; |
57 | |
58 | DisassemblerSP Disassembler::FindPlugin(const ArchSpec &arch, |
59 | const char *flavor, |
60 | const char *plugin_name) { |
61 | LLDB_SCOPED_TIMERF("Disassembler::FindPlugin (arch = %s, plugin_name = %s)" , |
62 | arch.GetArchitectureName(), plugin_name); |
63 | |
64 | DisassemblerCreateInstance create_callback = nullptr; |
65 | |
66 | if (plugin_name) { |
67 | create_callback = |
68 | PluginManager::GetDisassemblerCreateCallbackForPluginName(name: plugin_name); |
69 | if (create_callback) { |
70 | if (auto disasm_sp = create_callback(arch, flavor)) |
71 | return disasm_sp; |
72 | } |
73 | } else { |
74 | for (uint32_t idx = 0; |
75 | (create_callback = PluginManager::GetDisassemblerCreateCallbackAtIndex( |
76 | idx)) != nullptr; |
77 | ++idx) { |
78 | if (auto disasm_sp = create_callback(arch, flavor)) |
79 | return disasm_sp; |
80 | } |
81 | } |
82 | return DisassemblerSP(); |
83 | } |
84 | |
85 | DisassemblerSP Disassembler::FindPluginForTarget(const Target &target, |
86 | const ArchSpec &arch, |
87 | const char *flavor, |
88 | const char *plugin_name) { |
89 | if (flavor == nullptr) { |
90 | // FIXME - we don't have the mechanism in place to do per-architecture |
91 | // settings. But since we know that for now we only support flavors on x86 |
92 | // & x86_64, |
93 | if (arch.GetTriple().getArch() == llvm::Triple::x86 || |
94 | arch.GetTriple().getArch() == llvm::Triple::x86_64) |
95 | flavor = target.GetDisassemblyFlavor(); |
96 | } |
97 | return FindPlugin(arch, flavor, plugin_name); |
98 | } |
99 | |
100 | static Address ResolveAddress(Target &target, const Address &addr) { |
101 | if (!addr.IsSectionOffset()) { |
102 | Address resolved_addr; |
103 | // If we weren't passed in a section offset address range, try and resolve |
104 | // it to something |
105 | bool is_resolved = target.GetSectionLoadList().IsEmpty() |
106 | ? target.GetImages().ResolveFileAddress( |
107 | vm_addr: addr.GetOffset(), so_addr&: resolved_addr) |
108 | : target.GetSectionLoadList().ResolveLoadAddress( |
109 | load_addr: addr.GetOffset(), so_addr&: resolved_addr); |
110 | |
111 | // We weren't able to resolve the address, just treat it as a raw address |
112 | if (is_resolved && resolved_addr.IsValid()) |
113 | return resolved_addr; |
114 | } |
115 | return addr; |
116 | } |
117 | |
118 | lldb::DisassemblerSP Disassembler::DisassembleRange( |
119 | const ArchSpec &arch, const char *plugin_name, const char *flavor, |
120 | Target &target, const AddressRange &range, bool force_live_memory) { |
121 | if (range.GetByteSize() <= 0) |
122 | return {}; |
123 | |
124 | if (!range.GetBaseAddress().IsValid()) |
125 | return {}; |
126 | |
127 | lldb::DisassemblerSP disasm_sp = |
128 | Disassembler::FindPluginForTarget(target, arch, flavor, plugin_name); |
129 | |
130 | if (!disasm_sp) |
131 | return {}; |
132 | |
133 | const size_t bytes_disassembled = disasm_sp->ParseInstructions( |
134 | target, address: range.GetBaseAddress(), limit: {.kind: Limit::Bytes, .value: range.GetByteSize()}, |
135 | error_strm_ptr: nullptr, force_live_memory); |
136 | if (bytes_disassembled == 0) |
137 | return {}; |
138 | |
139 | return disasm_sp; |
140 | } |
141 | |
142 | lldb::DisassemblerSP |
143 | Disassembler::DisassembleBytes(const ArchSpec &arch, const char *plugin_name, |
144 | const char *flavor, const Address &start, |
145 | const void *src, size_t src_len, |
146 | uint32_t num_instructions, bool data_from_file) { |
147 | if (!src) |
148 | return {}; |
149 | |
150 | lldb::DisassemblerSP disasm_sp = |
151 | Disassembler::FindPlugin(arch, flavor, plugin_name); |
152 | |
153 | if (!disasm_sp) |
154 | return {}; |
155 | |
156 | DataExtractor data(src, src_len, arch.GetByteOrder(), |
157 | arch.GetAddressByteSize()); |
158 | |
159 | (void)disasm_sp->DecodeInstructions(base_addr: start, data, data_offset: 0, num_instructions, append: false, |
160 | data_from_file); |
161 | return disasm_sp; |
162 | } |
163 | |
164 | bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, |
165 | const char *plugin_name, const char *flavor, |
166 | const ExecutionContext &exe_ctx, |
167 | const Address &address, Limit limit, |
168 | bool mixed_source_and_assembly, |
169 | uint32_t num_mixed_context_lines, |
170 | uint32_t options, Stream &strm) { |
171 | if (!exe_ctx.GetTargetPtr()) |
172 | return false; |
173 | |
174 | lldb::DisassemblerSP disasm_sp(Disassembler::FindPluginForTarget( |
175 | target: exe_ctx.GetTargetRef(), arch, flavor, plugin_name)); |
176 | if (!disasm_sp) |
177 | return false; |
178 | |
179 | const bool force_live_memory = true; |
180 | size_t bytes_disassembled = disasm_sp->ParseInstructions( |
181 | target&: exe_ctx.GetTargetRef(), address, limit, error_strm_ptr: &strm, force_live_memory); |
182 | if (bytes_disassembled == 0) |
183 | return false; |
184 | |
185 | disasm_sp->PrintInstructions(debugger, arch, exe_ctx, |
186 | mixed_source_and_assembly, |
187 | num_mixed_context_lines, options, strm); |
188 | return true; |
189 | } |
190 | |
191 | Disassembler::SourceLine |
192 | Disassembler::GetFunctionDeclLineEntry(const SymbolContext &sc) { |
193 | if (!sc.function) |
194 | return {}; |
195 | |
196 | if (!sc.line_entry.IsValid()) |
197 | return {}; |
198 | |
199 | LineEntry prologue_end_line = sc.line_entry; |
200 | FileSpec func_decl_file; |
201 | uint32_t func_decl_line; |
202 | sc.function->GetStartLineSourceInfo(source_file&: func_decl_file, line_no&: func_decl_line); |
203 | |
204 | if (func_decl_file != prologue_end_line.GetFile() && |
205 | func_decl_file != prologue_end_line.original_file_sp->GetSpecOnly()) |
206 | return {}; |
207 | |
208 | SourceLine decl_line; |
209 | decl_line.file = func_decl_file; |
210 | decl_line.line = func_decl_line; |
211 | // TODO: Do we care about column on these entries? If so, we need to plumb |
212 | // that through GetStartLineSourceInfo. |
213 | decl_line.column = 0; |
214 | return decl_line; |
215 | } |
216 | |
217 | void Disassembler::AddLineToSourceLineTables( |
218 | SourceLine &line, |
219 | std::map<FileSpec, std::set<uint32_t>> &source_lines_seen) { |
220 | if (line.IsValid()) { |
221 | auto source_lines_seen_pos = source_lines_seen.find(x: line.file); |
222 | if (source_lines_seen_pos == source_lines_seen.end()) { |
223 | std::set<uint32_t> lines; |
224 | lines.insert(x: line.line); |
225 | source_lines_seen.emplace(args&: line.file, args&: lines); |
226 | } else { |
227 | source_lines_seen_pos->second.insert(x: line.line); |
228 | } |
229 | } |
230 | } |
231 | |
232 | bool Disassembler::ElideMixedSourceAndDisassemblyLine( |
233 | const ExecutionContext &exe_ctx, const SymbolContext &sc, |
234 | SourceLine &line) { |
235 | |
236 | // TODO: should we also check target.process.thread.step-avoid-libraries ? |
237 | |
238 | const RegularExpression *avoid_regex = nullptr; |
239 | |
240 | // Skip any line #0 entries - they are implementation details |
241 | if (line.line == 0) |
242 | return true; |
243 | |
244 | ThreadSP thread_sp = exe_ctx.GetThreadSP(); |
245 | if (thread_sp) { |
246 | avoid_regex = thread_sp->GetSymbolsToAvoidRegexp(); |
247 | } else { |
248 | TargetSP target_sp = exe_ctx.GetTargetSP(); |
249 | if (target_sp) { |
250 | Status error; |
251 | OptionValueSP value_sp = target_sp->GetDebugger().GetPropertyValue( |
252 | exe_ctx: &exe_ctx, property_path: "target.process.thread.step-avoid-regexp" , error); |
253 | if (value_sp && value_sp->GetType() == OptionValue::eTypeRegex) { |
254 | OptionValueRegex *re = value_sp->GetAsRegex(); |
255 | if (re) { |
256 | avoid_regex = re->GetCurrentValue(); |
257 | } |
258 | } |
259 | } |
260 | } |
261 | if (avoid_regex && sc.symbol != nullptr) { |
262 | const char *function_name = |
263 | sc.GetFunctionName(preference: Mangled::ePreferDemangledWithoutArguments) |
264 | .GetCString(); |
265 | if (function_name && avoid_regex->Execute(string: function_name)) { |
266 | // skip this source line |
267 | return true; |
268 | } |
269 | } |
270 | // don't skip this source line |
271 | return false; |
272 | } |
273 | |
274 | void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch, |
275 | const ExecutionContext &exe_ctx, |
276 | bool mixed_source_and_assembly, |
277 | uint32_t num_mixed_context_lines, |
278 | uint32_t options, Stream &strm) { |
279 | // We got some things disassembled... |
280 | size_t num_instructions_found = GetInstructionList().GetSize(); |
281 | |
282 | const uint32_t max_opcode_byte_size = |
283 | GetInstructionList().GetMaxOpcocdeByteSize(); |
284 | SymbolContext sc; |
285 | SymbolContext prev_sc; |
286 | AddressRange current_source_line_range; |
287 | const Address *pc_addr_ptr = nullptr; |
288 | StackFrame *frame = exe_ctx.GetFramePtr(); |
289 | |
290 | TargetSP target_sp(exe_ctx.GetTargetSP()); |
291 | SourceManager &source_manager = |
292 | target_sp ? target_sp->GetSourceManager() : debugger.GetSourceManager(); |
293 | |
294 | if (frame) { |
295 | pc_addr_ptr = &frame->GetFrameCodeAddress(); |
296 | } |
297 | const uint32_t scope = |
298 | eSymbolContextLineEntry | eSymbolContextFunction | eSymbolContextSymbol; |
299 | const bool use_inline_block_range = false; |
300 | |
301 | const FormatEntity::Entry *disassembly_format = nullptr; |
302 | FormatEntity::Entry format; |
303 | if (exe_ctx.HasTargetScope()) { |
304 | disassembly_format = |
305 | exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat(); |
306 | } else { |
307 | FormatEntity::Parse(format: "${addr}: " , entry&: format); |
308 | disassembly_format = &format; |
309 | } |
310 | |
311 | // First pass: step through the list of instructions, find how long the |
312 | // initial addresses strings are, insert padding in the second pass so the |
313 | // opcodes all line up nicely. |
314 | |
315 | // Also build up the source line mapping if this is mixed source & assembly |
316 | // mode. Calculate the source line for each assembly instruction (eliding |
317 | // inlined functions which the user wants to skip). |
318 | |
319 | std::map<FileSpec, std::set<uint32_t>> source_lines_seen; |
320 | Symbol *previous_symbol = nullptr; |
321 | |
322 | size_t address_text_size = 0; |
323 | for (size_t i = 0; i < num_instructions_found; ++i) { |
324 | Instruction *inst = GetInstructionList().GetInstructionAtIndex(idx: i).get(); |
325 | if (inst) { |
326 | const Address &addr = inst->GetAddress(); |
327 | ModuleSP module_sp(addr.GetModule()); |
328 | if (module_sp) { |
329 | const SymbolContextItem resolve_mask = eSymbolContextFunction | |
330 | eSymbolContextSymbol | |
331 | eSymbolContextLineEntry; |
332 | uint32_t resolved_mask = |
333 | module_sp->ResolveSymbolContextForAddress(so_addr: addr, resolve_scope: resolve_mask, sc); |
334 | if (resolved_mask) { |
335 | StreamString strmstr; |
336 | Debugger::FormatDisassemblerAddress(format: disassembly_format, sc: &sc, prev_sc: nullptr, |
337 | exe_ctx: &exe_ctx, addr: &addr, s&: strmstr); |
338 | size_t cur_line = strmstr.GetSizeOfLastLine(); |
339 | if (cur_line > address_text_size) |
340 | address_text_size = cur_line; |
341 | |
342 | // Add entries to our "source_lines_seen" map+set which list which |
343 | // sources lines occur in this disassembly session. We will print |
344 | // lines of context around a source line, but we don't want to print |
345 | // a source line that has a line table entry of its own - we'll leave |
346 | // that source line to be printed when it actually occurs in the |
347 | // disassembly. |
348 | |
349 | if (mixed_source_and_assembly && sc.line_entry.IsValid()) { |
350 | if (sc.symbol != previous_symbol) { |
351 | SourceLine decl_line = GetFunctionDeclLineEntry(sc); |
352 | if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, line&: decl_line)) |
353 | AddLineToSourceLineTables(line&: decl_line, source_lines_seen); |
354 | } |
355 | if (sc.line_entry.IsValid()) { |
356 | SourceLine this_line; |
357 | this_line.file = sc.line_entry.GetFile(); |
358 | this_line.line = sc.line_entry.line; |
359 | this_line.column = sc.line_entry.column; |
360 | if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, line&: this_line)) |
361 | AddLineToSourceLineTables(line&: this_line, source_lines_seen); |
362 | } |
363 | } |
364 | } |
365 | sc.Clear(clear_target: false); |
366 | } |
367 | } |
368 | } |
369 | |
370 | previous_symbol = nullptr; |
371 | SourceLine previous_line; |
372 | for (size_t i = 0; i < num_instructions_found; ++i) { |
373 | Instruction *inst = GetInstructionList().GetInstructionAtIndex(idx: i).get(); |
374 | |
375 | if (inst) { |
376 | const Address &addr = inst->GetAddress(); |
377 | const bool inst_is_at_pc = pc_addr_ptr && addr == *pc_addr_ptr; |
378 | SourceLinesToDisplay source_lines_to_display; |
379 | |
380 | prev_sc = sc; |
381 | |
382 | ModuleSP module_sp(addr.GetModule()); |
383 | if (module_sp) { |
384 | uint32_t resolved_mask = module_sp->ResolveSymbolContextForAddress( |
385 | so_addr: addr, resolve_scope: eSymbolContextEverything, sc); |
386 | if (resolved_mask) { |
387 | if (mixed_source_and_assembly) { |
388 | |
389 | // If we've started a new function (non-inlined), print all of the |
390 | // source lines from the function declaration until the first line |
391 | // table entry - typically the opening curly brace of the function. |
392 | if (previous_symbol != sc.symbol) { |
393 | // The default disassembly format puts an extra blank line |
394 | // between functions - so when we're displaying the source |
395 | // context for a function, we don't want to add a blank line |
396 | // after the source context or we'll end up with two of them. |
397 | if (previous_symbol != nullptr) |
398 | source_lines_to_display.print_source_context_end_eol = false; |
399 | |
400 | previous_symbol = sc.symbol; |
401 | if (sc.function && sc.line_entry.IsValid()) { |
402 | LineEntry prologue_end_line = sc.line_entry; |
403 | if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, |
404 | line&: prologue_end_line)) { |
405 | FileSpec func_decl_file; |
406 | uint32_t func_decl_line; |
407 | sc.function->GetStartLineSourceInfo(source_file&: func_decl_file, |
408 | line_no&: func_decl_line); |
409 | if (func_decl_file == prologue_end_line.GetFile() || |
410 | func_decl_file == |
411 | prologue_end_line.original_file_sp->GetSpecOnly()) { |
412 | // Add all the lines between the function declaration and |
413 | // the first non-prologue source line to the list of lines |
414 | // to print. |
415 | for (uint32_t lineno = func_decl_line; |
416 | lineno <= prologue_end_line.line; lineno++) { |
417 | SourceLine this_line; |
418 | this_line.file = func_decl_file; |
419 | this_line.line = lineno; |
420 | source_lines_to_display.lines.push_back(x: this_line); |
421 | } |
422 | // Mark the last line as the "current" one. Usually this |
423 | // is the open curly brace. |
424 | if (source_lines_to_display.lines.size() > 0) |
425 | source_lines_to_display.current_source_line = |
426 | source_lines_to_display.lines.size() - 1; |
427 | } |
428 | } |
429 | } |
430 | sc.GetAddressRange(scope, range_idx: 0, use_inline_block_range, |
431 | range&: current_source_line_range); |
432 | } |
433 | |
434 | // If we've left a previous source line's address range, print a |
435 | // new source line |
436 | if (!current_source_line_range.ContainsFileAddress(so_addr: addr)) { |
437 | sc.GetAddressRange(scope, range_idx: 0, use_inline_block_range, |
438 | range&: current_source_line_range); |
439 | |
440 | if (sc != prev_sc && sc.comp_unit && sc.line_entry.IsValid()) { |
441 | SourceLine this_line; |
442 | this_line.file = sc.line_entry.GetFile(); |
443 | this_line.line = sc.line_entry.line; |
444 | |
445 | if (!ElideMixedSourceAndDisassemblyLine(exe_ctx, sc, |
446 | line&: this_line)) { |
447 | // Only print this source line if it is different from the |
448 | // last source line we printed. There may have been inlined |
449 | // functions between these lines that we elided, resulting in |
450 | // the same line being printed twice in a row for a |
451 | // contiguous block of assembly instructions. |
452 | if (this_line != previous_line) { |
453 | |
454 | std::vector<uint32_t> previous_lines; |
455 | for (uint32_t i = 0; |
456 | i < num_mixed_context_lines && |
457 | (this_line.line - num_mixed_context_lines) > 0; |
458 | i++) { |
459 | uint32_t line = |
460 | this_line.line - num_mixed_context_lines + i; |
461 | auto pos = source_lines_seen.find(x: this_line.file); |
462 | if (pos != source_lines_seen.end()) { |
463 | if (pos->second.count(x: line) == 1) { |
464 | previous_lines.clear(); |
465 | } else { |
466 | previous_lines.push_back(x: line); |
467 | } |
468 | } |
469 | } |
470 | for (size_t i = 0; i < previous_lines.size(); i++) { |
471 | SourceLine previous_line; |
472 | previous_line.file = this_line.file; |
473 | previous_line.line = previous_lines[i]; |
474 | auto pos = source_lines_seen.find(x: previous_line.file); |
475 | if (pos != source_lines_seen.end()) { |
476 | pos->second.insert(x: previous_line.line); |
477 | } |
478 | source_lines_to_display.lines.push_back(x: previous_line); |
479 | } |
480 | |
481 | source_lines_to_display.lines.push_back(x: this_line); |
482 | source_lines_to_display.current_source_line = |
483 | source_lines_to_display.lines.size() - 1; |
484 | |
485 | for (uint32_t i = 0; i < num_mixed_context_lines; i++) { |
486 | SourceLine next_line; |
487 | next_line.file = this_line.file; |
488 | next_line.line = this_line.line + i + 1; |
489 | auto pos = source_lines_seen.find(x: next_line.file); |
490 | if (pos != source_lines_seen.end()) { |
491 | if (pos->second.count(x: next_line.line) == 1) |
492 | break; |
493 | pos->second.insert(x: next_line.line); |
494 | } |
495 | source_lines_to_display.lines.push_back(x: next_line); |
496 | } |
497 | } |
498 | previous_line = this_line; |
499 | } |
500 | } |
501 | } |
502 | } |
503 | } else { |
504 | sc.Clear(clear_target: true); |
505 | } |
506 | } |
507 | |
508 | if (source_lines_to_display.lines.size() > 0) { |
509 | strm.EOL(); |
510 | for (size_t idx = 0; idx < source_lines_to_display.lines.size(); |
511 | idx++) { |
512 | SourceLine ln = source_lines_to_display.lines[idx]; |
513 | const char *line_highlight = "" ; |
514 | if (inst_is_at_pc && (options & eOptionMarkPCSourceLine)) { |
515 | line_highlight = "->" ; |
516 | } else if (idx == source_lines_to_display.current_source_line) { |
517 | line_highlight = "**" ; |
518 | } |
519 | source_manager.DisplaySourceLinesWithLineNumbers( |
520 | file: ln.file, line: ln.line, column: ln.column, context_before: 0, context_after: 0, current_line_cstr: line_highlight, s: &strm); |
521 | } |
522 | if (source_lines_to_display.print_source_context_end_eol) |
523 | strm.EOL(); |
524 | } |
525 | |
526 | const bool show_bytes = (options & eOptionShowBytes) != 0; |
527 | const bool show_control_flow_kind = |
528 | (options & eOptionShowControlFlowKind) != 0; |
529 | inst->Dump(s: &strm, max_opcode_byte_size, show_address: true, show_bytes, |
530 | show_control_flow_kind, exe_ctx: &exe_ctx, sym_ctx: &sc, prev_sym_ctx: &prev_sc, disassembly_addr_format: nullptr, |
531 | max_address_text_size: address_text_size); |
532 | strm.EOL(); |
533 | } else { |
534 | break; |
535 | } |
536 | } |
537 | } |
538 | |
539 | bool Disassembler::Disassemble(Debugger &debugger, const ArchSpec &arch, |
540 | StackFrame &frame, Stream &strm) { |
541 | AddressRange range; |
542 | SymbolContext sc( |
543 | frame.GetSymbolContext(resolve_scope: eSymbolContextFunction | eSymbolContextSymbol)); |
544 | if (sc.function) { |
545 | range = sc.function->GetAddressRange(); |
546 | } else if (sc.symbol && sc.symbol->ValueIsAddress()) { |
547 | range.GetBaseAddress() = sc.symbol->GetAddressRef(); |
548 | range.SetByteSize(sc.symbol->GetByteSize()); |
549 | } else { |
550 | range.GetBaseAddress() = frame.GetFrameCodeAddress(); |
551 | } |
552 | |
553 | if (range.GetBaseAddress().IsValid() && range.GetByteSize() == 0) |
554 | range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE); |
555 | |
556 | Disassembler::Limit limit = {.kind: Disassembler::Limit::Bytes, |
557 | .value: range.GetByteSize()}; |
558 | if (limit.value == 0) |
559 | limit.value = DEFAULT_DISASM_BYTE_SIZE; |
560 | |
561 | return Disassemble(debugger, arch, plugin_name: nullptr, flavor: nullptr, exe_ctx: frame, |
562 | address: range.GetBaseAddress(), limit, mixed_source_and_assembly: false, num_mixed_context_lines: 0, options: 0, strm); |
563 | } |
564 | |
565 | Instruction::Instruction(const Address &address, AddressClass addr_class) |
566 | : m_address(address), m_address_class(addr_class), m_opcode(), |
567 | m_calculated_strings(false) {} |
568 | |
569 | Instruction::~Instruction() = default; |
570 | |
571 | AddressClass Instruction::GetAddressClass() { |
572 | if (m_address_class == AddressClass::eInvalid) |
573 | m_address_class = m_address.GetAddressClass(); |
574 | return m_address_class; |
575 | } |
576 | |
577 | const char *Instruction::GetNameForInstructionControlFlowKind( |
578 | lldb::InstructionControlFlowKind instruction_control_flow_kind) { |
579 | switch (instruction_control_flow_kind) { |
580 | case eInstructionControlFlowKindUnknown: |
581 | return "unknown" ; |
582 | case eInstructionControlFlowKindOther: |
583 | return "other" ; |
584 | case eInstructionControlFlowKindCall: |
585 | return "call" ; |
586 | case eInstructionControlFlowKindReturn: |
587 | return "return" ; |
588 | case eInstructionControlFlowKindJump: |
589 | return "jump" ; |
590 | case eInstructionControlFlowKindCondJump: |
591 | return "cond jump" ; |
592 | case eInstructionControlFlowKindFarCall: |
593 | return "far call" ; |
594 | case eInstructionControlFlowKindFarReturn: |
595 | return "far return" ; |
596 | case eInstructionControlFlowKindFarJump: |
597 | return "far jump" ; |
598 | } |
599 | llvm_unreachable("Fully covered switch above!" ); |
600 | } |
601 | |
602 | void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size, |
603 | bool show_address, bool show_bytes, |
604 | bool show_control_flow_kind, |
605 | const ExecutionContext *exe_ctx, |
606 | const SymbolContext *sym_ctx, |
607 | const SymbolContext *prev_sym_ctx, |
608 | const FormatEntity::Entry *disassembly_addr_format, |
609 | size_t max_address_text_size) { |
610 | size_t opcode_column_width = 7; |
611 | const size_t operand_column_width = 25; |
612 | |
613 | CalculateMnemonicOperandsAndCommentIfNeeded(exe_ctx); |
614 | |
615 | StreamString ss; |
616 | |
617 | if (show_address) { |
618 | Debugger::FormatDisassemblerAddress(format: disassembly_addr_format, sc: sym_ctx, |
619 | prev_sc: prev_sym_ctx, exe_ctx, addr: &m_address, s&: ss); |
620 | ss.FillLastLineToColumn(column: max_address_text_size, fill_char: ' '); |
621 | } |
622 | |
623 | if (show_bytes) { |
624 | if (m_opcode.GetType() == Opcode::eTypeBytes) { |
625 | // x86_64 and i386 are the only ones that use bytes right now so pad out |
626 | // the byte dump to be able to always show 15 bytes (3 chars each) plus a |
627 | // space |
628 | if (max_opcode_byte_size > 0) |
629 | m_opcode.Dump(s: &ss, min_byte_width: max_opcode_byte_size * 3 + 1); |
630 | else |
631 | m_opcode.Dump(s: &ss, min_byte_width: 15 * 3 + 1); |
632 | } else { |
633 | // Else, we have ARM or MIPS which can show up to a uint32_t 0x00000000 |
634 | // (10 spaces) plus two for padding... |
635 | if (max_opcode_byte_size > 0) |
636 | m_opcode.Dump(s: &ss, min_byte_width: max_opcode_byte_size * 3 + 1); |
637 | else |
638 | m_opcode.Dump(s: &ss, min_byte_width: 12); |
639 | } |
640 | } |
641 | |
642 | if (show_control_flow_kind) { |
643 | lldb::InstructionControlFlowKind instruction_control_flow_kind = |
644 | GetControlFlowKind(exe_ctx); |
645 | ss.Printf(format: "%-12s" , GetNameForInstructionControlFlowKind( |
646 | instruction_control_flow_kind)); |
647 | } |
648 | |
649 | bool show_color = false; |
650 | if (exe_ctx) { |
651 | if (TargetSP target_sp = exe_ctx->GetTargetSP()) { |
652 | show_color = target_sp->GetDebugger().GetUseColor(); |
653 | } |
654 | } |
655 | const size_t opcode_pos = ss.GetSizeOfLastLine(); |
656 | const std::string &opcode_name = |
657 | show_color ? m_markup_opcode_name : m_opcode_name; |
658 | const std::string &mnemonics = show_color ? m_markup_mnemonics : m_mnemonics; |
659 | |
660 | // The default opcode size of 7 characters is plenty for most architectures |
661 | // but some like arm can pull out the occasional vqrshrun.s16. We won't get |
662 | // consistent column spacing in these cases, unfortunately. Also note that we |
663 | // need to directly use m_opcode_name here (instead of opcode_name) so we |
664 | // don't include color codes as characters. |
665 | if (m_opcode_name.length() >= opcode_column_width) { |
666 | opcode_column_width = m_opcode_name.length() + 1; |
667 | } |
668 | |
669 | ss.PutCString(cstr: opcode_name); |
670 | ss.FillLastLineToColumn(column: opcode_pos + opcode_column_width, fill_char: ' '); |
671 | ss.PutCString(cstr: mnemonics); |
672 | |
673 | if (!m_comment.empty()) { |
674 | ss.FillLastLineToColumn( |
675 | column: opcode_pos + opcode_column_width + operand_column_width, fill_char: ' '); |
676 | ss.PutCString(cstr: " ; " ); |
677 | ss.PutCString(cstr: m_comment); |
678 | } |
679 | s->PutCString(cstr: ss.GetString()); |
680 | } |
681 | |
682 | bool Instruction::DumpEmulation(const ArchSpec &arch) { |
683 | std::unique_ptr<EmulateInstruction> insn_emulator_up( |
684 | EmulateInstruction::FindPlugin(arch, supported_inst_type: eInstructionTypeAny, plugin_name: nullptr)); |
685 | if (insn_emulator_up) { |
686 | insn_emulator_up->SetInstruction(insn_opcode: GetOpcode(), inst_addr: GetAddress(), target: nullptr); |
687 | return insn_emulator_up->EvaluateInstruction(evaluate_options: 0); |
688 | } |
689 | |
690 | return false; |
691 | } |
692 | |
693 | bool Instruction::CanSetBreakpoint () { |
694 | return !HasDelaySlot(); |
695 | } |
696 | |
697 | bool Instruction::HasDelaySlot() { |
698 | // Default is false. |
699 | return false; |
700 | } |
701 | |
702 | OptionValueSP Instruction::ReadArray(FILE *in_file, Stream &out_stream, |
703 | OptionValue::Type data_type) { |
704 | bool done = false; |
705 | char buffer[1024]; |
706 | |
707 | auto option_value_sp = std::make_shared<OptionValueArray>(args: 1u << data_type); |
708 | |
709 | int idx = 0; |
710 | while (!done) { |
711 | if (!fgets(s: buffer, n: 1023, stream: in_file)) { |
712 | out_stream.Printf( |
713 | format: "Instruction::ReadArray: Error reading file (fgets).\n" ); |
714 | option_value_sp.reset(); |
715 | return option_value_sp; |
716 | } |
717 | |
718 | std::string line(buffer); |
719 | |
720 | size_t len = line.size(); |
721 | if (line[len - 1] == '\n') { |
722 | line[len - 1] = '\0'; |
723 | line.resize(n: len - 1); |
724 | } |
725 | |
726 | if ((line.size() == 1) && line[0] == ']') { |
727 | done = true; |
728 | line.clear(); |
729 | } |
730 | |
731 | if (!line.empty()) { |
732 | std::string value; |
733 | static RegularExpression g_reg_exp( |
734 | llvm::StringRef("^[ \t]*([^ \t]+)[ \t]*$" )); |
735 | llvm::SmallVector<llvm::StringRef, 2> matches; |
736 | if (g_reg_exp.Execute(string: line, matches: &matches)) |
737 | value = matches[1].str(); |
738 | else |
739 | value = line; |
740 | |
741 | OptionValueSP data_value_sp; |
742 | switch (data_type) { |
743 | case OptionValue::eTypeUInt64: |
744 | data_value_sp = std::make_shared<OptionValueUInt64>(args: 0, args: 0); |
745 | data_value_sp->SetValueFromString(value); |
746 | break; |
747 | // Other types can be added later as needed. |
748 | default: |
749 | data_value_sp = std::make_shared<OptionValueString>(args: value.c_str(), args: "" ); |
750 | break; |
751 | } |
752 | |
753 | option_value_sp->GetAsArray()->InsertValue(idx, value_sp: data_value_sp); |
754 | ++idx; |
755 | } |
756 | } |
757 | |
758 | return option_value_sp; |
759 | } |
760 | |
761 | OptionValueSP Instruction::ReadDictionary(FILE *in_file, Stream &out_stream) { |
762 | bool done = false; |
763 | char buffer[1024]; |
764 | |
765 | auto option_value_sp = std::make_shared<OptionValueDictionary>(); |
766 | static constexpr llvm::StringLiteral encoding_key("data_encoding" ); |
767 | OptionValue::Type data_type = OptionValue::eTypeInvalid; |
768 | |
769 | while (!done) { |
770 | // Read the next line in the file |
771 | if (!fgets(s: buffer, n: 1023, stream: in_file)) { |
772 | out_stream.Printf( |
773 | format: "Instruction::ReadDictionary: Error reading file (fgets).\n" ); |
774 | option_value_sp.reset(); |
775 | return option_value_sp; |
776 | } |
777 | |
778 | // Check to see if the line contains the end-of-dictionary marker ("}") |
779 | std::string line(buffer); |
780 | |
781 | size_t len = line.size(); |
782 | if (line[len - 1] == '\n') { |
783 | line[len - 1] = '\0'; |
784 | line.resize(n: len - 1); |
785 | } |
786 | |
787 | if ((line.size() == 1) && (line[0] == '}')) { |
788 | done = true; |
789 | line.clear(); |
790 | } |
791 | |
792 | // Try to find a key-value pair in the current line and add it to the |
793 | // dictionary. |
794 | if (!line.empty()) { |
795 | static RegularExpression g_reg_exp(llvm::StringRef( |
796 | "^[ \t]*([a-zA-Z_][a-zA-Z0-9_]*)[ \t]*=[ \t]*(.*)[ \t]*$" )); |
797 | |
798 | llvm::SmallVector<llvm::StringRef, 3> matches; |
799 | |
800 | bool reg_exp_success = g_reg_exp.Execute(string: line, matches: &matches); |
801 | std::string key; |
802 | std::string value; |
803 | if (reg_exp_success) { |
804 | key = matches[1].str(); |
805 | value = matches[2].str(); |
806 | } else { |
807 | out_stream.Printf(format: "Instruction::ReadDictionary: Failure executing " |
808 | "regular expression.\n" ); |
809 | option_value_sp.reset(); |
810 | return option_value_sp; |
811 | } |
812 | |
813 | // Check value to see if it's the start of an array or dictionary. |
814 | |
815 | lldb::OptionValueSP value_sp; |
816 | assert(value.empty() == false); |
817 | assert(key.empty() == false); |
818 | |
819 | if (value[0] == '{') { |
820 | assert(value.size() == 1); |
821 | // value is a dictionary |
822 | value_sp = ReadDictionary(in_file, out_stream); |
823 | if (!value_sp) { |
824 | option_value_sp.reset(); |
825 | return option_value_sp; |
826 | } |
827 | } else if (value[0] == '[') { |
828 | assert(value.size() == 1); |
829 | // value is an array |
830 | value_sp = ReadArray(in_file, out_stream, data_type); |
831 | if (!value_sp) { |
832 | option_value_sp.reset(); |
833 | return option_value_sp; |
834 | } |
835 | // We've used the data_type to read an array; re-set the type to |
836 | // Invalid |
837 | data_type = OptionValue::eTypeInvalid; |
838 | } else if ((value[0] == '0') && (value[1] == 'x')) { |
839 | value_sp = std::make_shared<OptionValueUInt64>(args: 0, args: 0); |
840 | value_sp->SetValueFromString(value); |
841 | } else { |
842 | size_t len = value.size(); |
843 | if ((value[0] == '"') && (value[len - 1] == '"')) |
844 | value = value.substr(pos: 1, n: len - 2); |
845 | value_sp = std::make_shared<OptionValueString>(args: value.c_str(), args: "" ); |
846 | } |
847 | |
848 | if (key == encoding_key) { |
849 | // A 'data_encoding=..." is NOT a normal key-value pair; it is meta-data |
850 | // indicating the data type of an upcoming array (usually the next bit |
851 | // of data to be read in). |
852 | if (llvm::StringRef(value) == "uint32_t" ) |
853 | data_type = OptionValue::eTypeUInt64; |
854 | } else |
855 | option_value_sp->GetAsDictionary()->SetValueForKey(key, value_sp, |
856 | can_replace: false); |
857 | } |
858 | } |
859 | |
860 | return option_value_sp; |
861 | } |
862 | |
863 | bool Instruction::TestEmulation(Stream &out_stream, const char *file_name) { |
864 | if (!file_name) { |
865 | out_stream.Printf(format: "Instruction::TestEmulation: Missing file_name." ); |
866 | return false; |
867 | } |
868 | FILE *test_file = FileSystem::Instance().Fopen(path: file_name, mode: "r" ); |
869 | if (!test_file) { |
870 | out_stream.Printf( |
871 | format: "Instruction::TestEmulation: Attempt to open test file failed." ); |
872 | return false; |
873 | } |
874 | |
875 | char buffer[256]; |
876 | if (!fgets(s: buffer, n: 255, stream: test_file)) { |
877 | out_stream.Printf( |
878 | format: "Instruction::TestEmulation: Error reading first line of test file.\n" ); |
879 | fclose(stream: test_file); |
880 | return false; |
881 | } |
882 | |
883 | if (strncmp(s1: buffer, s2: "InstructionEmulationState={" , n: 27) != 0) { |
884 | out_stream.Printf(format: "Instructin::TestEmulation: Test file does not contain " |
885 | "emulation state dictionary\n" ); |
886 | fclose(stream: test_file); |
887 | return false; |
888 | } |
889 | |
890 | // Read all the test information from the test file into an |
891 | // OptionValueDictionary. |
892 | |
893 | OptionValueSP data_dictionary_sp(ReadDictionary(in_file: test_file, out_stream)); |
894 | if (!data_dictionary_sp) { |
895 | out_stream.Printf( |
896 | format: "Instruction::TestEmulation: Error reading Dictionary Object.\n" ); |
897 | fclose(stream: test_file); |
898 | return false; |
899 | } |
900 | |
901 | fclose(stream: test_file); |
902 | |
903 | OptionValueDictionary *data_dictionary = |
904 | data_dictionary_sp->GetAsDictionary(); |
905 | static constexpr llvm::StringLiteral description_key("assembly_string" ); |
906 | static constexpr llvm::StringLiteral triple_key("triple" ); |
907 | |
908 | OptionValueSP value_sp = data_dictionary->GetValueForKey(key: description_key); |
909 | |
910 | if (!value_sp) { |
911 | out_stream.Printf(format: "Instruction::TestEmulation: Test file does not " |
912 | "contain description string.\n" ); |
913 | return false; |
914 | } |
915 | |
916 | SetDescription(value_sp->GetValueAs<llvm::StringRef>().value_or(u: "" )); |
917 | |
918 | value_sp = data_dictionary->GetValueForKey(key: triple_key); |
919 | if (!value_sp) { |
920 | out_stream.Printf( |
921 | format: "Instruction::TestEmulation: Test file does not contain triple.\n" ); |
922 | return false; |
923 | } |
924 | |
925 | ArchSpec arch; |
926 | arch.SetTriple( |
927 | llvm::Triple(value_sp->GetValueAs<llvm::StringRef>().value_or(u: "" ))); |
928 | |
929 | bool success = false; |
930 | std::unique_ptr<EmulateInstruction> insn_emulator_up( |
931 | EmulateInstruction::FindPlugin(arch, supported_inst_type: eInstructionTypeAny, plugin_name: nullptr)); |
932 | if (insn_emulator_up) |
933 | success = |
934 | insn_emulator_up->TestEmulation(out_stream, arch, test_data: data_dictionary); |
935 | |
936 | if (success) |
937 | out_stream.Printf(format: "Emulation test succeeded." ); |
938 | else |
939 | out_stream.Printf(format: "Emulation test failed." ); |
940 | |
941 | return success; |
942 | } |
943 | |
944 | bool Instruction::Emulate( |
945 | const ArchSpec &arch, uint32_t evaluate_options, void *baton, |
946 | EmulateInstruction::ReadMemoryCallback read_mem_callback, |
947 | EmulateInstruction::WriteMemoryCallback write_mem_callback, |
948 | EmulateInstruction::ReadRegisterCallback read_reg_callback, |
949 | EmulateInstruction::WriteRegisterCallback write_reg_callback) { |
950 | std::unique_ptr<EmulateInstruction> insn_emulator_up( |
951 | EmulateInstruction::FindPlugin(arch, supported_inst_type: eInstructionTypeAny, plugin_name: nullptr)); |
952 | if (insn_emulator_up) { |
953 | insn_emulator_up->SetBaton(baton); |
954 | insn_emulator_up->SetCallbacks(read_mem_callback, write_mem_callback, |
955 | read_reg_callback, write_reg_callback); |
956 | insn_emulator_up->SetInstruction(insn_opcode: GetOpcode(), inst_addr: GetAddress(), target: nullptr); |
957 | return insn_emulator_up->EvaluateInstruction(evaluate_options); |
958 | } |
959 | |
960 | return false; |
961 | } |
962 | |
963 | uint32_t Instruction::(DataExtractor &data) { |
964 | return m_opcode.GetData(data); |
965 | } |
966 | |
967 | InstructionList::InstructionList() : m_instructions() {} |
968 | |
969 | InstructionList::~InstructionList() = default; |
970 | |
971 | size_t InstructionList::GetSize() const { return m_instructions.size(); } |
972 | |
973 | uint32_t InstructionList::GetMaxOpcocdeByteSize() const { |
974 | uint32_t max_inst_size = 0; |
975 | collection::const_iterator pos, end; |
976 | for (pos = m_instructions.begin(), end = m_instructions.end(); pos != end; |
977 | ++pos) { |
978 | uint32_t inst_size = (*pos)->GetOpcode().GetByteSize(); |
979 | if (max_inst_size < inst_size) |
980 | max_inst_size = inst_size; |
981 | } |
982 | return max_inst_size; |
983 | } |
984 | |
985 | InstructionSP InstructionList::GetInstructionAtIndex(size_t idx) const { |
986 | InstructionSP inst_sp; |
987 | if (idx < m_instructions.size()) |
988 | inst_sp = m_instructions[idx]; |
989 | return inst_sp; |
990 | } |
991 | |
992 | InstructionSP InstructionList::GetInstructionAtAddress(const Address &address) { |
993 | uint32_t index = GetIndexOfInstructionAtAddress(addr: address); |
994 | if (index != UINT32_MAX) |
995 | return GetInstructionAtIndex(idx: index); |
996 | return nullptr; |
997 | } |
998 | |
999 | void InstructionList::Dump(Stream *s, bool show_address, bool show_bytes, |
1000 | bool show_control_flow_kind, |
1001 | const ExecutionContext *exe_ctx) { |
1002 | const uint32_t max_opcode_byte_size = GetMaxOpcocdeByteSize(); |
1003 | collection::const_iterator pos, begin, end; |
1004 | |
1005 | const FormatEntity::Entry *disassembly_format = nullptr; |
1006 | FormatEntity::Entry format; |
1007 | if (exe_ctx && exe_ctx->HasTargetScope()) { |
1008 | disassembly_format = |
1009 | exe_ctx->GetTargetRef().GetDebugger().GetDisassemblyFormat(); |
1010 | } else { |
1011 | FormatEntity::Parse(format: "${addr}: " , entry&: format); |
1012 | disassembly_format = &format; |
1013 | } |
1014 | |
1015 | for (begin = m_instructions.begin(), end = m_instructions.end(), pos = begin; |
1016 | pos != end; ++pos) { |
1017 | if (pos != begin) |
1018 | s->EOL(); |
1019 | (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, |
1020 | show_control_flow_kind, exe_ctx, sym_ctx: nullptr, prev_sym_ctx: nullptr, |
1021 | disassembly_addr_format: disassembly_format, max_address_text_size: 0); |
1022 | } |
1023 | } |
1024 | |
1025 | void InstructionList::Clear() { m_instructions.clear(); } |
1026 | |
1027 | void InstructionList::Append(lldb::InstructionSP &inst_sp) { |
1028 | if (inst_sp) |
1029 | m_instructions.push_back(x: inst_sp); |
1030 | } |
1031 | |
1032 | uint32_t |
1033 | InstructionList::GetIndexOfNextBranchInstruction(uint32_t start, |
1034 | bool ignore_calls, |
1035 | bool *found_calls) const { |
1036 | size_t num_instructions = m_instructions.size(); |
1037 | |
1038 | uint32_t next_branch = UINT32_MAX; |
1039 | |
1040 | if (found_calls) |
1041 | *found_calls = false; |
1042 | for (size_t i = start; i < num_instructions; i++) { |
1043 | if (m_instructions[i]->DoesBranch()) { |
1044 | if (ignore_calls && m_instructions[i]->IsCall()) { |
1045 | if (found_calls) |
1046 | *found_calls = true; |
1047 | continue; |
1048 | } |
1049 | next_branch = i; |
1050 | break; |
1051 | } |
1052 | } |
1053 | |
1054 | return next_branch; |
1055 | } |
1056 | |
1057 | uint32_t |
1058 | InstructionList::GetIndexOfInstructionAtAddress(const Address &address) { |
1059 | size_t num_instructions = m_instructions.size(); |
1060 | uint32_t index = UINT32_MAX; |
1061 | for (size_t i = 0; i < num_instructions; i++) { |
1062 | if (m_instructions[i]->GetAddress() == address) { |
1063 | index = i; |
1064 | break; |
1065 | } |
1066 | } |
1067 | return index; |
1068 | } |
1069 | |
1070 | uint32_t |
1071 | InstructionList::GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr, |
1072 | Target &target) { |
1073 | Address address; |
1074 | address.SetLoadAddress(load_addr, target: &target); |
1075 | return GetIndexOfInstructionAtAddress(address); |
1076 | } |
1077 | |
1078 | size_t Disassembler::ParseInstructions(Target &target, Address start, |
1079 | Limit limit, Stream *error_strm_ptr, |
1080 | bool force_live_memory) { |
1081 | m_instruction_list.Clear(); |
1082 | |
1083 | if (!start.IsValid()) |
1084 | return 0; |
1085 | |
1086 | start = ResolveAddress(target, addr: start); |
1087 | |
1088 | addr_t byte_size = limit.value; |
1089 | if (limit.kind == Limit::Instructions) |
1090 | byte_size *= m_arch.GetMaximumOpcodeByteSize(); |
1091 | auto data_sp = std::make_shared<DataBufferHeap>(args&: byte_size, args: '\0'); |
1092 | |
1093 | Status error; |
1094 | lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; |
1095 | const size_t bytes_read = |
1096 | target.ReadMemory(addr: start, dst: data_sp->GetBytes(), dst_len: data_sp->GetByteSize(), |
1097 | error, force_live_memory, load_addr_ptr: &load_addr); |
1098 | const bool data_from_file = load_addr == LLDB_INVALID_ADDRESS; |
1099 | |
1100 | if (bytes_read == 0) { |
1101 | if (error_strm_ptr) { |
1102 | if (const char *error_cstr = error.AsCString()) |
1103 | error_strm_ptr->Printf(format: "error: %s\n" , error_cstr); |
1104 | } |
1105 | return 0; |
1106 | } |
1107 | |
1108 | if (bytes_read != data_sp->GetByteSize()) |
1109 | data_sp->SetByteSize(bytes_read); |
1110 | DataExtractor data(data_sp, m_arch.GetByteOrder(), |
1111 | m_arch.GetAddressByteSize()); |
1112 | return DecodeInstructions(base_addr: start, data, data_offset: 0, |
1113 | num_instructions: limit.kind == Limit::Instructions ? limit.value |
1114 | : UINT32_MAX, |
1115 | append: false, data_from_file); |
1116 | } |
1117 | |
1118 | // Disassembler copy constructor |
1119 | Disassembler::Disassembler(const ArchSpec &arch, const char *flavor) |
1120 | : m_arch(arch), m_instruction_list(), m_flavor() { |
1121 | if (flavor == nullptr) |
1122 | m_flavor.assign(s: "default" ); |
1123 | else |
1124 | m_flavor.assign(s: flavor); |
1125 | |
1126 | // If this is an arm variant that can only include thumb (T16, T32) |
1127 | // instructions, force the arch triple to be "thumbv.." instead of "armv..." |
1128 | if (arch.IsAlwaysThumbInstructions()) { |
1129 | std::string thumb_arch_name(arch.GetTriple().getArchName().str()); |
1130 | // Replace "arm" with "thumb" so we get all thumb variants correct |
1131 | if (thumb_arch_name.size() > 3) { |
1132 | thumb_arch_name.erase(pos: 0, n: 3); |
1133 | thumb_arch_name.insert(pos: 0, s: "thumb" ); |
1134 | } |
1135 | m_arch.SetTriple(thumb_arch_name.c_str()); |
1136 | } |
1137 | } |
1138 | |
1139 | Disassembler::~Disassembler() = default; |
1140 | |
1141 | InstructionList &Disassembler::GetInstructionList() { |
1142 | return m_instruction_list; |
1143 | } |
1144 | |
1145 | const InstructionList &Disassembler::GetInstructionList() const { |
1146 | return m_instruction_list; |
1147 | } |
1148 | |
1149 | // Class PseudoInstruction |
1150 | |
1151 | PseudoInstruction::PseudoInstruction() |
1152 | : Instruction(Address(), AddressClass::eUnknown), m_description() {} |
1153 | |
1154 | PseudoInstruction::~PseudoInstruction() = default; |
1155 | |
1156 | bool PseudoInstruction::DoesBranch() { |
1157 | // This is NOT a valid question for a pseudo instruction. |
1158 | return false; |
1159 | } |
1160 | |
1161 | bool PseudoInstruction::HasDelaySlot() { |
1162 | // This is NOT a valid question for a pseudo instruction. |
1163 | return false; |
1164 | } |
1165 | |
1166 | bool PseudoInstruction::IsLoad() { return false; } |
1167 | |
1168 | bool PseudoInstruction::IsAuthenticated() { return false; } |
1169 | |
1170 | size_t PseudoInstruction::(const lldb_private::Disassembler &disassembler, |
1171 | const lldb_private::DataExtractor &data, |
1172 | lldb::offset_t data_offset) { |
1173 | return m_opcode.GetByteSize(); |
1174 | } |
1175 | |
1176 | void PseudoInstruction::SetOpcode(size_t opcode_size, void *opcode_data) { |
1177 | if (!opcode_data) |
1178 | return; |
1179 | |
1180 | switch (opcode_size) { |
1181 | case 8: { |
1182 | uint8_t value8 = *((uint8_t *)opcode_data); |
1183 | m_opcode.SetOpcode8(inst: value8, order: eByteOrderInvalid); |
1184 | break; |
1185 | } |
1186 | case 16: { |
1187 | uint16_t value16 = *((uint16_t *)opcode_data); |
1188 | m_opcode.SetOpcode16(inst: value16, order: eByteOrderInvalid); |
1189 | break; |
1190 | } |
1191 | case 32: { |
1192 | uint32_t value32 = *((uint32_t *)opcode_data); |
1193 | m_opcode.SetOpcode32(inst: value32, order: eByteOrderInvalid); |
1194 | break; |
1195 | } |
1196 | case 64: { |
1197 | uint64_t value64 = *((uint64_t *)opcode_data); |
1198 | m_opcode.SetOpcode64(inst: value64, order: eByteOrderInvalid); |
1199 | break; |
1200 | } |
1201 | default: |
1202 | break; |
1203 | } |
1204 | } |
1205 | |
1206 | void PseudoInstruction::SetDescription(llvm::StringRef description) { |
1207 | m_description = std::string(description); |
1208 | } |
1209 | |
1210 | Instruction::Operand Instruction::Operand::BuildRegister(ConstString &r) { |
1211 | Operand ret; |
1212 | ret.m_type = Type::Register; |
1213 | ret.m_register = r; |
1214 | return ret; |
1215 | } |
1216 | |
1217 | Instruction::Operand Instruction::Operand::BuildImmediate(lldb::addr_t imm, |
1218 | bool neg) { |
1219 | Operand ret; |
1220 | ret.m_type = Type::Immediate; |
1221 | ret.m_immediate = imm; |
1222 | ret.m_negative = neg; |
1223 | return ret; |
1224 | } |
1225 | |
1226 | Instruction::Operand Instruction::Operand::BuildImmediate(int64_t imm) { |
1227 | Operand ret; |
1228 | ret.m_type = Type::Immediate; |
1229 | if (imm < 0) { |
1230 | ret.m_immediate = -imm; |
1231 | ret.m_negative = true; |
1232 | } else { |
1233 | ret.m_immediate = imm; |
1234 | ret.m_negative = false; |
1235 | } |
1236 | return ret; |
1237 | } |
1238 | |
1239 | Instruction::Operand |
1240 | Instruction::Operand::BuildDereference(const Operand &ref) { |
1241 | Operand ret; |
1242 | ret.m_type = Type::Dereference; |
1243 | ret.m_children = {ref}; |
1244 | return ret; |
1245 | } |
1246 | |
1247 | Instruction::Operand Instruction::Operand::BuildSum(const Operand &lhs, |
1248 | const Operand &rhs) { |
1249 | Operand ret; |
1250 | ret.m_type = Type::Sum; |
1251 | ret.m_children = {lhs, rhs}; |
1252 | return ret; |
1253 | } |
1254 | |
1255 | Instruction::Operand Instruction::Operand::BuildProduct(const Operand &lhs, |
1256 | const Operand &rhs) { |
1257 | Operand ret; |
1258 | ret.m_type = Type::Product; |
1259 | ret.m_children = {lhs, rhs}; |
1260 | return ret; |
1261 | } |
1262 | |
1263 | std::function<bool(const Instruction::Operand &)> |
1264 | lldb_private::OperandMatchers::MatchBinaryOp( |
1265 | std::function<bool(const Instruction::Operand &)> base, |
1266 | std::function<bool(const Instruction::Operand &)> left, |
1267 | std::function<bool(const Instruction::Operand &)> right) { |
1268 | return [base, left, right](const Instruction::Operand &op) -> bool { |
1269 | return (base(op) && op.m_children.size() == 2 && |
1270 | ((left(op.m_children[0]) && right(op.m_children[1])) || |
1271 | (left(op.m_children[1]) && right(op.m_children[0])))); |
1272 | }; |
1273 | } |
1274 | |
1275 | std::function<bool(const Instruction::Operand &)> |
1276 | lldb_private::OperandMatchers::MatchUnaryOp( |
1277 | std::function<bool(const Instruction::Operand &)> base, |
1278 | std::function<bool(const Instruction::Operand &)> child) { |
1279 | return [base, child](const Instruction::Operand &op) -> bool { |
1280 | return (base(op) && op.m_children.size() == 1 && child(op.m_children[0])); |
1281 | }; |
1282 | } |
1283 | |
1284 | std::function<bool(const Instruction::Operand &)> |
1285 | lldb_private::OperandMatchers::MatchRegOp(const RegisterInfo &info) { |
1286 | return [&info](const Instruction::Operand &op) { |
1287 | return (op.m_type == Instruction::Operand::Type::Register && |
1288 | (op.m_register == ConstString(info.name) || |
1289 | op.m_register == ConstString(info.alt_name))); |
1290 | }; |
1291 | } |
1292 | |
1293 | std::function<bool(const Instruction::Operand &)> |
1294 | lldb_private::OperandMatchers::FetchRegOp(ConstString ®) { |
1295 | return [®](const Instruction::Operand &op) { |
1296 | if (op.m_type != Instruction::Operand::Type::Register) { |
1297 | return false; |
1298 | } |
1299 | reg = op.m_register; |
1300 | return true; |
1301 | }; |
1302 | } |
1303 | |
1304 | std::function<bool(const Instruction::Operand &)> |
1305 | lldb_private::OperandMatchers::MatchImmOp(int64_t imm) { |
1306 | return [imm](const Instruction::Operand &op) { |
1307 | return (op.m_type == Instruction::Operand::Type::Immediate && |
1308 | ((op.m_negative && op.m_immediate == (uint64_t)-imm) || |
1309 | (!op.m_negative && op.m_immediate == (uint64_t)imm))); |
1310 | }; |
1311 | } |
1312 | |
1313 | std::function<bool(const Instruction::Operand &)> |
1314 | lldb_private::OperandMatchers::FetchImmOp(int64_t &imm) { |
1315 | return [&imm](const Instruction::Operand &op) { |
1316 | if (op.m_type != Instruction::Operand::Type::Immediate) { |
1317 | return false; |
1318 | } |
1319 | if (op.m_negative) { |
1320 | imm = -((int64_t)op.m_immediate); |
1321 | } else { |
1322 | imm = ((int64_t)op.m_immediate); |
1323 | } |
1324 | return true; |
1325 | }; |
1326 | } |
1327 | |
1328 | std::function<bool(const Instruction::Operand &)> |
1329 | lldb_private::OperandMatchers::MatchOpType(Instruction::Operand::Type type) { |
1330 | return [type](const Instruction::Operand &op) { return op.m_type == type; }; |
1331 | } |
1332 | |