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