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