1//===-- CommandObjectDisassemble.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 "CommandObjectDisassemble.h"
10#include "lldb/Core/AddressRange.h"
11#include "lldb/Core/Disassembler.h"
12#include "lldb/Core/Module.h"
13#include "lldb/Host/OptionParser.h"
14#include "lldb/Interpreter/CommandInterpreter.h"
15#include "lldb/Interpreter/CommandOptionArgumentTable.h"
16#include "lldb/Interpreter/CommandReturnObject.h"
17#include "lldb/Interpreter/OptionArgParser.h"
18#include "lldb/Interpreter/Options.h"
19#include "lldb/Symbol/Function.h"
20#include "lldb/Symbol/Symbol.h"
21#include "lldb/Target/SectionLoadList.h"
22#include "lldb/Target/StackFrame.h"
23#include "lldb/Target/Target.h"
24#include <iterator>
25
26static constexpr unsigned default_disasm_byte_size = 32;
27static constexpr unsigned default_disasm_num_ins = 4;
28
29using namespace lldb;
30using namespace lldb_private;
31
32#define LLDB_OPTIONS_disassemble
33#include "CommandOptions.inc"
34
35CommandObjectDisassemble::CommandOptions::CommandOptions() {
36 OptionParsingStarting(execution_context: nullptr);
37}
38
39CommandObjectDisassemble::CommandOptions::~CommandOptions() = default;
40
41Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
42 uint32_t option_idx, llvm::StringRef option_arg,
43 ExecutionContext *execution_context) {
44 Status error;
45
46 const int short_option = m_getopt_table[option_idx].val;
47
48 switch (short_option) {
49 case 'm':
50 show_mixed = true;
51 break;
52
53 case 'C':
54 if (option_arg.getAsInteger(Radix: 0, Result&: num_lines_context))
55 error = Status::FromErrorStringWithFormat(
56 format: "invalid num context lines string: \"%s\"", option_arg.str().c_str());
57 break;
58
59 case 'c':
60 if (option_arg.getAsInteger(Radix: 0, Result&: num_instructions))
61 error = Status::FromErrorStringWithFormat(
62 format: "invalid num of instructions string: \"%s\"",
63 option_arg.str().c_str());
64 break;
65
66 case 'b':
67 show_bytes = true;
68 break;
69
70 case 'k':
71 show_control_flow_kind = true;
72 break;
73
74 case 's': {
75 start_addr = OptionArgParser::ToAddress(exe_ctx: execution_context, s: option_arg,
76 LLDB_INVALID_ADDRESS, error_ptr: &error);
77 if (start_addr != LLDB_INVALID_ADDRESS)
78 some_location_specified = true;
79 } break;
80 case 'e': {
81 end_addr = OptionArgParser::ToAddress(exe_ctx: execution_context, s: option_arg,
82 LLDB_INVALID_ADDRESS, error_ptr: &error);
83 if (end_addr != LLDB_INVALID_ADDRESS)
84 some_location_specified = true;
85 } break;
86
87 case 'n':
88 func_name.assign(str: std::string(option_arg));
89 some_location_specified = true;
90 break;
91
92 case 'p':
93 at_pc = true;
94 some_location_specified = true;
95 break;
96
97 case 'l':
98 frame_line = true;
99 // Disassemble the current source line kind of implies showing mixed source
100 // code context.
101 show_mixed = true;
102 some_location_specified = true;
103 break;
104
105 case 'P':
106 plugin_name.assign(str: std::string(option_arg));
107 break;
108
109 case 'F': {
110 TargetSP target_sp =
111 execution_context ? execution_context->GetTargetSP() : TargetSP();
112 if (target_sp && (target_sp->GetArchitecture().GetTriple().getArch() ==
113 llvm::Triple::x86 ||
114 target_sp->GetArchitecture().GetTriple().getArch() ==
115 llvm::Triple::x86_64)) {
116 flavor_string.assign(str: std::string(option_arg));
117 } else
118 error = Status::FromErrorStringWithFormat(
119 format: "Disassembler flavors are currently only "
120 "supported for x86 and x86_64 targets.");
121 break;
122 }
123
124 case 'X':
125 cpu_string = std::string(option_arg);
126 break;
127
128 case 'Y':
129 features_string = std::string(option_arg);
130 break;
131
132 case 'r':
133 raw = true;
134 break;
135
136 case 'f':
137 current_function = true;
138 some_location_specified = true;
139 break;
140
141 case 'A':
142 if (execution_context) {
143 const auto &target_sp = execution_context->GetTargetSP();
144 auto platform_ptr = target_sp ? target_sp->GetPlatform().get() : nullptr;
145 arch = Platform::GetAugmentedArchSpec(platform: platform_ptr, triple: option_arg);
146 }
147 break;
148
149 case 'a': {
150 symbol_containing_addr = OptionArgParser::ToAddress(
151 exe_ctx: execution_context, s: option_arg, LLDB_INVALID_ADDRESS, error_ptr: &error);
152 if (symbol_containing_addr != LLDB_INVALID_ADDRESS) {
153 some_location_specified = true;
154 }
155 } break;
156
157 case '\x01':
158 force = true;
159 break;
160
161 default:
162 llvm_unreachable("Unimplemented option");
163 }
164
165 return error;
166}
167
168void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
169 ExecutionContext *execution_context) {
170 show_mixed = false;
171 show_bytes = false;
172 show_control_flow_kind = false;
173 num_lines_context = 0;
174 num_instructions = 0;
175 func_name.clear();
176 current_function = false;
177 at_pc = false;
178 frame_line = false;
179 start_addr = LLDB_INVALID_ADDRESS;
180 end_addr = LLDB_INVALID_ADDRESS;
181 symbol_containing_addr = LLDB_INVALID_ADDRESS;
182 raw = false;
183 plugin_name.clear();
184
185 Target *target =
186 execution_context ? execution_context->GetTargetPtr() : nullptr;
187
188 if (target) {
189 // This is a hack till we get the ability to specify features based on
190 // architecture. For now GetDisassemblyFlavor is really only valid for x86
191 // (and for the llvm assembler plugin, but I'm papering over that since that
192 // is the only disassembler plugin we have...
193 if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 ||
194 target->GetArchitecture().GetTriple().getArch() ==
195 llvm::Triple::x86_64) {
196 flavor_string.assign(s: target->GetDisassemblyFlavor());
197 } else {
198 flavor_string.assign(s: "default");
199 }
200 if (const char *cpu = target->GetDisassemblyCPU())
201 cpu_string.assign(s: cpu);
202 if (const char *features = target->GetDisassemblyFeatures())
203 features_string.assign(s: features);
204 } else {
205 flavor_string.assign(s: "default");
206 cpu_string.assign(s: "default");
207 features_string.assign(s: "default");
208 }
209
210 arch.Clear();
211 some_location_specified = false;
212 force = false;
213}
214
215Status CommandObjectDisassemble::CommandOptions::OptionParsingFinished(
216 ExecutionContext *execution_context) {
217 if (!some_location_specified)
218 current_function = true;
219 return Status();
220}
221
222llvm::ArrayRef<OptionDefinition>
223CommandObjectDisassemble::CommandOptions::GetDefinitions() {
224 return llvm::ArrayRef(g_disassemble_options);
225}
226
227// CommandObjectDisassemble
228
229CommandObjectDisassemble::CommandObjectDisassemble(
230 CommandInterpreter &interpreter)
231 : CommandObjectParsed(
232 interpreter, "disassemble",
233 "Disassemble specified instructions in the current target. "
234 "Defaults to the current function for the current thread and "
235 "stack frame.",
236 "disassemble [<cmd-options>]", eCommandRequiresTarget) {}
237
238CommandObjectDisassemble::~CommandObjectDisassemble() = default;
239
240llvm::Expected<std::vector<AddressRange>>
241CommandObjectDisassemble::CheckRangeSize(std::vector<AddressRange> ranges,
242 llvm::StringRef what) {
243 addr_t total_range_size = 0;
244 for (const AddressRange &r : ranges)
245 total_range_size += r.GetByteSize();
246
247 if (m_options.num_instructions > 0 || m_options.force ||
248 total_range_size < GetDebugger().GetStopDisassemblyMaxSize())
249 return ranges;
250
251 StreamString msg;
252 msg << "Not disassembling " << what << " because it is very large ";
253 for (const AddressRange &r : ranges)
254 r.Dump(s: &msg, target: &GetTarget(), style: Address::DumpStyleLoadAddress,
255 fallback_style: Address::DumpStyleFileAddress);
256 msg << ". To disassemble specify an instruction count limit, start/stop "
257 "addresses or use the --force option.";
258 return llvm::createStringError(S: msg.GetString());
259}
260
261llvm::Expected<std::vector<AddressRange>>
262CommandObjectDisassemble::GetContainingAddressRanges() {
263 std::vector<AddressRange> ranges;
264 const auto &get_ranges = [&](Address addr) {
265 ModuleSP module_sp(addr.GetModule());
266 SymbolContext sc;
267 bool resolve_tail_call_address = true;
268 addr.GetModule()->ResolveSymbolContextForAddress(
269 so_addr: addr, resolve_scope: eSymbolContextEverything, sc, resolve_tail_call_address);
270 if (sc.function || sc.symbol) {
271 AddressRange range;
272 for (uint32_t idx = 0;
273 sc.GetAddressRange(scope: eSymbolContextFunction | eSymbolContextSymbol,
274 range_idx: idx, use_inline_block_range: false, range);
275 ++idx)
276 ranges.push_back(x: range);
277 }
278 };
279
280 Target &target = GetTarget();
281 if (target.HasLoadedSections()) {
282 Address symbol_containing_address;
283 if (target.ResolveLoadAddress(load_addr: m_options.symbol_containing_addr,
284 so_addr&: symbol_containing_address)) {
285 get_ranges(symbol_containing_address);
286 }
287 } else {
288 for (lldb::ModuleSP module_sp : target.GetImages().Modules()) {
289 Address file_address;
290 if (module_sp->ResolveFileAddress(vm_addr: m_options.symbol_containing_addr,
291 so_addr&: file_address)) {
292 get_ranges(file_address);
293 }
294 }
295 }
296
297 if (ranges.empty()) {
298 return llvm::createStringError(
299 EC: llvm::inconvertibleErrorCode(),
300 Fmt: "Could not find function bounds for address 0x%" PRIx64,
301 Vals: m_options.symbol_containing_addr);
302 }
303
304 return CheckRangeSize(ranges: std::move(ranges), what: "the function");
305}
306
307llvm::Expected<std::vector<AddressRange>>
308CommandObjectDisassemble::GetCurrentFunctionRanges() {
309 Process *process = m_exe_ctx.GetProcessPtr();
310 StackFrame *frame = m_exe_ctx.GetFramePtr();
311 if (!frame) {
312 if (process) {
313 return llvm::createStringError(
314 Fmt: "Cannot disassemble around the current function without the process "
315 "being stopped.\n");
316 }
317 return llvm::createStringError(
318 Fmt: "Cannot disassemble around the current function without a selected "
319 "frame: no currently running process.\n");
320 }
321 SymbolContext sc =
322 frame->GetSymbolContext(resolve_scope: eSymbolContextFunction | eSymbolContextSymbol);
323 std::vector<AddressRange> ranges;
324 if (sc.function)
325 ranges = sc.function->GetAddressRanges();
326 else if (sc.symbol && sc.symbol->ValueIsAddress())
327 ranges.emplace_back(args: sc.symbol->GetAddress(), args: sc.symbol->GetByteSize());
328 else
329 ranges.emplace_back(args: frame->GetFrameCodeAddress(), args: default_disasm_byte_size);
330
331 return CheckRangeSize(ranges: std::move(ranges), what: "the current function");
332}
333
334llvm::Expected<std::vector<AddressRange>>
335CommandObjectDisassemble::GetCurrentLineRanges() {
336 Process *process = m_exe_ctx.GetProcessPtr();
337 StackFrame *frame = m_exe_ctx.GetFramePtr();
338 if (!frame) {
339 if (process) {
340 return llvm::createStringError(
341 EC: llvm::inconvertibleErrorCode(),
342 S: "Cannot disassemble around the current "
343 "function without the process being stopped.\n");
344 } else {
345 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
346 S: "Cannot disassemble around the current "
347 "line without a selected frame: "
348 "no currently running process.\n");
349 }
350 }
351
352 LineEntry pc_line_entry(
353 frame->GetSymbolContext(resolve_scope: eSymbolContextLineEntry).line_entry);
354 if (pc_line_entry.IsValid())
355 return std::vector<AddressRange>{pc_line_entry.range};
356
357 // No line entry, so just disassemble around the current pc
358 m_options.show_mixed = false;
359 return GetPCRanges();
360}
361
362llvm::Expected<std::vector<AddressRange>>
363CommandObjectDisassemble::GetNameRanges(CommandReturnObject &result) {
364 ConstString name(m_options.func_name.c_str());
365
366 ModuleFunctionSearchOptions function_options;
367 function_options.include_symbols = true;
368 function_options.include_inlines = true;
369
370 // Find functions matching the given name.
371 SymbolContextList sc_list;
372 GetTarget().GetImages().FindFunctions(name, name_type_mask: eFunctionNameTypeAuto,
373 options: function_options, sc_list);
374
375 std::vector<AddressRange> ranges;
376 llvm::Error range_errs = llvm::Error::success();
377 const uint32_t scope =
378 eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol;
379 const bool use_inline_block_range = true;
380 for (SymbolContext sc : sc_list.SymbolContexts()) {
381 std::vector<AddressRange> fn_ranges;
382 AddressRange range;
383 for (uint32_t range_idx = 0;
384 sc.GetAddressRange(scope, range_idx, use_inline_block_range, range);
385 ++range_idx)
386 fn_ranges.push_back(x: std::move(range));
387
388 if (llvm::Expected<std::vector<AddressRange>> checked_ranges =
389 CheckRangeSize(ranges: std::move(fn_ranges), what: "a function"))
390 llvm::move(Range&: *checked_ranges, Out: std::back_inserter(x&: ranges));
391 else
392 range_errs =
393 joinErrors(E1: std::move(range_errs), E2: checked_ranges.takeError());
394 }
395 if (ranges.empty()) {
396 if (range_errs)
397 return std::move(range_errs);
398 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
399 Fmt: "Unable to find symbol with name '%s'.\n",
400 Vals: name.GetCString());
401 }
402 if (range_errs)
403 result.AppendWarning(in_string: toString(E: std::move(range_errs)));
404 return ranges;
405}
406
407llvm::Expected<std::vector<AddressRange>>
408CommandObjectDisassemble::GetPCRanges() {
409 Process *process = m_exe_ctx.GetProcessPtr();
410 StackFrame *frame = m_exe_ctx.GetFramePtr();
411 if (!frame) {
412 if (process) {
413 return llvm::createStringError(
414 EC: llvm::inconvertibleErrorCode(),
415 S: "Cannot disassemble around the current "
416 "function without the process being stopped.\n");
417 } else {
418 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
419 S: "Cannot disassemble around the current "
420 "PC without a selected frame: "
421 "no currently running process.\n");
422 }
423 }
424
425 if (m_options.num_instructions == 0) {
426 // Disassembling at the PC always disassembles some number of
427 // instructions (not the whole function).
428 m_options.num_instructions = default_disasm_num_ins;
429 }
430 return std::vector<AddressRange>{{frame->GetFrameCodeAddress(), 0}};
431}
432
433llvm::Expected<std::vector<AddressRange>>
434CommandObjectDisassemble::GetStartEndAddressRanges() {
435 addr_t size = 0;
436 if (m_options.end_addr != LLDB_INVALID_ADDRESS) {
437 if (m_options.end_addr <= m_options.start_addr) {
438 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),
439 S: "End address before start address.");
440 }
441 size = m_options.end_addr - m_options.start_addr;
442 }
443 return std::vector<AddressRange>{{Address(m_options.start_addr), size}};
444}
445
446llvm::Expected<std::vector<AddressRange>>
447CommandObjectDisassemble::GetRangesForSelectedMode(
448 CommandReturnObject &result) {
449 if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS)
450 return CommandObjectDisassemble::GetContainingAddressRanges();
451 if (m_options.current_function)
452 return CommandObjectDisassemble::GetCurrentFunctionRanges();
453 if (m_options.frame_line)
454 return CommandObjectDisassemble::GetCurrentLineRanges();
455 if (!m_options.func_name.empty())
456 return CommandObjectDisassemble::GetNameRanges(result);
457 if (m_options.start_addr != LLDB_INVALID_ADDRESS)
458 return CommandObjectDisassemble::GetStartEndAddressRanges();
459 return CommandObjectDisassemble::GetPCRanges();
460}
461
462void CommandObjectDisassemble::DoExecute(Args &command,
463 CommandReturnObject &result) {
464 Target &target = GetTarget();
465
466 if (!m_options.arch.IsValid())
467 m_options.arch = target.GetArchitecture();
468
469 if (!m_options.arch.IsValid()) {
470 result.AppendError(
471 in_string: "use the --arch option or set the target architecture to disassemble");
472 return;
473 }
474
475 const char *plugin_name = m_options.GetPluginName();
476 const char *flavor_string = m_options.GetFlavorString();
477 const char *cpu_string = m_options.GetCPUString();
478 const char *features_string = m_options.GetFeaturesString();
479
480 DisassemblerSP disassembler = Disassembler::FindPlugin(
481 arch: m_options.arch, flavor: flavor_string, cpu: cpu_string, features: features_string, plugin_name);
482
483 if (!disassembler) {
484 if (plugin_name) {
485 result.AppendErrorWithFormat(
486 format: "Unable to find Disassembler plug-in named '%s' that supports the "
487 "'%s' architecture.\n",
488 plugin_name, m_options.arch.GetArchitectureName());
489 } else
490 result.AppendErrorWithFormat(
491 format: "Unable to find Disassembler plug-in for the '%s' architecture.\n",
492 m_options.arch.GetArchitectureName());
493 return;
494 } else if (flavor_string != nullptr && !disassembler->FlavorValidForArchSpec(
495 arch: m_options.arch, flavor: flavor_string))
496 result.AppendWarningWithFormat(
497 format: "invalid disassembler flavor \"%s\", using default.\n", flavor_string);
498
499 result.SetStatus(eReturnStatusSuccessFinishResult);
500
501 if (!command.empty()) {
502 result.AppendErrorWithFormat(
503 format: "\"disassemble\" arguments are specified as options.\n");
504 const int terminal_width =
505 GetCommandInterpreter().GetDebugger().GetTerminalWidth();
506 GetOptions()->GenerateOptionUsage(strm&: result.GetErrorStream(), cmd&: *this,
507 screen_width: terminal_width);
508 return;
509 }
510
511 if (m_options.show_mixed && m_options.num_lines_context == 0)
512 m_options.num_lines_context = 2;
513
514 // Always show the PC in the disassembly
515 uint32_t options = Disassembler::eOptionMarkPCAddress;
516
517 // Mark the source line for the current PC only if we are doing mixed source
518 // and assembly
519 if (m_options.show_mixed)
520 options |= Disassembler::eOptionMarkPCSourceLine;
521
522 if (m_options.show_bytes)
523 options |= Disassembler::eOptionShowBytes;
524
525 if (m_options.show_control_flow_kind)
526 options |= Disassembler::eOptionShowControlFlowKind;
527
528 if (m_options.raw)
529 options |= Disassembler::eOptionRawOuput;
530
531 llvm::Expected<std::vector<AddressRange>> ranges =
532 GetRangesForSelectedMode(result);
533 if (!ranges) {
534 result.AppendError(in_string: toString(E: ranges.takeError()));
535 return;
536 }
537
538 bool print_sc_header = ranges->size() > 1;
539 for (AddressRange cur_range : *ranges) {
540 Disassembler::Limit limit;
541 if (m_options.num_instructions == 0) {
542 limit = {.kind: Disassembler::Limit::Bytes, .value: cur_range.GetByteSize()};
543 if (limit.value == 0)
544 limit.value = default_disasm_byte_size;
545 } else {
546 limit = {.kind: Disassembler::Limit::Instructions, .value: m_options.num_instructions};
547 }
548 if (Disassembler::Disassemble(
549 debugger&: GetDebugger(), arch: m_options.arch, plugin_name, flavor: flavor_string,
550 cpu: cpu_string, features: features_string, exe_ctx: m_exe_ctx, start: cur_range.GetBaseAddress(),
551 limit, mixed_source_and_assembly: m_options.show_mixed,
552 num_mixed_context_lines: m_options.show_mixed ? m_options.num_lines_context : 0, options,
553 strm&: result.GetOutputStream())) {
554 result.SetStatus(eReturnStatusSuccessFinishResult);
555 } else {
556 if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) {
557 result.AppendErrorWithFormat(
558 format: "Failed to disassemble memory in function at 0x%8.8" PRIx64 ".\n",
559 m_options.symbol_containing_addr);
560 } else {
561 result.AppendErrorWithFormat(
562 format: "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
563 cur_range.GetBaseAddress().GetLoadAddress(target: &target));
564 }
565 }
566 if (print_sc_header)
567 result.GetOutputStream() << "\n";
568 }
569}
570

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of lldb/source/Commands/CommandObjectDisassemble.cpp