MipsNaClELFStreamer.cpp revision dce4a407a24b04eebc6a376f8e62b41aaa7b071f
1//===-- MipsNaClELFStreamer.cpp - ELF Object Output for Mips NaCl ---------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements MCELFStreamer for Mips NaCl.  It emits .o object files
11// as required by NaCl's SFI sandbox.  It inserts address-masking instructions
12// before dangerous control-flow and memory access instructions.  It inserts
13// address-masking instructions after instructions that change the stack
14// pointer.  It ensures that the mask and the dangerous instruction are always
15// emitted in the same bundle.  It aligns call + branch delay to the bundle end,
16// so that return address is always aligned to the start of next bundle.
17//
18//===----------------------------------------------------------------------===//
19
20#include "Mips.h"
21#include "MipsELFStreamer.h"
22#include "MipsMCNaCl.h"
23#include "llvm/MC/MCELFStreamer.h"
24
25using namespace llvm;
26
27#define DEBUG_TYPE "mips-mc-nacl"
28
29namespace {
30
31const unsigned IndirectBranchMaskReg = Mips::T6;
32const unsigned LoadStoreStackMaskReg = Mips::T7;
33
34/// Extend the generic MCELFStreamer class so that it can mask dangerous
35/// instructions.
36
37class MipsNaClELFStreamer : public MipsELFStreamer {
38public:
39  MipsNaClELFStreamer(MCContext &Context, MCAsmBackend &TAB, raw_ostream &OS,
40                      MCCodeEmitter *Emitter, const MCSubtargetInfo &STI)
41    : MipsELFStreamer(Context, TAB, OS, Emitter, STI), PendingCall(false) {}
42
43  ~MipsNaClELFStreamer() {}
44
45private:
46  // Whether we started the sandboxing sequence for calls.  Calls are bundled
47  // with branch delays and aligned to the bundle end.
48  bool PendingCall;
49
50  bool isIndirectJump(const MCInst &MI) {
51    return MI.getOpcode() == Mips::JR || MI.getOpcode() == Mips::RET;
52  }
53
54  bool isStackPointerFirstOperand(const MCInst &MI) {
55    return (MI.getNumOperands() > 0 && MI.getOperand(0).isReg()
56            && MI.getOperand(0).getReg() == Mips::SP);
57  }
58
59  bool isCall(unsigned Opcode, bool *IsIndirectCall) {
60    *IsIndirectCall = false;
61
62    switch (Opcode) {
63    default:
64      return false;
65
66    case Mips::JAL:
67    case Mips::BAL_BR:
68    case Mips::BLTZAL:
69    case Mips::BGEZAL:
70      return true;
71
72    case Mips::JALR:
73      *IsIndirectCall = true;
74      return true;
75    }
76  }
77
78  void emitMask(unsigned AddrReg, unsigned MaskReg,
79                const MCSubtargetInfo &STI) {
80    MCInst MaskInst;
81    MaskInst.setOpcode(Mips::AND);
82    MaskInst.addOperand(MCOperand::CreateReg(AddrReg));
83    MaskInst.addOperand(MCOperand::CreateReg(AddrReg));
84    MaskInst.addOperand(MCOperand::CreateReg(MaskReg));
85    MipsELFStreamer::EmitInstruction(MaskInst, STI);
86  }
87
88  // Sandbox indirect branch or return instruction by inserting mask operation
89  // before it.
90  void sandboxIndirectJump(const MCInst &MI, const MCSubtargetInfo &STI) {
91    unsigned AddrReg = MI.getOperand(0).getReg();
92
93    EmitBundleLock(false);
94    emitMask(AddrReg, IndirectBranchMaskReg, STI);
95    MipsELFStreamer::EmitInstruction(MI, STI);
96    EmitBundleUnlock();
97  }
98
99  // Sandbox memory access or SP change.  Insert mask operation before and/or
100  // after the instruction.
101  void sandboxLoadStoreStackChange(const MCInst &MI, unsigned AddrIdx,
102                                   const MCSubtargetInfo &STI, bool MaskBefore,
103                                   bool MaskAfter) {
104    EmitBundleLock(false);
105    if (MaskBefore) {
106      // Sandbox memory access.
107      unsigned BaseReg = MI.getOperand(AddrIdx).getReg();
108      emitMask(BaseReg, LoadStoreStackMaskReg, STI);
109    }
110    MipsELFStreamer::EmitInstruction(MI, STI);
111    if (MaskAfter) {
112      // Sandbox SP change.
113      unsigned SPReg = MI.getOperand(0).getReg();
114      assert((Mips::SP == SPReg) && "Unexpected stack-pointer register.");
115      emitMask(SPReg, LoadStoreStackMaskReg, STI);
116    }
117    EmitBundleUnlock();
118  }
119
120public:
121  /// This function is the one used to emit instruction data into the ELF
122  /// streamer.  We override it to mask dangerous instructions.
123  void EmitInstruction(const MCInst &Inst,
124                       const MCSubtargetInfo &STI) override {
125    // Sandbox indirect jumps.
126    if (isIndirectJump(Inst)) {
127      if (PendingCall)
128        report_fatal_error("Dangerous instruction in branch delay slot!");
129      sandboxIndirectJump(Inst, STI);
130      return;
131    }
132
133    // Sandbox loads, stores and SP changes.
134    unsigned AddrIdx;
135    bool IsStore;
136    bool IsMemAccess = isBasePlusOffsetMemoryAccess(Inst.getOpcode(), &AddrIdx,
137                                                    &IsStore);
138    bool IsSPFirstOperand = isStackPointerFirstOperand(Inst);
139    if (IsMemAccess || IsSPFirstOperand) {
140      if (PendingCall)
141        report_fatal_error("Dangerous instruction in branch delay slot!");
142
143      bool MaskBefore = (IsMemAccess
144                         && baseRegNeedsLoadStoreMask(Inst.getOperand(AddrIdx)
145                                                          .getReg()));
146      bool MaskAfter = IsSPFirstOperand && !IsStore;
147      if (MaskBefore || MaskAfter)
148        sandboxLoadStoreStackChange(Inst, AddrIdx, STI, MaskBefore, MaskAfter);
149      else
150        MipsELFStreamer::EmitInstruction(Inst, STI);
151      return;
152    }
153
154    // Sandbox calls by aligning call and branch delay to the bundle end.
155    // For indirect calls, emit the mask before the call.
156    bool IsIndirectCall;
157    if (isCall(Inst.getOpcode(), &IsIndirectCall)) {
158      if (PendingCall)
159        report_fatal_error("Dangerous instruction in branch delay slot!");
160
161      // Start the sandboxing sequence by emitting call.
162      EmitBundleLock(true);
163      if (IsIndirectCall) {
164        unsigned TargetReg = Inst.getOperand(1).getReg();
165        emitMask(TargetReg, IndirectBranchMaskReg, STI);
166      }
167      MipsELFStreamer::EmitInstruction(Inst, STI);
168      PendingCall = true;
169      return;
170    }
171    if (PendingCall) {
172      // Finish the sandboxing sequence by emitting branch delay.
173      MipsELFStreamer::EmitInstruction(Inst, STI);
174      EmitBundleUnlock();
175      PendingCall = false;
176      return;
177    }
178
179    // None of the sandboxing applies, just emit the instruction.
180    MipsELFStreamer::EmitInstruction(Inst, STI);
181  }
182};
183
184} // end anonymous namespace
185
186namespace llvm {
187
188bool isBasePlusOffsetMemoryAccess(unsigned Opcode, unsigned *AddrIdx,
189                                  bool *IsStore) {
190  if (IsStore)
191    *IsStore = false;
192
193  switch (Opcode) {
194  default:
195    return false;
196
197  // Load instructions with base address register in position 1.
198  case Mips::LB:
199  case Mips::LBu:
200  case Mips::LH:
201  case Mips::LHu:
202  case Mips::LW:
203  case Mips::LWC1:
204  case Mips::LDC1:
205  case Mips::LL:
206  case Mips::LWL:
207  case Mips::LWR:
208    *AddrIdx = 1;
209    return true;
210
211  // Store instructions with base address register in position 1.
212  case Mips::SB:
213  case Mips::SH:
214  case Mips::SW:
215  case Mips::SWC1:
216  case Mips::SDC1:
217  case Mips::SWL:
218  case Mips::SWR:
219    *AddrIdx = 1;
220    if (IsStore)
221      *IsStore = true;
222    return true;
223
224  // Store instructions with base address register in position 2.
225  case Mips::SC:
226    *AddrIdx = 2;
227    if (IsStore)
228      *IsStore = true;
229    return true;
230  }
231}
232
233bool baseRegNeedsLoadStoreMask(unsigned Reg) {
234  // The contents of SP and thread pointer register do not require masking.
235  return Reg != Mips::SP && Reg != Mips::T8;
236}
237
238MCELFStreamer *createMipsNaClELFStreamer(MCContext &Context, MCAsmBackend &TAB,
239                                         raw_ostream &OS,
240                                         MCCodeEmitter *Emitter,
241                                         const MCSubtargetInfo &STI,
242                                         bool RelaxAll, bool NoExecStack) {
243  MipsNaClELFStreamer *S = new MipsNaClELFStreamer(Context, TAB, OS, Emitter,
244                                                   STI);
245  if (RelaxAll)
246    S->getAssembler().setRelaxAll(true);
247  if (NoExecStack)
248    S->getAssembler().setNoExecStack(true);
249
250  // Set bundle-alignment as required by the NaCl ABI for the target.
251  S->EmitBundleAlignMode(MIPS_NACL_BUNDLE_ALIGN);
252
253  return S;
254}
255
256}
257