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