blob: dbf29e606cc516b1d65c2321ccc0b7c6a2811933 [file] [log] [blame]
//===-- ARMNaClRewritePass.cpp - Native Client Rewrite Pass ------*- C++ -*-=//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Native Client Rewrite Pass
// This final pass inserts the sandboxing instructions needed to run inside
// the Native Client sandbox. Native Client requires certain software fault
// isolation (SFI) constructions to be put in place, to prevent escape from
// the sandbox. Native Client refuses to execute binaries without the correct
// SFI sequences.
//
// Potentially dangerous operations which are protected include:
// * Stores
// * Branches
// * Changes to SP
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "arm-sfi"
#include "ARM.h"
#include "ARMBaseInstrInfo.h"
#include "ARMNaClRewritePass.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetSubtargetInfo.h"
#include <set>
#include <stdio.h>
using namespace llvm;
namespace {
class ARMNaClRewritePass : public MachineFunctionPass {
public:
static char ID;
ARMNaClRewritePass() : MachineFunctionPass(ID) {}
const ARMBaseInstrInfo *TII;
const TargetRegisterInfo *TRI;
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
virtual bool runOnMachineFunction(MachineFunction &Fn);
virtual const char *getPassName() const {
return "ARM Native Client Rewrite Pass";
}
private:
bool SandboxMemoryReferencesInBlock(MachineBasicBlock &MBB);
void SandboxMemory(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
MachineInstr &MI,
int AddrIdx,
bool IsLoad);
bool SandboxBranchesInBlock(MachineBasicBlock &MBB);
bool SandboxStackChangesInBlock(MachineBasicBlock &MBB);
void SandboxStackChange(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI);
void LightweightVerify(MachineFunction &MF);
};
char ARMNaClRewritePass::ID = 0;
}
static bool IsReturn(const MachineInstr &MI) {
return (MI.getOpcode() == ARM::BX_RET);
}
static bool IsIndirectJump(const MachineInstr &MI) {
switch (MI.getOpcode()) {
default: return false;
case ARM::BX:
case ARM::TAILJMPr:
return true;
}
}
static bool IsIndirectCall(const MachineInstr &MI) {
return MI.getOpcode() == ARM::BLX;
}
static bool IsDirectCall(const MachineInstr &MI) {
switch (MI.getOpcode()) {
default: return false;
case ARM::BL:
case ARM::BL_pred:
case ARM::TPsoft:
return true;
}
}
static void DumpInstructionVerbose(const MachineInstr &MI) {
DEBUG({
dbgs() << MI;
dbgs() << MI.getNumOperands() << " operands:" << "\n";
for (unsigned i = 0; i < MI.getNumOperands(); ++i) {
const MachineOperand& op = MI.getOperand(i);
dbgs() << " " << i << "(" << (unsigned)op.getType() << "):" << op
<< "\n";
}
dbgs() << "\n";
});
}
static void DumpBasicBlockVerbose(const MachineBasicBlock &MBB) {
DEBUG({
dbgs() << "\n<<<<< DUMP BASIC BLOCK START\n";
for (MachineBasicBlock::const_iterator
MBBI = MBB.begin(), MBBE = MBB.end();
MBBI != MBBE;
++MBBI) {
DumpInstructionVerbose(*MBBI);
}
dbgs() << "<<<<< DUMP BASIC BLOCK END\n\n";
});
}
/**********************************************************************/
/* Exported functions */
namespace ARM_SFI {
bool IsStackChange(const MachineInstr &MI, const TargetRegisterInfo *TRI) {
return MI.modifiesRegister(ARM::SP, TRI);
}
bool NextInstrMasksSP(const MachineInstr &MI) {
MachineBasicBlock::const_iterator It = &MI;
const MachineBasicBlock *MBB = MI.getParent();
MachineBasicBlock::const_iterator next = ++It;
if (next == MBB->end()) {
return false;
}
const MachineInstr &next_instr = *next;
unsigned opcode = next_instr.getOpcode();
return (opcode == ARM::SFI_DATA_MASK) &&
(next_instr.getOperand(0).getReg() == ARM::SP);
}
bool IsSandboxedStackChange(const MachineInstr &MI) {
// Calls do not change the stack on ARM but they have implicit-defs, so
// make sure they do not get sandboxed.
if (MI.getDesc().isCall())
return true;
unsigned opcode = MI.getOpcode();
switch (opcode) {
default: break;
// Our mask instructions correctly update the stack pointer.
case ARM::SFI_DATA_MASK:
return true;
// These just bump SP by a little (and access the stack),
// so that is okay due to guard pages.
case ARM::STMIA_UPD:
case ARM::STMDA_UPD:
case ARM::STMDB_UPD:
case ARM::STMIB_UPD:
case ARM::VSTMDIA_UPD:
case ARM::VSTMDDB_UPD:
case ARM::VSTMSIA_UPD:
case ARM::VSTMSDB_UPD:
return true;
// Similar, unless it is a load into SP...
case ARM::LDMIA_UPD:
case ARM::LDMDA_UPD:
case ARM::LDMDB_UPD:
case ARM::LDMIB_UPD:
case ARM::VLDMDIA_UPD:
case ARM::VLDMDDB_UPD:
case ARM::VLDMSIA_UPD:
case ARM::VLDMSDB_UPD: {
bool dest_SP = false;
// Dest regs start at operand index 4.
for (unsigned i = 4; i < MI.getNumOperands(); ++i) {
const MachineOperand &DestReg = MI.getOperand(i);
dest_SP = dest_SP || (DestReg.getReg() == ARM::SP);
}
if (dest_SP) {
break;
}
return true;
}
// Some localmods *should* prevent selecting a reg offset
// (see SelectAddrMode2 in ARMISelDAGToDAG.cpp).
// Otherwise, the store is already a potential violation.
case ARM::STR_PRE_REG:
case ARM::STR_PRE_IMM:
case ARM::STRH_PRE:
case ARM::STRB_PRE_REG:
case ARM::STRB_PRE_IMM:
return true;
// Similar, unless it is a load into SP...
case ARM::LDRi12:
case ARM::LDR_PRE_REG:
case ARM::LDR_PRE_IMM:
case ARM::LDRH_PRE:
case ARM::LDRB_PRE_REG:
case ARM::LDRB_PRE_IMM:
case ARM::LDRSH_PRE:
case ARM::LDRSB_PRE: {
const MachineOperand &DestReg = MI.getOperand(0);
if (DestReg.getReg() == ARM::SP) {
break;
}
return true;
}
// Here, if SP is the base / write-back reg, we need to check if
// a reg is used as offset (otherwise it is not a small nudge).
case ARM::STR_POST_REG:
case ARM::STR_POST_IMM:
case ARM::STRH_POST:
case ARM::STRB_POST_REG:
case ARM::STRB_POST_IMM: {
const MachineOperand &WBReg = MI.getOperand(0);
const MachineOperand &OffReg = MI.getOperand(3);
if (WBReg.getReg() == ARM::SP && OffReg.getReg() != 0) {
break;
}
return true;
}
// Similar, but also check that DestReg is not SP.
case ARM::LDR_POST_REG:
case ARM::LDR_POST_IMM:
case ARM::LDRB_POST_REG:
case ARM::LDRB_POST_IMM:
case ARM::LDRH_POST:
case ARM::LDRSH_POST:
case ARM::LDRSB_POST: {
const MachineOperand &DestReg = MI.getOperand(0);
if (DestReg.getReg() == ARM::SP) {
break;
}
const MachineOperand &WBReg = MI.getOperand(1);
const MachineOperand &OffReg = MI.getOperand(3);
if (WBReg.getReg() == ARM::SP && OffReg.getReg() != 0) {
break;
}
return true;
}
}
return (NextInstrMasksSP(MI));
}
bool NeedSandboxStackChange(const MachineInstr &MI,
const TargetRegisterInfo *TRI) {
return (IsStackChange(MI, TRI) && !IsSandboxedStackChange(MI));
}
} // namespace ARM_SFI
/**********************************************************************/
void ARMNaClRewritePass::getAnalysisUsage(AnalysisUsage &AU) const {
// Slight (possibly unnecessary) efficiency tweak:
// Promise not to modify the CFG.
AU.setPreservesCFG();
MachineFunctionPass::getAnalysisUsage(AU);
}
/*
* A primitive validator to catch problems at compile time.
* E.g., it could be used along with bugpoint to reduce a bitcode file.
*/
void ARMNaClRewritePass::LightweightVerify(MachineFunction &MF) {
DEBUG({
for (MachineFunction::iterator MFI = MF.begin(), MFE = MF.end();
MFI != MFE;
++MFI) {
MachineBasicBlock &MBB = *MFI;
for (MachineBasicBlock::iterator MBBI = MBB.begin(), MBBE = MBB.end();
MBBI != MBBE;
++MBBI) {
MachineInstr &MI = *MBBI;
if (ARM_SFI::NeedSandboxStackChange(MI, TRI)) {
dbgs() << "LightWeightVerify for function: "
<< MF.getFunction()->getName() << " (BAD STACK CHANGE)\n";
DumpInstructionVerbose(MI);
DumpBasicBlockVerbose(MBB);
}
}
}
});
}
void ARMNaClRewritePass::SandboxStackChange(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI) {
// (1) Ensure there is room in the bundle for a data mask instruction
// (nop'ing to the next bundle if needed).
// (2) Do a data mask on SP after the instruction that updated SP.
MachineInstr &MI = *MBBI;
// Use same predicate as current instruction.
unsigned PredReg = 0;
ARMCC::CondCodes Pred = llvm::getInstrPredicate(&MI, PredReg);
BuildMI(MBB, MBBI, MI.getDebugLoc(),
TII->get(ARM::SFI_NOP_IF_AT_BUNDLE_END));
// Get to next instr.
MachineBasicBlock::iterator MBBINext = (++MBBI);
BuildMI(MBB, MBBINext, MI.getDebugLoc(),
TII->get(ARM::SFI_DATA_MASK))
.addReg(ARM::SP, RegState::Define) // modify SP (as dst)
.addReg(ARM::SP, RegState::Kill) // start with SP (as src)
.addImm((int64_t) Pred) // predicate condition
.addReg(PredReg); // predicate source register (CPSR)
}
bool ARMNaClRewritePass::SandboxStackChangesInBlock(MachineBasicBlock &MBB) {
bool Modified = false;
for (MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
MBBI != E;
++MBBI) {
MachineInstr &MI = *MBBI;
if (ARM_SFI::NeedSandboxStackChange(MI, TRI)) {
SandboxStackChange(MBB, MBBI);
Modified |= true;
}
}
return Modified;
}
bool ARMNaClRewritePass::SandboxBranchesInBlock(MachineBasicBlock &MBB) {
bool Modified = false;
for (MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
MBBI != E;
++MBBI) {
MachineInstr &MI = *MBBI;
// Use same predicate as current instruction.
unsigned PredReg = 0;
ARMCC::CondCodes Pred = llvm::getInstrPredicate(&MI, PredReg);
if (IsReturn(MI)) {
BuildMI(MBB, MBBI, MI.getDebugLoc(),
TII->get(ARM::SFI_GUARD_RETURN))
.addImm((int64_t) Pred) // predicate condition
.addReg(PredReg); // predicate source register (CPSR)
Modified = true;
}
if (IsIndirectJump(MI)) {
unsigned Addr = MI.getOperand(0).getReg();
BuildMI(MBB, MBBI, MI.getDebugLoc(),
TII->get(ARM::SFI_GUARD_INDIRECT_JMP))
.addReg(Addr, RegState::Define) // Destination definition (as dst)
.addReg(Addr, RegState::Kill) // Destination read (as src)
.addImm((int64_t) Pred) // predicate condition
.addReg(PredReg); // predicate source register (CPSR)
Modified = true;
}
if (IsDirectCall(MI)) {
BuildMI(MBB, MBBI, MI.getDebugLoc(),
TII->get(ARM::SFI_GUARD_CALL))
.addImm((int64_t) Pred) // predicate condition
.addReg(PredReg); // predicate source register (CPSR)
Modified = true;
}
if (IsIndirectCall(MI)) {
unsigned Addr = MI.getOperand(0).getReg();
BuildMI(MBB, MBBI, MI.getDebugLoc(),
TII->get(ARM::SFI_GUARD_INDIRECT_CALL))
.addReg(Addr, RegState::Define) // Destination definition (as dst)
.addReg(Addr, RegState::Kill) // Destination read (as src)
.addImm((int64_t) Pred) // predicate condition
.addReg(PredReg); // predicate source register (CPSR)
Modified = true;
}
}
return Modified;
}
static bool IsDangerousLoad(const MachineInstr &MI, int *AddrIdx) {
unsigned Opcode = MI.getOpcode();
switch (Opcode) {
default: return false;
// Instructions with base address register in position 0...
case ARM::LDMIA:
case ARM::LDMDA:
case ARM::LDMDB:
case ARM::LDMIB:
case ARM::VLDMDIA:
case ARM::VLDMSIA:
case ARM::PLDi12:
case ARM::PLDWi12:
case ARM::PLIi12:
*AddrIdx = 0;
break;
// Instructions with base address register in position 1...
case ARM::LDMIA_UPD: // same reg at position 0 and position 1
case ARM::LDMDA_UPD:
case ARM::LDMDB_UPD:
case ARM::LDMIB_UPD:
case ARM::LDRSB:
case ARM::LDRH:
case ARM::LDRSH:
case ARM::LDRi12:
case ARM::LDRrs:
case ARM::LDRBi12:
case ARM::LDRBrs:
case ARM::VLDMDIA_UPD:
case ARM::VLDMDDB_UPD:
case ARM::VLDMSIA_UPD:
case ARM::VLDMSDB_UPD:
case ARM::VLDRS:
case ARM::VLDRD:
case ARM::LDREX:
case ARM::LDREXB:
case ARM::LDREXH:
case ARM::LDREXD:
*AddrIdx = 1;
break;
// Instructions with base address register in position 2...
case ARM::LDR_PRE_REG:
case ARM::LDR_PRE_IMM:
case ARM::LDR_POST_REG:
case ARM::LDR_POST_IMM:
case ARM::LDRB_PRE_REG:
case ARM::LDRB_PRE_IMM:
case ARM::LDRB_POST_REG:
case ARM::LDRB_POST_IMM:
case ARM::LDRSB_PRE:
case ARM::LDRSB_POST:
case ARM::LDRH_PRE:
case ARM::LDRH_POST:
case ARM::LDRSH_PRE:
case ARM::LDRSH_POST:
case ARM::LDRD:
*AddrIdx = 2;
break;
//
// NEON loads
//
// VLD1
case ARM::VLD1d8:
case ARM::VLD1d16:
case ARM::VLD1d32:
case ARM::VLD1d64:
case ARM::VLD1q8:
case ARM::VLD1q16:
case ARM::VLD1q32:
case ARM::VLD1q64:
*AddrIdx = 1;
break;
case ARM::VLD1d8wb_fixed:
case ARM::VLD1d16wb_fixed:
case ARM::VLD1d32wb_fixed:
case ARM::VLD1d64wb_fixed:
case ARM::VLD1q8wb_fixed:
case ARM::VLD1q16wb_fixed:
case ARM::VLD1q32wb_fixed:
case ARM::VLD1q64wb_fixed:
case ARM::VLD1d8wb_register:
case ARM::VLD1d16wb_register:
case ARM::VLD1d32wb_register:
case ARM::VLD1d64wb_register:
case ARM::VLD1q8wb_register:
case ARM::VLD1q16wb_register:
case ARM::VLD1q32wb_register:
case ARM::VLD1q64wb_register:
*AddrIdx = 2;
break;
// VLD1T
case ARM::VLD1d8T:
case ARM::VLD1d16T:
case ARM::VLD1d32T:
case ARM::VLD1d64T:
*AddrIdx = 1;
break;
case ARM::VLD1d8Twb_fixed:
case ARM::VLD1d16Twb_fixed:
case ARM::VLD1d32Twb_fixed:
case ARM::VLD1d64Twb_fixed:
case ARM::VLD1d8Twb_register:
case ARM::VLD1d16Twb_register:
case ARM::VLD1d32Twb_register:
case ARM::VLD1d64Twb_register:
*AddrIdx = 2;
break;
// VLD1Q
case ARM::VLD1d8Q:
case ARM::VLD1d16Q:
case ARM::VLD1d32Q:
case ARM::VLD1d64Q:
*AddrIdx = 1;
break;
case ARM::VLD1d8Qwb_fixed:
case ARM::VLD1d16Qwb_fixed:
case ARM::VLD1d32Qwb_fixed:
case ARM::VLD1d64Qwb_fixed:
case ARM::VLD1d8Qwb_register:
case ARM::VLD1d16Qwb_register:
case ARM::VLD1d32Qwb_register:
case ARM::VLD1d64Qwb_register:
*AddrIdx = 2;
break;
// VLD1LN
case ARM::VLD1LNd8:
case ARM::VLD1LNd16:
case ARM::VLD1LNd32:
case ARM::VLD1LNd8_UPD:
case ARM::VLD1LNd16_UPD:
case ARM::VLD1LNd32_UPD:
// VLD1DUP
case ARM::VLD1DUPd8:
case ARM::VLD1DUPd16:
case ARM::VLD1DUPd32:
case ARM::VLD1DUPq8:
case ARM::VLD1DUPq16:
case ARM::VLD1DUPq32:
case ARM::VLD1DUPd8wb_fixed:
case ARM::VLD1DUPd16wb_fixed:
case ARM::VLD1DUPd32wb_fixed:
case ARM::VLD1DUPq8wb_fixed:
case ARM::VLD1DUPq16wb_fixed:
case ARM::VLD1DUPq32wb_fixed:
case ARM::VLD1DUPd8wb_register:
case ARM::VLD1DUPd16wb_register:
case ARM::VLD1DUPd32wb_register:
case ARM::VLD1DUPq8wb_register:
case ARM::VLD1DUPq16wb_register:
case ARM::VLD1DUPq32wb_register:
// VLD2
case ARM::VLD2d8:
case ARM::VLD2d16:
case ARM::VLD2d32:
case ARM::VLD2b8:
case ARM::VLD2b16:
case ARM::VLD2b32:
case ARM::VLD2q8:
case ARM::VLD2q16:
case ARM::VLD2q32:
*AddrIdx = 1;
break;
case ARM::VLD2d8wb_fixed:
case ARM::VLD2d16wb_fixed:
case ARM::VLD2d32wb_fixed:
case ARM::VLD2b8wb_fixed:
case ARM::VLD2b16wb_fixed:
case ARM::VLD2b32wb_fixed:
case ARM::VLD2q8wb_fixed:
case ARM::VLD2q16wb_fixed:
case ARM::VLD2q32wb_fixed:
case ARM::VLD2d8wb_register:
case ARM::VLD2d16wb_register:
case ARM::VLD2d32wb_register:
case ARM::VLD2b8wb_register:
case ARM::VLD2b16wb_register:
case ARM::VLD2b32wb_register:
case ARM::VLD2q8wb_register:
case ARM::VLD2q16wb_register:
case ARM::VLD2q32wb_register:
*AddrIdx = 2;
break;
// VLD2LN
case ARM::VLD2LNd8:
case ARM::VLD2LNd16:
case ARM::VLD2LNd32:
case ARM::VLD2LNq16:
case ARM::VLD2LNq32:
*AddrIdx = 2;
break;
case ARM::VLD2LNd8_UPD:
case ARM::VLD2LNd16_UPD:
case ARM::VLD2LNd32_UPD:
case ARM::VLD2LNq16_UPD:
case ARM::VLD2LNq32_UPD:
*AddrIdx = 3;
break;
// VLD2DUP
case ARM::VLD2DUPd8:
case ARM::VLD2DUPd16:
case ARM::VLD2DUPd32:
case ARM::VLD2DUPd8x2:
case ARM::VLD2DUPd16x2:
case ARM::VLD2DUPd32x2:
*AddrIdx = 1;
break;
case ARM::VLD2DUPd8wb_fixed:
case ARM::VLD2DUPd16wb_fixed:
case ARM::VLD2DUPd32wb_fixed:
case ARM::VLD2DUPd8wb_register:
case ARM::VLD2DUPd16wb_register:
case ARM::VLD2DUPd32wb_register:
case ARM::VLD2DUPd8x2wb_fixed:
case ARM::VLD2DUPd16x2wb_fixed:
case ARM::VLD2DUPd32x2wb_fixed:
case ARM::VLD2DUPd8x2wb_register:
case ARM::VLD2DUPd16x2wb_register:
case ARM::VLD2DUPd32x2wb_register:
*AddrIdx = 2;
break;
// VLD3
case ARM::VLD3d8:
case ARM::VLD3d16:
case ARM::VLD3d32:
case ARM::VLD3q8:
case ARM::VLD3q16:
case ARM::VLD3q32:
case ARM::VLD3d8_UPD:
case ARM::VLD3d16_UPD:
case ARM::VLD3d32_UPD:
case ARM::VLD3q8_UPD:
case ARM::VLD3q16_UPD:
case ARM::VLD3q32_UPD:
// VLD3LN
case ARM::VLD3LNd8:
case ARM::VLD3LNd16:
case ARM::VLD3LNd32:
case ARM::VLD3LNq16:
case ARM::VLD3LNq32:
*AddrIdx = 3;
break;
case ARM::VLD3LNd8_UPD:
case ARM::VLD3LNd16_UPD:
case ARM::VLD3LNd32_UPD:
case ARM::VLD3LNq16_UPD:
case ARM::VLD3LNq32_UPD:
*AddrIdx = 4;
break;
// VLD3DUP
case ARM::VLD3DUPd8:
case ARM::VLD3DUPd16:
case ARM::VLD3DUPd32:
case ARM::VLD3DUPq8:
case ARM::VLD3DUPq16:
case ARM::VLD3DUPq32:
*AddrIdx = 3;
break;
case ARM::VLD3DUPd8_UPD:
case ARM::VLD3DUPd16_UPD:
case ARM::VLD3DUPd32_UPD:
case ARM::VLD3DUPq8_UPD:
case ARM::VLD3DUPq16_UPD:
case ARM::VLD3DUPq32_UPD:
*AddrIdx = 4;
break;
// VLD4
case ARM::VLD4d8:
case ARM::VLD4d16:
case ARM::VLD4d32:
case ARM::VLD4q8:
case ARM::VLD4q16:
case ARM::VLD4q32:
*AddrIdx = 4;
break;
case ARM::VLD4d8_UPD:
case ARM::VLD4d16_UPD:
case ARM::VLD4d32_UPD:
case ARM::VLD4q8_UPD:
case ARM::VLD4q16_UPD:
case ARM::VLD4q32_UPD:
*AddrIdx = 5;
break;
// VLD4LN
case ARM::VLD4LNd8:
case ARM::VLD4LNd16:
case ARM::VLD4LNd32:
case ARM::VLD4LNq16:
case ARM::VLD4LNq32:
*AddrIdx = 4;
break;
case ARM::VLD4LNd8_UPD:
case ARM::VLD4LNd16_UPD:
case ARM::VLD4LNd32_UPD:
case ARM::VLD4LNq16_UPD:
case ARM::VLD4LNq32_UPD:
*AddrIdx = 5;
break;
case ARM::VLD4DUPd8:
case ARM::VLD4DUPd16:
case ARM::VLD4DUPd32:
case ARM::VLD4DUPq16:
case ARM::VLD4DUPq32:
*AddrIdx = 4;
break;
case ARM::VLD4DUPd8_UPD:
case ARM::VLD4DUPd16_UPD:
case ARM::VLD4DUPd32_UPD:
case ARM::VLD4DUPq16_UPD:
case ARM::VLD4DUPq32_UPD:
*AddrIdx = 5;
break;
}
if (MI.getOperand(*AddrIdx).getReg() == ARM::SP) {
// The contents of SP do not require masking.
return false;
}
return true;
}
/*
* Sandboxes a memory reference instruction by inserting an appropriate mask
* or check operation before it.
*/
void ARMNaClRewritePass::SandboxMemory(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
MachineInstr &MI,
int AddrIdx,
bool IsLoad) {
unsigned Addr = MI.getOperand(AddrIdx).getReg();
if (Addr == ARM::R9) {
// R9-relative loads are no longer sandboxed.
assert(IsLoad && "There should be no r9-relative stores");
} else {
unsigned Opcode;
if (IsLoad && (MI.getOperand(0).getReg() == ARM::SP)) {
Opcode = ARM::SFI_GUARD_SP_LOAD;
} else {
Opcode = ARM::SFI_GUARD_LOADSTORE;
}
// Use same predicate as current instruction.
unsigned PredReg = 0;
ARMCC::CondCodes Pred = llvm::getInstrPredicate(&MI, PredReg);
// Use the older BIC sandbox, which is universal, but incurs a stall.
BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(Opcode))
.addReg(Addr, RegState::Define) // Address definition (as dst).
.addReg(Addr, RegState::Kill) // Address read (as src).
.addImm((int64_t) Pred) // predicate condition
.addReg(PredReg); // predicate source register (CPSR)
/*
* This pseudo-instruction is intended to generate something resembling the
* following, but with alignment enforced.
* TODO(cbiffle): move alignment into this function, use the code below.
*
* // bic<cc> Addr, Addr, #0xC0000000
* BuildMI(MBB, MBBI, MI.getDebugLoc(),
* TII->get(ARM::BICri))
* .addReg(Addr) // rD
* .addReg(Addr) // rN
* .addImm(0xC0000000) // imm
* .addImm((int64_t) Pred) // predicate condition
* .addReg(PredReg) // predicate source register (CPSR)
* .addReg(0); // flag output register (0 == no flags)
*/
}
}
static bool IsDangerousStore(const MachineInstr &MI, int *AddrIdx) {
unsigned Opcode = MI.getOpcode();
switch (Opcode) {
default: return false;
// Instructions with base address register in position 0...
case ARM::STMIA:
case ARM::STMDA:
case ARM::STMDB:
case ARM::STMIB:
case ARM::VSTMDIA:
case ARM::VSTMSIA:
*AddrIdx = 0;
break;
// Instructions with base address register in position 1...
case ARM::STMIA_UPD: // same reg at position 0 and position 1
case ARM::STMDA_UPD:
case ARM::STMDB_UPD:
case ARM::STMIB_UPD:
case ARM::STRH:
case ARM::STRi12:
case ARM::STRrs:
case ARM::STRBi12:
case ARM::STRBrs:
case ARM::VSTMDIA_UPD:
case ARM::VSTMDDB_UPD:
case ARM::VSTMSIA_UPD:
case ARM::VSTMSDB_UPD:
case ARM::VSTRS:
case ARM::VSTRD:
*AddrIdx = 1;
break;
//
// NEON stores
//
// VST1
case ARM::VST1d8:
case ARM::VST1d16:
case ARM::VST1d32:
case ARM::VST1d64:
case ARM::VST1q8:
case ARM::VST1q16:
case ARM::VST1q32:
case ARM::VST1q64:
*AddrIdx = 0;
break;
case ARM::VST1d8wb_fixed:
case ARM::VST1d16wb_fixed:
case ARM::VST1d32wb_fixed:
case ARM::VST1d64wb_fixed:
case ARM::VST1q8wb_fixed:
case ARM::VST1q16wb_fixed:
case ARM::VST1q32wb_fixed:
case ARM::VST1q64wb_fixed:
case ARM::VST1d8wb_register:
case ARM::VST1d16wb_register:
case ARM::VST1d32wb_register:
case ARM::VST1d64wb_register:
case ARM::VST1q8wb_register:
case ARM::VST1q16wb_register:
case ARM::VST1q32wb_register:
case ARM::VST1q64wb_register:
*AddrIdx = 1;
break;
// VST1LN
case ARM::VST1LNd8:
case ARM::VST1LNd16:
case ARM::VST1LNd32:
*AddrIdx = 0;
break;
case ARM::VST1LNd8_UPD:
case ARM::VST1LNd16_UPD:
case ARM::VST1LNd32_UPD:
*AddrIdx = 1;
break;
// VST2
case ARM::VST2d8:
case ARM::VST2d16:
case ARM::VST2d32:
case ARM::VST2q8:
case ARM::VST2q16:
case ARM::VST2q32:
*AddrIdx = 0;
break;
case ARM::VST2d8wb_fixed:
case ARM::VST2d16wb_fixed:
case ARM::VST2d32wb_fixed:
case ARM::VST2q8wb_fixed:
case ARM::VST2q16wb_fixed:
case ARM::VST2q32wb_fixed:
case ARM::VST2d8wb_register:
case ARM::VST2d16wb_register:
case ARM::VST2d32wb_register:
case ARM::VST2q8wb_register:
case ARM::VST2q16wb_register:
case ARM::VST2q32wb_register:
*AddrIdx = 1;
break;
// VST2LN
case ARM::VST2LNd8:
case ARM::VST2LNd16:
case ARM::VST2LNq16:
case ARM::VST2LNd32:
case ARM::VST2LNq32:
*AddrIdx = 0;
break;
case ARM::VST2LNd8_UPD:
case ARM::VST2LNd16_UPD:
case ARM::VST2LNq16_UPD:
case ARM::VST2LNd32_UPD:
case ARM::VST2LNq32_UPD:
*AddrIdx = 1;
break;
// VST3
case ARM::VST3d8:
case ARM::VST3d16:
case ARM::VST3d32:
case ARM::VST3q8:
case ARM::VST3q16:
case ARM::VST3q32:
*AddrIdx = 0;
break;
case ARM::VST3d8_UPD:
case ARM::VST3d16_UPD:
case ARM::VST3d32_UPD:
case ARM::VST3q8_UPD:
case ARM::VST3q16_UPD:
case ARM::VST3q32_UPD:
*AddrIdx = 1;
break;
// VST3LN
case ARM::VST3LNd8:
case ARM::VST3LNd16:
case ARM::VST3LNq16:
case ARM::VST3LNd32:
case ARM::VST3LNq32:
*AddrIdx = 0;
break;
case ARM::VST3LNd8_UPD:
case ARM::VST3LNd16_UPD:
case ARM::VST3LNq16_UPD:
case ARM::VST3LNd32_UPD:
case ARM::VST3LNq32_UPD:
*AddrIdx = 1;
break;
// VST4
case ARM::VST4d8:
case ARM::VST4d16:
case ARM::VST4d32:
case ARM::VST4q8:
case ARM::VST4q16:
case ARM::VST4q32:
*AddrIdx = 0;
break;
case ARM::VST4d8_UPD:
case ARM::VST4d16_UPD:
case ARM::VST4d32_UPD:
case ARM::VST4q8_UPD:
case ARM::VST4q16_UPD:
case ARM::VST4q32_UPD:
*AddrIdx = 1;
break;
// VST4LN
case ARM::VST4LNd8:
case ARM::VST4LNd16:
case ARM::VST4LNq16:
case ARM::VST4LNd32:
case ARM::VST4LNq32:
*AddrIdx = 0;
break;
case ARM::VST4LNd8_UPD:
case ARM::VST4LNd16_UPD:
case ARM::VST4LNq16_UPD:
case ARM::VST4LNd32_UPD:
case ARM::VST4LNq32_UPD:
*AddrIdx = 1;
break;
// Instructions with base address register in position 2...
case ARM::STR_PRE_REG:
case ARM::STR_PRE_IMM:
case ARM::STR_POST_REG:
case ARM::STR_POST_IMM:
case ARM::STRB_PRE_REG:
case ARM::STRB_PRE_IMM:
case ARM::STRB_POST_REG:
case ARM::STRB_POST_IMM:
case ARM::STRH_PRE:
case ARM::STRH_POST:
case ARM::STRD:
case ARM::STREX:
case ARM::STREXB:
case ARM::STREXH:
case ARM::STREXD:
*AddrIdx = 2;
break;
}
if (MI.getOperand(*AddrIdx).getReg() == ARM::SP) {
// The contents of SP do not require masking.
return false;
}
return true;
}
bool ARMNaClRewritePass::SandboxMemoryReferencesInBlock(
MachineBasicBlock &MBB) {
bool Modified = false;
for (MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end();
MBBI != E;
++MBBI) {
MachineInstr &MI = *MBBI;
int AddrIdx;
if (IsDangerousLoad(MI, &AddrIdx)) {
SandboxMemory(MBB, MBBI, MI, AddrIdx, true);
Modified = true;
}
if (IsDangerousStore(MI, &AddrIdx)) {
SandboxMemory(MBB, MBBI, MI, AddrIdx, false);
Modified = true;
}
}
return Modified;
}
/**********************************************************************/
bool ARMNaClRewritePass::runOnMachineFunction(MachineFunction &MF) {
TII = static_cast<const ARMBaseInstrInfo*>(MF.getSubtarget().getInstrInfo());
TRI = MF.getSubtarget().getRegisterInfo();
bool Modified = false;
for (MachineFunction::iterator MFI = MF.begin(), E = MF.end();
MFI != E;
++MFI) {
MachineBasicBlock &MBB = *MFI;
if (MBB.hasAddressTaken()) {
//FIXME: use symbolic constant or get this value from some configuration
MBB.setAlignment(4);
Modified = true;
}
Modified |= SandboxMemoryReferencesInBlock(MBB);
Modified |= SandboxBranchesInBlock(MBB);
Modified |= SandboxStackChangesInBlock(MBB);
}
DEBUG(LightweightVerify(MF));
return Modified;
}
/// createARMNaClRewritePass - returns an instance of the NaClRewritePass.
FunctionPass *llvm::createARMNaClRewritePass() {
return new ARMNaClRewritePass();
}