aboutsummaryrefslogtreecommitdiff
path: root/lib/Target/ARM/MCTargetDesc/ARMMCNaCl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Target/ARM/MCTargetDesc/ARMMCNaCl.cpp')
-rw-r--r--lib/Target/ARM/MCTargetDesc/ARMMCNaCl.cpp329
1 files changed, 329 insertions, 0 deletions
diff --git a/lib/Target/ARM/MCTargetDesc/ARMMCNaCl.cpp b/lib/Target/ARM/MCTargetDesc/ARMMCNaCl.cpp
new file mode 100644
index 0000000000..ce68d4d92b
--- /dev/null
+++ b/lib/Target/ARM/MCTargetDesc/ARMMCNaCl.cpp
@@ -0,0 +1,329 @@
+//=== ARMMCNaCl.cpp - Expansion of NaCl pseudo-instructions --*- C++ -*-=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+#define DEBUG_TYPE "arm-mc-nacl"
+
+#include "MCTargetDesc/ARMBaseInfo.h"
+#include "MCTargetDesc/ARMMCExpr.h"
+#include "MCTargetDesc/ARMMCTargetDesc.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+
+using namespace llvm;
+
+namespace llvm {
+ cl::opt<bool> FlagSfiZeroMask("sfi-zero-mask");
+}
+
+/// Two helper functions for emitting the actual guard instructions
+
+static void EmitBICMask(MCStreamer &Out,
+ unsigned Addr, int64_t Pred, unsigned Mask) {
+ // bic\Pred \Addr, \Addr, #Mask
+ MCInst BICInst;
+ BICInst.setOpcode(ARM::BICri);
+ BICInst.addOperand(MCOperand::CreateReg(Addr)); // rD
+ BICInst.addOperand(MCOperand::CreateReg(Addr)); // rS
+ if (FlagSfiZeroMask) {
+ BICInst.addOperand(MCOperand::CreateImm(0)); // imm
+ } else {
+ BICInst.addOperand(MCOperand::CreateImm(Mask)); // imm
+ }
+ BICInst.addOperand(MCOperand::CreateImm(Pred)); // predicate
+ BICInst.addOperand(MCOperand::CreateReg(ARM::CPSR)); // CPSR
+ BICInst.addOperand(MCOperand::CreateReg(0)); // flag out
+ Out.EmitInstruction(BICInst);
+}
+
+static void EmitTST(MCStreamer &Out, unsigned Reg) {
+ // tst \reg, #\MASK typically 0xc0000000
+ const unsigned Mask = 0xC0000000;
+ MCInst TSTInst;
+ TSTInst.setOpcode(ARM::TSTri);
+ TSTInst.addOperand(MCOperand::CreateReg(Reg)); // rS
+ if (FlagSfiZeroMask) {
+ TSTInst.addOperand(MCOperand::CreateImm(0)); // imm
+ } else {
+ TSTInst.addOperand(MCOperand::CreateImm(Mask)); // imm
+ }
+ TSTInst.addOperand(MCOperand::CreateImm((int64_t)ARMCC::AL)); // Always
+ TSTInst.addOperand(MCOperand::CreateImm(0)); // flag out
+}
+
+
+// This is ONLY used for sandboxing stack changes.
+// The reason why SFI_NOP_IF_AT_BUNDLE_END gets handled here is that
+// it must ensure that the two instructions are in the same bundle.
+// It just so happens that the SFI_NOP_IF_AT_BUNDLE_END is always
+// emitted in conjunction with a SFI_DATA_MASK
+//
+static void EmitDataMask(int I, MCInst Saved[], MCStreamer &Out) {
+ assert(I == 3 &&
+ (ARM::SFI_NOP_IF_AT_BUNDLE_END == Saved[0].getOpcode()) &&
+ (ARM::SFI_DATA_MASK == Saved[2].getOpcode()) &&
+ "Unexpected SFI Pseudo while lowering");
+
+ unsigned Addr = Saved[2].getOperand(0).getReg();
+ int64_t Pred = Saved[2].getOperand(2).getImm();
+ assert((ARM::SP == Addr) && "Unexpected register at stack guard");
+
+ Out.EmitBundleLock();
+ Out.EmitInstruction(Saved[1]);
+ EmitBICMask(Out, Addr, Pred, 0xC0000000);
+ Out.EmitBundleUnlock();
+}
+
+static void EmitDirectGuardCall(int I, MCInst Saved[],
+ MCStreamer &Out) {
+ // sfi_call_preamble cond=
+ // sfi_nops_to_force_slot3
+ assert(I == 2 && (ARM::SFI_GUARD_CALL == Saved[0].getOpcode()) &&
+ "Unexpected SFI Pseudo while lowering SFI_GUARD_CALL");
+ Out.EmitBundleAlignEnd();
+ Out.EmitBundleLock();
+ Out.EmitInstruction(Saved[1]);
+ Out.EmitBundleUnlock();
+}
+
+static void EmitIndirectGuardCall(int I, MCInst Saved[],
+ MCStreamer &Out) {
+ // sfi_indirect_call_preamble link cond=
+ // sfi_nops_to_force_slot2
+ // sfi_code_mask \link \cond
+ assert(I == 2 && (ARM::SFI_GUARD_INDIRECT_CALL == Saved[0].getOpcode()) &&
+ "Unexpected SFI Pseudo while lowering SFI_GUARD_CALL");
+ unsigned Reg = Saved[0].getOperand(0).getReg();
+ int64_t Pred = Saved[0].getOperand(2).getImm();
+ Out.EmitBundleAlignEnd();
+ Out.EmitBundleLock();
+ EmitBICMask(Out, Reg, Pred, 0xC000000F);
+ Out.EmitInstruction(Saved[1]);
+ Out.EmitBundleUnlock();
+}
+
+static void EmitIndirectGuardJmp(int I, MCInst Saved[], MCStreamer &Out) {
+ // sfi_indirect_jump_preamble link cond=
+ // sfi_nop_if_at_bundle_end
+ // sfi_code_mask \link \cond
+ assert(I == 2 && (ARM::SFI_GUARD_INDIRECT_JMP == Saved[0].getOpcode()) &&
+ "Unexpected SFI Pseudo while lowering SFI_GUARD_CALL");
+ unsigned Reg = Saved[0].getOperand(0).getReg();
+ int64_t Pred = Saved[0].getOperand(2).getImm();
+
+ Out.EmitBundleLock();
+ EmitBICMask(Out, Reg, Pred, 0xC000000F);
+ Out.EmitInstruction(Saved[1]);
+ Out.EmitBundleUnlock();
+}
+
+static void EmitGuardReturn(int I, MCInst Saved[], MCStreamer &Out) {
+ // sfi_return_preamble reg cond=
+ // sfi_nop_if_at_bundle_end
+ // sfi_code_mask \reg \cond
+ assert(I == 2 && (ARM::SFI_GUARD_RETURN == Saved[0].getOpcode()) &&
+ "Unexpected SFI Pseudo while lowering SFI_GUARD_RETURN");
+ int64_t Pred = Saved[0].getOperand(0).getImm();
+
+ Out.EmitBundleLock();
+ EmitBICMask(Out, ARM::LR, Pred, 0xC000000F);
+ Out.EmitInstruction(Saved[1]);
+ Out.EmitBundleUnlock();
+}
+
+static void EmitGuardLoadOrStore(int I, MCInst Saved[], MCStreamer &Out) {
+ // sfi_store_preamble reg cond ---->
+ // sfi_nop_if_at_bundle_end
+ // sfi_data_mask \reg, \cond
+ assert(I == 2 && (ARM::SFI_GUARD_LOADSTORE == Saved[0].getOpcode()) &&
+ "Unexpected SFI Pseudo while lowering SFI_GUARD_RETURN");
+ unsigned Reg = Saved[0].getOperand(0).getReg();
+ int64_t Pred = Saved[0].getOperand(2).getImm();
+
+ Out.EmitBundleLock();
+ EmitBICMask(Out, Reg, Pred, 0xC0000000);
+ Out.EmitInstruction(Saved[1]);
+ Out.EmitBundleUnlock();
+}
+
+static void EmitGuardLoadOrStoreTst(int I, MCInst Saved[], MCStreamer &Out) {
+ // sfi_cstore_preamble reg -->
+ // sfi_nop_if_at_bundle_end
+ // sfi_data_tst \reg
+ assert(I == 2 && (ARM::SFI_GUARD_LOADSTORE_TST == Saved[0].getOpcode()) &&
+ "Unexpected SFI Pseudo while lowering");
+ unsigned Reg = Saved[0].getOperand(0).getReg();
+
+ Out.EmitBundleLock();
+ EmitTST(Out, Reg);
+ Out.EmitInstruction(Saved[1]);
+ Out.EmitBundleUnlock();
+}
+
+// This is ONLY used for loads into the stack pointer.
+static void EmitGuardSpLoad(int I, MCInst Saved[], MCStreamer &Out) {
+ assert(I == 4 &&
+ (ARM::SFI_GUARD_SP_LOAD == Saved[0].getOpcode()) &&
+ (ARM::SFI_NOP_IF_AT_BUNDLE_END == Saved[1].getOpcode()) &&
+ (ARM::SFI_DATA_MASK == Saved[3].getOpcode()) &&
+ "Unexpected SFI Pseudo while lowering");
+
+ unsigned AddrReg = Saved[0].getOperand(0).getReg();
+ unsigned SpReg = Saved[3].getOperand(0).getReg();
+ int64_t Pred = Saved[3].getOperand(2).getImm();
+ assert((ARM::SP == SpReg) && "Unexpected register at stack guard");
+
+ Out.EmitBundleLock();
+ EmitBICMask(Out, AddrReg, Pred, 0xC0000000);
+ Out.EmitInstruction(Saved[2]);
+ EmitBICMask(Out, SpReg, Pred, 0xC0000000);
+ Out.EmitBundleUnlock();
+}
+
+namespace llvm {
+// CustomExpandInstNaClARM -
+// If Inst is a NaCl pseudo instruction, emits the substitute
+// expansion to the MCStreamer and returns true.
+// Otherwise, returns false.
+//
+// NOTE: Each time this function calls Out.EmitInstruction(), it will be
+// called again recursively to rewrite the new instruction being emitted.
+// Care must be taken to ensure that this does not result in an infinite
+// loop. Also, global state must be managed carefully so that it is
+// consistent during recursive calls.
+//
+// We need global state to keep track of the explicit prefix (PREFIX_*)
+// instructions. Unfortunately, the assembly parser prefers to generate
+// these instead of combined instructions. At this time, having only
+// one explicit prefix is supported.
+
+
+bool CustomExpandInstNaClARM(const MCInst &Inst, MCStreamer &Out) {
+ const int MaxSaved = 4;
+ static MCInst Saved[MaxSaved];
+ static int SaveCount = 0;
+ static int I = 0;
+ // This routine only executes if RecurseGuard == 0
+ static bool RecurseGuard = false;
+
+ // If we are emitting to .s, just emit all pseudo-instructions directly.
+ if (Out.hasRawTextSupport()) {
+ return false;
+ }
+
+ //No recursive calls allowed;
+ if (RecurseGuard) return false;
+
+ unsigned Opc = Inst.getOpcode();
+
+ DEBUG(dbgs() << "CustomExpandInstNaClARM("; Inst.dump(); dbgs() << ")\n");
+
+ // Note: SFI_NOP_IF_AT_BUNDLE_END is only emitted directly as part of
+ // a stack guard in conjunction with a SFI_DATA_MASK
+
+ // Logic:
+ // This is somewhat convoluted, but in the current model, the SFI
+ // guard pseudo instructions occur PRIOR to the actual instruction.
+ // So, the bundling/alignment operation has to refer to the FOLLOWING
+ // one or two instructions.
+ //
+ // When a SFI_* pseudo is detected, it is saved. Then, the saved SFI_*
+ // pseudo and the very next one or two instructions are used as arguments to
+ // the Emit*() functions in this file. This is the reason why we have a
+ // doublely nested switch here. First, to save the SFI_* pseudo, then to
+ // emit it and the next instruction
+
+ // By default, we only need to save two or three instructions
+
+ if ((I == 0) && (SaveCount == 0)) {
+ // Base State, no saved instructions.
+ // If the current instruction is a SFI instruction, set the SaveCount
+ // and fall through.
+ switch (Opc) {
+ default:
+ SaveCount = 0; // Nothing to do.
+ return false; // Handle this Inst elsewhere.
+ case ARM::SFI_NOP_IF_AT_BUNDLE_END:
+ SaveCount = 3;
+ break;
+ case ARM::SFI_DATA_MASK:
+ SaveCount = 0; // Do nothing.
+ break;
+ case ARM::SFI_GUARD_CALL:
+ case ARM::SFI_GUARD_INDIRECT_CALL:
+ case ARM::SFI_GUARD_INDIRECT_JMP:
+ case ARM::SFI_GUARD_RETURN:
+ case ARM::SFI_GUARD_LOADSTORE:
+ case ARM::SFI_GUARD_LOADSTORE_TST:
+ SaveCount = 2;
+ break;
+ case ARM::SFI_GUARD_SP_LOAD:
+ SaveCount = 4;
+ break;
+ }
+ }
+
+ if (I < SaveCount) {
+ // Othewise, save the current Inst and return
+ Saved[I++] = Inst;
+ if (I < SaveCount)
+ return true;
+ // Else fall through to next stat
+ }
+
+ if (SaveCount > 0) {
+ assert(I == SaveCount && "Bookeeping Error");
+ SaveCount = 0; // Reset for next iteration
+ // The following calls may call Out.EmitInstruction()
+ // which must not again call CustomExpandInst ...
+ // So set RecurseGuard = 1;
+ RecurseGuard = true;
+
+ switch (Saved[0].getOpcode()) {
+ default: /* No action required */ break;
+ case ARM::SFI_NOP_IF_AT_BUNDLE_END:
+ EmitDataMask(I, Saved, Out);
+ break;
+ case ARM::SFI_DATA_MASK:
+ assert(0 && "Unexpected NOP_IF_AT_BUNDLE_END as a Saved Inst");
+ break;
+ case ARM::SFI_GUARD_CALL:
+ EmitDirectGuardCall(I, Saved, Out);
+ break;
+ case ARM::SFI_GUARD_INDIRECT_CALL:
+ EmitIndirectGuardCall(I, Saved, Out);
+ break;
+ case ARM::SFI_GUARD_INDIRECT_JMP:
+ EmitIndirectGuardJmp(I, Saved, Out);
+ break;
+ case ARM::SFI_GUARD_RETURN:
+ EmitGuardReturn(I, Saved, Out);
+ break;
+ case ARM::SFI_GUARD_LOADSTORE:
+ EmitGuardLoadOrStore(I, Saved, Out);
+ break;
+ case ARM::SFI_GUARD_LOADSTORE_TST:
+ EmitGuardLoadOrStoreTst(I, Saved, Out);
+ break;
+ case ARM::SFI_GUARD_SP_LOAD:
+ EmitGuardSpLoad(I, Saved, Out);
+ break;
+ }
+ I = 0; // Reset I for next.
+ assert(RecurseGuard && "Illegal Depth");
+ RecurseGuard = false;
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace llvm