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(L&: IgnoreDWARFFlag), cl::init(Val: 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(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 | |
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(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 | |
138 | const Instr * |
139 | FileAnalysis::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 | |
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(x: 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(x: 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(Opcode: 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(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 | |
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(Opcode: InstrMeta.Instruction.getOpcode()); |
197 | if (InstrDesc.mayAffectControlFlow(MI: InstrMeta.Instruction, RI: *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(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 | |
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(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 | |
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(ModuleName: std::string(Object->getFileName()), |
282 | ModuleOffset: Address); |
283 | } |
284 | |
285 | CFIProtectionStatus |
286 | FileAnalysis::validateCFIProtection(const GraphResult &Graph) const { |
287 | const Instr *InstrMetaPtr = getInstruction(Address: Graph.BaseAddress); |
288 | if (!InstrMetaPtr) |
289 | return CFIProtectionStatus::FAIL_INVALID_INSTRUCTION; |
290 | |
291 | const auto &InstrDesc = MII->get(Opcode: InstrMetaPtr->Instruction.getOpcode()); |
292 | if (!InstrDesc.mayAffectControlFlow(MI: InstrMetaPtr->Instruction, RI: *RegisterInfo)) |
293 | return CFIProtectionStatus::FAIL_NOT_INDIRECT_CF; |
294 | |
295 | if (!usesRegisterOperand(InstrMeta: *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(Address: Graph.BaseAddress); |
317 | DenseSet<unsigned> RegisterNumbers; |
318 | for (const auto &Operand : IndirectCF.Instruction) { |
319 | if (Operand.isReg()) |
320 | RegisterNumbers.insert(V: 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(Opcode: 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(Address: Node); |
340 | for (auto I = Nodes.rbegin() + 1, E = Nodes.rend(); I != E; ++I) { |
341 | Node = *I; |
342 | const Instr &NodeInstr = getInstructionOrDie(Address: Node); |
343 | const auto &InstrDesc = MII->get(Opcode: 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(MI: NodeInstr.Instruction, Reg: RegNum, |
349 | RI: *RegisterInfo)) { |
350 | if (!canLoad || !InstrDesc.mayLoad()) |
351 | return Node; |
352 | canLoad = false; |
353 | CurRegisterNumbers.erase(I: 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(V: 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(MI: &InstrMeta.Instruction, Address: 0, Annot: "" , STI: *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(p: new LLVMSymbolizer(Opt)); |
384 | |
385 | ObjectTarget = |
386 | TargetRegistry::lookupTarget(ArchName, TheTriple&: ObjectTriple, Error&: ErrorString); |
387 | if (!ObjectTarget) |
388 | return make_error<UnsupportedDisassembly>( |
389 | Args: (Twine("Couldn't find target \"" ) + ObjectTriple.getTriple() + |
390 | "\", failed with error: " + ErrorString) |
391 | .str()); |
392 | |
393 | RegisterInfo.reset(p: ObjectTarget->createMCRegInfo(TT: TripleName)); |
394 | if (!RegisterInfo) |
395 | return make_error<UnsupportedDisassembly>( |
396 | Args: "Failed to initialise RegisterInfo." ); |
397 | |
398 | MCTargetOptions MCOptions; |
399 | AsmInfo.reset( |
400 | p: ObjectTarget->createMCAsmInfo(MRI: *RegisterInfo, TheTriple: TripleName, Options: MCOptions)); |
401 | if (!AsmInfo) |
402 | return make_error<UnsupportedDisassembly>(Args: "Failed to initialise AsmInfo." ); |
403 | |
404 | SubtargetInfo.reset(p: ObjectTarget->createMCSubtargetInfo( |
405 | TheTriple: TripleName, CPU: MCPU, Features: Features.getString())); |
406 | if (!SubtargetInfo) |
407 | return make_error<UnsupportedDisassembly>( |
408 | Args: "Failed to initialise SubtargetInfo." ); |
409 | |
410 | MII.reset(p: ObjectTarget->createMCInstrInfo()); |
411 | if (!MII) |
412 | return make_error<UnsupportedDisassembly>(Args: "Failed to initialise MII." ); |
413 | |
414 | Context.reset(p: new MCContext(Triple(TripleName), AsmInfo.get(), |
415 | RegisterInfo.get(), SubtargetInfo.get())); |
416 | |
417 | Disassembler.reset( |
418 | p: ObjectTarget->createMCDisassembler(STI: *SubtargetInfo, Ctx&: *Context)); |
419 | |
420 | if (!Disassembler) |
421 | return make_error<UnsupportedDisassembly>( |
422 | Args: "No disassembler available for target" ); |
423 | |
424 | MIA.reset(p: ObjectTarget->createMCInstrAnalysis(Info: MII.get())); |
425 | |
426 | Printer.reset(p: ObjectTarget->createMCInstPrinter( |
427 | T: ObjectTriple, SyntaxVariant: AsmInfo->getAssemblerDialect(), MAI: *AsmInfo, MII: *MII, |
428 | MRI: *RegisterInfo)); |
429 | |
430 | return Error::success(); |
431 | } |
432 | |
433 | Error FileAnalysis::parseCodeSections() { |
434 | if (!IgnoreDWARFFlag) { |
435 | std::unique_ptr<DWARFContext> DWARF = DWARFContext::create(Obj: *Object); |
436 | if (!DWARF) |
437 | return make_error<StringError>(Args: "Could not create DWARF information." , |
438 | Args: inconvertibleErrorCode()); |
439 | |
440 | bool LineInfoValid = false; |
441 | |
442 | for (auto &Unit : DWARF->compile_units()) { |
443 | const auto &LineTable = DWARF->getLineTableForUnit(U: 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 | Args: "DWARF line information missing. Did you compile with '-g'?" , |
453 | Args: 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(Err: NameOrErr.takeError()); |
467 | |
468 | Expected<StringRef> Contents = Section.getContents(); |
469 | if (!Contents) |
470 | return Contents.takeError(); |
471 | ArrayRef<uint8_t> SectionBytes = arrayRefFromStringRef(Input: *Contents); |
472 | |
473 | parseSectionContents(SectionBytes, |
474 | Address: {.Address: Section.getAddress(), .SectionIndex: 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(Instr&: Instruction, Size&: InstructionSize, |
489 | Bytes: SectionBytes.drop_front(N: Byte), Address: 0, |
490 | CStream&: 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(Instruction: 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(Opcode: Instruction.getOpcode()); |
508 | if (!InstrDesc.mayAffectControlFlow(MI: Instruction, RI: *RegisterInfo)) |
509 | continue; |
510 | |
511 | uint64_t Target; |
512 | if (MIA->evaluateBranch(Inst: Instruction, Addr: VMAddress, Size: InstructionSize, Target)) { |
513 | // If the target can be evaluated, it's not indirect. |
514 | StaticBranchTargetings[Target].push_back(x: 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(ModuleName: std::string(Object->getFileName()), |
528 | ModuleOffset: {.Address: VMAddress, .SectionIndex: Address.SectionIndex}); |
529 | if (!LineInfo) { |
530 | handleAllErrors(E: LineInfo.takeError(), Handlers: [](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(x: {.Address: VMAddress, .SectionIndex: Address.SectionIndex}); |
541 | } |
542 | } |
543 | |
544 | void FileAnalysis::addInstruction(const Instr &Instruction) { |
545 | const auto &KV = |
546 | Instructions.insert(x: std::make_pair(x: Instruction.VMAddress, y: Instruction)); |
547 | if (!KV.second) { |
548 | errs() << "Failed to add instruction at address " |
549 | << format_hex(N: Instruction.VMAddress, Width: 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(V: "__cfi_slowpath" ); |
559 | TrapOnFailFunctions.insert(V: "__cfi_slowpath_diag" ); |
560 | TrapOnFailFunctions.insert(V: "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(Err: SymNameOrErr.takeError()); |
568 | else if (TrapOnFailFunctions.contains(V: *SymNameOrErr)) { |
569 | auto AddrOrErr = Sym.getAddress(); |
570 | if (!AddrOrErr) |
571 | consumeError(Err: AddrOrErr.takeError()); |
572 | else |
573 | TrapOnFailFunctionAddresses.insert(V: *AddrOrErr); |
574 | } |
575 | } |
576 | if (auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Val: Object)) { |
577 | for (const auto &Plt : ElfObject->getPltEntries()) { |
578 | if (!Plt.Symbol) |
579 | continue; |
580 | object::SymbolRef Sym(*Plt.Symbol, Object); |
581 | auto SymNameOrErr = Sym.getName(); |
582 | if (!SymNameOrErr) |
583 | consumeError(Err: SymNameOrErr.takeError()); |
584 | else if (TrapOnFailFunctions.contains(V: *SymNameOrErr)) |
585 | TrapOnFailFunctionAddresses.insert(V: Plt.Address); |
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 | |