mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 10:42:39 +01:00
[X86] Added support for nocf_check attribute for indirect Branch Tracking
X86 Supports Indirect Branch Tracking (IBT) as part of Control-Flow Enforcement Technology (CET). IBT instruments ENDBR instructions used to specify valid targets of indirect call / jmp. The `nocf_check` attribute has two roles in the context of X86 IBT technology: 1. Appertains to a function - do not add ENDBR instruction at the beginning of the function. 2. Appertains to a function pointer - do not track the target function of this pointer by adding nocf_check prefix to the indirect-call instruction. This patch implements `nocf_check` context for Indirect Branch Tracking. It also auto generates `nocf_check` prefixes before indirect branchs to jump tables that are guarded by range checks. Differential Revision: https://reviews.llvm.org/D41879 llvm-svn: 327767
This commit is contained in:
parent
6634c27ef1
commit
3960b83ba4
@ -89,6 +89,7 @@ func TestAttributes(t *testing.T) {
|
||||
"uwtable",
|
||||
"zeroext",
|
||||
"cold",
|
||||
"nocf_check"
|
||||
}
|
||||
|
||||
for _, name := range attrTests {
|
||||
|
@ -1054,6 +1054,7 @@ The integer codes are mapped to well-known attributes as follows.
|
||||
* code 53: ``speculatable``
|
||||
* code 54: ``strictfp``
|
||||
* code 55: ``sanitize_hwaddress``
|
||||
* code 56: ``nocf_check``
|
||||
|
||||
.. note::
|
||||
The ``allocsize`` attribute has a special encoding for its arguments. Its two
|
||||
|
@ -1699,6 +1699,12 @@ example:
|
||||
show that no exceptions passes by it. This is normally the case for
|
||||
the ELF x86-64 abi, but it can be disabled for some compilation
|
||||
units.
|
||||
``nocf_check``
|
||||
This attribute indicates that no control-flow check will be perfomed on
|
||||
the attributed entity. It disables -fcf-protection=<> for a specific
|
||||
entity to fine grain the HW control flow protection mechanism. The flag
|
||||
is target independant and currently appertains to a function or function
|
||||
pointer.
|
||||
|
||||
.. _glattrs:
|
||||
|
||||
|
@ -587,6 +587,7 @@ enum AttributeKindCodes {
|
||||
ATTR_KIND_SPECULATABLE = 53,
|
||||
ATTR_KIND_STRICT_FP = 54,
|
||||
ATTR_KIND_SANITIZE_HWADDRESS = 55,
|
||||
ATTR_KIND_NOCF_CHECK = 56,
|
||||
};
|
||||
|
||||
enum ComdatSelectionKindCodes {
|
||||
|
@ -3584,6 +3584,13 @@ public:
|
||||
virtual SDValue LowerToTLSEmulatedModel(const GlobalAddressSDNode *GA,
|
||||
SelectionDAG &DAG) const;
|
||||
|
||||
/// Expands target specific indirect branch for the case of JumpTable
|
||||
/// expanasion.
|
||||
virtual SDValue expandIndirectJTBranch(const SDLoc& dl, SDValue Value, SDValue Addr,
|
||||
SelectionDAG &DAG) const {
|
||||
return DAG.getNode(ISD::BRIND, dl, MVT::Other, Value, Addr);
|
||||
}
|
||||
|
||||
// seteq(x, 0) -> truncate(srl(ctlz(zext(x)), log2(#bits)))
|
||||
// If we're comparing for equality to zero and isCtlzFast is true, expose the
|
||||
// fact that this can be implemented as a ctlz/srl pair, so that the dag
|
||||
|
@ -106,6 +106,9 @@ def NoRedZone : EnumAttr<"noredzone">;
|
||||
/// Mark the function as not returning.
|
||||
def NoReturn : EnumAttr<"noreturn">;
|
||||
|
||||
/// Disable Indirect Branch Tracking.
|
||||
def NoCfCheck : EnumAttr<"nocf_check">;
|
||||
|
||||
/// Function doesn't unwind stack.
|
||||
def NoUnwind : EnumAttr<"nounwind">;
|
||||
|
||||
|
@ -494,6 +494,9 @@ public:
|
||||
addFnAttr(Attribute::NoReturn);
|
||||
}
|
||||
|
||||
/// Determine if the function should not perform indirect branch tracking.
|
||||
bool doesNoCfCheck() const { return hasFnAttribute(Attribute::NoCfCheck); }
|
||||
|
||||
/// @brief Determine if the function cannot unwind.
|
||||
bool doesNotThrow() const {
|
||||
return hasFnAttribute(Attribute::NoUnwind);
|
||||
|
@ -1795,6 +1795,9 @@ public:
|
||||
addAttribute(AttributeList::FunctionIndex, Attribute::NoReturn);
|
||||
}
|
||||
|
||||
/// Determine if the call should not perform indirect branch tracking.
|
||||
bool doesNoCfCheck() const { return hasFnAttr(Attribute::NoCfCheck); }
|
||||
|
||||
/// Determine if the call cannot unwind.
|
||||
bool doesNotThrow() const { return hasFnAttr(Attribute::NoUnwind); }
|
||||
void setDoesNotThrow() {
|
||||
@ -3830,8 +3833,15 @@ public:
|
||||
static InvokeInst *Create(InvokeInst *II, ArrayRef<OperandBundleDef> Bundles,
|
||||
Instruction *InsertPt = nullptr);
|
||||
|
||||
/// Determine if the call should not perform indirect branch tracking.
|
||||
bool doesNoCfCheck() const { return hasFnAttr(Attribute::NoCfCheck); }
|
||||
|
||||
|
||||
/// Determine if the call cannot unwind.
|
||||
bool doesNotThrow() const { return hasFnAttr(Attribute::NoUnwind); }
|
||||
void setDoesNotThrow() {
|
||||
addAttribute(AttributeList::FunctionIndex, Attribute::NoUnwind);
|
||||
}
|
||||
|
||||
/// Return the function called, or null if this is an
|
||||
/// indirect function invocation.
|
||||
///
|
||||
|
@ -648,6 +648,7 @@ lltok::Kind LLLexer::LexIdentifier() {
|
||||
KEYWORD(nonnull);
|
||||
KEYWORD(noredzone);
|
||||
KEYWORD(noreturn);
|
||||
KEYWORD(nocf_check);
|
||||
KEYWORD(nounwind);
|
||||
KEYWORD(optnone);
|
||||
KEYWORD(optsize);
|
||||
|
@ -1131,6 +1131,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
|
||||
case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break;
|
||||
case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break;
|
||||
case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); break;
|
||||
case lltok::kw_nocf_check: B.addAttribute(Attribute::NoCfCheck); break;
|
||||
case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break;
|
||||
case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break;
|
||||
case lltok::kw_optnone: B.addAttribute(Attribute::OptimizeNone); break;
|
||||
@ -1468,6 +1469,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
|
||||
case lltok::kw_nonlazybind:
|
||||
case lltok::kw_noredzone:
|
||||
case lltok::kw_noreturn:
|
||||
case lltok::kw_nocf_check:
|
||||
case lltok::kw_nounwind:
|
||||
case lltok::kw_optnone:
|
||||
case lltok::kw_optsize:
|
||||
@ -1561,6 +1563,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
|
||||
case lltok::kw_nonlazybind:
|
||||
case lltok::kw_noredzone:
|
||||
case lltok::kw_noreturn:
|
||||
case lltok::kw_nocf_check:
|
||||
case lltok::kw_nounwind:
|
||||
case lltok::kw_optnone:
|
||||
case lltok::kw_optsize:
|
||||
|
@ -199,6 +199,7 @@ enum Kind {
|
||||
kw_nonnull,
|
||||
kw_noredzone,
|
||||
kw_noreturn,
|
||||
kw_nocf_check,
|
||||
kw_nounwind,
|
||||
kw_optnone,
|
||||
kw_optsize,
|
||||
|
@ -1160,6 +1160,7 @@ static uint64_t getRawAttributeMask(Attribute::AttrKind Val) {
|
||||
case Attribute::Speculatable: return 1ULL << 54;
|
||||
case Attribute::StrictFP: return 1ULL << 55;
|
||||
case Attribute::SanitizeHWAddress: return 1ULL << 56;
|
||||
case Attribute::NoCfCheck: return 1ULL << 57;
|
||||
case Attribute::Dereferenceable:
|
||||
llvm_unreachable("dereferenceable attribute not supported in raw format");
|
||||
break;
|
||||
@ -1338,6 +1339,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
|
||||
return Attribute::NoRedZone;
|
||||
case bitc::ATTR_KIND_NO_RETURN:
|
||||
return Attribute::NoReturn;
|
||||
case bitc::ATTR_KIND_NOCF_CHECK:
|
||||
return Attribute::NoCfCheck;
|
||||
case bitc::ATTR_KIND_NO_UNWIND:
|
||||
return Attribute::NoUnwind;
|
||||
case bitc::ATTR_KIND_OPTIMIZE_FOR_SIZE:
|
||||
|
@ -638,6 +638,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
|
||||
return bitc::ATTR_KIND_NO_RED_ZONE;
|
||||
case Attribute::NoReturn:
|
||||
return bitc::ATTR_KIND_NO_RETURN;
|
||||
case Attribute::NoCfCheck:
|
||||
return bitc::ATTR_KIND_NOCF_CHECK;
|
||||
case Attribute::NoUnwind:
|
||||
return bitc::ATTR_KIND_NO_UNWIND;
|
||||
case Attribute::OptimizeForSize:
|
||||
|
@ -3706,7 +3706,8 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
|
||||
Addr = DAG.getNode(ISD::ADD, dl, PTy, Addr,
|
||||
TLI.getPICJumpTableRelocBase(Table, DAG));
|
||||
}
|
||||
Tmp1 = DAG.getNode(ISD::BRIND, dl, MVT::Other, LD.getValue(1), Addr);
|
||||
|
||||
Tmp1 = TLI.expandIndirectJTBranch(dl, LD.getValue(1), Addr, DAG);
|
||||
Results.push_back(Tmp1);
|
||||
break;
|
||||
}
|
||||
|
@ -299,6 +299,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
|
||||
return "noredzone";
|
||||
if (hasAttribute(Attribute::NoReturn))
|
||||
return "noreturn";
|
||||
if (hasAttribute(Attribute::NoCfCheck))
|
||||
return "nocf_check";
|
||||
if (hasAttribute(Attribute::NoRecurse))
|
||||
return "norecurse";
|
||||
if (hasAttribute(Attribute::NoUnwind))
|
||||
|
@ -1404,6 +1404,7 @@ Verifier::visitModuleFlag(const MDNode *Op,
|
||||
static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
|
||||
switch (Kind) {
|
||||
case Attribute::NoReturn:
|
||||
case Attribute::NoCfCheck:
|
||||
case Attribute::NoUnwind:
|
||||
case Attribute::NoInline:
|
||||
case Attribute::AlwaysInline:
|
||||
|
@ -2353,21 +2353,22 @@ bool X86AsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
|
||||
.Cases("acquire", "release", isParsingIntelSyntax())
|
||||
.Default(false);
|
||||
|
||||
auto isLockRepeatPrefix = [](StringRef N) {
|
||||
auto isLockRepeatNtPrefix = [](StringRef N) {
|
||||
return StringSwitch<bool>(N)
|
||||
.Cases("lock", "rep", "repe", "repz", "repne", "repnz", true)
|
||||
.Cases("lock", "rep", "repe", "repz", "repne", "repnz", "notrack", true)
|
||||
.Default(false);
|
||||
};
|
||||
|
||||
bool CurlyAsEndOfStatement = false;
|
||||
|
||||
unsigned Flags = X86::IP_NO_PREFIX;
|
||||
while (isLockRepeatPrefix(Name.lower())) {
|
||||
while (isLockRepeatNtPrefix(Name.lower())) {
|
||||
unsigned Prefix =
|
||||
StringSwitch<unsigned>(Name)
|
||||
.Cases("lock", "lock", X86::IP_HAS_LOCK)
|
||||
.Cases("rep", "repe", "repz", X86::IP_HAS_REPEAT)
|
||||
.Cases("repne", "repnz", X86::IP_HAS_REPEAT_NE)
|
||||
.Cases("notrack", "notrack", X86::IP_HAS_NOTRACK)
|
||||
.Default(X86::IP_NO_PREFIX); // Invalid prefix (impossible)
|
||||
Flags |= Prefix;
|
||||
if (getLexer().is(AsmToken::EndOfStatement)) {
|
||||
|
@ -54,6 +54,9 @@ void X86ATTInstPrinter::printInst(const MCInst *MI, raw_ostream &OS,
|
||||
if ((TSFlags & X86II::LOCK) || (Flags & X86::IP_HAS_LOCK))
|
||||
OS << "\tlock\t";
|
||||
|
||||
if ((TSFlags & X86II::NOTRACK) || (Flags & X86::IP_HAS_NOTRACK))
|
||||
OS << "\tnotrack\t";
|
||||
|
||||
if (Flags & X86::IP_HAS_REPEAT_NE)
|
||||
OS << "\trepne\t";
|
||||
else if (Flags & X86::IP_HAS_REPEAT)
|
||||
|
@ -49,6 +49,9 @@ void X86IntelInstPrinter::printInst(const MCInst *MI, raw_ostream &OS,
|
||||
else if (Flags & X86::IP_HAS_REPEAT)
|
||||
OS << "\trep\t";
|
||||
|
||||
if ((TSFlags & X86II::NOTRACK) || (Flags & X86::IP_HAS_NOTRACK))
|
||||
OS << "\tnotrack\t";
|
||||
|
||||
printInstruction(MI, OS);
|
||||
|
||||
// Next always print the annotation.
|
||||
|
@ -60,8 +60,9 @@ namespace X86 {
|
||||
IP_HAS_REPEAT_NE = 4,
|
||||
IP_HAS_REPEAT = 8,
|
||||
IP_HAS_LOCK = 16,
|
||||
NO_SCHED_INFO = 32 // Don't add sched comment to the current instr because
|
||||
// it was already added
|
||||
NO_SCHED_INFO = 32, // Don't add sched comment to the current instr because
|
||||
// it was already added
|
||||
IP_HAS_NOTRACK = 64
|
||||
};
|
||||
} // end namespace X86;
|
||||
|
||||
@ -572,7 +573,11 @@ namespace X86II {
|
||||
|
||||
/// Explicitly specified rounding control
|
||||
EVEX_RCShift = Has3DNow0F0FOpcodeShift + 1,
|
||||
EVEX_RC = 1ULL << EVEX_RCShift
|
||||
EVEX_RC = 1ULL << EVEX_RCShift,
|
||||
|
||||
// NOTRACK prefix
|
||||
NoTrackShift = EVEX_RCShift + 1,
|
||||
NOTRACK = 1ULL << NoTrackShift
|
||||
};
|
||||
|
||||
// getBaseOpcodeFor - This function returns the "base" X86 opcode for the
|
||||
|
@ -1134,6 +1134,10 @@ bool X86MCCodeEmitter::emitOpcodePrefix(uint64_t TSFlags, unsigned &CurByte,
|
||||
if (TSFlags & X86II::LOCK || MI.getFlags() & X86::IP_HAS_LOCK)
|
||||
EmitByte(0xF0, CurByte, OS);
|
||||
|
||||
// Emit the NOTRACK opcode prefix.
|
||||
if (TSFlags & X86II::NOTRACK || MI.getFlags() & X86::IP_HAS_NOTRACK)
|
||||
EmitByte(0x3E, CurByte, OS);
|
||||
|
||||
switch (TSFlags & X86II::OpPrefixMask) {
|
||||
case X86II::PD: // 66
|
||||
EmitByte(0x66, CurByte, OS);
|
||||
|
@ -3174,6 +3174,13 @@ bool X86FastISel::fastLowerCall(CallLoweringInfo &CLI) {
|
||||
CLI.CS ? dyn_cast<CallInst>(CLI.CS->getInstruction()) : nullptr;
|
||||
const Function *CalledFn = CI ? CI->getCalledFunction() : nullptr;
|
||||
|
||||
// Call / invoke instructions with NoCfCheck attribute require special
|
||||
// handling.
|
||||
const auto *II =
|
||||
CLI.CS ? dyn_cast<InvokeInst>(CLI.CS->getInstruction()) : nullptr;
|
||||
if ((CI && CI->doesNoCfCheck()) || (II && II->doesNoCfCheck()))
|
||||
return false;
|
||||
|
||||
// Functions with no_caller_saved_registers that need special handling.
|
||||
if ((CI && CI->hasFnAttr("no_caller_saved_registers")) ||
|
||||
(CalledFn && CalledFn->hasFnAttribute("no_caller_saved_registers")))
|
||||
|
@ -3415,6 +3415,11 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
||||
const Function *Fn = CI ? CI->getCalledFunction() : nullptr;
|
||||
bool HasNCSR = (CI && CI->hasFnAttr("no_caller_saved_registers")) ||
|
||||
(Fn && Fn->hasFnAttribute("no_caller_saved_registers"));
|
||||
const auto *II = dyn_cast_or_null<InvokeInst>(CLI.CS.getInstruction());
|
||||
bool HasNoCfCheck =
|
||||
(CI && CI->doesNoCfCheck()) || (II && II->doesNoCfCheck());
|
||||
const Module *M = MF.getMMI().getModule();
|
||||
Metadata *IsCFProtectionSupported = M->getModuleFlag("cf-protection-branch");
|
||||
|
||||
if (CallConv == CallingConv::X86_INTR)
|
||||
report_fatal_error("X86 interrupts may not be called directly");
|
||||
@ -3898,7 +3903,11 @@ X86TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
||||
return DAG.getNode(X86ISD::TC_RETURN, dl, NodeTys, Ops);
|
||||
}
|
||||
|
||||
Chain = DAG.getNode(X86ISD::CALL, dl, NodeTys, Ops);
|
||||
if (HasNoCfCheck && IsCFProtectionSupported) {
|
||||
Chain = DAG.getNode(X86ISD::NT_CALL, dl, NodeTys, Ops);
|
||||
} else {
|
||||
Chain = DAG.getNode(X86ISD::CALL, dl, NodeTys, Ops);
|
||||
}
|
||||
InFlag = Chain.getValue(1);
|
||||
|
||||
// Create the CALLSEQ_END node.
|
||||
@ -25852,6 +25861,8 @@ const char *X86TargetLowering::getTargetNodeName(unsigned Opcode) const {
|
||||
case X86ISD::GF2P8MULB: return "X86ISD::GF2P8MULB";
|
||||
case X86ISD::GF2P8AFFINEQB: return "X86ISD::GF2P8AFFINEQB";
|
||||
case X86ISD::GF2P8AFFINEINVQB: return "X86ISD::GF2P8AFFINEINVQB";
|
||||
case X86ISD::NT_CALL: return "X86ISD::NT_CALL";
|
||||
case X86ISD::NT_BRIND: return "X86ISD::NT_BRIND";
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -38709,6 +38720,22 @@ void X86TargetLowering::finalizeLowering(MachineFunction &MF) const {
|
||||
TargetLoweringBase::finalizeLowering(MF);
|
||||
}
|
||||
|
||||
SDValue X86TargetLowering::expandIndirectJTBranch(const SDLoc& dl,
|
||||
SDValue Value, SDValue Addr,
|
||||
SelectionDAG &DAG) const {
|
||||
const Module *M = DAG.getMachineFunction().getMMI().getModule();
|
||||
Metadata *IsCFProtectionSupported = M->getModuleFlag("cf-protection-branch");
|
||||
if (IsCFProtectionSupported) {
|
||||
// In case control-flow branch protection is enabled, we need to add
|
||||
// notrack prefix to the indirect branch.
|
||||
// In order to do that we create NT_BRIND SDNode.
|
||||
// Upon ISEL, the pattern will convert it to jmp with NoTrack prefix.
|
||||
return DAG.getNode(X86ISD::NT_BRIND, dl, MVT::Other, Value, Addr);
|
||||
}
|
||||
|
||||
return TargetLowering::expandIndirectJTBranch(dl, Value, Addr, DAG);
|
||||
}
|
||||
|
||||
/// This method query the target whether it is beneficial for dag combiner to
|
||||
/// promote the specified node. If true, it should return the desired promotion
|
||||
/// type by reference.
|
||||
|
@ -75,6 +75,9 @@ namespace llvm {
|
||||
///
|
||||
CALL,
|
||||
|
||||
/// Same as call except it adds the NoTrack prefix.
|
||||
NT_CALL,
|
||||
|
||||
/// This operation implements the lowering for readcyclecounter.
|
||||
RDTSC_DAG,
|
||||
|
||||
@ -122,6 +125,10 @@ namespace llvm {
|
||||
/// or TEST instruction.
|
||||
BRCOND,
|
||||
|
||||
/// BRIND node with NoTrack prefix. Operand 0 is the chain operand and
|
||||
/// operand 1 is the target address.
|
||||
NT_BRIND,
|
||||
|
||||
/// Return with a flag operand. Operand 0 is the chain operand, operand
|
||||
/// 1 is the number of bytes of stack to pop.
|
||||
RET_FLAG,
|
||||
@ -1114,9 +1121,12 @@ namespace llvm {
|
||||
bool lowerInterleavedStore(StoreInst *SI, ShuffleVectorInst *SVI,
|
||||
unsigned Factor) const override;
|
||||
|
||||
|
||||
void finalizeLowering(MachineFunction &MF) const override;
|
||||
|
||||
SDValue expandIndirectJTBranch(const SDLoc& dl, SDValue Value,
|
||||
SDValue Addr, SelectionDAG &DAG)
|
||||
const override;
|
||||
|
||||
protected:
|
||||
std::pair<const TargetRegisterClass *, uint8_t>
|
||||
findRepresentativeClass(const TargetRegisterInfo *TRI,
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/CodeGen/MachineFunctionPass.h"
|
||||
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
||||
#include "llvm/CodeGen/MachineJumpTableInfo.h"
|
||||
#include "llvm/CodeGen/MachineModuleInfo.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -58,7 +57,8 @@ private:
|
||||
/// Adds a new ENDBR instruction to the begining of the MBB.
|
||||
/// The function will not add it if already exists.
|
||||
/// It will add ENDBR32 or ENDBR64 opcode, depending on the target.
|
||||
void addENDBR(MachineBasicBlock &MBB) const;
|
||||
/// \returns true if the ENDBR was added and false otherwise.
|
||||
bool addENDBR(MachineBasicBlock &MBB) const;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
@ -69,7 +69,7 @@ FunctionPass *llvm::createX86IndirectBranchTrackingPass() {
|
||||
return new X86IndirectBranchTrackingPass();
|
||||
}
|
||||
|
||||
void X86IndirectBranchTrackingPass::addENDBR(MachineBasicBlock &MBB) const {
|
||||
bool X86IndirectBranchTrackingPass::addENDBR(MachineBasicBlock &MBB) const {
|
||||
assert(TII && "Target instruction info was not initialized");
|
||||
assert((X86::ENDBR64 == EndbrOpcode || X86::ENDBR32 == EndbrOpcode) &&
|
||||
"Unexpected Endbr opcode");
|
||||
@ -80,13 +80,16 @@ void X86IndirectBranchTrackingPass::addENDBR(MachineBasicBlock &MBB) const {
|
||||
if (MI == MBB.end() || EndbrOpcode != MI->getOpcode()) {
|
||||
BuildMI(MBB, MI, MBB.findDebugLoc(MI), TII->get(EndbrOpcode));
|
||||
NumEndBranchAdded++;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool X86IndirectBranchTrackingPass::runOnMachineFunction(MachineFunction &MF) {
|
||||
const X86Subtarget &SubTarget = MF.getSubtarget<X86Subtarget>();
|
||||
|
||||
// Make sure that the target supports ENDBR instruction.
|
||||
// Make sure that the target supports IBT instruction.
|
||||
if (!SubTarget.hasIBT())
|
||||
return false;
|
||||
|
||||
@ -103,50 +106,20 @@ bool X86IndirectBranchTrackingPass::runOnMachineFunction(MachineFunction &MF) {
|
||||
EndbrOpcode = SubTarget.is64Bit() ? X86::ENDBR64 : X86::ENDBR32;
|
||||
|
||||
// Non-internal function or function whose address was taken, can be
|
||||
// invoked through indirect calls. Mark the first BB with ENDBR instruction.
|
||||
// TODO: Do not add ENDBR instruction in case notrack attribute is used.
|
||||
if (MF.getFunction().hasAddressTaken() ||
|
||||
!MF.getFunction().hasLocalLinkage()) {
|
||||
// accessed through indirect calls. Mark the first BB with ENDBR instruction
|
||||
// unless nocf_check attribute is used.
|
||||
if ((MF.getFunction().hasAddressTaken() ||
|
||||
!MF.getFunction().hasLocalLinkage()) &&
|
||||
!MF.getFunction().doesNoCfCheck()) {
|
||||
auto MBB = MF.begin();
|
||||
addENDBR(*MBB);
|
||||
Changed = true;
|
||||
Changed |= addENDBR(*MBB);
|
||||
}
|
||||
|
||||
for (auto &MBB : MF) {
|
||||
// Find all basic blocks that thier address was taken (for example
|
||||
for (auto &MBB : MF)
|
||||
// Find all basic blocks that their address was taken (for example
|
||||
// in the case of indirect jump) and add ENDBR instruction.
|
||||
if (MBB.hasAddressTaken()) {
|
||||
addENDBR(MBB);
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Adds ENDBR instructions to MBB destinations of the jump table.
|
||||
// TODO: In case of more than 50 destinations, do not add ENDBR and
|
||||
// instead add DS_PREFIX.
|
||||
if (MachineJumpTableInfo *JTI = MF.getJumpTableInfo()) {
|
||||
for (const auto &JT : JTI->getJumpTables()) {
|
||||
for (auto *MBB : JT.MBBs) {
|
||||
// This assert verifies the assumption that this MBB has an indirect
|
||||
// jump terminator in one of its predecessor.
|
||||
// Jump tables are generated when lowering switch-case statements or
|
||||
// setjmp/longjump functions. As a result only indirect jumps use jump
|
||||
// tables.
|
||||
#ifndef NDEBUG
|
||||
bool hasIndirectJumpTerm = false;
|
||||
for (auto &PredMBB : MBB->predecessors())
|
||||
for (auto &TermI : PredMBB->terminators())
|
||||
if (TermI.isIndirectBranch())
|
||||
hasIndirectJumpTerm = true;
|
||||
assert(hasIndirectJumpTerm &&
|
||||
"The MBB is not the destination of an indirect jump");
|
||||
(void)hasIndirectJumpTerm;
|
||||
#endif
|
||||
addENDBR(*MBB);
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (MBB.hasAddressTaken())
|
||||
Changed |= addENDBR(MBB);
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
@ -153,6 +153,30 @@ let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1 in {
|
||||
[(brind (loadi64 addr:$dst))], IIC_JMP_MEM>,
|
||||
Requires<[In64BitMode]>, Sched<[WriteJumpLd]>;
|
||||
|
||||
let isCodeGenOnly = 1, Predicates = [HasIBT] in {
|
||||
def JMP16r_NT : I<0xFF, MRM4r, (outs), (ins GR16 : $dst), "jmp{w}\t{*}$dst",
|
||||
[(X86NoTrackBrind GR16 : $dst)], IIC_JMP_REG>, Requires<[Not64BitMode]>,
|
||||
OpSize16, Sched<[WriteJump]>, NOTRACK;
|
||||
|
||||
def JMP16m_NT : I<0xFF, MRM4m, (outs), (ins i16mem : $dst), "jmp{w}\t{*}$dst",
|
||||
[(X86NoTrackBrind (loadi16 addr : $dst))], IIC_JMP_MEM>,
|
||||
Requires<[Not64BitMode]>, OpSize16, Sched<[WriteJumpLd]>, NOTRACK;
|
||||
|
||||
def JMP32r_NT : I<0xFF, MRM4r, (outs), (ins GR32 : $dst), "jmp{l}\t{*}$dst",
|
||||
[(X86NoTrackBrind GR32 : $dst)], IIC_JMP_REG>, Requires<[Not64BitMode]>,
|
||||
OpSize32, Sched<[WriteJump]>, NOTRACK;
|
||||
def JMP32m_NT : I<0xFF, MRM4m, (outs), (ins i32mem : $dst), "jmp{l}\t{*}$dst",
|
||||
[(X86NoTrackBrind (loadi32 addr : $dst))], IIC_JMP_MEM>,
|
||||
Requires<[Not64BitMode]>, OpSize32, Sched<[WriteJumpLd]>, NOTRACK;
|
||||
|
||||
def JMP64r_NT : I<0xFF, MRM4r, (outs), (ins GR64 : $dst), "jmp{q}\t{*}$dst",
|
||||
[(X86NoTrackBrind GR64 : $dst)], IIC_JMP_REG>, Requires<[In64BitMode]>,
|
||||
Sched<[WriteJump]>, NOTRACK;
|
||||
def JMP64m_NT : I<0xFF, MRM4m, (outs), (ins i64mem : $dst), "jmp{q}\t{*}$dst",
|
||||
[(X86NoTrackBrind(loadi64 addr : $dst))], IIC_JMP_MEM>,
|
||||
Requires<[In64BitMode]>, Sched<[WriteJumpLd]>, NOTRACK;
|
||||
}
|
||||
|
||||
let Predicates = [Not64BitMode] in {
|
||||
def FARJMP16i : Iseg16<0xEA, RawFrmImm16, (outs),
|
||||
(ins i16imm:$off, i16imm:$seg),
|
||||
@ -175,7 +199,6 @@ let isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1 in {
|
||||
Sched<[WriteJumpLd]>;
|
||||
}
|
||||
|
||||
|
||||
// Loop instructions
|
||||
let SchedRW = [WriteJump] in {
|
||||
def LOOP : Ii8PCRel<0xE2, RawFrm, (outs), (ins brtarget8:$dst), "loop\t$dst", [], IIC_LOOP>;
|
||||
@ -219,6 +242,25 @@ let isCall = 1 in
|
||||
Requires<[Not64BitMode,FavorMemIndirectCall,NotUseRetpoline]>,
|
||||
Sched<[WriteJumpLd]>;
|
||||
|
||||
let isCodeGenOnly = 1, Predicates = [HasIBT] in {
|
||||
def CALL16r_NT : I<0xFF, MRM2r, (outs), (ins GR16 : $dst),
|
||||
"call{w}\t{*}$dst",[(X86NoTrackCall GR16 : $dst)], IIC_CALL_RI>,
|
||||
OpSize16, Requires<[Not64BitMode]>, Sched<[WriteJump]>, NOTRACK;
|
||||
def CALL16m_NT : I<0xFF, MRM2m, (outs), (ins i16mem : $dst),
|
||||
"call{w}\t{*}$dst",[(X86NoTrackCall(loadi16 addr : $dst))],
|
||||
IIC_CALL_MEM>, OpSize16,
|
||||
Requires<[Not64BitMode,FavorMemIndirectCall]>,
|
||||
Sched<[WriteJumpLd]>, NOTRACK;
|
||||
def CALL32r_NT : I<0xFF, MRM2r, (outs), (ins GR32 : $dst),
|
||||
"call{l}\t{*}$dst",[(X86NoTrackCall GR32 : $dst)], IIC_CALL_RI>,
|
||||
OpSize32, Requires<[Not64BitMode]>, Sched<[WriteJump]>, NOTRACK;
|
||||
def CALL32m_NT : I<0xFF, MRM2m, (outs), (ins i32mem : $dst),
|
||||
"call{l}\t{*}$dst",[(X86NoTrackCall(loadi32 addr : $dst))],
|
||||
IIC_CALL_MEM>, OpSize32,
|
||||
Requires<[Not64BitMode,FavorMemIndirectCall]>,
|
||||
Sched<[WriteJumpLd]>, NOTRACK;
|
||||
}
|
||||
|
||||
let Predicates = [Not64BitMode] in {
|
||||
def FARCALL16i : Iseg16<0x9A, RawFrmImm16, (outs),
|
||||
(ins i16imm:$off, i16imm:$seg),
|
||||
@ -306,6 +348,17 @@ let isCall = 1, Uses = [RSP, SSP], SchedRW = [WriteJump] in {
|
||||
Requires<[In64BitMode,FavorMemIndirectCall,
|
||||
NotUseRetpoline]>;
|
||||
|
||||
let isCodeGenOnly = 1, Predicates = [HasIBT] in{
|
||||
def CALL64r_NT : I<0xFF, MRM2r, (outs), (ins GR64 : $dst),
|
||||
"call{q}\t{*}$dst",[(X86NoTrackCall GR64 : $dst)],
|
||||
IIC_CALL_RI>,
|
||||
Requires<[In64BitMode]>, NOTRACK;
|
||||
def CALL64m_NT : I<0xFF, MRM2m, (outs), (ins i64mem : $dst),
|
||||
"call{q}\t{*}$dst",[(X86NoTrackCall(loadi64 addr : $dst))],
|
||||
IIC_CALL_MEM>,
|
||||
Requires<[In64BitMode,FavorMemIndirectCall]>, NOTRACK;
|
||||
}
|
||||
|
||||
def FARCALL64 : RI<0xFF, MRM3m, (outs), (ins opaque80mem:$dst),
|
||||
"lcall{q}\t{*}$dst", [], IIC_CALL_FAR_MEM>;
|
||||
}
|
||||
|
@ -215,6 +215,7 @@ class EVEX_RC { bit hasEVEX_RC = 1; }
|
||||
class EVEX_V512 { bit hasEVEX_L2 = 1; bit hasVEX_L = 0; }
|
||||
class EVEX_V256 { bit hasEVEX_L2 = 0; bit hasVEX_L = 1; }
|
||||
class EVEX_V128 { bit hasEVEX_L2 = 0; bit hasVEX_L = 0; }
|
||||
class NOTRACK { bit hasNoTrackPrefix = 1; }
|
||||
|
||||
// Specify AVX512 8-bit compressed displacement encoding based on the vector
|
||||
// element size in bits (8, 16, 32, 64) and the CDisp8 form.
|
||||
@ -296,6 +297,7 @@ class X86Inst<bits<8> opcod, Format f, ImmType i, dag outs, dag ins,
|
||||
int CD8_EltSize = 0; // Compressed disp8 form - element-size in bytes.
|
||||
bit has3DNow0F0FOpcode =0;// Wacky 3dNow! encoding?
|
||||
bit hasEVEX_RC = 0; // Explicitly specified rounding control in FP instruction.
|
||||
bit hasNoTrackPrefix = 0; // Does this inst has 0x3E (NoTrack) prefix?
|
||||
|
||||
bits<2> EVEX_LL;
|
||||
let EVEX_LL{0} = hasVEX_L;
|
||||
@ -347,6 +349,7 @@ class X86Inst<bits<8> opcod, Format f, ImmType i, dag outs, dag ins,
|
||||
let TSFlags{52-46} = CD8_Scale;
|
||||
let TSFlags{53} = has3DNow0F0FOpcode;
|
||||
let TSFlags{54} = hasEVEX_RC;
|
||||
let TSFlags{55} = hasNoTrackPrefix;
|
||||
}
|
||||
|
||||
class PseudoI<dag oops, dag iops, list<dag> pattern,
|
||||
|
@ -94,6 +94,8 @@ def SDT_X86CallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>,
|
||||
|
||||
def SDT_X86Call : SDTypeProfile<0, -1, [SDTCisVT<0, iPTR>]>;
|
||||
|
||||
def SDT_X86NtBrind : SDTypeProfile<0, -1, [SDTCisVT<0, iPTR>]>;
|
||||
|
||||
def SDT_X86VASTART_SAVE_XMM_REGS : SDTypeProfile<0, -1, [SDTCisVT<0, i8>,
|
||||
SDTCisVT<1, iPTR>,
|
||||
SDTCisVT<2, iPTR>]>;
|
||||
@ -196,6 +198,12 @@ def X86call : SDNode<"X86ISD::CALL", SDT_X86Call,
|
||||
[SDNPHasChain, SDNPOutGlue, SDNPOptInGlue,
|
||||
SDNPVariadic]>;
|
||||
|
||||
def X86NoTrackCall : SDNode<"X86ISD::NT_CALL", SDT_X86Call,
|
||||
[SDNPHasChain, SDNPOutGlue, SDNPOptInGlue,
|
||||
SDNPVariadic]>;
|
||||
def X86NoTrackBrind : SDNode<"X86ISD::NT_BRIND", SDT_X86NtBrind,
|
||||
[SDNPHasChain]>;
|
||||
|
||||
def X86rep_stos: SDNode<"X86ISD::REP_STOS", SDTX86RepStr,
|
||||
[SDNPHasChain, SDNPInGlue, SDNPOutGlue, SDNPMayStore]>;
|
||||
def X86rep_movs: SDNode<"X86ISD::REP_MOVS", SDTX86RepStr,
|
||||
|
@ -42,6 +42,7 @@ static Attribute::AttrKind parseAttrKind(StringRef Kind) {
|
||||
.Case("nonlazybind", Attribute::NonLazyBind)
|
||||
.Case("noredzone", Attribute::NoRedZone)
|
||||
.Case("noreturn", Attribute::NoReturn)
|
||||
.Case("nocf_check", Attribute::NoCfCheck)
|
||||
.Case("norecurse", Attribute::NoRecurse)
|
||||
.Case("nounwind", Attribute::NoUnwind)
|
||||
.Case("optnone", Attribute::OptimizeNone)
|
||||
|
@ -696,6 +696,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
|
||||
case Attribute::StackProtectStrong:
|
||||
case Attribute::StrictFP:
|
||||
case Attribute::UWTable:
|
||||
case Attribute::NoCfCheck:
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
; RUN: llc -mtriple=x86_64-unknown-unknown -mattr=+ibt -x86-indirect-branch-tracking < %s | FileCheck %s --check-prefix=ALL --check-prefix=X86_64
|
||||
; RUN: llc -mtriple=i386-unknown-unknown -mattr=+ibt -x86-indirect-branch-tracking < %s | FileCheck %s --check-prefix=ALL --check-prefix=X86
|
||||
; RUN: llc -mtriple=x86_64-unknown-unknown -mattr=+ibt < %s | FileCheck %s --check-prefix=ALL --check-prefix=X86_64
|
||||
; RUN: llc -mtriple=i386-unknown-unknown -mattr=+ibt < %s | FileCheck %s --check-prefix=ALL --check-prefix=X86
|
||||
; RUN: llc -mtriple i386-windows-gnu -mattr=+ibt -exception-model sjlj < %s | FileCheck %s --check-prefix=SJLJ
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Test1
|
||||
;; -----
|
||||
;; Checks ENDBR insertion in case of indirect branch IR instruction.
|
||||
;; Checks ENDBR insertion in case of switch case statement.
|
||||
;; Also since the function is not internal, make sure that endbr32/64 was
|
||||
;; added at the beginning of the function.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@ -34,7 +35,8 @@ bb6: ; preds = %entry
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Test2
|
||||
;; -----
|
||||
;; Checks ENDBR insertion in case of switch case statement.
|
||||
;; Checks NOTRACK insertion in case of switch case statement.
|
||||
;; Check that there is no ENDBR insertion in the following case statements.
|
||||
;; Also since the function is not internal, ENDBR instruction should be
|
||||
;; added to its first basic block.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@ -43,25 +45,9 @@ define i32 @test2(i32 %a) {
|
||||
; ALL-LABEL: test2
|
||||
; X86_64: endbr64
|
||||
; X86: endbr32
|
||||
; ALL: jmp{{q|l}} *
|
||||
; ALL: .LBB1_2:
|
||||
; X86_64-NEXT: endbr64
|
||||
; X86-NEXT: endbr32
|
||||
; ALL: .LBB1_7:
|
||||
; ALL: notrack jmp{{q|l}} *
|
||||
; X86_64-NOT: endbr64
|
||||
; X86-NOT: endbr32
|
||||
; ALL: .LBB1_3:
|
||||
; X86_64-NEXT: endbr64
|
||||
; X86-NEXT: endbr32
|
||||
; ALL: .LBB1_4:
|
||||
; X86_64-NEXT: endbr64
|
||||
; X86-NEXT: endbr32
|
||||
; ALL: .LBB1_5:
|
||||
; X86_64-NEXT: endbr64
|
||||
; X86-NEXT: endbr32
|
||||
; ALL: .LBB1_6:
|
||||
; X86_64-NEXT: endbr64
|
||||
; X86-NEXT: endbr32
|
||||
entry:
|
||||
%retval = alloca i32, align 4
|
||||
%a.addr = alloca i32, align 4
|
||||
@ -198,3 +184,38 @@ define i32 @test7() {
|
||||
; X86: endbr32
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Test8
|
||||
;; -----
|
||||
;; Checks that NO TRACK prefix is not added for indirect jumps to a jump-
|
||||
;; table that was created for SJLJ dispatch.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
declare void @_Z20function_that_throwsv()
|
||||
declare i32 @__gxx_personality_sj0(...)
|
||||
declare i8* @__cxa_begin_catch(i8*)
|
||||
declare void @__cxa_end_catch()
|
||||
|
||||
define void @test8() personality i8* bitcast (i32 (...)* @__gxx_personality_sj0 to i8*) {
|
||||
;SJLJ-LABEL: test8
|
||||
;SJLJ-NOT: ds
|
||||
entry:
|
||||
invoke void @_Z20function_that_throwsv()
|
||||
to label %try.cont unwind label %lpad
|
||||
|
||||
lpad:
|
||||
%0 = landingpad { i8*, i32 }
|
||||
catch i8* null
|
||||
%1 = extractvalue { i8*, i32 } %0, 0
|
||||
%2 = tail call i8* @__cxa_begin_catch(i8* %1)
|
||||
tail call void @__cxa_end_catch()
|
||||
br label %try.cont
|
||||
|
||||
try.cont:
|
||||
ret void
|
||||
}
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
|
||||
!0 = !{i32 4, !"cf-protection-branch", i32 1}
|
46
test/CodeGen/X86/nocf_check.ll
Normal file
46
test/CodeGen/X86/nocf_check.ll
Normal file
@ -0,0 +1,46 @@
|
||||
; RUN: llc -mtriple=x86_64-unknown-unknown -mattr=+ibt -x86-indirect-branch-tracking < %s | FileCheck %s
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; This test verify the handling of ''nocf_check'' attribute by the backend. ;;
|
||||
;; The file was generated using the following C code: ;;
|
||||
;; ;;
|
||||
;; void __attribute__((nocf_check)) NoCfCheckFunc(void) {} ;;
|
||||
;; ;;
|
||||
;; typedef void(*FuncPointer)(void); ;;
|
||||
;; void NoCfCheckCall(FuncPointer f) { ;;
|
||||
;; __attribute__((nocf_check)) FuncPointer p = f; ;;
|
||||
;; (*p)(); ;;
|
||||
;; } ;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
; Make sure that a function with ''nocf_check'' attribute is not instrumented
|
||||
; with endbr instruction at the beginning.
|
||||
define void @NoCfCheckFunc() #0 {
|
||||
; CHECK-LABEL: NoCfCheckFunc
|
||||
; CHECK-NOT: endbr64
|
||||
; CHECK: retq
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Make sure that notrack prefix is added before a call with ''nocf_check'' attribute.
|
||||
define void @NoCfCheckCall(void ()* %f) {
|
||||
; CHECK-LABEL: NoCfCheckCall
|
||||
; CHECK: notrack call
|
||||
entry:
|
||||
%f.addr = alloca void ()*, align 4
|
||||
%p = alloca void ()*, align 4
|
||||
store void ()* %f, void ()** %f.addr, align 4
|
||||
%0 = load void ()*, void ()** %f.addr, align 4
|
||||
store void ()* %0, void ()** %p, align 4
|
||||
%1 = load void ()*, void ()** %p, align 4
|
||||
call void %1() #1
|
||||
ret void
|
||||
}
|
||||
|
||||
attributes #0 = { noinline nocf_check nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-features"="+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
|
||||
attributes #1 = { nocf_check }
|
||||
|
||||
!llvm.module.flags = !{!0}
|
||||
|
||||
!0 = !{i32 4, !"cf-protection-branch", i32 1}
|
@ -1655,10 +1655,18 @@
|
||||
// CHECK: encoding: [0xff,0xd1]
|
||||
call *%ecx
|
||||
|
||||
// CHECK: notrack calll *%ecx
|
||||
// CHECK: encoding: [0x3e,0xff,0xd1]
|
||||
notrack call *%ecx
|
||||
|
||||
// CHECK: calll *3735928559(%ebx,%ecx,8)
|
||||
// CHECK: encoding: [0xff,0x94,0xcb,0xef,0xbe,0xad,0xde]
|
||||
call *0xdeadbeef(%ebx,%ecx,8)
|
||||
|
||||
// CHECK: notrack calll *3735928559(%ebx,%ecx,8)
|
||||
// CHECK: encoding: [0x3e,0xff,0x94,0xcb,0xef,0xbe,0xad,0xde]
|
||||
notrack call *0xdeadbeef(%ebx,%ecx,8)
|
||||
|
||||
// CHECK: calll *3135175374
|
||||
// CHECK: encoding: [0xff,0x15,0xce,0xfa,0xde,0xba]
|
||||
call *0xbadeface
|
||||
@ -1687,6 +1695,10 @@
|
||||
// CHECK: encoding: [0xff,0xa4,0xcb,0xef,0xbe,0xad,0xde]
|
||||
jmp *0xdeadbeef(%ebx,%ecx,8)
|
||||
|
||||
// CHECK: notrack jmpl *3735928559(%ebx,%ecx,8)
|
||||
// CHECK: encoding: [0x3e,0xff,0xa4,0xcb,0xef,0xbe,0xad,0xde]
|
||||
notrack jmp *0xdeadbeef(%ebx,%ecx,8)
|
||||
|
||||
// CHECK: jmpl *3135175374
|
||||
// CHECK: encoding: [0xff,0x25,0xce,0xfa,0xde,0xba]
|
||||
jmp *0xbadeface
|
||||
|
@ -350,6 +350,8 @@ public:
|
||||
MemRec->getValueAsBit("hasREX_WPrefix") ||
|
||||
RegRec->getValueAsBit("hasLockPrefix") !=
|
||||
MemRec->getValueAsBit("hasLockPrefix") ||
|
||||
RegRec->getValueAsBit("hasNoTrackPrefix") !=
|
||||
MemRec->getValueAsBit("hasNoTrackPrefix") ||
|
||||
!equalBitsInits(RegRec->getValueAsBitsInit("EVEX_LL"),
|
||||
MemRec->getValueAsBitsInit("EVEX_LL")) ||
|
||||
!equalBitsInits(RegRec->getValueAsBitsInit("VEX_WPrefix"),
|
||||
|
Loading…
Reference in New Issue
Block a user