Index: test/tools/llvm-cfi-verify/X86/dot-printing.s =================================================================== --- /dev/null +++ test/tools/llvm-cfi-verify/X86/dot-printing.s @@ -0,0 +1,18 @@ +# RUN: llvm-mc %S/Inputs/protected-lineinfo.s -filetype obj \ +# RUN: -triple x86_64-linux-elf -o %t.o +# RUN: llvm-cfi-verify -print-graphs %t.o | FileCheck %s + +# The expected output is as follows: +# P 0x7b | callq *%rax +# digraph graph_0x7b { +# "0x77: jbe 2" -> "0x7b: callq *%rax" +# "0x77: jbe 2" -> "0x79: ud2" +# } +# 0x7b = tiny.cc:11:3 (main) + +# CHECK: {{^P.*callq +\*%rax.*$}} +# CHECK-NEXT: digraph +# CHECK-NEXT: {{^.*jbe.*->.*callq \*%rax}} +# CHECK-NEXT: {{^.*jbe.*->.*ud2}} +# CHECK-NEXT: } +# CHECK-NEXT: tiny.cc:11 Index: tools/llvm-cfi-verify/lib/FileAnalysis.h =================================================================== --- tools/llvm-cfi-verify/lib/FileAnalysis.h +++ tools/llvm-cfi-verify/lib/FileAnalysis.h @@ -145,6 +145,10 @@ // flow instruction in this file. CFIProtectionStatus validateCFIProtection(const GraphResult &Graph) const; + // Prints an instruction to the provided stream using this object's pretty- + // printers. + void printInstruction(const Instr &InstrMeta, raw_ostream &OS) const; + protected: // Construct a blank object with the provided triple and features. Used in // testing, where a sub class will dependency inject protected methods to Index: tools/llvm-cfi-verify/lib/FileAnalysis.cpp =================================================================== --- tools/llvm-cfi-verify/lib/FileAnalysis.cpp +++ tools/llvm-cfi-verify/lib/FileAnalysis.cpp @@ -273,6 +273,11 @@ return CFIProtectionStatus::PROTECTED; } +void FileAnalysis::printInstruction(const Instr &InstrMeta, + raw_ostream &OS) const { + Printer->printInst(&InstrMeta.Instruction, OS, "", *SubtargetInfo.get()); +} + Error FileAnalysis::initialiseDisassemblyMembers() { std::string TripleName = ObjectTriple.getTriple(); ArchName = ""; Index: tools/llvm-cfi-verify/lib/GraphBuilder.h =================================================================== --- tools/llvm-cfi-verify/lib/GraphBuilder.h +++ tools/llvm-cfi-verify/lib/GraphBuilder.h @@ -89,6 +89,9 @@ // base. The provided address must be part of this graph, and must not be a // conditional branch. std::vector flattenAddress(uint64_t Address) const; + + // Print the DOT representation of this result. + void printToDOT(const FileAnalysis &Analysis, raw_ostream &OS) const; }; class GraphBuilder { Index: tools/llvm-cfi-verify/lib/GraphBuilder.cpp =================================================================== --- tools/llvm-cfi-verify/lib/GraphBuilder.cpp +++ tools/llvm-cfi-verify/lib/GraphBuilder.cpp @@ -71,6 +71,30 @@ return Addresses; } +void printPairToDOT(const FileAnalysis &Analysis, raw_ostream &OS, + uint64_t From, uint64_t To) { + OS << " \"" << format_hex(From, 2) << ": "; + Analysis.printInstruction(Analysis.getInstructionOrDie(From), OS); + OS << "\" -> \"" << format_hex(To, 2) << ": "; + Analysis.printInstruction(Analysis.getInstructionOrDie(To), OS); + OS << "\"\n"; +} + +void GraphResult::printToDOT(const FileAnalysis &Analysis, + raw_ostream &OS) const { + std::map SortedIntermediateNodes( + IntermediateNodes.begin(), IntermediateNodes.end()); + OS << "digraph graph_" << format_hex(BaseAddress, 2) << " {\n"; + for (const auto &KV : SortedIntermediateNodes) + printPairToDOT(Analysis, OS, KV.first, KV.second); + + for (auto &BranchNode : ConditionalBranchNodes) { + for (auto &V : {BranchNode.Target, BranchNode.Fallthrough}) + printPairToDOT(Analysis, OS, BranchNode.Address, V); + } + OS << "}\n"; +} + GraphResult GraphBuilder::buildFlowGraph(const FileAnalysis &Analysis, uint64_t Address) { GraphResult Result; Index: tools/llvm-cfi-verify/llvm-cfi-verify.cpp =================================================================== --- tools/llvm-cfi-verify/llvm-cfi-verify.cpp +++ tools/llvm-cfi-verify/llvm-cfi-verify.cpp @@ -37,6 +37,10 @@ cl::opt BlacklistFilename(cl::Positional, cl::desc("[blacklist file]"), cl::init("-")); +cl::opt PrintGraphs( + "print-graphs", + cl::desc("Print graphs around indirect CF instructions in DOT format."), + cl::init(false)); ExitOnError ExitOnErr; @@ -62,10 +66,12 @@ else outs() << "U "; - outs() << format_hex(Address, 2) << " | " - << Analysis.getMCInstrInfo()->getName( - InstrMeta.Instruction.getOpcode()) - << " \n"; + outs() << format_hex(Address, 2) << " | "; + Analysis.printInstruction(InstrMeta, outs()); + outs() << " \n"; + + if (PrintGraphs) + Graph.printToDOT(Analysis, outs()); if (IgnoreDWARFFlag) { if (CFIProtected)