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