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
39using Instr = llvm::cfi_verify::FileAnalysis::Instr;
40using LLVMSymbolizer = llvm::symbolize::LLVMSymbolizer;
41
42namespace llvm {
43namespace cfi_verify {
44
45bool IgnoreDWARFFlag;
46
47static 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(L&: IgnoreDWARFFlag), cl::init(Val: false));
54
55StringRef 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
73Expected<FileAnalysis> FileAnalysis::Create(StringRef Filename) {
74 // Open the filename provided.
75 Expected<object::OwningBinary<object::Binary>> BinaryOrErr =
76 object::createBinary(Path: 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>(Val: Analysis.Binary.getBinary());
85 if (!Analysis.Object)
86 return make_error<UnsupportedDisassembly>(Args: "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>(Args: "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
118FileAnalysis::FileAnalysis(object::OwningBinary<object::Binary> Binary)
119 : Binary(std::move(Binary)) {}
120
121FileAnalysis::FileAnalysis(const Triple &ObjectTriple,
122 const SubtargetFeatures &Features)
123 : ObjectTriple(ObjectTriple), Features(Features) {}
124
125const Instr *
126FileAnalysis::getPrevInstructionSequential(const Instr &InstrMeta) const {
127 std::map<uint64_t, Instr>::const_iterator KV =
128 Instructions.find(x: 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
138const Instr *
139FileAnalysis::getNextInstructionSequential(const Instr &InstrMeta) const {
140 std::map<uint64_t, Instr>::const_iterator KV =
141 Instructions.find(x: 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
151bool 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
159const Instr *FileAnalysis::getInstruction(uint64_t Address) const {
160 const auto &InstrKV = Instructions.find(x: Address);
161 if (InstrKV == Instructions.end())
162 return nullptr;
163
164 return &InstrKV->second;
165}
166
167const Instr &FileAnalysis::getInstructionOrDie(uint64_t Address) const {
168 const auto &InstrKV = Instructions.find(x: Address);
169 assert(InstrKV != Instructions.end() && "Address doesn't exist.");
170 return InstrKV->second;
171}
172
173bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const {
174 const auto &InstrDesc = MII->get(Opcode: InstrMeta.Instruction.getOpcode());
175 return InstrDesc.isTrap() || willTrapOnCFIViolation(InstrMeta);
176}
177
178bool FileAnalysis::willTrapOnCFIViolation(const Instr &InstrMeta) const {
179 const auto &InstrDesc = MII->get(Opcode: InstrMeta.Instruction.getOpcode());
180 if (!InstrDesc.isCall())
181 return false;
182 uint64_t Target;
183 if (!MIA->evaluateBranch(Inst: InstrMeta.Instruction, Addr: InstrMeta.VMAddress,
184 Size: InstrMeta.InstructionSize, Target))
185 return false;
186 return TrapOnFailFunctionAddresses.contains(V: Target);
187}
188
189bool 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(Opcode: InstrMeta.Instruction.getOpcode());
197 if (InstrDesc.mayAffectControlFlow(MI: InstrMeta.Instruction, RI: *RegisterInfo))
198 return InstrDesc.isConditionalBranch();
199
200 return true;
201}
202
203const Instr *
204FileAnalysis::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(Opcode: InstrMeta.Instruction.getOpcode());
212 const Instr *NextMetaPtr;
213 if (InstrDesc.mayAffectControlFlow(MI: InstrMeta.Instruction, RI: *RegisterInfo)) {
214 if (InstrDesc.isConditionalBranch())
215 return nullptr;
216
217 uint64_t Target;
218 if (!MIA->evaluateBranch(Inst: InstrMeta.Instruction, Addr: InstrMeta.VMAddress,
219 Size: InstrMeta.InstructionSize, Target))
220 return nullptr;
221
222 NextMetaPtr = getInstruction(Address: Target);
223 } else {
224 NextMetaPtr =
225 getInstruction(Address: InstrMeta.VMAddress + InstrMeta.InstructionSize);
226 }
227
228 if (!NextMetaPtr || !NextMetaPtr->Valid)
229 return nullptr;
230
231 return NextMetaPtr;
232}
233
234std::set<const Instr *>
235FileAnalysis::getDirectControlFlowXRefs(const Instr &InstrMeta) const {
236 std::set<const Instr *> CFCrossReferences;
237 const Instr *PrevInstruction = getPrevInstructionSequential(InstrMeta);
238
239 if (PrevInstruction && canFallThrough(InstrMeta: *PrevInstruction))
240 CFCrossReferences.insert(x: PrevInstruction);
241
242 const auto &TargetRefsKV = StaticBranchTargetings.find(Val: InstrMeta.VMAddress);
243 if (TargetRefsKV == StaticBranchTargetings.end())
244 return CFCrossReferences;
245
246 for (uint64_t SourceInstrAddress : TargetRefsKV->second) {
247 const auto &SourceInstrKV = Instructions.find(x: SourceInstrAddress);
248 if (SourceInstrKV == Instructions.end()) {
249 errs() << "Failed to find source instruction at address "
250 << format_hex(N: SourceInstrAddress, Width: 2)
251 << " for the cross-reference to instruction at address "
252 << format_hex(N: InstrMeta.VMAddress, Width: 2) << ".\n";
253 continue;
254 }
255
256 CFCrossReferences.insert(x: &SourceInstrKV->second);
257 }
258
259 return CFCrossReferences;
260}
261
262const std::set<object::SectionedAddress> &
263FileAnalysis::getIndirectInstructions() const {
264 return IndirectInstructions;
265}
266
267const MCRegisterInfo *FileAnalysis::getRegisterInfo() const {
268 return RegisterInfo.get();
269}
270
271const MCInstrInfo *FileAnalysis::getMCInstrInfo() const { return MII.get(); }
272
273const MCInstrAnalysis *FileAnalysis::getMCInstrAnalysis() const {
274 return MIA.get();
275}
276
277Expected<DIInliningInfo>
278FileAnalysis::symbolizeInlinedCode(object::SectionedAddress Address) {
279 assert(Symbolizer != nullptr && "Symbolizer is invalid.");
280
281 return Symbolizer->symbolizeInlinedCode(ModuleName: Object->getFileName(), ModuleOffset: Address);
282}
283
284CFIProtectionStatus
285FileAnalysis::validateCFIProtection(const GraphResult &Graph) const {
286 const Instr *InstrMetaPtr = getInstruction(Address: Graph.BaseAddress);
287 if (!InstrMetaPtr)
288 return CFIProtectionStatus::FAIL_INVALID_INSTRUCTION;
289
290 const auto &InstrDesc = MII->get(Opcode: InstrMetaPtr->Instruction.getOpcode());
291 if (!InstrDesc.mayAffectControlFlow(MI: InstrMetaPtr->Instruction, RI: *RegisterInfo))
292 return CFIProtectionStatus::FAIL_NOT_INDIRECT_CF;
293
294 if (!usesRegisterOperand(InstrMeta: *InstrMetaPtr))
295 return CFIProtectionStatus::FAIL_NOT_INDIRECT_CF;
296
297 if (!Graph.OrphanedNodes.empty())
298 return CFIProtectionStatus::FAIL_ORPHANS;
299
300 for (const auto &BranchNode : Graph.ConditionalBranchNodes) {
301 if (!BranchNode.CFIProtection)
302 return CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH;
303 }
304
305 if (indirectCFOperandClobber(Graph) != Graph.BaseAddress)
306 return CFIProtectionStatus::FAIL_REGISTER_CLOBBERED;
307
308 return CFIProtectionStatus::PROTECTED;
309}
310
311uint64_t FileAnalysis::indirectCFOperandClobber(const GraphResult &Graph) const {
312 assert(Graph.OrphanedNodes.empty() && "Orphaned nodes should be empty.");
313
314 // Get the set of registers we must check to ensure they're not clobbered.
315 const Instr &IndirectCF = getInstructionOrDie(Address: Graph.BaseAddress);
316 DenseSet<unsigned> RegisterNumbers;
317 for (const auto &Operand : IndirectCF.Instruction) {
318 if (Operand.isReg())
319 RegisterNumbers.insert(V: Operand.getReg());
320 }
321 assert(RegisterNumbers.size() && "Zero register operands on indirect CF.");
322
323 // Now check all branches to indirect CFs and ensure no clobbering happens.
324 for (const auto &Branch : Graph.ConditionalBranchNodes) {
325 uint64_t Node;
326 if (Branch.IndirectCFIsOnTargetPath)
327 Node = Branch.Target;
328 else
329 Node = Branch.Fallthrough;
330
331 // Some architectures (e.g., AArch64) cannot load in an indirect branch, so
332 // we allow them one load.
333 bool canLoad = !MII->get(Opcode: IndirectCF.Instruction.getOpcode()).mayLoad();
334
335 // We walk backwards from the indirect CF. It is the last node returned by
336 // Graph.flattenAddress, so we skip it since we already handled it.
337 DenseSet<unsigned> CurRegisterNumbers = RegisterNumbers;
338 std::vector<uint64_t> Nodes = Graph.flattenAddress(Address: Node);
339 for (auto I = Nodes.rbegin() + 1, E = Nodes.rend(); I != E; ++I) {
340 Node = *I;
341 const Instr &NodeInstr = getInstructionOrDie(Address: Node);
342 const auto &InstrDesc = MII->get(Opcode: NodeInstr.Instruction.getOpcode());
343
344 for (auto RI = CurRegisterNumbers.begin(), RE = CurRegisterNumbers.end();
345 RI != RE; ++RI) {
346 unsigned RegNum = *RI;
347 if (InstrDesc.hasDefOfPhysReg(MI: NodeInstr.Instruction, Reg: RegNum,
348 RI: *RegisterInfo)) {
349 if (!canLoad || !InstrDesc.mayLoad())
350 return Node;
351 canLoad = false;
352 CurRegisterNumbers.erase(I: RI);
353 // Add the registers this load reads to those we check for clobbers.
354 for (unsigned i = InstrDesc.getNumDefs(),
355 e = InstrDesc.getNumOperands(); i != e; i++) {
356 const auto &Operand = NodeInstr.Instruction.getOperand(i);
357 if (Operand.isReg())
358 CurRegisterNumbers.insert(V: Operand.getReg());
359 }
360 break;
361 }
362 }
363 }
364 }
365
366 return Graph.BaseAddress;
367}
368
369void FileAnalysis::printInstruction(const Instr &InstrMeta,
370 raw_ostream &OS) const {
371 Printer->printInst(MI: &InstrMeta.Instruction, Address: 0, Annot: "", STI: *SubtargetInfo, OS);
372}
373
374Error FileAnalysis::initialiseDisassemblyMembers() {
375 std::string TripleName = ObjectTriple.getTriple();
376 ArchName = "";
377 MCPU = "";
378 std::string ErrorString;
379
380 LLVMSymbolizer::Options Opt;
381 Opt.UseSymbolTable = false;
382 Symbolizer.reset(p: new LLVMSymbolizer(Opt));
383
384 ObjectTarget =
385 TargetRegistry::lookupTarget(ArchName, TheTriple&: ObjectTriple, Error&: ErrorString);
386 if (!ObjectTarget)
387 return make_error<UnsupportedDisassembly>(
388 Args: (Twine("Couldn't find target \"") + ObjectTriple.getTriple() +
389 "\", failed with error: " + ErrorString)
390 .str());
391
392 RegisterInfo.reset(p: ObjectTarget->createMCRegInfo(TT: TripleName));
393 if (!RegisterInfo)
394 return make_error<UnsupportedDisassembly>(
395 Args: "Failed to initialise RegisterInfo.");
396
397 MCTargetOptions MCOptions;
398 AsmInfo.reset(
399 p: ObjectTarget->createMCAsmInfo(MRI: *RegisterInfo, TheTriple: TripleName, Options: MCOptions));
400 if (!AsmInfo)
401 return make_error<UnsupportedDisassembly>(Args: "Failed to initialise AsmInfo.");
402
403 SubtargetInfo.reset(p: ObjectTarget->createMCSubtargetInfo(
404 TheTriple: TripleName, CPU: MCPU, Features: Features.getString()));
405 if (!SubtargetInfo)
406 return make_error<UnsupportedDisassembly>(
407 Args: "Failed to initialise SubtargetInfo.");
408
409 MII.reset(p: ObjectTarget->createMCInstrInfo());
410 if (!MII)
411 return make_error<UnsupportedDisassembly>(Args: "Failed to initialise MII.");
412
413 Context.reset(p: new MCContext(Triple(TripleName), AsmInfo.get(),
414 RegisterInfo.get(), SubtargetInfo.get()));
415
416 Disassembler.reset(
417 p: ObjectTarget->createMCDisassembler(STI: *SubtargetInfo, Ctx&: *Context));
418
419 if (!Disassembler)
420 return make_error<UnsupportedDisassembly>(
421 Args: "No disassembler available for target");
422
423 MIA.reset(p: ObjectTarget->createMCInstrAnalysis(Info: MII.get()));
424
425 Printer.reset(p: ObjectTarget->createMCInstPrinter(
426 T: ObjectTriple, SyntaxVariant: AsmInfo->getAssemblerDialect(), MAI: *AsmInfo, MII: *MII,
427 MRI: *RegisterInfo));
428
429 return Error::success();
430}
431
432Error FileAnalysis::parseCodeSections() {
433 if (!IgnoreDWARFFlag) {
434 std::unique_ptr<DWARFContext> DWARF = DWARFContext::create(Obj: *Object);
435 if (!DWARF)
436 return make_error<StringError>(Args: "Could not create DWARF information.",
437 Args: inconvertibleErrorCode());
438
439 bool LineInfoValid = false;
440
441 for (auto &Unit : DWARF->compile_units()) {
442 const auto &LineTable = DWARF->getLineTableForUnit(U: Unit.get());
443 if (LineTable && !LineTable->Rows.empty()) {
444 LineInfoValid = true;
445 break;
446 }
447 }
448
449 if (!LineInfoValid)
450 return make_error<StringError>(
451 Args: "DWARF line information missing. Did you compile with '-g'?",
452 Args: inconvertibleErrorCode());
453 }
454
455 for (const object::SectionRef &Section : Object->sections()) {
456 // Ensure only executable sections get analysed.
457 if (!(object::ELFSectionRef(Section).getFlags() & ELF::SHF_EXECINSTR))
458 continue;
459
460 // Avoid checking the PLT since it produces spurious failures on AArch64
461 // when ignoring DWARF data.
462 Expected<StringRef> NameOrErr = Section.getName();
463 if (NameOrErr && *NameOrErr == ".plt")
464 continue;
465 consumeError(Err: NameOrErr.takeError());
466
467 Expected<StringRef> Contents = Section.getContents();
468 if (!Contents)
469 return Contents.takeError();
470 ArrayRef<uint8_t> SectionBytes = arrayRefFromStringRef(Input: *Contents);
471
472 parseSectionContents(SectionBytes,
473 Address: {.Address: Section.getAddress(), .SectionIndex: Section.getIndex()});
474 }
475 return Error::success();
476}
477
478void FileAnalysis::parseSectionContents(ArrayRef<uint8_t> SectionBytes,
479 object::SectionedAddress Address) {
480 assert(Symbolizer && "Symbolizer is uninitialised.");
481 MCInst Instruction;
482 Instr InstrMeta;
483 uint64_t InstructionSize;
484
485 for (uint64_t Byte = 0; Byte < SectionBytes.size();) {
486 bool ValidInstruction =
487 Disassembler->getInstruction(Instr&: Instruction, Size&: InstructionSize,
488 Bytes: SectionBytes.drop_front(N: Byte), Address: 0,
489 CStream&: outs()) == MCDisassembler::Success;
490
491 Byte += InstructionSize;
492
493 uint64_t VMAddress = Address.Address + Byte - InstructionSize;
494 InstrMeta.Instruction = Instruction;
495 InstrMeta.VMAddress = VMAddress;
496 InstrMeta.InstructionSize = InstructionSize;
497 InstrMeta.Valid = ValidInstruction;
498
499 addInstruction(Instruction: InstrMeta);
500
501 if (!ValidInstruction)
502 continue;
503
504 // Skip additional parsing for instructions that do not affect the control
505 // flow.
506 const auto &InstrDesc = MII->get(Opcode: Instruction.getOpcode());
507 if (!InstrDesc.mayAffectControlFlow(MI: Instruction, RI: *RegisterInfo))
508 continue;
509
510 uint64_t Target;
511 if (MIA->evaluateBranch(Inst: Instruction, Addr: VMAddress, Size: InstructionSize, Target)) {
512 // If the target can be evaluated, it's not indirect.
513 StaticBranchTargetings[Target].push_back(x: VMAddress);
514 continue;
515 }
516
517 if (!usesRegisterOperand(InstrMeta))
518 continue;
519
520 if (InstrDesc.isReturn())
521 continue;
522
523 // Check if this instruction exists in the range of the DWARF metadata.
524 if (!IgnoreDWARFFlag) {
525 auto LineInfo = Symbolizer->symbolizeCode(
526 ModuleName: Object->getFileName(), ModuleOffset: {.Address: VMAddress, .SectionIndex: Address.SectionIndex});
527 if (!LineInfo) {
528 handleAllErrors(E: LineInfo.takeError(), Handlers: [](const ErrorInfoBase &E) {
529 errs() << "Symbolizer failed to get line: " << E.message() << "\n";
530 });
531 continue;
532 }
533
534 if (LineInfo->FileName == DILineInfo::BadString)
535 continue;
536 }
537
538 IndirectInstructions.insert(x: {.Address: VMAddress, .SectionIndex: Address.SectionIndex});
539 }
540}
541
542void FileAnalysis::addInstruction(const Instr &Instruction) {
543 const auto &KV =
544 Instructions.insert(x: std::make_pair(x: Instruction.VMAddress, y: Instruction));
545 if (!KV.second) {
546 errs() << "Failed to add instruction at address "
547 << format_hex(N: Instruction.VMAddress, Width: 2)
548 << ": Instruction at this address already exists.\n";
549 exit(EXIT_FAILURE);
550 }
551}
552
553Error FileAnalysis::parseSymbolTable() {
554 // Functions that will trap on CFI violations.
555 SmallSet<StringRef, 4> TrapOnFailFunctions;
556 TrapOnFailFunctions.insert(V: "__cfi_slowpath");
557 TrapOnFailFunctions.insert(V: "__cfi_slowpath_diag");
558 TrapOnFailFunctions.insert(V: "abort");
559
560 // Look through the list of symbols for functions that will trap on CFI
561 // violations.
562 for (auto &Sym : Object->symbols()) {
563 auto SymNameOrErr = Sym.getName();
564 if (!SymNameOrErr)
565 consumeError(Err: SymNameOrErr.takeError());
566 else if (TrapOnFailFunctions.contains(V: *SymNameOrErr)) {
567 auto AddrOrErr = Sym.getAddress();
568 if (!AddrOrErr)
569 consumeError(Err: AddrOrErr.takeError());
570 else
571 TrapOnFailFunctionAddresses.insert(V: *AddrOrErr);
572 }
573 }
574 if (auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Val: Object)) {
575 for (const auto &Plt : ElfObject->getPltEntries(STI: *SubtargetInfo)) {
576 if (!Plt.Symbol)
577 continue;
578 object::SymbolRef Sym(*Plt.Symbol, Object);
579 auto SymNameOrErr = Sym.getName();
580 if (!SymNameOrErr)
581 consumeError(Err: SymNameOrErr.takeError());
582 else if (TrapOnFailFunctions.contains(V: *SymNameOrErr))
583 TrapOnFailFunctionAddresses.insert(V: Plt.Address);
584 }
585 }
586 return Error::success();
587}
588
589UnsupportedDisassembly::UnsupportedDisassembly(StringRef Text)
590 : Text(std::string(Text)) {}
591
592char UnsupportedDisassembly::ID;
593void UnsupportedDisassembly::log(raw_ostream &OS) const {
594 OS << "Could not initialise disassembler: " << Text;
595}
596
597std::error_code UnsupportedDisassembly::convertToErrorCode() const {
598 return std::error_code();
599}
600
601} // namespace cfi_verify
602} // namespace llvm
603