1 //===- FileAnalysis.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 "FileAnalysis.h" 10 #include "GraphBuilder.h" 11 12 #include "llvm/BinaryFormat/ELF.h" 13 #include "llvm/DebugInfo/DWARF/DWARFContext.h" 14 #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" 15 #include "llvm/MC/MCAsmInfo.h" 16 #include "llvm/MC/MCContext.h" 17 #include "llvm/MC/MCDisassembler/MCDisassembler.h" 18 #include "llvm/MC/MCInst.h" 19 #include "llvm/MC/MCInstPrinter.h" 20 #include "llvm/MC/MCInstrAnalysis.h" 21 #include "llvm/MC/MCInstrDesc.h" 22 #include "llvm/MC/MCInstrInfo.h" 23 #include "llvm/MC/MCObjectFileInfo.h" 24 #include "llvm/MC/MCRegisterInfo.h" 25 #include "llvm/MC/MCSubtargetInfo.h" 26 #include "llvm/MC/MCTargetOptions.h" 27 #include "llvm/MC/TargetRegistry.h" 28 #include "llvm/Object/Binary.h" 29 #include "llvm/Object/COFF.h" 30 #include "llvm/Object/ELFObjectFile.h" 31 #include "llvm/Object/ObjectFile.h" 32 #include "llvm/Support/Casting.h" 33 #include "llvm/Support/CommandLine.h" 34 #include "llvm/Support/Error.h" 35 #include "llvm/Support/MemoryBuffer.h" 36 #include "llvm/Support/TargetSelect.h" 37 #include "llvm/Support/raw_ostream.h" 38 39 using Instr = llvm::cfi_verify::FileAnalysis::Instr; 40 using LLVMSymbolizer = llvm::symbolize::LLVMSymbolizer; 41 42 namespace llvm { 43 namespace cfi_verify { 44 45 bool IgnoreDWARFFlag; 46 47 static cl::opt<bool, true> IgnoreDWARFArg( 48 "ignore-dwarf", 49 cl::desc( 50 "Ignore all DWARF data. This relaxes the requirements for all " 51 "statically linked libraries to have been compiled with '-g', but " 52 "will result in false positives for 'CFI unprotected' instructions."), 53 cl::location(IgnoreDWARFFlag), cl::init(false)); 54 55 StringRef stringCFIProtectionStatus(CFIProtectionStatus Status) { 56 switch (Status) { 57 case CFIProtectionStatus::PROTECTED: 58 return "PROTECTED"; 59 case CFIProtectionStatus::FAIL_NOT_INDIRECT_CF: 60 return "FAIL_NOT_INDIRECT_CF"; 61 case CFIProtectionStatus::FAIL_ORPHANS: 62 return "FAIL_ORPHANS"; 63 case CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH: 64 return "FAIL_BAD_CONDITIONAL_BRANCH"; 65 case CFIProtectionStatus::FAIL_REGISTER_CLOBBERED: 66 return "FAIL_REGISTER_CLOBBERED"; 67 case CFIProtectionStatus::FAIL_INVALID_INSTRUCTION: 68 return "FAIL_INVALID_INSTRUCTION"; 69 } 70 llvm_unreachable("Attempted to stringify an unknown enum value."); 71 } 72 73 Expected<FileAnalysis> FileAnalysis::Create(StringRef Filename) { 74 // Open the filename provided. 75 Expected<object::OwningBinary<object::Binary>> BinaryOrErr = 76 object::createBinary(Filename); 77 if (!BinaryOrErr) 78 return BinaryOrErr.takeError(); 79 80 // Construct the object and allow it to take ownership of the binary. 81 object::OwningBinary<object::Binary> Binary = std::move(BinaryOrErr.get()); 82 FileAnalysis Analysis(std::move(Binary)); 83 84 Analysis.Object = dyn_cast<object::ObjectFile>(Analysis.Binary.getBinary()); 85 if (!Analysis.Object) 86 return make_error<UnsupportedDisassembly>("Failed to cast object"); 87 88 switch (Analysis.Object->getArch()) { 89 case Triple::x86: 90 case Triple::x86_64: 91 case Triple::aarch64: 92 case Triple::aarch64_be: 93 break; 94 default: 95 return make_error<UnsupportedDisassembly>("Unsupported architecture."); 96 } 97 98 Analysis.ObjectTriple = Analysis.Object->makeTriple(); 99 Expected<SubtargetFeatures> Features = Analysis.Object->getFeatures(); 100 if (!Features) 101 return Features.takeError(); 102 103 Analysis.Features = *Features; 104 105 // Init the rest of the object. 106 if (auto InitResponse = Analysis.initialiseDisassemblyMembers()) 107 return std::move(InitResponse); 108 109 if (auto SectionParseResponse = Analysis.parseCodeSections()) 110 return std::move(SectionParseResponse); 111 112 if (auto SymbolTableParseResponse = Analysis.parseSymbolTable()) 113 return std::move(SymbolTableParseResponse); 114 115 return std::move(Analysis); 116 } 117 118 FileAnalysis::FileAnalysis(object::OwningBinary<object::Binary> Binary) 119 : Binary(std::move(Binary)) {} 120 121 FileAnalysis::FileAnalysis(const Triple &ObjectTriple, 122 const SubtargetFeatures &Features) 123 : ObjectTriple(ObjectTriple), Features(Features) {} 124 125 const Instr * 126 FileAnalysis::getPrevInstructionSequential(const Instr &InstrMeta) const { 127 std::map<uint64_t, Instr>::const_iterator KV = 128 Instructions.find(InstrMeta.VMAddress); 129 if (KV == Instructions.end() || KV == Instructions.begin()) 130 return nullptr; 131 132 if (!(--KV)->second.Valid) 133 return nullptr; 134 135 return &KV->second; 136 } 137 138 const Instr * 139 FileAnalysis::getNextInstructionSequential(const Instr &InstrMeta) const { 140 std::map<uint64_t, Instr>::const_iterator KV = 141 Instructions.find(InstrMeta.VMAddress); 142 if (KV == Instructions.end() || ++KV == Instructions.end()) 143 return nullptr; 144 145 if (!KV->second.Valid) 146 return nullptr; 147 148 return &KV->second; 149 } 150 151 bool FileAnalysis::usesRegisterOperand(const Instr &InstrMeta) const { 152 for (const auto &Operand : InstrMeta.Instruction) { 153 if (Operand.isReg()) 154 return true; 155 } 156 return false; 157 } 158 159 const Instr *FileAnalysis::getInstruction(uint64_t Address) const { 160 const auto &InstrKV = Instructions.find(Address); 161 if (InstrKV == Instructions.end()) 162 return nullptr; 163 164 return &InstrKV->second; 165 } 166 167 const Instr &FileAnalysis::getInstructionOrDie(uint64_t Address) const { 168 const auto &InstrKV = Instructions.find(Address); 169 assert(InstrKV != Instructions.end() && "Address doesn't exist."); 170 return InstrKV->second; 171 } 172 173 bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const { 174 const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode()); 175 return InstrDesc.isTrap() || willTrapOnCFIViolation(InstrMeta); 176 } 177 178 bool FileAnalysis::willTrapOnCFIViolation(const Instr &InstrMeta) const { 179 const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode()); 180 if (!InstrDesc.isCall()) 181 return false; 182 uint64_t Target; 183 if (!MIA->evaluateBranch(InstrMeta.Instruction, InstrMeta.VMAddress, 184 InstrMeta.InstructionSize, Target)) 185 return false; 186 return TrapOnFailFunctionAddresses.contains(Target); 187 } 188 189 bool FileAnalysis::canFallThrough(const Instr &InstrMeta) const { 190 if (!InstrMeta.Valid) 191 return false; 192 193 if (isCFITrap(InstrMeta)) 194 return false; 195 196 const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode()); 197 if (InstrDesc.mayAffectControlFlow(InstrMeta.Instruction, *RegisterInfo)) 198 return InstrDesc.isConditionalBranch(); 199 200 return true; 201 } 202 203 const Instr * 204 FileAnalysis::getDefiniteNextInstruction(const Instr &InstrMeta) const { 205 if (!InstrMeta.Valid) 206 return nullptr; 207 208 if (isCFITrap(InstrMeta)) 209 return nullptr; 210 211 const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode()); 212 const Instr *NextMetaPtr; 213 if (InstrDesc.mayAffectControlFlow(InstrMeta.Instruction, *RegisterInfo)) { 214 if (InstrDesc.isConditionalBranch()) 215 return nullptr; 216 217 uint64_t Target; 218 if (!MIA->evaluateBranch(InstrMeta.Instruction, InstrMeta.VMAddress, 219 InstrMeta.InstructionSize, Target)) 220 return nullptr; 221 222 NextMetaPtr = getInstruction(Target); 223 } else { 224 NextMetaPtr = 225 getInstruction(InstrMeta.VMAddress + InstrMeta.InstructionSize); 226 } 227 228 if (!NextMetaPtr || !NextMetaPtr->Valid) 229 return nullptr; 230 231 return NextMetaPtr; 232 } 233 234 std::set<const Instr *> 235 FileAnalysis::getDirectControlFlowXRefs(const Instr &InstrMeta) const { 236 std::set<const Instr *> CFCrossReferences; 237 const Instr *PrevInstruction = getPrevInstructionSequential(InstrMeta); 238 239 if (PrevInstruction && canFallThrough(*PrevInstruction)) 240 CFCrossReferences.insert(PrevInstruction); 241 242 const auto &TargetRefsKV = StaticBranchTargetings.find(InstrMeta.VMAddress); 243 if (TargetRefsKV == StaticBranchTargetings.end()) 244 return CFCrossReferences; 245 246 for (uint64_t SourceInstrAddress : TargetRefsKV->second) { 247 const auto &SourceInstrKV = Instructions.find(SourceInstrAddress); 248 if (SourceInstrKV == Instructions.end()) { 249 errs() << "Failed to find source instruction at address " 250 << format_hex(SourceInstrAddress, 2) 251 << " for the cross-reference to instruction at address " 252 << format_hex(InstrMeta.VMAddress, 2) << ".\n"; 253 continue; 254 } 255 256 CFCrossReferences.insert(&SourceInstrKV->second); 257 } 258 259 return CFCrossReferences; 260 } 261 262 const std::set<object::SectionedAddress> & 263 FileAnalysis::getIndirectInstructions() const { 264 return IndirectInstructions; 265 } 266 267 const MCRegisterInfo *FileAnalysis::getRegisterInfo() const { 268 return RegisterInfo.get(); 269 } 270 271 const MCInstrInfo *FileAnalysis::getMCInstrInfo() const { return MII.get(); } 272 273 const MCInstrAnalysis *FileAnalysis::getMCInstrAnalysis() const { 274 return MIA.get(); 275 } 276 277 Expected<DIInliningInfo> 278 FileAnalysis::symbolizeInlinedCode(object::SectionedAddress Address) { 279 assert(Symbolizer != nullptr && "Symbolizer is invalid."); 280 281 return Symbolizer->symbolizeInlinedCode(std::string(Object->getFileName()), 282 Address); 283 } 284 285 CFIProtectionStatus 286 FileAnalysis::validateCFIProtection(const GraphResult &Graph) const { 287 const Instr *InstrMetaPtr = getInstruction(Graph.BaseAddress); 288 if (!InstrMetaPtr) 289 return CFIProtectionStatus::FAIL_INVALID_INSTRUCTION; 290 291 const auto &InstrDesc = MII->get(InstrMetaPtr->Instruction.getOpcode()); 292 if (!InstrDesc.mayAffectControlFlow(InstrMetaPtr->Instruction, *RegisterInfo)) 293 return CFIProtectionStatus::FAIL_NOT_INDIRECT_CF; 294 295 if (!usesRegisterOperand(*InstrMetaPtr)) 296 return CFIProtectionStatus::FAIL_NOT_INDIRECT_CF; 297 298 if (!Graph.OrphanedNodes.empty()) 299 return CFIProtectionStatus::FAIL_ORPHANS; 300 301 for (const auto &BranchNode : Graph.ConditionalBranchNodes) { 302 if (!BranchNode.CFIProtection) 303 return CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH; 304 } 305 306 if (indirectCFOperandClobber(Graph) != Graph.BaseAddress) 307 return CFIProtectionStatus::FAIL_REGISTER_CLOBBERED; 308 309 return CFIProtectionStatus::PROTECTED; 310 } 311 312 uint64_t FileAnalysis::indirectCFOperandClobber(const GraphResult &Graph) const { 313 assert(Graph.OrphanedNodes.empty() && "Orphaned nodes should be empty."); 314 315 // Get the set of registers we must check to ensure they're not clobbered. 316 const Instr &IndirectCF = getInstructionOrDie(Graph.BaseAddress); 317 DenseSet<unsigned> RegisterNumbers; 318 for (const auto &Operand : IndirectCF.Instruction) { 319 if (Operand.isReg()) 320 RegisterNumbers.insert(Operand.getReg()); 321 } 322 assert(RegisterNumbers.size() && "Zero register operands on indirect CF."); 323 324 // Now check all branches to indirect CFs and ensure no clobbering happens. 325 for (const auto &Branch : Graph.ConditionalBranchNodes) { 326 uint64_t Node; 327 if (Branch.IndirectCFIsOnTargetPath) 328 Node = Branch.Target; 329 else 330 Node = Branch.Fallthrough; 331 332 // Some architectures (e.g., AArch64) cannot load in an indirect branch, so 333 // we allow them one load. 334 bool canLoad = !MII->get(IndirectCF.Instruction.getOpcode()).mayLoad(); 335 336 // We walk backwards from the indirect CF. It is the last node returned by 337 // Graph.flattenAddress, so we skip it since we already handled it. 338 DenseSet<unsigned> CurRegisterNumbers = RegisterNumbers; 339 std::vector<uint64_t> Nodes = Graph.flattenAddress(Node); 340 for (auto I = Nodes.rbegin() + 1, E = Nodes.rend(); I != E; ++I) { 341 Node = *I; 342 const Instr &NodeInstr = getInstructionOrDie(Node); 343 const auto &InstrDesc = MII->get(NodeInstr.Instruction.getOpcode()); 344 345 for (auto RI = CurRegisterNumbers.begin(), RE = CurRegisterNumbers.end(); 346 RI != RE; ++RI) { 347 unsigned RegNum = *RI; 348 if (InstrDesc.hasDefOfPhysReg(NodeInstr.Instruction, RegNum, 349 *RegisterInfo)) { 350 if (!canLoad || !InstrDesc.mayLoad()) 351 return Node; 352 canLoad = false; 353 CurRegisterNumbers.erase(RI); 354 // Add the registers this load reads to those we check for clobbers. 355 for (unsigned i = InstrDesc.getNumDefs(), 356 e = InstrDesc.getNumOperands(); i != e; i++) { 357 const auto &Operand = NodeInstr.Instruction.getOperand(i); 358 if (Operand.isReg()) 359 CurRegisterNumbers.insert(Operand.getReg()); 360 } 361 break; 362 } 363 } 364 } 365 } 366 367 return Graph.BaseAddress; 368 } 369 370 void FileAnalysis::printInstruction(const Instr &InstrMeta, 371 raw_ostream &OS) const { 372 Printer->printInst(&InstrMeta.Instruction, 0, "", *SubtargetInfo, OS); 373 } 374 375 Error FileAnalysis::initialiseDisassemblyMembers() { 376 std::string TripleName = ObjectTriple.getTriple(); 377 ArchName = ""; 378 MCPU = ""; 379 std::string ErrorString; 380 381 LLVMSymbolizer::Options Opt; 382 Opt.UseSymbolTable = false; 383 Symbolizer.reset(new LLVMSymbolizer(Opt)); 384 385 ObjectTarget = 386 TargetRegistry::lookupTarget(ArchName, ObjectTriple, ErrorString); 387 if (!ObjectTarget) 388 return make_error<UnsupportedDisassembly>( 389 (Twine("Couldn't find target \"") + ObjectTriple.getTriple() + 390 "\", failed with error: " + ErrorString) 391 .str()); 392 393 RegisterInfo.reset(ObjectTarget->createMCRegInfo(TripleName)); 394 if (!RegisterInfo) 395 return make_error<UnsupportedDisassembly>( 396 "Failed to initialise RegisterInfo."); 397 398 MCTargetOptions MCOptions; 399 AsmInfo.reset( 400 ObjectTarget->createMCAsmInfo(*RegisterInfo, TripleName, MCOptions)); 401 if (!AsmInfo) 402 return make_error<UnsupportedDisassembly>("Failed to initialise AsmInfo."); 403 404 SubtargetInfo.reset(ObjectTarget->createMCSubtargetInfo( 405 TripleName, MCPU, Features.getString())); 406 if (!SubtargetInfo) 407 return make_error<UnsupportedDisassembly>( 408 "Failed to initialise SubtargetInfo."); 409 410 MII.reset(ObjectTarget->createMCInstrInfo()); 411 if (!MII) 412 return make_error<UnsupportedDisassembly>("Failed to initialise MII."); 413 414 Context.reset(new MCContext(Triple(TripleName), AsmInfo.get(), 415 RegisterInfo.get(), SubtargetInfo.get())); 416 417 Disassembler.reset( 418 ObjectTarget->createMCDisassembler(*SubtargetInfo, *Context)); 419 420 if (!Disassembler) 421 return make_error<UnsupportedDisassembly>( 422 "No disassembler available for target"); 423 424 MIA.reset(ObjectTarget->createMCInstrAnalysis(MII.get())); 425 426 Printer.reset(ObjectTarget->createMCInstPrinter( 427 ObjectTriple, AsmInfo->getAssemblerDialect(), *AsmInfo, *MII, 428 *RegisterInfo)); 429 430 return Error::success(); 431 } 432 433 Error FileAnalysis::parseCodeSections() { 434 if (!IgnoreDWARFFlag) { 435 std::unique_ptr<DWARFContext> DWARF = DWARFContext::create(*Object); 436 if (!DWARF) 437 return make_error<StringError>("Could not create DWARF information.", 438 inconvertibleErrorCode()); 439 440 bool LineInfoValid = false; 441 442 for (auto &Unit : DWARF->compile_units()) { 443 const auto &LineTable = DWARF->getLineTableForUnit(Unit.get()); 444 if (LineTable && !LineTable->Rows.empty()) { 445 LineInfoValid = true; 446 break; 447 } 448 } 449 450 if (!LineInfoValid) 451 return make_error<StringError>( 452 "DWARF line information missing. Did you compile with '-g'?", 453 inconvertibleErrorCode()); 454 } 455 456 for (const object::SectionRef &Section : Object->sections()) { 457 // Ensure only executable sections get analysed. 458 if (!(object::ELFSectionRef(Section).getFlags() & ELF::SHF_EXECINSTR)) 459 continue; 460 461 // Avoid checking the PLT since it produces spurious failures on AArch64 462 // when ignoring DWARF data. 463 Expected<StringRef> NameOrErr = Section.getName(); 464 if (NameOrErr && *NameOrErr == ".plt") 465 continue; 466 consumeError(NameOrErr.takeError()); 467 468 Expected<StringRef> Contents = Section.getContents(); 469 if (!Contents) 470 return Contents.takeError(); 471 ArrayRef<uint8_t> SectionBytes = arrayRefFromStringRef(*Contents); 472 473 parseSectionContents(SectionBytes, 474 {Section.getAddress(), Section.getIndex()}); 475 } 476 return Error::success(); 477 } 478 479 void FileAnalysis::parseSectionContents(ArrayRef<uint8_t> SectionBytes, 480 object::SectionedAddress Address) { 481 assert(Symbolizer && "Symbolizer is uninitialised."); 482 MCInst Instruction; 483 Instr InstrMeta; 484 uint64_t InstructionSize; 485 486 for (uint64_t Byte = 0; Byte < SectionBytes.size();) { 487 bool ValidInstruction = 488 Disassembler->getInstruction(Instruction, InstructionSize, 489 SectionBytes.drop_front(Byte), 0, 490 outs()) == MCDisassembler::Success; 491 492 Byte += InstructionSize; 493 494 uint64_t VMAddress = Address.Address + Byte - InstructionSize; 495 InstrMeta.Instruction = Instruction; 496 InstrMeta.VMAddress = VMAddress; 497 InstrMeta.InstructionSize = InstructionSize; 498 InstrMeta.Valid = ValidInstruction; 499 500 addInstruction(InstrMeta); 501 502 if (!ValidInstruction) 503 continue; 504 505 // Skip additional parsing for instructions that do not affect the control 506 // flow. 507 const auto &InstrDesc = MII->get(Instruction.getOpcode()); 508 if (!InstrDesc.mayAffectControlFlow(Instruction, *RegisterInfo)) 509 continue; 510 511 uint64_t Target; 512 if (MIA->evaluateBranch(Instruction, VMAddress, InstructionSize, Target)) { 513 // If the target can be evaluated, it's not indirect. 514 StaticBranchTargetings[Target].push_back(VMAddress); 515 continue; 516 } 517 518 if (!usesRegisterOperand(InstrMeta)) 519 continue; 520 521 if (InstrDesc.isReturn()) 522 continue; 523 524 // Check if this instruction exists in the range of the DWARF metadata. 525 if (!IgnoreDWARFFlag) { 526 auto LineInfo = 527 Symbolizer->symbolizeCode(std::string(Object->getFileName()), 528 {VMAddress, Address.SectionIndex}); 529 if (!LineInfo) { 530 handleAllErrors(LineInfo.takeError(), [](const ErrorInfoBase &E) { 531 errs() << "Symbolizer failed to get line: " << E.message() << "\n"; 532 }); 533 continue; 534 } 535 536 if (LineInfo->FileName == DILineInfo::BadString) 537 continue; 538 } 539 540 IndirectInstructions.insert({VMAddress, Address.SectionIndex}); 541 } 542 } 543 544 void FileAnalysis::addInstruction(const Instr &Instruction) { 545 const auto &KV = 546 Instructions.insert(std::make_pair(Instruction.VMAddress, Instruction)); 547 if (!KV.second) { 548 errs() << "Failed to add instruction at address " 549 << format_hex(Instruction.VMAddress, 2) 550 << ": Instruction at this address already exists.\n"; 551 exit(EXIT_FAILURE); 552 } 553 } 554 555 Error FileAnalysis::parseSymbolTable() { 556 // Functions that will trap on CFI violations. 557 SmallSet<StringRef, 4> TrapOnFailFunctions; 558 TrapOnFailFunctions.insert("__cfi_slowpath"); 559 TrapOnFailFunctions.insert("__cfi_slowpath_diag"); 560 TrapOnFailFunctions.insert("abort"); 561 562 // Look through the list of symbols for functions that will trap on CFI 563 // violations. 564 for (auto &Sym : Object->symbols()) { 565 auto SymNameOrErr = Sym.getName(); 566 if (!SymNameOrErr) 567 consumeError(SymNameOrErr.takeError()); 568 else if (TrapOnFailFunctions.contains(*SymNameOrErr)) { 569 auto AddrOrErr = Sym.getAddress(); 570 if (!AddrOrErr) 571 consumeError(AddrOrErr.takeError()); 572 else 573 TrapOnFailFunctionAddresses.insert(*AddrOrErr); 574 } 575 } 576 if (auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Object)) { 577 for (const auto &Addr : ElfObject->getPltAddresses()) { 578 if (!Addr.first) 579 continue; 580 object::SymbolRef Sym(*Addr.first, Object); 581 auto SymNameOrErr = Sym.getName(); 582 if (!SymNameOrErr) 583 consumeError(SymNameOrErr.takeError()); 584 else if (TrapOnFailFunctions.contains(*SymNameOrErr)) 585 TrapOnFailFunctionAddresses.insert(Addr.second); 586 } 587 } 588 return Error::success(); 589 } 590 591 UnsupportedDisassembly::UnsupportedDisassembly(StringRef Text) 592 : Text(std::string(Text)) {} 593 594 char UnsupportedDisassembly::ID; 595 void UnsupportedDisassembly::log(raw_ostream &OS) const { 596 OS << "Could not initialise disassembler: " << Text; 597 } 598 599 std::error_code UnsupportedDisassembly::convertToErrorCode() const { 600 return std::error_code(); 601 } 602 603 } // namespace cfi_verify 604 } // namespace llvm 605