280 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			280 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- MipsNaClELFStreamer.cpp - ELF Object Output for Mips NaCl ---------===//
 | 
						|
//
 | 
						|
//                     The LLVM Compiler Infrastructure
 | 
						|
//
 | 
						|
// This file is distributed under the University of Illinois Open Source
 | 
						|
// License. See LICENSE.TXT for details.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// This file implements MCELFStreamer for Mips NaCl.  It emits .o object files
 | 
						|
// as required by NaCl's SFI sandbox.  It inserts address-masking instructions
 | 
						|
// before dangerous control-flow and memory access instructions.  It inserts
 | 
						|
// address-masking instructions after instructions that change the stack
 | 
						|
// pointer.  It ensures that the mask and the dangerous instruction are always
 | 
						|
// emitted in the same bundle.  It aligns call + branch delay to the bundle end,
 | 
						|
// so that return address is always aligned to the start of next bundle.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "Mips.h"
 | 
						|
#include "MipsELFStreamer.h"
 | 
						|
#include "MipsMCNaCl.h"
 | 
						|
#include "llvm/MC/MCAsmBackend.h"
 | 
						|
#include "llvm/MC/MCAssembler.h"
 | 
						|
#include "llvm/MC/MCCodeEmitter.h"
 | 
						|
#include "llvm/MC/MCELFStreamer.h"
 | 
						|
#include "llvm/MC/MCInst.h"
 | 
						|
#include "llvm/MC/MCObjectWriter.h"
 | 
						|
#include "llvm/Support/ErrorHandling.h"
 | 
						|
#include <cassert>
 | 
						|
 | 
						|
using namespace llvm;
 | 
						|
 | 
						|
#define DEBUG_TYPE "mips-mc-nacl"
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
const unsigned IndirectBranchMaskReg = Mips::T6;
 | 
						|
const unsigned LoadStoreStackMaskReg = Mips::T7;
 | 
						|
 | 
						|
/// Extend the generic MCELFStreamer class so that it can mask dangerous
 | 
						|
/// instructions.
 | 
						|
 | 
						|
