[cfi-verify] Support cross-DSO

When used in cross-DSO mode, CFI will generate calls to special functions rather than trap instructions.  For example, instead of generating

if (!InlinedFastCheck(f))
  abort();
call *f

CFI generates

if (!InlinedFastCheck(f))
  __cfi_slowpath(CallSiteTypeId, f);
call *f

This patch teaches cfi-verify to recognize calls to __cfi_slowpath and abort and treat them as trap functions.

In addition to normal symbols, we also parse the dynamic relocations to handle cross-DSO calls in libraries.

We also extend cfi-verify to recognize other patterns that occur using cross-DSO.  For example, some indirect calls are not guarded by a branch to a trap but instead follow a call to __cfi_slowpath.  For example:

if (!InlinedFastCheck(f))
  call *f
else {
  __cfi_slowpath(CallSiteTypeId, f);
  call *f
}

In this case, the second call to f is not marked as protected by the current code.  We thus recognize if indirect calls directly follow a call to a function that will trap on CFI violations and treat them as protected.

We also ignore indirect calls in the PLT, since on AArch64 each entry contains an indirect call that should not be protected by CFI, and these are labeled incorrectly when debug information is not present.

Differential Revision: https://reviews.llvm.org/D49383

llvm-svn: 340612
diff --git a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
index 29819d8..8dc0802 100644
--- a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
+++ b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
@@ -105,6 +105,9 @@
   if (auto SectionParseResponse = Analysis.parseCodeSections())
     return std::move(SectionParseResponse);
 
+  if (auto SymbolTableParseResponse = Analysis.parseSymbolTable())
+    return std::move(SymbolTableParseResponse);
+
   return std::move(Analysis);
 }
 
@@ -165,7 +168,18 @@
 
 bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const {
   const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
-  return InstrDesc.isTrap();
+  return InstrDesc.isTrap() || willTrapOnCFIViolation(InstrMeta);
+}
+
+bool FileAnalysis::willTrapOnCFIViolation(const Instr &InstrMeta) const {
+  const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
+  if (!InstrDesc.isCall())
+    return false;
+  uint64_t Target;
+  if (!MIA->evaluateBranch(InstrMeta.Instruction, InstrMeta.VMAddress,
+                           InstrMeta.InstructionSize, Target))
+    return false;
+  return TrapOnFailFunctionAddresses.count(Target) > 0;
 }
 
 bool FileAnalysis::canFallThrough(const Instr &InstrMeta) const {
@@ -431,6 +445,12 @@
     if (!(object::ELFSectionRef(Section).getFlags() & ELF::SHF_EXECINSTR))
       continue;
 
+    // Avoid checking the PLT since it produces spurious failures on AArch64
+    // when ignoring DWARF data.
+    StringRef SectionName;
+    if (!Section.getName(SectionName) && SectionName == ".plt")
+      continue;
+
     StringRef SectionContents;
     if (Section.getContents(SectionContents))
       return make_error<StringError>("Failed to retrieve section contents",
@@ -518,6 +538,40 @@
   }
 }
 
+Error FileAnalysis::parseSymbolTable() {
+  // Functions that will trap on CFI violations.
+  SmallSet<StringRef, 4> TrapOnFailFunctions;
+  TrapOnFailFunctions.insert("__cfi_slowpath");
+  TrapOnFailFunctions.insert("__cfi_slowpath_diag");
+  TrapOnFailFunctions.insert("abort");
+
+  // Look through the list of symbols for functions that will trap on CFI
+  // violations.
+  for (auto &Sym : Object->symbols()) {
+    auto SymNameOrErr = Sym.getName();
+    if (!SymNameOrErr)
+      consumeError(SymNameOrErr.takeError());
+    else if (TrapOnFailFunctions.count(*SymNameOrErr) > 0) {
+      auto AddrOrErr = Sym.getAddress();
+      if (!AddrOrErr)
+        consumeError(AddrOrErr.takeError());
+      else
+        TrapOnFailFunctionAddresses.insert(*AddrOrErr);
+    }
+  }
+  if (auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Object)) {
+    for (const auto &Addr : ElfObject->getPltAddresses()) {
+      object::SymbolRef Sym(Addr.first, Object);
+      auto SymNameOrErr = Sym.getName();
+      if (!SymNameOrErr)
+        consumeError(SymNameOrErr.takeError());
+      else if (TrapOnFailFunctions.count(*SymNameOrErr) > 0)
+        TrapOnFailFunctionAddresses.insert(Addr.second);
+    }
+  }
+  return Error::success();
+}
+
 UnsupportedDisassembly::UnsupportedDisassembly(StringRef Text) : Text(Text) {}
 
 char UnsupportedDisassembly::ID;