| //=== 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 "ARMAddressingModes.h" |
| #include "MCTargetDesc/ARMBaseInfo.h" |
| #include "MCTargetDesc/ARMMCExpr.h" |
| #include "MCTargetDesc/ARMMCNaCl.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; |
| |
| /// Two helper functions for emitting the actual guard instructions |
| |
| static void EmitBICMask(const MCSubtargetInfo &STI, MCStreamer &Out, |
| unsigned Addr, int64_t Pred, unsigned Mask) { |
| // bic\Pred \Addr, \Addr, #Mask |
| MCInst BICInst; |
| const int32_t EncodedMask = ARM_AM::getSOImmVal(Mask); |
| assert(EncodedMask != -1); |
| BICInst.setOpcode(ARM::BICri); |
| BICInst.addOperand(MCOperand::CreateReg(Addr)); // rD |
| BICInst.addOperand(MCOperand::CreateReg(Addr)); // rS |
| BICInst.addOperand(MCOperand::CreateImm(EncodedMask)); // imm |
| BICInst.addOperand(MCOperand::CreateImm(Pred)); // predicate |
| BICInst.addOperand(MCOperand::CreateReg(ARM::CPSR)); // CPSR |
| BICInst.addOperand(MCOperand::CreateReg(0)); // flag out |
| Out.EmitInstruction(BICInst, STI); |
| } |
| |
| static void EmitTST(const MCSubtargetInfo &STI, MCStreamer &Out, unsigned Reg) { |
| // tst \reg, #\MASK typically 0xc0000000 |
| const int32_t Mask = ARM_AM::getSOImmVal(0xC0000000U); |
| assert(Mask != -1); |
| MCInst TSTInst; |
| TSTInst.setOpcode(ARM::TSTri); |
| TSTInst.addOperand(MCOperand::CreateReg(Reg)); // rS |
| TSTInst.addOperand(MCOperand::CreateImm(Mask)); // imm |
| TSTInst.addOperand(MCOperand::CreateImm((int64_t)ARMCC::AL)); // Always |
| TSTInst.addOperand(MCOperand::CreateImm(0)); // flag out |
| Out.EmitInstruction(TSTInst, STI); |
| } |
| |
| |
| // 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(const MCSubtargetInfo &STI, 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(false); |
| Out.EmitInstruction(Saved[1], STI); |
| EmitBICMask(STI, Out, Addr, Pred, 0xC0000000); |
| Out.EmitBundleUnlock(); |
| } |
| |
| static void EmitDirectGuardCall(const MCSubtargetInfo &STI, 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.EmitBundleLock(true); |
| Out.EmitInstruction(Saved[1], STI); |
| Out.EmitBundleUnlock(); |
| } |
| |
| static void EmitIndirectGuardCall(const MCSubtargetInfo &STI, 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.EmitBundleLock(true); |
| EmitBICMask(STI, Out, Reg, Pred, 0xC000000F); |
| Out.EmitInstruction(Saved[1], STI); |
| Out.EmitBundleUnlock(); |
| } |
| |
| static void EmitIndirectGuardJmp(const MCSubtargetInfo &STI, 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(false); |
| EmitBICMask(STI, Out, Reg, Pred, 0xC000000F); |
| Out.EmitInstruction(Saved[1], STI); |
| Out.EmitBundleUnlock(); |
| } |
| |
| static void EmitGuardReturn(const MCSubtargetInfo &STI, 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(false); |
| EmitBICMask(STI, Out, ARM::LR, Pred, 0xC000000F); |
| Out.EmitInstruction(Saved[1], STI); |
| Out.EmitBundleUnlock(); |
| } |
| |
| static void EmitGuardLoadOrStore(const MCSubtargetInfo &STI, 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(false); |
| EmitBICMask(STI, Out, Reg, Pred, 0xC0000000); |
| Out.EmitInstruction(Saved[1], STI); |
| Out.EmitBundleUnlock(); |
| } |
| |
| static void EmitGuardLoadOrStoreTst(const MCSubtargetInfo &STI, 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(false); |
| EmitTST(STI, Out, Reg); |
| Out.EmitInstruction(Saved[1], STI); |
| Out.EmitBundleUnlock(); |
| } |
| |
| // This is ONLY used for loads into the stack pointer. |
| static void EmitGuardSpLoad(const MCSubtargetInfo &STI, 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(false); |
| EmitBICMask(STI, Out, AddrReg, Pred, 0xC0000000); |
| Out.EmitInstruction(Saved[2], STI); |
| EmitBICMask(STI, Out, SpReg, Pred, 0xC0000000); |
| Out.EmitBundleUnlock(); |
| } |
| |
| namespace llvm { |
| |
| const int ARMMCNaClSFIState::MaxSaved; |
| |
| // 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. |
| bool CustomExpandInstNaClARM(const MCSubtargetInfo &STI, const MCInst &Inst, |
| MCStreamer &Out, ARMMCNaClSFIState &State) { |
| // 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 |
| // instructions. |
| // |
| // When a SFI pseudo is detected, it is saved. Then, the saved SFI |
| // pseudo and the very next instructions (their amount depending on the kind |
| // of the SFI pseudo) are used as arguments to the Emit*() functions in |
| // this file. |
| // |
| // Some state data is used to preserve state accross calls: |
| // |
| // Saved: the saved instructions (starting with the SFI_ pseudo). |
| // SavedCount: the amount of saved instructions required for the SFI pseudo |
| // that's being expanded. |
| // I: the index of the currently saved instruction - used to track |
| // where in Saved to insert the instruction and how many more |
| // remain. |
| // |
| |
| // If we are emitting to .s, just emit all pseudo-instructions directly. |
| if (Out.hasRawTextSupport()) { |
| return false; |
| } |
| |
| // Protect against recursive execution. If State.RecurseiveCall == true, it |
| // means we're already in the process of expanding a custom instruction, and |
| // we don't need to run recursively on anything generated by such an |
| // expansion. |
| if (State.RecursiveCall) |
| return false; |
| |
| DEBUG(dbgs() << "CustomExpandInstNaClARM("; Inst.dump(); dbgs() << ")\n"); |
| |
| if ((State.I == 0) && (State.SaveCount == 0)) { |
| // Base state: no SFI guard identified yet and no saving started. |
| switch (Inst.getOpcode()) { |
| default: |
| // We don't handle non-SFI guards here |
| return false; |
| case ARM::SFI_NOP_IF_AT_BUNDLE_END: |
| // Note: SFI_NOP_IF_AT_BUNDLE_END is only emitted directly as part of |
| // a stack guard in conjunction with a SFI_DATA_MASK. |
| State.SaveCount = 3; |
| break; |
| case ARM::SFI_DATA_MASK: |
| llvm_unreachable( |
| "SFI_DATA_MASK found without preceding SFI_NOP_IF_AT_BUNDLE_END"); |
| return false; |
| 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: |
| State.SaveCount = 2; |
| break; |
| case ARM::SFI_GUARD_SP_LOAD: |
| State.SaveCount = 4; |
| break; |
| } |
| } |
| |
| // We're in "saving instructions" state |
| if (State.I < State.SaveCount) { |
| // This instruction has to be saved |
| assert(State.I < State.MaxSaved && "Trying to save too many instructions"); |
| State.Saved[State.I++] = Inst; |
| if (State.I < State.SaveCount) |
| return true; |
| } |
| |
| // We're in "saved enough instructions, time to emit" state |
| assert(State.I == State.SaveCount && State.SaveCount > 0 && "Bookeeping Error"); |
| |
| // When calling Emit* functions, do that with RecurseGuard set (the comment |
| // at the beginning of this function explains why) |
| State.RecursiveCall = true; |
| switch (State.Saved[0].getOpcode()) { |
| default: |
| break; |
| case ARM::SFI_NOP_IF_AT_BUNDLE_END: |
| EmitDataMask(STI, State.I, State.Saved, Out); |
| break; |
| case ARM::SFI_DATA_MASK: |
| llvm_unreachable("SFI_DATA_MASK can't start a SFI sequence"); |
| break; |
| case ARM::SFI_GUARD_CALL: |
| EmitDirectGuardCall(STI, State.I, State.Saved, Out); |
| break; |
| case ARM::SFI_GUARD_INDIRECT_CALL: |
| EmitIndirectGuardCall(STI, State.I, State.Saved, Out); |
| break; |
| case ARM::SFI_GUARD_INDIRECT_JMP: |
| EmitIndirectGuardJmp(STI, State.I, State.Saved, Out); |
| break; |
| case ARM::SFI_GUARD_RETURN: |
| EmitGuardReturn(STI, State.I, State.Saved, Out); |
| break; |
| case ARM::SFI_GUARD_LOADSTORE: |
| EmitGuardLoadOrStore(STI, State.I, State.Saved, Out); |
| break; |
| case ARM::SFI_GUARD_LOADSTORE_TST: |
| EmitGuardLoadOrStoreTst(STI, State.I, State.Saved, Out); |
| break; |
| case ARM::SFI_GUARD_SP_LOAD: |
| EmitGuardSpLoad(STI, State.I, State.Saved, Out); |
| break; |
| } |
| assert(State.RecursiveCall && "Illegal Depth"); |
| State.RecursiveCall = false; |
| |
| // We're done expanding a SFI guard. Reset state vars. |
| State.SaveCount = 0; |
| State.I = 0; |
| return true; |
| } |
| |
| } // namespace llvm |