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