1 //===-- CommandObjectDisassemble.cpp ----------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 // C Includes 11 // C++ Includes 12 // Other libraries and framework includes 13 // Project includes 14 #include "CommandObjectDisassemble.h" 15 #include "lldb/Core/AddressRange.h" 16 #include "lldb/Core/Disassembler.h" 17 #include "lldb/Core/Module.h" 18 #include "lldb/Core/SourceManager.h" 19 #include "lldb/Host/OptionParser.h" 20 #include "lldb/Interpreter/CommandCompletions.h" 21 #include "lldb/Interpreter/CommandInterpreter.h" 22 #include "lldb/Interpreter/CommandReturnObject.h" 23 #include "lldb/Interpreter/Options.h" 24 #include "lldb/Symbol/Function.h" 25 #include "lldb/Symbol/Symbol.h" 26 #include "lldb/Target/Process.h" 27 #include "lldb/Target/SectionLoadList.h" 28 #include "lldb/Target/StackFrame.h" 29 #include "lldb/Target/Target.h" 30 31 #define DEFAULT_DISASM_BYTE_SIZE 32 32 #define DEFAULT_DISASM_NUM_INS 4 33 34 using namespace lldb; 35 using namespace lldb_private; 36 37 static OptionDefinition g_disassemble_options[] = { 38 // clang-format off 39 { LLDB_OPT_SET_ALL, false, "bytes", 'b', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Show opcode bytes when disassembling." }, 40 { LLDB_OPT_SET_ALL, false, "context", 'C', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNumLines, "Number of context lines of source to show." }, 41 { LLDB_OPT_SET_ALL, false, "mixed", 'm', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Enable mixed source and assembly display." }, 42 { LLDB_OPT_SET_ALL, false, "raw", 'r', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Print raw disassembly with no symbol information." }, 43 { LLDB_OPT_SET_ALL, false, "plugin", 'P', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypePlugin, "Name of the disassembler plugin you want to use." }, 44 { LLDB_OPT_SET_ALL, false, "flavor", 'F', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeDisassemblyFlavor, "Name of the disassembly flavor you want to use. " 45 "Currently the only valid options are default, and for Intel " 46 "architectures, att and intel." }, 47 { LLDB_OPT_SET_ALL, false, "arch", 'A', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeArchitecture, "Specify the architecture to use from cross disassembly." }, 48 { LLDB_OPT_SET_1 | 49 LLDB_OPT_SET_2, true, "start-address", 's', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Address at which to start disassembling." }, 50 { LLDB_OPT_SET_1, false, "end-address", 'e', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Address at which to end disassembling." }, 51 { LLDB_OPT_SET_2 | 52 LLDB_OPT_SET_3 | 53 LLDB_OPT_SET_4 | 54 LLDB_OPT_SET_5, false, "count", 'c', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeNumLines, "Number of instructions to display." }, 55 { LLDB_OPT_SET_3, false, "name", 'n', OptionParser::eRequiredArgument, nullptr, nullptr, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, "Disassemble entire contents of the given function name." }, 56 { LLDB_OPT_SET_4, false, "frame", 'f', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Disassemble from the start of the current frame's function." }, 57 { LLDB_OPT_SET_5, false, "pc", 'p', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Disassemble around the current pc." }, 58 { LLDB_OPT_SET_6, false, "line", 'l', OptionParser::eNoArgument, nullptr, nullptr, 0, eArgTypeNone, "Disassemble the current frame's current source line instructions if there is debug line " 59 "table information, else disassemble around the pc." }, 60 { LLDB_OPT_SET_7, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddressOrExpression, "Disassemble function containing this address." }, 61 // clang-format on 62 }; 63 64 CommandObjectDisassemble::CommandOptions::CommandOptions() 65 : Options(), num_lines_context(0), num_instructions(0), func_name(), 66 current_function(false), start_addr(), end_addr(), at_pc(false), 67 frame_line(false), plugin_name(), flavor_string(), arch(), 68 some_location_specified(false), symbol_containing_addr() { 69 OptionParsingStarting(nullptr); 70 } 71 72 CommandObjectDisassemble::CommandOptions::~CommandOptions() = default; 73 74 Status CommandObjectDisassemble::CommandOptions::SetOptionValue( 75 uint32_t option_idx, llvm::StringRef option_arg, 76 ExecutionContext *execution_context) { 77 Status error; 78 79 const int short_option = m_getopt_table[option_idx].val; 80 81 switch (short_option) { 82 case 'm': 83 show_mixed = true; 84 break; 85 86 case 'C': 87 if (option_arg.getAsInteger(0, num_lines_context)) 88 error.SetErrorStringWithFormat("invalid num context lines string: \"%s\"", 89 option_arg.str().c_str()); 90 break; 91 92 case 'c': 93 if (option_arg.getAsInteger(0, num_instructions)) 94 error.SetErrorStringWithFormat( 95 "invalid num of instructions string: \"%s\"", 96 option_arg.str().c_str()); 97 break; 98 99 case 'b': 100 show_bytes = true; 101 break; 102 103 case 's': { 104 start_addr = Args::StringToAddress(execution_context, option_arg, 105 LLDB_INVALID_ADDRESS, &error); 106 if (start_addr != LLDB_INVALID_ADDRESS) 107 some_location_specified = true; 108 } break; 109 case 'e': { 110 end_addr = Args::StringToAddress(execution_context, option_arg, 111 LLDB_INVALID_ADDRESS, &error); 112 if (end_addr != LLDB_INVALID_ADDRESS) 113 some_location_specified = true; 114 } break; 115 116 case 'n': 117 func_name.assign(option_arg); 118 some_location_specified = true; 119 break; 120 121 case 'p': 122 at_pc = true; 123 some_location_specified = true; 124 break; 125 126 case 'l': 127 frame_line = true; 128 // Disassemble the current source line kind of implies showing mixed 129 // source code context. 130 show_mixed = true; 131 some_location_specified = true; 132 break; 133 134 case 'P': 135 plugin_name.assign(option_arg); 136 break; 137 138 case 'F': { 139 TargetSP target_sp = 140 execution_context ? execution_context->GetTargetSP() : TargetSP(); 141 if (target_sp && (target_sp->GetArchitecture().GetTriple().getArch() == 142 llvm::Triple::x86 || 143 target_sp->GetArchitecture().GetTriple().getArch() == 144 llvm::Triple::x86_64)) { 145 flavor_string.assign(option_arg); 146 } else 147 error.SetErrorStringWithFormat("Disassembler flavors are currently only " 148 "supported for x86 and x86_64 targets."); 149 break; 150 } 151 152 case 'r': 153 raw = true; 154 break; 155 156 case 'f': 157 current_function = true; 158 some_location_specified = true; 159 break; 160 161 case 'A': 162 if (execution_context) { 163 auto target_sp = 164 execution_context ? execution_context->GetTargetSP() : TargetSP(); 165 auto platform_sp = target_sp ? target_sp->GetPlatform() : PlatformSP(); 166 arch = Platform::GetAugmentedArchSpec(platform_sp.get(), option_arg); 167 } 168 break; 169 170 case 'a': { 171 symbol_containing_addr = Args::StringToAddress( 172 execution_context, option_arg, LLDB_INVALID_ADDRESS, &error); 173 if (symbol_containing_addr != LLDB_INVALID_ADDRESS) { 174 some_location_specified = true; 175 } 176 } break; 177 178 default: 179 error.SetErrorStringWithFormat("unrecognized short option '%c'", 180 short_option); 181 break; 182 } 183 184 return error; 185 } 186 187 void CommandObjectDisassemble::CommandOptions::OptionParsingStarting( 188 ExecutionContext *execution_context) { 189 show_mixed = false; 190 show_bytes = false; 191 num_lines_context = 0; 192 num_instructions = 0; 193 func_name.clear(); 194 current_function = false; 195 at_pc = false; 196 frame_line = false; 197 start_addr = LLDB_INVALID_ADDRESS; 198 end_addr = LLDB_INVALID_ADDRESS; 199 symbol_containing_addr = LLDB_INVALID_ADDRESS; 200 raw = false; 201 plugin_name.clear(); 202 203 Target *target = 204 execution_context ? execution_context->GetTargetPtr() : nullptr; 205 206 // This is a hack till we get the ability to specify features based on 207 // architecture. For now GetDisassemblyFlavor 208 // is really only valid for x86 (and for the llvm assembler plugin, but I'm 209 // papering over that since that is the 210 // only disassembler plugin we have... 211 if (target) { 212 if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 || 213 target->GetArchitecture().GetTriple().getArch() == 214 llvm::Triple::x86_64) { 215 flavor_string.assign(target->GetDisassemblyFlavor()); 216 } else 217 flavor_string.assign("default"); 218 219 } else 220 flavor_string.assign("default"); 221 222 arch.Clear(); 223 some_location_specified = false; 224 } 225 226 Status CommandObjectDisassemble::CommandOptions::OptionParsingFinished( 227 ExecutionContext *execution_context) { 228 if (!some_location_specified) 229 current_function = true; 230 return Status(); 231 } 232 233 llvm::ArrayRef<OptionDefinition> 234 CommandObjectDisassemble::CommandOptions::GetDefinitions() { 235 return llvm::makeArrayRef(g_disassemble_options); 236 } 237 238 //------------------------------------------------------------------------- 239 // CommandObjectDisassemble 240 //------------------------------------------------------------------------- 241 242 CommandObjectDisassemble::CommandObjectDisassemble( 243 CommandInterpreter &interpreter) 244 : CommandObjectParsed( 245 interpreter, "disassemble", 246 "Disassemble specified instructions in the current target. " 247 "Defaults to the current function for the current thread and " 248 "stack frame.", 249 "disassemble [<cmd-options>]"), 250 m_options() {} 251 252 CommandObjectDisassemble::~CommandObjectDisassemble() = default; 253 254 bool CommandObjectDisassemble::DoExecute(Args &command, 255 CommandReturnObject &result) { 256 Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); 257 if (target == nullptr) { 258 result.AppendError("invalid target, create a debug target using the " 259 "'target create' command"); 260 result.SetStatus(eReturnStatusFailed); 261 return false; 262 } 263 if (!m_options.arch.IsValid()) 264 m_options.arch = target->GetArchitecture(); 265 266 if (!m_options.arch.IsValid()) { 267 result.AppendError( 268 "use the --arch option or set the target architecture to disassemble"); 269 result.SetStatus(eReturnStatusFailed); 270 return false; 271 } 272 273 const char *plugin_name = m_options.GetPluginName(); 274 const char *flavor_string = m_options.GetFlavorString(); 275 276 DisassemblerSP disassembler = 277 Disassembler::FindPlugin(m_options.arch, flavor_string, plugin_name); 278 279 if (!disassembler) { 280 if (plugin_name) { 281 result.AppendErrorWithFormat( 282 "Unable to find Disassembler plug-in named '%s' that supports the " 283 "'%s' architecture.\n", 284 plugin_name, m_options.arch.GetArchitectureName()); 285 } else 286 result.AppendErrorWithFormat( 287 "Unable to find Disassembler plug-in for the '%s' architecture.\n", 288 m_options.arch.GetArchitectureName()); 289 result.SetStatus(eReturnStatusFailed); 290 return false; 291 } else if (flavor_string != nullptr && 292 !disassembler->FlavorValidForArchSpec(m_options.arch, 293 flavor_string)) 294 result.AppendWarningWithFormat( 295 "invalid disassembler flavor \"%s\", using default.\n", flavor_string); 296 297 result.SetStatus(eReturnStatusSuccessFinishResult); 298 299 if (!command.empty()) { 300 result.AppendErrorWithFormat( 301 "\"disassemble\" arguments are specified as options.\n"); 302 const int terminal_width = 303 GetCommandInterpreter().GetDebugger().GetTerminalWidth(); 304 GetOptions()->GenerateOptionUsage(result.GetErrorStream(), this, 305 terminal_width); 306 result.SetStatus(eReturnStatusFailed); 307 return false; 308 } 309 310 if (m_options.show_mixed && m_options.num_lines_context == 0) 311 m_options.num_lines_context = 2; 312 313 // Always show the PC in the disassembly 314 uint32_t options = Disassembler::eOptionMarkPCAddress; 315 316 // Mark the source line for the current PC only if we are doing mixed source 317 // and assembly 318 if (m_options.show_mixed) 319 options |= Disassembler::eOptionMarkPCSourceLine; 320 321 if (m_options.show_bytes) 322 options |= Disassembler::eOptionShowBytes; 323 324 if (m_options.raw) 325 options |= Disassembler::eOptionRawOuput; 326 327 if (!m_options.func_name.empty()) { 328 ConstString name(m_options.func_name.c_str()); 329 330 if (Disassembler::Disassemble( 331 m_interpreter.GetDebugger(), m_options.arch, plugin_name, 332 flavor_string, m_exe_ctx, name, 333 nullptr, // Module * 334 m_options.num_instructions, m_options.show_mixed, 335 m_options.show_mixed ? m_options.num_lines_context : 0, options, 336 result.GetOutputStream())) { 337 result.SetStatus(eReturnStatusSuccessFinishResult); 338 } else { 339 result.AppendErrorWithFormat("Unable to find symbol with name '%s'.\n", 340 name.GetCString()); 341 result.SetStatus(eReturnStatusFailed); 342 } 343 } else { 344 std::vector<AddressRange> ranges; 345 AddressRange range; 346 StackFrame *frame = m_exe_ctx.GetFramePtr(); 347 if (m_options.frame_line) { 348 if (frame == nullptr) { 349 result.AppendError("Cannot disassemble around the current line without " 350 "a selected frame.\n"); 351 result.SetStatus(eReturnStatusFailed); 352 return false; 353 } 354 LineEntry pc_line_entry( 355 frame->GetSymbolContext(eSymbolContextLineEntry).line_entry); 356 if (pc_line_entry.IsValid()) { 357 range = pc_line_entry.range; 358 } else { 359 m_options.at_pc = 360 true; // No line entry, so just disassemble around the current pc 361 m_options.show_mixed = false; 362 } 363 } else if (m_options.current_function) { 364 if (frame == nullptr) { 365 result.AppendError("Cannot disassemble around the current function " 366 "without a selected frame.\n"); 367 result.SetStatus(eReturnStatusFailed); 368 return false; 369 } 370 Symbol *symbol = frame->GetSymbolContext(eSymbolContextSymbol).symbol; 371 if (symbol) { 372 range.GetBaseAddress() = symbol->GetAddress(); 373 range.SetByteSize(symbol->GetByteSize()); 374 } 375 } 376 377 // Did the "m_options.frame_line" find a valid range already? If so 378 // skip the rest... 379 if (range.GetByteSize() == 0) { 380 if (m_options.at_pc) { 381 if (frame == nullptr) { 382 result.AppendError("Cannot disassemble around the current PC without " 383 "a selected frame.\n"); 384 result.SetStatus(eReturnStatusFailed); 385 return false; 386 } 387 range.GetBaseAddress() = frame->GetFrameCodeAddress(); 388 if (m_options.num_instructions == 0) { 389 // Disassembling at the PC always disassembles some number of 390 // instructions (not the whole function). 391 m_options.num_instructions = DEFAULT_DISASM_NUM_INS; 392 } 393 ranges.push_back(range); 394 } else { 395 range.GetBaseAddress().SetOffset(m_options.start_addr); 396 if (range.GetBaseAddress().IsValid()) { 397 if (m_options.end_addr != LLDB_INVALID_ADDRESS) { 398 if (m_options.end_addr <= m_options.start_addr) { 399 result.AppendErrorWithFormat( 400 "End address before start address.\n"); 401 result.SetStatus(eReturnStatusFailed); 402 return false; 403 } 404 range.SetByteSize(m_options.end_addr - m_options.start_addr); 405 } 406 ranges.push_back(range); 407 } else { 408 if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS && 409 target) { 410 if (!target->GetSectionLoadList().IsEmpty()) { 411 bool failed = false; 412 Address symbol_containing_address; 413 if (target->GetSectionLoadList().ResolveLoadAddress( 414 m_options.symbol_containing_addr, 415 symbol_containing_address)) { 416 ModuleSP module_sp(symbol_containing_address.GetModule()); 417 SymbolContext sc; 418 bool resolve_tail_call_address = true; // PC can be one past the 419 // address range of the 420 // function. 421 module_sp->ResolveSymbolContextForAddress( 422 symbol_containing_address, eSymbolContextEverything, sc, 423 resolve_tail_call_address); 424 if (sc.function || sc.symbol) { 425 sc.GetAddressRange(eSymbolContextFunction | 426 eSymbolContextSymbol, 427 0, false, range); 428 } else { 429 failed = true; 430 } 431 } else { 432 failed = true; 433 } 434 if (failed) { 435 result.AppendErrorWithFormat( 436 "Could not find function bounds for address 0x%" PRIx64 437 "\n", 438 m_options.symbol_containing_addr); 439 result.SetStatus(eReturnStatusFailed); 440 return false; 441 } 442 ranges.push_back(range); 443 } else { 444 for (lldb::ModuleSP module_sp : target->GetImages().Modules()) { 445 lldb::addr_t file_addr = m_options.symbol_containing_addr; 446 Address file_address; 447 if (module_sp->ResolveFileAddress(file_addr, file_address)) { 448 SymbolContext sc; 449 bool resolve_tail_call_address = true; // PC can be one past 450 // the address range of 451 // the function. 452 module_sp->ResolveSymbolContextForAddress( 453 file_address, eSymbolContextEverything, sc, 454 resolve_tail_call_address); 455 if (sc.function || sc.symbol) { 456 sc.GetAddressRange(eSymbolContextFunction | 457 eSymbolContextSymbol, 458 0, false, range); 459 ranges.push_back(range); 460 } 461 } 462 } 463 } 464 } 465 } 466 } 467 } else 468 ranges.push_back(range); 469 470 if (m_options.num_instructions != 0) { 471 if (ranges.empty()) { 472 // The default action is to disassemble the current frame function. 473 if (frame) { 474 SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction | 475 eSymbolContextSymbol)); 476 if (sc.function) 477 range.GetBaseAddress() = 478 sc.function->GetAddressRange().GetBaseAddress(); 479 else if (sc.symbol && sc.symbol->ValueIsAddress()) 480 range.GetBaseAddress() = sc.symbol->GetAddress(); 481 else 482 range.GetBaseAddress() = frame->GetFrameCodeAddress(); 483 } 484 485 if (!range.GetBaseAddress().IsValid()) { 486 result.AppendError("invalid frame"); 487 result.SetStatus(eReturnStatusFailed); 488 return false; 489 } 490 } 491 492 bool print_sc_header = ranges.size() > 1; 493 for (AddressRange cur_range : ranges) { 494 if (Disassembler::Disassemble( 495 m_interpreter.GetDebugger(), m_options.arch, plugin_name, 496 flavor_string, m_exe_ctx, cur_range.GetBaseAddress(), 497 m_options.num_instructions, 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.start_addr != LLDB_INVALID_ADDRESS) 503 result.AppendErrorWithFormat( 504 "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n", 505 m_options.start_addr); 506 else if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS) 507 result.AppendErrorWithFormat( 508 "Failed to disassemble memory in function at 0x%8.8" PRIx64 509 ".\n", 510 m_options.symbol_containing_addr); 511 result.SetStatus(eReturnStatusFailed); 512 } 513 } 514 if (print_sc_header) 515 result.AppendMessage("\n"); 516 } else { 517 if (ranges.empty()) { 518 // The default action is to disassemble the current frame function. 519 if (frame) { 520 SymbolContext sc(frame->GetSymbolContext(eSymbolContextFunction | 521 eSymbolContextSymbol)); 522 if (sc.function) 523 range = sc.function->GetAddressRange(); 524 else if (sc.symbol && sc.symbol->ValueIsAddress()) { 525 range.GetBaseAddress() = sc.symbol->GetAddress(); 526 range.SetByteSize(sc.symbol->GetByteSize()); 527 } else 528 range.GetBaseAddress() = frame->GetFrameCodeAddress(); 529 } else { 530 result.AppendError("invalid frame"); 531 result.SetStatus(eReturnStatusFailed); 532 return false; 533 } 534 ranges.push_back(range); 535 } 536 537 bool print_sc_header = ranges.size() > 1; 538 for (AddressRange cur_range : ranges) { 539 if (cur_range.GetByteSize() == 0) 540 cur_range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE); 541 542 if (Disassembler::Disassemble( 543 m_interpreter.GetDebugger(), m_options.arch, plugin_name, 544 flavor_string, m_exe_ctx, cur_range, m_options.num_instructions, 545 m_options.show_mixed, 546 m_options.show_mixed ? m_options.num_lines_context : 0, options, 547 result.GetOutputStream())) { 548 result.SetStatus(eReturnStatusSuccessFinishResult); 549 } else { 550 result.AppendErrorWithFormat( 551 "Failed to disassemble memory at 0x%8.8" PRIx64 ".\n", 552 m_options.start_addr); 553 result.SetStatus(eReturnStatusFailed); 554 } 555 if (print_sc_header) 556 result.AppendMessage("\n"); 557 } 558 } 559 } 560 561 return result.Succeeded(); 562 } 563