1 //===-- CommandObjectDisassemble.cpp --------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
25 static constexpr unsigned default_disasm_byte_size
= 32;
26 static constexpr unsigned default_disasm_num_ins
= 4;
29 using namespace lldb_private
;
31 #define LLDB_OPTIONS_disassemble
32 #include "CommandOptions.inc"
34 CommandObjectDisassemble::CommandOptions::CommandOptions() {
35 OptionParsingStarting(nullptr);
38 CommandObjectDisassemble::CommandOptions::~CommandOptions() = default;
40 Status
CommandObjectDisassemble::CommandOptions::SetOptionValue(
41 uint32_t option_idx
, llvm::StringRef option_arg
,
42 ExecutionContext
*execution_context
) {
45 const int short_option
= m_getopt_table
[option_idx
].val
;
47 switch (short_option
) {
53 if (option_arg
.getAsInteger(0, num_lines_context
))
54 error
= Status::FromErrorStringWithFormat(
55 "invalid num context lines string: \"%s\"", option_arg
.str().c_str());
59 if (option_arg
.getAsInteger(0, num_instructions
))
60 error
= Status::FromErrorStringWithFormat(
61 "invalid num of instructions string: \"%s\"",
62 option_arg
.str().c_str());
70 show_control_flow_kind
= true;
74 start_addr
= OptionArgParser::ToAddress(execution_context
, option_arg
,
75 LLDB_INVALID_ADDRESS
, &error
);
76 if (start_addr
!= LLDB_INVALID_ADDRESS
)
77 some_location_specified
= true;
80 end_addr
= OptionArgParser::ToAddress(execution_context
, option_arg
,
81 LLDB_INVALID_ADDRESS
, &error
);
82 if (end_addr
!= LLDB_INVALID_ADDRESS
)
83 some_location_specified
= true;
87 func_name
.assign(std::string(option_arg
));
88 some_location_specified
= true;
93 some_location_specified
= true;
98 // Disassemble the current source line kind of implies showing mixed source
101 some_location_specified
= true;
105 plugin_name
.assign(std::string(option_arg
));
110 execution_context
? execution_context
->GetTargetSP() : TargetSP();
111 if (target_sp
&& (target_sp
->GetArchitecture().GetTriple().getArch() ==
113 target_sp
->GetArchitecture().GetTriple().getArch() ==
114 llvm::Triple::x86_64
)) {
115 flavor_string
.assign(std::string(option_arg
));
117 error
= Status::FromErrorStringWithFormat(
118 "Disassembler flavors are currently only "
119 "supported for x86 and x86_64 targets.");
124 cpu_string
= std::string(option_arg
);
128 features_string
= std::string(option_arg
);
136 current_function
= true;
137 some_location_specified
= true;
141 if (execution_context
) {
142 const auto &target_sp
= execution_context
->GetTargetSP();
143 auto platform_ptr
= target_sp
? target_sp
->GetPlatform().get() : nullptr;
144 arch
= Platform::GetAugmentedArchSpec(platform_ptr
, option_arg
);
149 symbol_containing_addr
= OptionArgParser::ToAddress(
150 execution_context
, option_arg
, LLDB_INVALID_ADDRESS
, &error
);
151 if (symbol_containing_addr
!= LLDB_INVALID_ADDRESS
) {
152 some_location_specified
= true;
161 llvm_unreachable("Unimplemented option");
167 void CommandObjectDisassemble::CommandOptions::OptionParsingStarting(
168 ExecutionContext
*execution_context
) {
171 show_control_flow_kind
= false;
172 num_lines_context
= 0;
173 num_instructions
= 0;
175 current_function
= false;
178 start_addr
= LLDB_INVALID_ADDRESS
;
179 end_addr
= LLDB_INVALID_ADDRESS
;
180 symbol_containing_addr
= LLDB_INVALID_ADDRESS
;
185 execution_context
? execution_context
->GetTargetPtr() : nullptr;
188 // This is a hack till we get the ability to specify features based on
189 // architecture. For now GetDisassemblyFlavor is really only valid for x86
190 // (and for the llvm assembler plugin, but I'm papering over that since that
191 // is the only disassembler plugin we have...
192 if (target
->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86
||
193 target
->GetArchitecture().GetTriple().getArch() ==
194 llvm::Triple::x86_64
) {
195 flavor_string
.assign(target
->GetDisassemblyFlavor());
197 flavor_string
.assign("default");
199 if (const char *cpu
= target
->GetDisassemblyCPU())
200 cpu_string
.assign(cpu
);
201 if (const char *features
= target
->GetDisassemblyFeatures())
202 features_string
.assign(features
);
204 flavor_string
.assign("default");
205 cpu_string
.assign("default");
206 features_string
.assign("default");
210 some_location_specified
= false;
214 Status
CommandObjectDisassemble::CommandOptions::OptionParsingFinished(
215 ExecutionContext
*execution_context
) {
216 if (!some_location_specified
)
217 current_function
= true;
221 llvm::ArrayRef
<OptionDefinition
>
222 CommandObjectDisassemble::CommandOptions::GetDefinitions() {
223 return llvm::ArrayRef(g_disassemble_options
);
226 // CommandObjectDisassemble
228 CommandObjectDisassemble::CommandObjectDisassemble(
229 CommandInterpreter
&interpreter
)
230 : CommandObjectParsed(
231 interpreter
, "disassemble",
232 "Disassemble specified instructions in the current target. "
233 "Defaults to the current function for the current thread and "
235 "disassemble [<cmd-options>]", eCommandRequiresTarget
) {}
237 CommandObjectDisassemble::~CommandObjectDisassemble() = default;
239 llvm::Error
CommandObjectDisassemble::CheckRangeSize(const AddressRange
&range
,
240 llvm::StringRef what
) {
241 if (m_options
.num_instructions
> 0 || m_options
.force
||
242 range
.GetByteSize() < GetDebugger().GetStopDisassemblyMaxSize())
243 return llvm::Error::success();
245 msg
<< "Not disassembling " << what
<< " because it is very large ";
246 range
.Dump(&msg
, &GetTarget(), Address::DumpStyleLoadAddress
,
247 Address::DumpStyleFileAddress
);
248 msg
<< ". To disassemble specify an instruction count limit, start/stop "
249 "addresses or use the --force option.";
250 return llvm::createStringError(llvm::inconvertibleErrorCode(),
254 llvm::Expected
<std::vector
<AddressRange
>>
255 CommandObjectDisassemble::GetContainingAddressRanges() {
256 std::vector
<AddressRange
> ranges
;
257 const auto &get_range
= [&](Address addr
) {
258 ModuleSP
module_sp(addr
.GetModule());
260 bool resolve_tail_call_address
= true;
261 addr
.GetModule()->ResolveSymbolContextForAddress(
262 addr
, eSymbolContextEverything
, sc
, resolve_tail_call_address
);
263 if (sc
.function
|| sc
.symbol
) {
265 sc
.GetAddressRange(eSymbolContextFunction
| eSymbolContextSymbol
, 0,
267 ranges
.push_back(range
);
271 Target
&target
= GetTarget();
272 if (!target
.GetSectionLoadList().IsEmpty()) {
273 Address symbol_containing_address
;
274 if (target
.GetSectionLoadList().ResolveLoadAddress(
275 m_options
.symbol_containing_addr
, symbol_containing_address
)) {
276 get_range(symbol_containing_address
);
279 for (lldb::ModuleSP module_sp
: target
.GetImages().Modules()) {
280 Address file_address
;
281 if (module_sp
->ResolveFileAddress(m_options
.symbol_containing_addr
,
283 get_range(file_address
);
288 if (ranges
.empty()) {
289 return llvm::createStringError(
290 llvm::inconvertibleErrorCode(),
291 "Could not find function bounds for address 0x%" PRIx64
,
292 m_options
.symbol_containing_addr
);
295 if (llvm::Error err
= CheckRangeSize(ranges
[0], "the function"))
296 return std::move(err
);
300 llvm::Expected
<std::vector
<AddressRange
>>
301 CommandObjectDisassemble::GetCurrentFunctionRanges() {
302 Process
*process
= m_exe_ctx
.GetProcessPtr();
303 StackFrame
*frame
= m_exe_ctx
.GetFramePtr();
306 return llvm::createStringError(
307 llvm::inconvertibleErrorCode(),
308 "Cannot disassemble around the current "
309 "function without the process being stopped.\n");
311 return llvm::createStringError(llvm::inconvertibleErrorCode(),
312 "Cannot disassemble around the current "
313 "function without a selected frame: "
314 "no currently running process.\n");
318 frame
->GetSymbolContext(eSymbolContextFunction
| eSymbolContextSymbol
));
321 range
= sc
.function
->GetAddressRange();
322 else if (sc
.symbol
&& sc
.symbol
->ValueIsAddress()) {
323 range
= {sc
.symbol
->GetAddress(), sc
.symbol
->GetByteSize()};
325 range
= {frame
->GetFrameCodeAddress(), default_disasm_byte_size
};
327 if (llvm::Error err
= CheckRangeSize(range
, "the current function"))
328 return std::move(err
);
329 return std::vector
<AddressRange
>{range
};
332 llvm::Expected
<std::vector
<AddressRange
>>
333 CommandObjectDisassemble::GetCurrentLineRanges() {
334 Process
*process
= m_exe_ctx
.GetProcessPtr();
335 StackFrame
*frame
= m_exe_ctx
.GetFramePtr();
338 return llvm::createStringError(
339 llvm::inconvertibleErrorCode(),
340 "Cannot disassemble around the current "
341 "function without the process being stopped.\n");
343 return llvm::createStringError(llvm::inconvertibleErrorCode(),
344 "Cannot disassemble around the current "
345 "line without a selected frame: "
346 "no currently running process.\n");
350 LineEntry
pc_line_entry(
351 frame
->GetSymbolContext(eSymbolContextLineEntry
).line_entry
);
352 if (pc_line_entry
.IsValid())
353 return std::vector
<AddressRange
>{pc_line_entry
.range
};
355 // No line entry, so just disassemble around the current pc
356 m_options
.show_mixed
= false;
357 return GetPCRanges();
360 llvm::Expected
<std::vector
<AddressRange
>>
361 CommandObjectDisassemble::GetNameRanges(CommandReturnObject
&result
) {
362 ConstString
name(m_options
.func_name
.c_str());
364 ModuleFunctionSearchOptions function_options
;
365 function_options
.include_symbols
= true;
366 function_options
.include_inlines
= true;
368 // Find functions matching the given name.
369 SymbolContextList sc_list
;
370 GetTarget().GetImages().FindFunctions(name
, eFunctionNameTypeAuto
,
371 function_options
, sc_list
);
373 std::vector
<AddressRange
> ranges
;
374 llvm::Error range_errs
= llvm::Error::success();
376 const uint32_t scope
=
377 eSymbolContextBlock
| eSymbolContextFunction
| eSymbolContextSymbol
;
378 const bool use_inline_block_range
= true;
379 for (SymbolContext sc
: sc_list
.SymbolContexts()) {
380 for (uint32_t range_idx
= 0;
381 sc
.GetAddressRange(scope
, range_idx
, use_inline_block_range
, range
);
383 if (llvm::Error err
= CheckRangeSize(range
, "a range"))
384 range_errs
= joinErrors(std::move(range_errs
), std::move(err
));
386 ranges
.push_back(range
);
389 if (ranges
.empty()) {
391 return std::move(range_errs
);
392 return llvm::createStringError(llvm::inconvertibleErrorCode(),
393 "Unable to find symbol with name '%s'.\n",
397 result
.AppendWarning(toString(std::move(range_errs
)));
401 llvm::Expected
<std::vector
<AddressRange
>>
402 CommandObjectDisassemble::GetPCRanges() {
403 Process
*process
= m_exe_ctx
.GetProcessPtr();
404 StackFrame
*frame
= m_exe_ctx
.GetFramePtr();
407 return llvm::createStringError(
408 llvm::inconvertibleErrorCode(),
409 "Cannot disassemble around the current "
410 "function without the process being stopped.\n");
412 return llvm::createStringError(llvm::inconvertibleErrorCode(),
413 "Cannot disassemble around the current "
414 "PC without a selected frame: "
415 "no currently running process.\n");
419 if (m_options
.num_instructions
== 0) {
420 // Disassembling at the PC always disassembles some number of
421 // instructions (not the whole function).
422 m_options
.num_instructions
= default_disasm_num_ins
;
424 return std::vector
<AddressRange
>{{frame
->GetFrameCodeAddress(), 0}};
427 llvm::Expected
<std::vector
<AddressRange
>>
428 CommandObjectDisassemble::GetStartEndAddressRanges() {
430 if (m_options
.end_addr
!= LLDB_INVALID_ADDRESS
) {
431 if (m_options
.end_addr
<= m_options
.start_addr
) {
432 return llvm::createStringError(llvm::inconvertibleErrorCode(),
433 "End address before start address.");
435 size
= m_options
.end_addr
- m_options
.start_addr
;
437 return std::vector
<AddressRange
>{{Address(m_options
.start_addr
), size
}};
440 llvm::Expected
<std::vector
<AddressRange
>>
441 CommandObjectDisassemble::GetRangesForSelectedMode(
442 CommandReturnObject
&result
) {
443 if (m_options
.symbol_containing_addr
!= LLDB_INVALID_ADDRESS
)
444 return CommandObjectDisassemble::GetContainingAddressRanges();
445 if (m_options
.current_function
)
446 return CommandObjectDisassemble::GetCurrentFunctionRanges();
447 if (m_options
.frame_line
)
448 return CommandObjectDisassemble::GetCurrentLineRanges();
449 if (!m_options
.func_name
.empty())
450 return CommandObjectDisassemble::GetNameRanges(result
);
451 if (m_options
.start_addr
!= LLDB_INVALID_ADDRESS
)
452 return CommandObjectDisassemble::GetStartEndAddressRanges();
453 return CommandObjectDisassemble::GetPCRanges();
456 void CommandObjectDisassemble::DoExecute(Args
&command
,
457 CommandReturnObject
&result
) {
458 Target
&target
= GetTarget();
460 if (!m_options
.arch
.IsValid())
461 m_options
.arch
= target
.GetArchitecture();
463 if (!m_options
.arch
.IsValid()) {
465 "use the --arch option or set the target architecture to disassemble");
469 const char *plugin_name
= m_options
.GetPluginName();
470 const char *flavor_string
= m_options
.GetFlavorString();
471 const char *cpu_string
= m_options
.GetCPUString();
472 const char *features_string
= m_options
.GetFeaturesString();
474 DisassemblerSP disassembler
= Disassembler::FindPlugin(
475 m_options
.arch
, flavor_string
, cpu_string
, features_string
, plugin_name
);
479 result
.AppendErrorWithFormat(
480 "Unable to find Disassembler plug-in named '%s' that supports the "
481 "'%s' architecture.\n",
482 plugin_name
, m_options
.arch
.GetArchitectureName());
484 result
.AppendErrorWithFormat(
485 "Unable to find Disassembler plug-in for the '%s' architecture.\n",
486 m_options
.arch
.GetArchitectureName());
488 } else if (flavor_string
!= nullptr && !disassembler
->FlavorValidForArchSpec(
489 m_options
.arch
, flavor_string
))
490 result
.AppendWarningWithFormat(
491 "invalid disassembler flavor \"%s\", using default.\n", flavor_string
);
493 result
.SetStatus(eReturnStatusSuccessFinishResult
);
495 if (!command
.empty()) {
496 result
.AppendErrorWithFormat(
497 "\"disassemble\" arguments are specified as options.\n");
498 const int terminal_width
=
499 GetCommandInterpreter().GetDebugger().GetTerminalWidth();
500 GetOptions()->GenerateOptionUsage(result
.GetErrorStream(), *this,
505 if (m_options
.show_mixed
&& m_options
.num_lines_context
== 0)
506 m_options
.num_lines_context
= 2;
508 // Always show the PC in the disassembly
509 uint32_t options
= Disassembler::eOptionMarkPCAddress
;
511 // Mark the source line for the current PC only if we are doing mixed source
513 if (m_options
.show_mixed
)
514 options
|= Disassembler::eOptionMarkPCSourceLine
;
516 if (m_options
.show_bytes
)
517 options
|= Disassembler::eOptionShowBytes
;
519 if (m_options
.show_control_flow_kind
)
520 options
|= Disassembler::eOptionShowControlFlowKind
;
523 options
|= Disassembler::eOptionRawOuput
;
525 llvm::Expected
<std::vector
<AddressRange
>> ranges
=
526 GetRangesForSelectedMode(result
);
528 result
.AppendError(toString(ranges
.takeError()));
532 bool print_sc_header
= ranges
->size() > 1;
533 for (AddressRange cur_range
: *ranges
) {
534 Disassembler::Limit limit
;
535 if (m_options
.num_instructions
== 0) {
536 limit
= {Disassembler::Limit::Bytes
, cur_range
.GetByteSize()};
537 if (limit
.value
== 0)
538 limit
.value
= default_disasm_byte_size
;
540 limit
= {Disassembler::Limit::Instructions
, m_options
.num_instructions
};
542 if (Disassembler::Disassemble(
543 GetDebugger(), m_options
.arch
, plugin_name
, flavor_string
,
544 cpu_string
, features_string
, m_exe_ctx
, cur_range
.GetBaseAddress(),
545 limit
, m_options
.show_mixed
,
546 m_options
.show_mixed
? m_options
.num_lines_context
: 0, options
,
547 result
.GetOutputStream())) {
548 result
.SetStatus(eReturnStatusSuccessFinishResult
);
550 if (m_options
.symbol_containing_addr
!= LLDB_INVALID_ADDRESS
) {
551 result
.AppendErrorWithFormat(
552 "Failed to disassemble memory in function at 0x%8.8" PRIx64
".\n",
553 m_options
.symbol_containing_addr
);
555 result
.AppendErrorWithFormat(
556 "Failed to disassemble memory at 0x%8.8" PRIx64
".\n",
557 cur_range
.GetBaseAddress().GetLoadAddress(&target
));
561 result
.GetOutputStream() << "\n";