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