forked from OSchip/llvm-project
MC Helpers for llvm-cfi-verify.
Add instruction analysis and machinecode traversal helpers in preparation for control flow graph generation implementation. Reviewers: vlad.tsyrklevich Reviewed By: vlad.tsyrklevich Subscribers: mgorny, llvm-commits, pcc, kcc Differential Revision: https://reviews.llvm.org/D38424 llvm-svn: 315528
This commit is contained in:
parent
bffdd9aa39
commit
0ee2632403
|
@ -124,6 +124,83 @@ const Instr &FileAnalysis::getInstructionOrDie(uint64_t Address) const {
|
|||
return InstrKV->second;
|
||||
}
|
||||
|
||||
bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const {
|
||||
return MII->getName(InstrMeta.Instruction.getOpcode()) == "TRAP";
|
||||
}
|
||||
|
||||
bool FileAnalysis::canFallThrough(const Instr &InstrMeta) const {
|
||||
if (!InstrMeta.Valid)
|
||||
return false;
|
||||
|
||||
if (isCFITrap(InstrMeta))
|
||||
return false;
|
||||
|
||||
const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
|
||||
if (InstrDesc.mayAffectControlFlow(InstrMeta.Instruction, *RegisterInfo))
|
||||
return InstrDesc.isConditionalBranch();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Instr *
|
||||
FileAnalysis::getDefiniteNextInstruction(const Instr &InstrMeta) const {
|
||||
if (!InstrMeta.Valid)
|
||||
return nullptr;
|
||||
|
||||
if (isCFITrap(InstrMeta))
|
||||
return nullptr;
|
||||
|
||||
const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());
|
||||
const Instr *NextMetaPtr;
|
||||
if (InstrDesc.mayAffectControlFlow(InstrMeta.Instruction, *RegisterInfo)) {
|
||||
if (InstrDesc.isConditionalBranch())
|
||||
return nullptr;
|
||||
|
||||
uint64_t Target;
|
||||
if (!MIA->evaluateBranch(InstrMeta.Instruction, InstrMeta.VMAddress,
|
||||
InstrMeta.InstructionSize, Target))
|
||||
return nullptr;
|
||||
|
||||
NextMetaPtr = getInstruction(Target);
|
||||
} else {
|
||||
NextMetaPtr =
|
||||
getInstruction(InstrMeta.VMAddress + InstrMeta.InstructionSize);
|
||||
}
|
||||
|
||||
if (!NextMetaPtr || !NextMetaPtr->Valid)
|
||||
return nullptr;
|
||||
|
||||
return NextMetaPtr;
|
||||
}
|
||||
|
||||
std::set<const Instr *>
|
||||
FileAnalysis::getDirectControlFlowXRefs(const Instr &InstrMeta) const {
|
||||
std::set<const Instr *> CFCrossReferences;
|
||||
const Instr *PrevInstruction = getPrevInstructionSequential(InstrMeta);
|
||||
|
||||
if (PrevInstruction && canFallThrough(*PrevInstruction))
|
||||
CFCrossReferences.insert(PrevInstruction);
|
||||
|
||||
const auto &TargetRefsKV = StaticBranchTargetings.find(InstrMeta.VMAddress);
|
||||
if (TargetRefsKV == StaticBranchTargetings.end())
|
||||
return CFCrossReferences;
|
||||
|
||||
for (uint64_t SourceInstrAddress : TargetRefsKV->second) {
|
||||
const auto &SourceInstrKV = Instructions.find(SourceInstrAddress);
|
||||
if (SourceInstrKV == Instructions.end()) {
|
||||
errs() << "Failed to find source instruction at address "
|
||||
<< format_hex(SourceInstrAddress, 2)
|
||||
<< " for the cross-reference to instruction at address "
|
||||
<< format_hex(InstrMeta.VMAddress, 2) << ".\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
CFCrossReferences.insert(&SourceInstrKV->second);
|
||||
}
|
||||
|
||||
return CFCrossReferences;
|
||||
}
|
||||
|
||||
const std::set<uint64_t> &FileAnalysis::getIndirectInstructions() const {
|
||||
return IndirectInstructions;
|
||||
}
|
||||
|
|
|
@ -79,6 +79,30 @@ public:
|
|||
const Instr *getPrevInstructionSequential(const Instr &InstrMeta) const;
|
||||
const Instr *getNextInstructionSequential(const Instr &InstrMeta) const;
|
||||
|
||||
// Returns whether this instruction is used by CFI to trap the program.
|
||||
bool isCFITrap(const Instr &InstrMeta) const;
|
||||
|
||||
// Returns whether this function can fall through to the next instruction.
|
||||
// Undefined (and bad) instructions cannot fall through, and instruction that
|
||||
// modify the control flow can only fall through if they are conditional
|
||||
// branches or calls.
|
||||
bool canFallThrough(const Instr &InstrMeta) const;
|
||||
|
||||
// Returns the definitive next instruction. This is different from the next
|
||||
// instruction sequentially as it will follow unconditional branches (assuming
|
||||
// they can be resolved at compile time, i.e. not indirect). This method
|
||||
// returns nullptr if the provided instruction does not transfer control flow
|
||||
// to exactly one instruction that is known deterministically at compile time.
|
||||
// Also returns nullptr if the deterministic target does not exist in this
|
||||
// file.
|
||||
const Instr *getDefiniteNextInstruction(const Instr &InstrMeta) const;
|
||||
|
||||
// Get a list of deterministic control flows that lead to the provided
|
||||
// instruction. This list includes all static control flow cross-references as
|
||||
// well as the previous instruction if it can fall through.
|
||||
std::set<const Instr *>
|
||||
getDirectControlFlowXRefs(const Instr &InstrMeta) const;
|
||||
|
||||
// Returns whether this instruction uses a register operand.
|
||||
bool usesRegisterOperand(const Instr &InstrMeta) const;
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
using Instr = ::llvm::cfi_verify::FileAnalysis::Instr;
|
||||
using ::testing::Eq;
|
||||
using ::testing::Field;
|
||||
|
||||
namespace llvm {
|
||||
namespace cfi_verify {
|
||||
|
@ -199,6 +200,268 @@ TEST_F(BasicFileAnalysisTest, PrevAndNextFromBadInst) {
|
|||
EXPECT_EQ(1u, GoodInstrMeta->InstructionSize);
|
||||
}
|
||||
|
||||
TEST_F(BasicFileAnalysisTest, CFITrapTest) {
|
||||
Analysis.parseSectionContents(
|
||||
{
|
||||
0x90, // 0: nop
|
||||
0xb0, 0x00, // 1: mov $0x0, %al
|
||||
0x48, 0x89, 0xe5, // 3: mov %rsp, %rbp
|
||||
0x48, 0x83, 0xec, 0x18, // 6: sub $0x18, %rsp
|
||||
0x48, 0xbe, 0xc4, 0x07, 0x40,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, // 10: movabs $0x4007c4, %rsi
|
||||
0x2f, // 20: (bad)
|
||||
0x41, 0x0e, // 21: rex.B (bad)
|
||||
0x62, 0x72, 0x65, 0x61, 0x6b, // 23: (bad) {%k1}
|
||||
0x0f, 0x0b // 28: ud2
|
||||
},
|
||||
0xDEADBEEF);
|
||||
|
||||
EXPECT_FALSE(Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 3)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 6)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 10)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 20)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 21)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 23)));
|
||||
EXPECT_TRUE(
|
||||
Analysis.isCFITrap(Analysis.getInstructionOrDie(0xDEADBEEF + 28)));
|
||||
}
|
||||
|
||||
TEST_F(BasicFileAnalysisTest, FallThroughTest) {
|
||||
Analysis.parseSectionContents(
|
||||
{
|
||||
0x90, // 0: nop
|
||||
0xb0, 0x00, // 1: mov $0x0, %al
|
||||
0x2f, // 3: (bad)
|
||||
0x0f, 0x0b, // 4: ud2
|
||||
0xff, 0x20, // 6: jmpq *(%rax)
|
||||
0xeb, 0x00, // 8: jmp +0
|
||||
0xe8, 0x45, 0xfe, 0xff, 0xff, // 10: callq [some loc]
|
||||
0xff, 0x10, // 15: callq *(rax)
|
||||
0x75, 0x00, // 17: jne +0
|
||||
0xc3, // 19: retq
|
||||
},
|
||||
0xDEADBEEF);
|
||||
|
||||
EXPECT_TRUE(
|
||||
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF)));
|
||||
EXPECT_TRUE(
|
||||
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 1)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 3)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 4)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 6)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 8)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 10)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 15)));
|
||||
EXPECT_TRUE(
|
||||
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 17)));
|
||||
EXPECT_FALSE(
|
||||
Analysis.canFallThrough(Analysis.getInstructionOrDie(0xDEADBEEF + 19)));
|
||||
}
|
||||
|
||||
TEST_F(BasicFileAnalysisTest, DefiniteNextInstructionTest) {
|
||||
Analysis.parseSectionContents(
|
||||
{
|
||||
0x90, // 0: nop
|
||||
0xb0, 0x00, // 1: mov $0x0, %al
|
||||
0x2f, // 3: (bad)
|
||||
0x0f, 0x0b, // 4: ud2
|
||||
0xff, 0x20, // 6: jmpq *(%rax)
|
||||
0xeb, 0x00, // 8: jmp 10 [+0]
|
||||
0xeb, 0x05, // 10: jmp 17 [+5]
|
||||
0xe8, 0x00, 0x00, 0x00, 0x00, // 12: callq 17 [+0]
|
||||
0xe8, 0x78, 0x56, 0x34, 0x12, // 17: callq 0x1234569f [+0x12345678]
|
||||
0xe8, 0x04, 0x00, 0x00, 0x00, // 22: callq 31 [+4]
|
||||
0xff, 0x10, // 27: callq *(rax)
|
||||
0x75, 0x00, // 29: jne 31 [+0]
|
||||
0x75, 0xe0, // 31: jne 1 [-32]
|
||||
0xc3, // 33: retq
|
||||
0xeb, 0xdd, // 34: jmp 1 [-35]
|
||||
0xeb, 0xdd, // 36: jmp 3 [-35]
|
||||
0xeb, 0xdc, // 38: jmp 4 [-36]
|
||||
},
|
||||
0xDEADBEEF);
|
||||
|
||||
const auto *Current = Analysis.getInstruction(0xDEADBEEF);
|
||||
const auto *Next = Analysis.getDefiniteNextInstruction(*Current);
|
||||
EXPECT_NE(nullptr, Next);
|
||||
EXPECT_EQ(0xDEADBEEF + 1, Next->VMAddress);
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 1);
|
||||
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 3);
|
||||
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 4);
|
||||
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 6);
|
||||
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 8);
|
||||
Next = Analysis.getDefiniteNextInstruction(*Current);
|
||||
EXPECT_NE(nullptr, Next);
|
||||
EXPECT_EQ(0xDEADBEEF + 10, Next->VMAddress);
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 10);
|
||||
Next = Analysis.getDefiniteNextInstruction(*Current);
|
||||
EXPECT_NE(nullptr, Next);
|
||||
EXPECT_EQ(0xDEADBEEF + 17, Next->VMAddress);
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 12);
|
||||
Next = Analysis.getDefiniteNextInstruction(*Current);
|
||||
EXPECT_NE(nullptr, Next);
|
||||
EXPECT_EQ(0xDEADBEEF + 17, Next->VMAddress);
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 17);
|
||||
// Note, definite next instruction address is out of range and should fail.
|
||||
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
|
||||
Next = Analysis.getDefiniteNextInstruction(*Current);
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 22);
|
||||
Next = Analysis.getDefiniteNextInstruction(*Current);
|
||||
EXPECT_NE(nullptr, Next);
|
||||
EXPECT_EQ(0xDEADBEEF + 31, Next->VMAddress);
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 27);
|
||||
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 29);
|
||||
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 31);
|
||||
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 33);
|
||||
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 34);
|
||||
Next = Analysis.getDefiniteNextInstruction(*Current);
|
||||
EXPECT_NE(nullptr, Next);
|
||||
EXPECT_EQ(0xDEADBEEF + 1, Next->VMAddress);
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 36);
|
||||
EXPECT_EQ(nullptr, Analysis.getDefiniteNextInstruction(*Current));
|
||||
|
||||
Current = Analysis.getInstruction(0xDEADBEEF + 38);
|
||||
Next = Analysis.getDefiniteNextInstruction(*Current);
|
||||
EXPECT_NE(nullptr, Next);
|
||||
EXPECT_EQ(0xDEADBEEF + 4, Next->VMAddress);
|
||||
}
|
||||
|
||||
TEST_F(BasicFileAnalysisTest, ControlFlowXRefsTest) {
|
||||
Analysis.parseSectionContents(
|
||||
{
|
||||
0x90, // 0: nop
|
||||
0xb0, 0x00, // 1: mov $0x0, %al
|
||||
0x2f, // 3: (bad)
|
||||
0x0f, 0x0b, // 4: ud2
|
||||
0xff, 0x20, // 6: jmpq *(%rax)
|
||||
0xeb, 0x00, // 8: jmp 10 [+0]
|
||||
0xeb, 0x05, // 10: jmp 17 [+5]
|
||||
0xe8, 0x00, 0x00, 0x00, 0x00, // 12: callq 17 [+0]
|
||||
0xe8, 0x78, 0x56, 0x34, 0x12, // 17: callq 0x1234569f [+0x12345678]
|
||||
0xe8, 0x04, 0x00, 0x00, 0x00, // 22: callq 31 [+4]
|
||||
0xff, 0x10, // 27: callq *(rax)
|
||||
0x75, 0x00, // 29: jne 31 [+0]
|
||||
0x75, 0xe0, // 31: jne 1 [-32]
|
||||
0xc3, // 33: retq
|
||||
0xeb, 0xdd, // 34: jmp 1 [-35]
|
||||
0xeb, 0xdd, // 36: jmp 3 [-35]
|
||||
0xeb, 0xdc, // 38: jmp 4 [-36]
|
||||
},
|
||||
0xDEADBEEF);
|
||||
const auto *InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF);
|
||||
std::set<const Instr *> XRefs =
|
||||
Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_TRUE(XRefs.empty());
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 1);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_THAT(XRefs, UnorderedElementsAre(
|
||||
Field(&Instr::VMAddress, Eq(0xDEADBEEF)),
|
||||
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 31)),
|
||||
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 34))));
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 3);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_THAT(XRefs, UnorderedElementsAre(
|
||||
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 1)),
|
||||
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 36))));
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 4);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_THAT(XRefs, UnorderedElementsAre(
|
||||
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 38))));
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 6);
|
||||
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 8);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 10);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_THAT(XRefs, UnorderedElementsAre(
|
||||
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 8))));
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 12);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 17);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_THAT(XRefs, UnorderedElementsAre(
|
||||
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 10)),
|
||||
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 12))));
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 22);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 27);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 29);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 31);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_THAT(XRefs, UnorderedElementsAre(
|
||||
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 22)),
|
||||
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 29))));
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 33);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_THAT(XRefs, UnorderedElementsAre(
|
||||
Field(&Instr::VMAddress, Eq(0xDEADBEEF + 31))));
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 34);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 36);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
|
||||
|
||||
InstrMetaPtr = &Analysis.getInstructionOrDie(0xDEADBEEF + 38);
|
||||
XRefs = Analysis.getDirectControlFlowXRefs(*InstrMetaPtr);
|
||||
EXPECT_TRUE(Analysis.getDirectControlFlowXRefs(*InstrMetaPtr).empty());
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
} // end namespace cfi_verify
|
||||
} // end namespace llvm
|
||||
|
|
Loading…
Reference in New Issue