1 //===-- CommandObjectDisassemble.cpp ----------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "CommandObjectDisassemble.h"
11 #include "lldb/Core/AddressRange.h"
12 #include "lldb/Core/Disassembler.h"
13 #include "lldb/Core/Module.h"
14 #include "lldb/Core/SourceManager.h"
15 #include "lldb/Host/OptionParser.h"
16 #include "lldb/Interpreter/CommandCompletions.h"
17 #include "lldb/Interpreter/CommandInterpreter.h"
18 #include "lldb/Interpreter/CommandReturnObject.h"
19 #include "lldb/Interpreter/OptionArgParser.h"
20 #include "lldb/Interpreter/Options.h"
21 #include "lldb/Symbol/Function.h"
22 #include "lldb/Symbol/Symbol.h"
23 #include "lldb/Target/Process.h"
24 #include "lldb/Target/SectionLoadList.h"
25 #include "lldb/Target/StackFrame.h"
26 #include "lldb/Target/Target.h"
28 #define DEFAULT_DISASM_BYTE_SIZE 32
29 #define DEFAULT_DISASM_NUM_INS 4
32 using namespace lldb_private;
34 static constexpr OptionDefinition g_disassemble_options[] = {
36 { LLDB_OPT_SET_ALL, false, "bytes", 'b', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Show opcode bytes when disassembling." },
37 { LLDB_OPT_SET_ALL, false, "context", 'C', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNumLines, "Number of context lines of source to show." },
38 { LLDB_OPT_SET_ALL, false, "mixed", 'm', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Enable mixed source and assembly display." },
39 { LLDB_OPT_SET_ALL, false, "raw", 'r', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Print raw disassembly with no symbol information." },
40 { LLDB_OPT_SET_ALL, false, "plugin", 'P', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypePlugin, "Name of the disassembler plugin you want to use." },
41 { LLDB_OPT_SET_ALL, false, "flavor", 'F', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeDisassemblyFlavor, "Name of the disassembly flavor you want to use. "
42 "Currently the only valid options are default, and for Intel "
43 "architectures, att and intel." },
44 { LLDB_OPT_SET_ALL, false, "arch", 'A', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeArchitecture, "Specify the architecture to use from cross disassembly." },
46 LLDB_OPT_SET_2, true, "start-address", 's', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddressOrExpression, "Address at which to start disassembling." },
47 { LLDB_OPT_SET_1, false, "end-address", 'e', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddressOrExpression, "Address at which to end disassembling." },
51 LLDB_OPT_SET_5, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeNumLines, "Number of instructions to display." },
52 { LLDB_OPT_SET_3, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, "Disassemble entire contents of the given function name." },
53 { LLDB_OPT_SET_4, false, "frame", 'f', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Disassemble from the start of the current frame's function." },
54 { LLDB_OPT_SET_5, false, "pc", 'p', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Disassemble around the current pc." },
55 { LLDB_OPT_SET_6, false, "line", 'l', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Disassemble the current frame's current source line instructions if there is debug line "
56 "table information, else disassemble around the pc." },
57 { LLDB_OPT_SET_7, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, {}, 0, eArgTypeAddressOrExpression, "Disassemble function containing this address." },
61 CommandObjectDisassemble::CommandOptions::CommandOptions()
62 : Options(), num_lines_context(0), num_instructions(0), func_name(),
63 current_function(false), start_addr(), end_addr(), at_pc(false),
64 frame_line(false), plugin_name(), flavor_string(), arch(),
65 some_location_specified(false), symbol_containing_addr() {
66 OptionParsingStarting(nullptr);
69 CommandObjectDisassemble::CommandOptions::~CommandOptions() = default;
71 Status CommandObjectDisassemble::CommandOptions::SetOptionValue(
72 uint32_t option_idx, llvm::StringRef option_arg,
73 ExecutionContext *execution_context) {
76 const int short_option = m_getopt_table[option_idx].val;
78 switch (short_option) {
84 if (option_arg.getAsInteger(0, num_lines_context))
85 error.SetErrorStringWithFormat("invalid num context lines string: \"%s\"",
86 option_arg.str().c_str());
90 if (option_arg.getAsInteger(0, num_instructions))
91 error.SetErrorStringWithFormat(
92 "invalid num of instructions string: \"%s\"",
93 option_arg.str().c_str());
101 start_addr = OptionArgParser::ToAddress(execution_context, option_arg,
102 LLDB_INVALID_ADDRESS, &error);
103 if (start_addr != LLDB_INVALID_ADDRESS)
104 some_location_specified = true;
107 end_addr = OptionArgParser::ToAddress(execution_context, option_arg,
108 LLDB_INVALID_ADDRESS, &error);
109 if (end_addr != LLDB_INVALID_ADDRESS)
110 some_location_specified = true;
114 func_name.assign(option_arg);
115 some_location_specified = true;
120 some_location_specified = true;
125 // Disassemble the current source line kind of implies showing mixed source
128 some_location_specified = true;
132 plugin_name.assign(option_arg);
137 execution_context ? execution_context->GetTargetSP() : TargetSP();
138 if (target_sp && (target_sp->GetArchitecture().GetTriple().getArch() ==
140 target_sp->GetArchitecture().GetTriple().getArch() ==
141 llvm::Triple::x86_64)) {
142 flavor_string.assign(option_arg);
144 error.SetErrorStringWithFormat("Disassembler flavors are currently only "
145 "supported for x86 and x86_64 targets.");
154 current_function = true;
155 some_location_specified = true;
159 if (execution_context) {
160 const auto &target_sp = execution_context->GetTargetSP();
161 auto platform_ptr = target_sp ? target_sp->GetPlatform().get() : nullptr;
162 arch = Platform::GetAugmentedArchSpec(platform_ptr, option_arg);
167 symbol_containing_addr = OptionArgParser::ToAddress(
168 execution_context, option_arg, LLDB_INVALID_ADDRESS, &error);
169 if (symbol_containing_addr != LLDB_INVALID_ADDRESS) {
170 some_location_specified = true;
175 error.SetErrorStringWithFormat("unrecognized short option '%c'",
183 void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
184 ExecutionContext *execution_context) {
187 num_lines_context = 0;
188 num_instructions = 0;
190 current_function = false;
193 start_addr = LLDB_INVALID_ADDRESS;
194 end_addr = LLDB_INVALID_ADDRESS;
195 symbol_containing_addr = LLDB_INVALID_ADDRESS;
200 execution_context ? execution_context->GetTargetPtr() : nullptr;
202 // This is a hack till we get the ability to specify features based on
203 // architecture. For now GetDisassemblyFlavor is really only valid for x86
204 // (and for the llvm assembler plugin, but I'm papering over that since that
205 // is the only disassembler plugin we have...
207 if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 ||
208 target->GetArchitecture().GetTriple().getArch() ==
209 llvm::Triple::x86_64) {
210 flavor_string.assign(target->GetDisassemblyFlavor());
212 flavor_string.assign("default");
215 flavor_string.assign("default");
218 some_location_specified = false;
221 Status CommandObjectDisassemble::CommandOptions::OptionParsingFinished(
222 ExecutionContext *execution_context) {
223 if (!some_location_specified)
224 current_function = true;
228 llvm::ArrayRef<OptionDefinition>
229 CommandObjectDisassemble::CommandOptions::GetDefinitions() {
230 return llvm::makeArrayRef(g_disassemble_options);
233 //-------------------------------------------------------------------------
234 // CommandObjectDisassemble
235 //-------------------------------------------------------------------------
237 CommandObjectDisassemble::CommandObjectDisassemble(
238 CommandInterpreter &interpreter)
239 : CommandObjectParsed(
240 interpreter, "disassemble",
241 "Disassemble specified instructions in the current target. "
242 "Defaults to the current function for the current thread and "
244 "disassemble [<cmd-options>]"),
247 CommandObjectDisassemble::~CommandObjectDisassemble() = default;
249 bool CommandObjectDisassemble::DoExecute(Args &command,
250 CommandReturnObject &result) {
251 Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
252 if (target == nullptr) {
253 result.AppendError("invalid target, create a debug target using the "
254 "'target create' command");
255 result.SetStatus(eReturnStatusFailed);
258 if (!m_options.arch.IsValid())
259 m_options.arch = target->GetArchitecture();
261 if (!m_options.arch.IsValid()) {
263 "use the --arch option or set the target architecture to disassemble");
264 result.SetStatus(eReturnStatusFailed);
268 const char *plugin_name = m_options.GetPluginName();
269 const char *flavor_string = m_options.GetFlavorString();
271 DisassemblerSP disassembler =
272 Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name);
276 result.AppendErrorWithFormat(
277 "Unable to find Disassembler plug-in named '%s' that supports the "
278 "'%s' architecture.\n",
279 plugin_name, m_options.arch.GetArchitectureName());
281 result.AppendErrorWithFormat(
282 "Unable to find Disassembler plug-in for the '%s' architecture.\n",
283 m_options.arch.GetArchitectureName());
284 result.SetStatus(eReturnStatusFailed);
286 } else if (flavor_string != nullptr &&
287 !disassembler->FlavorValidForArchSpec(m_options.arch,
289 result.AppendWarningWithFormat(
290 "invalid disassembler flavor \"%s\", using default.\n", flavor_string);
292 result.SetStatus(eReturnStatusSuccessFinishResult);
294 if (!command.empty()) {
295 result.AppendErrorWithFormat(
296 "\"disassemble\" arguments are specified as options.\n");
297 const int terminal_width =
298 GetCommandInterpreter().GetDebugger().GetTerminalWidth();
299 GetOptions()->GenerateOptionUsage(result.GetErrorStream(), this,
301 result.SetStatus(eReturnStatusFailed);
305 if (m_options.show_mixed && m_options.num_lines_context == 0)
306 m_options.num_lines_context = 2;
308 // Always show the PC in the disassembly
309 uint32_t options = Disassembler::eOptionMarkPCAddress;
311 // Mark the source line for the current PC only if we are doing mixed source
313 if (m_options.show_mixed)
314 options |= Disassembler::eOptionMarkPCSourceLine;
316 if (m_options.show_bytes)
317 options |= Disassembler::eOptionShowBytes;
320 options |= Disassembler::eOptionRawOuput;
322 if (!m_options.func_name.empty()) {
323 ConstString name(m_options.func_name.c_str());
325 if (Disassembler::Disassemble(
326 m_interpreter.GetDebugger(), m_options.arch, plugin_name,
327 flavor_string, m_exe_ctx, name,
329 m_options.num_instructions, m_options.show_mixed,
330 m_options.show_mixed ? m_options.num_lines_context : 0, options,
331 result.GetOutputStream())) {
332 result.SetStatus(eReturnStatusSuccessFinishResult);
334 result.AppendErrorWithFormat("Unable to find symbol with name '%s'.\n",
336 result.SetStatus(eReturnStatusFailed);
339 std::vector<AddressRange> ranges;
341 StackFrame *frame = m_exe_ctx.GetFramePtr();
342 if (m_options.frame_line) {
343 if (frame == nullptr) {
344 result.AppendError("Cannot disassemble around the current line without "
345 "a selected frame.\n");
346 result.SetStatus(eReturnStatusFailed);
349 LineEntry pc_line_entry(
350 frame->GetSymbolContext(eSymbolContextLineEntry).line_entry);
351 if (pc_line_entry.IsValid()) {
352 range = pc_line_entry.range;
355 true; // No line entry, so just disassemble around the current pc
356 m_options.show_mixed = false;
358 } else if (m_options.current_function) {
359 if (frame == nullptr) {
360 result.AppendError("Cannot disassemble around the current function "
361 "without a selected frame.\n");
362 result.SetStatus(eReturnStatusFailed);
365 Symbol *symbol = frame->GetSymbolContext(eSymbolContextSymbol).symbol;
367 range.GetBaseAddress() = symbol->GetAddress();
368 range.SetByteSize(symbol->GetByteSize());
372 // Did the "m_options.frame_line" find a valid range already? If so skip
374 if (range.GetByteSize() == 0) {
375 if (m_options.at_pc) {
376 if (frame == nullptr) {
377 result.AppendError("Cannot disassemble around the current PC without "
378 "a selected frame.\n");
379 result.SetStatus(eReturnStatusFailed);
382 range.GetBaseAddress() = frame->GetFrameCodeAddress();
383 if (m_options.num_instructions == 0) {
384 // Disassembling at the PC always disassembles some number of
385 // instructions (not the whole function).
386 m_options.num_instructions = DEFAULT_DISASM_NUM_INS;
388 ranges.push_back(range);
390 range.GetBaseAddress().SetOffset(m_options.start_addr);
391 if (range.GetBaseAddress().IsValid()) {
392 if (m_options.end_addr != LLDB_INVALID_ADDRESS) {
393 if (m_options.end_addr <= m_options.start_addr) {
394 result.AppendErrorWithFormat(
395 "End address before start address.\n");
396 result.SetStatus(eReturnStatusFailed);
399 range.SetByteSize(m_options.end_addr - m_options.start_addr);
401 ranges.push_back(range);
403 if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS &&
405 if (!target->GetSectionLoadList().IsEmpty()) {
407 Address symbol_containing_address;
408 if (target->GetSectionLoadList().ResolveLoadAddress(
409 m_options.symbol_containing_addr,
410 symbol_containing_address)) {
411 ModuleSP module_sp(symbol_containing_address.GetModule());
413 bool resolve_tail_call_address = true; // PC can be one past the
414 // address range of the
416 module_sp->ResolveSymbolContextForAddress(
417 symbol_containing_address, eSymbolContextEverything, sc,
418 resolve_tail_call_address);
419 if (sc.function || sc.symbol) {
420 sc.GetAddressRange(eSymbolContextFunction |
421 eSymbolContextSymbol,
430 result.AppendErrorWithFormat(
431 "Could not find function bounds for address 0x%" PRIx64
433 m_options.symbol_containing_addr);
434 result.SetStatus(eReturnStatusFailed);
437 ranges.push_back(range);
439 for (lldb::ModuleSP module_sp : target->GetImages().Modules()) {
440 lldb::addr_t file_addr = m_options.symbol_containing_addr;
441 Address file_address;
442 if (module_sp->ResolveFileAddress(file_addr, file_address)) {
444 bool resolve_tail_call_address = true; // PC can be one past
445 // the address range of
447 module_sp->ResolveSymbolContextForAddress(
448 file_address, eSymbolContextEverything, sc,
449 resolve_tail_call_address);
450 if (sc.function || sc.symbol) {
451 sc.GetAddressRange(eSymbolContextFunction |
452 eSymbolContextSymbol,
454 ranges.push_back(range);
463 ranges.push_back(range);
465 if (m_options.num_instructions != 0) {
466 if (ranges.empty()) {
467 // The default action is to disassemble the current frame function.
469 SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction |
470 eSymbolContextSymbol));
472 range.GetBaseAddress() =
473 sc.function->GetAddressRange().GetBaseAddress();
474 else if (sc.symbol && sc.symbol->ValueIsAddress())
475 range.GetBaseAddress() = sc.symbol->GetAddress();
477 range.GetBaseAddress() = frame->GetFrameCodeAddress();
480 if (!range.GetBaseAddress().IsValid()) {
481 result.AppendError("invalid frame");
482 result.SetStatus(eReturnStatusFailed);
487 bool print_sc_header = ranges.size() > 1;
488 for (AddressRange cur_range : ranges) {
489 if (Disassembler::Disassemble(
490 m_interpreter.GetDebugger(), m_options.arch, plugin_name,
491 flavor_string, m_exe_ctx, cur_range.GetBaseAddress(),
492 m_options.num_instructions, m_options.show_mixed,
493 m_options.show_mixed ? m_options.num_lines_context : 0, options,
494 result.GetOutputStream())) {
495 result.SetStatus(eReturnStatusSuccessFinishResult);
497 if (m_options.start_addr != LLDB_INVALID_ADDRESS)
498 result.AppendErrorWithFormat(
499 "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
500 m_options.start_addr);
501 else if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS)
502 result.AppendErrorWithFormat(
503 "Failed to disassemble memory in function at 0x%8.8" PRIx64
505 m_options.symbol_containing_addr);
506 result.SetStatus(eReturnStatusFailed);
510 result.AppendMessage("\n");
512 if (ranges.empty()) {
513 // The default action is to disassemble the current frame function.
515 SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction |
516 eSymbolContextSymbol));
518 range = sc.function->GetAddressRange();
519 else if (sc.symbol && sc.symbol->ValueIsAddress()) {
520 range.GetBaseAddress() = sc.symbol->GetAddress();
521 range.SetByteSize(sc.symbol->GetByteSize());
523 range.GetBaseAddress() = frame->GetFrameCodeAddress();
525 result.AppendError("invalid frame");
526 result.SetStatus(eReturnStatusFailed);
529 ranges.push_back(range);
532 bool print_sc_header = ranges.size() > 1;
533 for (AddressRange cur_range : ranges) {
534 if (cur_range.GetByteSize() == 0)
535 cur_range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE);
537 if (Disassembler::Disassemble(
538 m_interpreter.GetDebugger(), m_options.arch, plugin_name,
539 flavor_string, m_exe_ctx, cur_range, m_options.num_instructions,
540 m_options.show_mixed,
541 m_options.show_mixed ? m_options.num_lines_context : 0, options,
542 result.GetOutputStream())) {
543 result.SetStatus(eReturnStatusSuccessFinishResult);
545 result.AppendErrorWithFormat(
546 "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n",
547 m_options.start_addr);
548 result.SetStatus(eReturnStatusFailed);
551 result.AppendMessage("\n");
556 return result.Succeeded();