| //===-- 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(MIPS_NACL_BUNDLE_ALIGN); | |
| return S; | |
| } | |
| } // end namespace llvm |