diff options
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp')
| -rw-r--r-- | contrib/llvm-project/llvm/lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp | 278 | 
1 files changed, 278 insertions, 0 deletions
diff --git a/contrib/llvm-project/llvm/lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp b/contrib/llvm-project/llvm/lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp new file mode 100644 index 000000000000..eade2d9bd745 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/Target/Mips/MCTargetDesc/MipsNaClELFStreamer.cpp @@ -0,0 +1,278 @@ +//===-- MipsNaClELFStreamer.cpp - ELF Object Output for Mips NaCl ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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) 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 = 0; +    bool IsStore = false; +    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(Log2(MIPS_NACL_BUNDLE_ALIGN)); + +  return S; +} + +} // end namespace llvm  | 