class MipsNaClELFStreamer : public MipsELFStreamer {
 | 
						|
public:
 | 
						|
  MipsNaClELFStreamer(MCContext &Context, std::unique_ptr<MCAsmBackend> TAB,
 | 
						|
                      std::unique_ptr<MCObjectWriter> OW,
 | 
						|
                      std::unique_ptr<MCCodeEmitter> Emitter)
 | 
						|
      : MipsELFStreamer(Context, std::move(TAB), std::move(OW),
 | 
						|
                        std::move(Emitter)) {}
 | 
						|
 | 
						|
  ~MipsNaClELFStreamer() override = default;
 | 
						|
 | 
						|
private:
 | 
						|
  // Whether we started the sandboxing sequence for calls.  Calls are bundled
 | 
						|
  // with branch delays and aligned to the bundle end.
 | 
						|
  bool PendingCall = false;
 | 
						|
 | 
						|
  bool isIndirectJump(const MCInst &MI) {
 | 
						|
    if (MI.getOpcode() == Mips::JALR) {
 | 
						|
      // MIPS32r6/MIPS64r6 doesn't have a JR instruction and uses JALR instead.
 | 
						|
      // JALR is an indirect branch if the link register is $0.
 | 
						|
      assert(MI.getOperand(0).isReg());
 | 
						|
      return MI.getOperand(0).getReg() == Mips::ZERO;
 | 
						|
    }
 | 
						|
    return MI.getOpcode() == Mips::JR;
 | 
						|
  }
 | 
						|
 | 
						|
  bool isStackPointerFirstOperand(const MCInst &MI) {
 | 
						|
    return (MI.getNumOperands() > 0 && MI.getOperand(0).isReg()
 | 
						|
            && MI.getOperand(0).getReg() == Mips::SP);
 | 
						|
  }
 | 
						|
 | 
						|
  bool isCall(const MCInst &MI, bool *IsIndirectCall) {
 | 
						|
    unsigned Opcode = MI.getOpcode();
 | 
						|
 | 
						|
    *IsIndirectCall = false;
 | 
						|
 | 
						|
    switch (Opcode) {
 | 
						|
    default:
 | 
						|
      return false;
 | 
						|
 | 
						|
    case Mips::JAL:
 | 
						|
    case Mips::BAL:
 | 
						|
    case Mips::BAL_BR:
 | 
						|
    case Mips::BLTZAL:
 | 
						|
    case Mips::BGEZAL:
 | 
						|
      return true;
 | 
						|
 | 
						|
    case Mips::JALR:
 | 
						|
      // JALR is only a call if the link register is not $0. Otherwise it's an
 | 
						|
      // indirect branch.
 | 
						|
      assert(MI.getOperand(0).isReg());
 | 
						|
      if (MI.getOperand(0).getReg() == Mips::ZERO)
 | 
						|
        return false;
 | 
						|
 | 
						|
      *IsIndirectCall = true;
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  void emitMask(unsigned AddrReg, unsigned MaskReg,
 | 
						|
                const MCSubtargetInfo &STI) {
 | 
						|
    MCInst MaskInst;
 | 
						|
    MaskInst.setOpcode(Mips::AND);
 | 
						|
    MaskInst.addOperand(MCOperand::createReg(AddrReg));
 | 
						|
    MaskInst.addOperand(MCOperand::createReg(AddrReg));
 | 
						|
    MaskInst.addOperand(MCOperand::createReg(MaskReg));
 | 
						|
    MipsELFStreamer::EmitInstruction(MaskInst, STI);
 | 
						|
  }
 | 
						|
 | 
						|
  // Sandbox indirect branch or return instruction by inserting mask operation
 | 
						|
  // before it.
 | 
						|
  void sandboxIndirectJump(const MCInst &MI, const MCSubtargetInfo &STI) {
 | 
						|
    unsigned AddrReg = MI.getOperand(0).getReg();
 | 
						|
 | 
						|
    EmitBundleLock(false);
 | 
						|
    emitMask(AddrReg, IndirectBranchMaskReg, STI);
 | 
						|
    MipsELFStreamer::EmitInstruction(MI, STI);
 | 
						|
    EmitBundleUnlock();
 | 
						|
  }
 | 
						|
 | 
						|
  // Sandbox memory access or SP change.  Insert mask operation before and/or
 | 
						|
  // after the instruction.
 | 
						|
  void sandboxLoadStoreStackChange(const MCInst &MI, unsigned AddrIdx,
 | 
						|
                                   const MCSubtargetInfo &STI, bool MaskBefore,
 | 
						|
                                   bool MaskAfter) {
 | 
						|
    EmitBundleLock(false);
 | 
						|
    if (MaskBefore) {
 | 
						|
      // Sandbox memory access.
 | 
						|
      unsigned BaseReg = MI.getOperand(AddrIdx).getReg();
 | 
						|
      emitMask(BaseReg, LoadStoreStackMaskReg, STI);
 | 
						|
    }
 | 
						|
    MipsELFStreamer::EmitInstruction(MI, STI);
 | 
						|
    if (MaskAfter) {
 | 
						|
      // Sandbox SP change.
 | 
						|
      unsigned SPReg = MI.getOperand(0).getReg();
 | 
						|
      assert((Mips::SP == SPReg) && "Unexpected stack-pointer register.");
 | 
						|
      emitMask(SPReg, LoadStoreStackMaskReg, STI);
 | 
						|
    }
 | 
						|
    EmitBundleUnlock();
 | 
						|
  }
 | 
						|
 | 
						|
public:
 | 
						|
  /// This function is the one used to emit instruction data into the ELF
 | 
						|
  /// streamer.  We override it to mask dangerous instructions.
 | 
						|
  void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI,
 | 
						|
                       bool) override {
 | 
						|
    // Sandbox indirect jumps.
 | 
						|
    if (isIndirectJump(Inst)) {
 | 
						|
      if (PendingCall)
 | 
						|
        report_fatal_error("Dangerous instruction in branch delay slot!");
 | 
						|
      sandboxIndirectJump(Inst, STI);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // Sandbox loads, stores and SP changes.
 | 
						|
    unsigned AddrIdx;
 | 
						|
    bool IsStore;
 | 
						|
    bool IsMemAccess = isBasePlusOffsetMemoryAccess(Inst.getOpcode(), &AddrIdx,
 | 
						|
                                                    &IsStore);
 | 
						|
    bool IsSPFirstOperand = isStackPointerFirstOperand(Inst);
 | 
						|
    if (IsMemAccess || IsSPFirstOperand) {
 | 
						|
      bool MaskBefore = (IsMemAccess
 | 
						|
                         && baseRegNeedsLoadStoreMask(Inst.getOperand(AddrIdx)
 | 
						|
                                                          .getReg()));
 | 
						|
      bool MaskAfter = IsSPFirstOperand && !IsStore;
 | 
						|
      if (MaskBefore || MaskAfter) {
 | 
						|
        if (PendingCall)
 | 
						|
          report_fatal_error("Dangerous instruction in branch delay slot!");
 | 
						|
        sandboxLoadStoreStackChange(Inst, AddrIdx, STI, MaskBefore, MaskAfter);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      // fallthrough
 | 
						|
    }
 | 
						|
 | 
						|
    // Sandbox calls by aligning call and branch delay to the bundle end.
 | 
						|
    // For indirect calls, emit the mask before the call.
 | 
						|
    bool IsIndirectCall;
 | 
						|
    if (isCall(Inst, &IsIndirectCall)) {
 | 
						|
      if (PendingCall)
 | 
						|
        report_fatal_error("Dangerous instruction in branch delay slot!");
 | 
						|
 | 
						|
      // Start the sandboxing sequence by emitting call.
 | 
						|
      EmitBundleLock(true);
 | 
						|
      if (IsIndirectCall) {
 | 
						|
        unsigned TargetReg = Inst.getOperand(1).getReg();
 | 
						|
        emitMask(TargetReg, IndirectBranchMaskReg, STI);
 | 
						|
      }
 | 
						|
      MipsELFStreamer::EmitInstruction(Inst, STI);
 | 
						|
      PendingCall = true;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (PendingCall) {
 | 
						|
      // Finish the sandboxing sequence by emitting branch delay.
 | 
						|
      MipsELFStreamer::EmitInstruction(Inst, STI);
 | 
						|
      EmitBundleUnlock();
 | 
						|
      PendingCall = false;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // None of the sandboxing applies, just emit the instruction.
 | 
						|
    MipsELFStreamer::EmitInstruction(Inst, STI);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
} // end anonymous namespace
 | 
						|
 | 
						|
namespace llvm {
 | 
						|
 | 
						|
bool isBasePlusOffsetMemoryAccess(unsigned Opcode, unsigned *AddrIdx,
 | 
						|
                                  bool *IsStore) {
 | 
						|
  if (IsStore)
 | 
						|
    *IsStore = false;
 | 
						|
 | 
						|
  switch (Opcode) {
 | 
						|
  default:
 | 
						|
    return false;
 | 
						|
 | 
						|
  // Load instructions with base address register in position 1.
 | 
						|
  case Mips::LB:
 | 
						|
  case Mips::LBu:
 | 
						|
  case Mips::LH:
 | 
						|
  case Mips::LHu:
 | 
						|
  case Mips::LW:
 | 
						|
  case Mips::LWC1:
 | 
						|
  case Mips::LDC1:
 | 
						|
  case Mips::LL:
 | 
						|
  case Mips::LL_R6:
 | 
						|
  case Mips::LWL:
 | 
						|
  case Mips::LWR:
 | 
						|
    *AddrIdx = 1;
 | 
						|
    return true;
 | 
						|
 | 
						|
  // Store instructions with base address register in position 1.
 | 
						|
  case Mips::SB:
 | 
						|
  case Mips::SH:
 | 
						|
  case Mips::SW:
 | 
						|
  case Mips::SWC1:
 | 
						|
  case Mips::SDC1:
 | 
						|
  case Mips::SWL:
 | 
						|
  case Mips::SWR:
 | 
						|
    *AddrIdx = 1;
 | 
						|
    if (IsStore)
 | 
						|
      *IsStore = true;
 | 
						|
    return true;
 | 
						|
 | 
						|
  // Store instructions with base address register in position 2.
 | 
						|
  case Mips::SC:
 | 
						|
  case Mips::SC_R6:
 | 
						|
    *AddrIdx = 2;
 | 
						|
    if (IsStore)
 | 
						|
      *IsStore = true;
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool baseRegNeedsLoadStoreMask(unsigned Reg) {
 | 
						|
  // The contents of SP and thread pointer register do not require masking.
 | 
						|
  return Reg != Mips::SP && Reg != Mips::T8;
 | 
						|
}
 | 
						|
 | 
						|
MCELFStreamer *createMipsNaClELFStreamer(MCContext &Context,
 | 
						|
                                         std::unique_ptr<MCAsmBackend> TAB,
 | 
						|
                                         std::unique_ptr<MCObjectWriter> OW,
 | 
						|
                                         std::unique_ptr<MCCodeEmitter> Emitter,
 | 
						|
                                         bool RelaxAll) {
 | 
						|
  MipsNaClELFStreamer *S = new MipsNaClELFStreamer(
 | 
						|
      Context, std::move(TAB), std::move(OW), std::move(Emitter));
 | 
						|
  if (RelaxAll)
 | 
						|
    S->getAssembler().setRelaxAll(true);
 | 
						|
 | 
						|
  // Set bundle-alignment as required by the NaCl ABI for the target.
 | 
						|
  S->EmitBundleAlignMode(MIPS_NACL_BUNDLE_ALIGN);
 | 
						|
 | 
						|
  return S;
 | 
						|
}
 | 
						|
 | 
						|
} // end namespace llvm
 |