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