Index: tools/llvm-cfi-verify/CMakeLists.txt =================================================================== --- tools/llvm-cfi-verify/CMakeLists.txt +++ tools/llvm-cfi-verify/CMakeLists.txt @@ -9,6 +9,7 @@ MCParser Object Support + DebugInfoDWARF ) add_llvm_tool(llvm-cfi-verify Index: tools/llvm-cfi-verify/FileAnalysis.h =================================================================== --- tools/llvm-cfi-verify/FileAnalysis.h +++ tools/llvm-cfi-verify/FileAnalysis.h @@ -11,6 +11,7 @@ #define LLVM_CFI_VERIFY_FILE_ANALYSIS_H #include "llvm/BinaryFormat/ELF.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" @@ -42,6 +43,8 @@ namespace llvm { namespace cfi_verify { +extern bool IgnoreDWARF; + // Disassembler and analysis tool for machine code files. Keeps track of non- // sequential control flows, including indirect control flow instructions. class FileAnalysis { @@ -117,6 +120,8 @@ const MCInstrInfo *getMCInstrInfo() const; const MCInstrAnalysis *getMCInstrAnalysis() const; + DWARFContext *getDWARF(); + protected: // Construct a blank object with the provided triple and features. Used in // testing, where a sub class will dependency inject protected methods to @@ -159,8 +164,12 @@ std::unique_ptr MIA; std::unique_ptr Printer; + // DWARF debug information. + std::unique_ptr DWARF; + // A mapping between the virtual memory address to the instruction metadata - // struct. + // struct. TODO(hctim): Reimplement this as a sorted vector to avoid per- + // insertion allocation. std::map Instructions; // Contains a mapping between a specific address, and a list of instructions Index: tools/llvm-cfi-verify/FileAnalysis.cpp =================================================================== --- tools/llvm-cfi-verify/FileAnalysis.cpp +++ tools/llvm-cfi-verify/FileAnalysis.cpp @@ -11,6 +11,7 @@ #include "GraphBuilder.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler/MCDisassembler.h" @@ -42,6 +43,18 @@ namespace llvm { namespace cfi_verify { +bool IgnoreDWARF; + +static cl::opt IgnoreDWARFArg( + "ignore-dwarf", + cl::desc("Ignore all DWARF data (warning: may generate false positives)."), + cl::location(IgnoreDWARF), cl::init(false)); + +cl::opt DWARFSearchRange( + "dwarf-search-range", + cl::desc("Address search range used to determine if instruction is valid."), + cl::init(0x10)); + Expected FileAnalysis::Create(StringRef Filename) { // Open the filename provided. Expected> BinaryOrErr = @@ -254,6 +267,10 @@ return MIA.get(); } +DWARFContext *FileAnalysis::getDWARF() { + return DWARF.get(); +} + Error FileAnalysis::initialiseDisassemblyMembers() { std::string TripleName = ObjectTriple.getTriple(); ArchName = ""; @@ -310,6 +327,40 @@ } Error FileAnalysis::parseCodeSections() { + if (!IgnoreDWARF) { + DWARF.reset(DWARFContext::create(*Object).release()); + if (!DWARF) + return make_error("Could not create DWARF information.", + inconvertibleErrorCode()); + + bool LineInfoValid = false; + + for (auto &Unit : DWARF->compile_units()) { + const auto &LineTable = DWARF->getLineTableForUnit(Unit.get()); + if (LineTable && !LineTable->Rows.empty()) { + LineInfoValid = true; + break; + } + } + + if (!LineInfoValid) { + for (auto &Range : DWARF->type_unit_sections()) { + for (auto &Unit : Range) { + const auto &LineTable = DWARF->getLineTableForUnit(Unit.get()); + if (LineTable && !LineTable->Rows.empty()) { + LineInfoValid = true; + break; + } + } + } + } + + if (!LineInfoValid) + return make_error( + "DWARF line information missing. Did you compile with '-g'?", + inconvertibleErrorCode()); + } + for (const object::SectionRef &Section : Object->sections()) { // Ensure only executable sections get analysed. if (!(object::ELFSectionRef(Section).getFlags() & ELF::SHF_EXECINSTR)) @@ -346,6 +397,13 @@ InstrMeta.VMAddress = VMAddress; InstrMeta.InstructionSize = InstructionSize; InstrMeta.Valid = ValidInstruction; + + // Check if this instruction exists in the range of the DWARF metadata. + if (DWARF && + DWARF->getLineInfoForAddressRange(InstrMeta.VMAddress, DWARFSearchRange) + .empty()) + continue; + addInstruction(InstrMeta); if (!ValidInstruction) Index: tools/llvm-cfi-verify/GraphBuilder.cpp =================================================================== --- tools/llvm-cfi-verify/GraphBuilder.cpp +++ tools/llvm-cfi-verify/GraphBuilder.cpp @@ -303,6 +303,12 @@ continue; } + // Call instructions are not valid in the upwards traversal. + if (ParentDesc.isCall()) { + OrphanedNodes->push_back(createParentNode(ParentMeta, ChildNode)); + continue; + } + // Allow unconditional branches to be part of the upwards traversal. // Ensures that the unconditional branch is actually an XRef to the child. if (ParentDesc.isUnconditionalBranch()) { @@ -344,8 +350,10 @@ // Ensure that any unknown CFs are caught. if (!ParentDesc.isConditionalBranch()) { errs() << "Unknown control flow encountered when building graph at " - << format_hex(ChildMeta.VMAddress, 2) << "\n."; - OrphanedNodes->push_back(createParentNode(ParentMeta, ChildNode)); + << format_hex(ParentMeta.VMAddress, 2) << ".\n"; + const auto& ParentNode = createParentNode(ParentMeta, ChildNode); + OrphanedNodes->push_back(ParentNode); + ParentNode->printControlFlowNode(1, errs()); continue; } Index: tools/llvm-cfi-verify/LLVMBuild.txt =================================================================== --- tools/llvm-cfi-verify/LLVMBuild.txt +++ tools/llvm-cfi-verify/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-cfi-verify parent = Tools -required_libraries = MC MCDisassembler MCParser Support all-targets +required_libraries = MC MCDisassembler MCParser Support all-targets DebugInfoDWARF 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 @@ -22,6 +22,7 @@ #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" +#include "llvm/Support/FormatVariadic.h" #include @@ -34,17 +35,47 @@ ExitOnError ExitOnErr; -void printIndirectCFInstructions(const FileAnalysis &Verifier) { - for (uint64_t Address : Verifier.getIndirectInstructions()) { - const auto &InstrMeta = Verifier.getInstructionOrDie(Address); - outs() << format_hex(Address, 2) << " |" - << Verifier.getMCInstrInfo()->getName( +void printIndirectCFInstructions(FileAnalysis &Analysis) { + uint64_t ProtectedCount = 0; + uint64_t UnprotectedCount = 0; + DWARFContext *DWARF = Analysis.getDWARF(); + + for (uint64_t Address : Analysis.getIndirectInstructions()) { + const auto &InstrMeta = Analysis.getInstructionOrDie(Address); + + if (Analysis.isCFIProtected(Address)) { + outs() << "P "; + ProtectedCount++; + } else { + outs() << "U "; + UnprotectedCount++; + } + + outs() << format_hex(Address, 2) << " | " + << Analysis.getMCInstrInfo()->getName( InstrMeta.Instruction.getOpcode()) << " "; - InstrMeta.Instruction.print(outs()); outs() << "\n"; - outs() << " Protected? " << Verifier.isCFIProtected(Address) << "\n"; + + if (DWARF) { + for (const auto &LineKV : + DWARF->getLineInfoForAddressRange(Address, 0x10)) { + outs() << " " << format_hex(LineKV.first, 2) << " = " + << LineKV.second.FileName << ":" << LineKV.second.Line << ":" + << LineKV.second.Column << " (" + << LineKV.second.FunctionName << ")\n"; + } + } } + + if (ProtectedCount || UnprotectedCount) + outs() << formatv( + "Unprotected: {0} ({1:P}), Protected: {2} ({3:P})\n", UnprotectedCount, + (((double)UnprotectedCount) / (UnprotectedCount + ProtectedCount)), + ProtectedCount, + (((double)ProtectedCount) / (UnprotectedCount + ProtectedCount))); + else + outs() << "No indirect CF instructions found.\n"; } int main(int argc, char **argv) { @@ -55,8 +86,8 @@ InitializeAllAsmParsers(); InitializeAllDisassemblers(); - FileAnalysis Verifier = ExitOnErr(FileAnalysis::Create(InputFilename)); - printIndirectCFInstructions(Verifier); + FileAnalysis Analysis = ExitOnErr(FileAnalysis::Create(InputFilename)); + printIndirectCFInstructions(Analysis); return EXIT_SUCCESS; } Index: tools/llvm-cfi-verify/unittests/CMakeLists.txt =================================================================== --- tools/llvm-cfi-verify/unittests/CMakeLists.txt +++ tools/llvm-cfi-verify/unittests/CMakeLists.txt @@ -9,6 +9,7 @@ MCParser Object Support + DebugInfoDWARF ) add_llvm_unittest(CFIVerifyTests