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