• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
25 using namespace llvm;
26 
27 #define DEBUG_TYPE "mips-mc-nacl"
28 
29 namespace {
30 
31 const unsigned IndirectBranchMaskReg = Mips::T6;
32 const unsigned LoadStoreStackMaskReg = Mips::T7;
33 
34 /// Extend the generic MCELFStreamer class so that it can mask dangerous
35 /// instructions.
36 
37 class MipsNaClELFStreamer : public MipsELFStreamer {
38 public:
MipsNaClELFStreamer(MCContext & Context,MCAsmBackend & TAB,raw_ostream & OS,MCCodeEmitter * Emitter,const MCSubtargetInfo & STI)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 
~MipsNaClELFStreamer()43   ~MipsNaClELFStreamer() {}
44 
45 private:
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 
isIndirectJump(const MCInst & MI)50   bool isIndirectJump(const MCInst &MI) {
51     if (MI.getOpcode() == Mips::JALR) {
52       // MIPS32r6/MIPS64r6 doesn't have a JR instruction and uses JALR instead.
53       // JALR is an indirect branch if the link register is $0.
54       assert(MI.getOperand(0).isReg());
55       return MI.getOperand(0).getReg() == Mips::ZERO;
56     }
57     return MI.getOpcode() == Mips::JR;
58   }
59 
isStackPointerFirstOperand(const MCInst & MI)60   bool isStackPointerFirstOperand(const MCInst &MI) {
61     return (MI.getNumOperands() > 0 && MI.getOperand(0).isReg()
62             && MI.getOperand(0).getReg() == Mips::SP);
63   }
64 
isCall(const MCInst & MI,bool * IsIndirectCall)65   bool isCall(const MCInst &MI, bool *IsIndirectCall) {
66     unsigned Opcode = MI.getOpcode();
67 
68     *IsIndirectCall = false;
69 
70     switch (Opcode) {
71     default:
72       return false;
73 
74     case Mips::JAL:
75     case Mips::BAL:
76     case Mips::BAL_BR:
77     case Mips::BLTZAL:
78     case Mips::BGEZAL:
79       return true;
80 
81     case Mips::JALR:
82       // JALR is only a call if the link register is not $0. Otherwise it's an
83       // indirect branch.
84       assert(MI.getOperand(0).isReg());
85       if (MI.getOperand(0).getReg() == Mips::ZERO)
86         return false;
87 
88       *IsIndirectCall = true;
89       return true;
90     }
91   }
92 
emitMask(unsigned AddrReg,unsigned MaskReg,const MCSubtargetInfo & STI)93   void emitMask(unsigned AddrReg, unsigned MaskReg,
94                 const MCSubtargetInfo &STI) {
95     MCInst MaskInst;
96     MaskInst.setOpcode(Mips::AND);
97     MaskInst.addOperand(MCOperand::CreateReg(AddrReg));
98     MaskInst.addOperand(MCOperand::CreateReg(AddrReg));
99     MaskInst.addOperand(MCOperand::CreateReg(MaskReg));
100     MipsELFStreamer::EmitInstruction(MaskInst, STI);
101   }
102 
103   // Sandbox indirect branch or return instruction by inserting mask operation
104   // before it.
sandboxIndirectJump(const MCInst & MI,const MCSubtargetInfo & STI)105   void sandboxIndirectJump(const MCInst &MI, const MCSubtargetInfo &STI) {
106     unsigned AddrReg = MI.getOperand(0).getReg();
107 
108     EmitBundleLock(false);
109     emitMask(AddrReg, IndirectBranchMaskReg, STI);
110     MipsELFStreamer::EmitInstruction(MI, STI);
111     EmitBundleUnlock();
112   }
113 
114   // Sandbox memory access or SP change.  Insert mask operation before and/or
115   // after the instruction.
sandboxLoadStoreStackChange(const MCInst & MI,unsigned AddrIdx,const MCSubtargetInfo & STI,bool MaskBefore,bool MaskAfter)116   void sandboxLoadStoreStackChange(const MCInst &MI, unsigned AddrIdx,
117                                    const MCSubtargetInfo &STI, bool MaskBefore,
118                                    bool MaskAfter) {
119     EmitBundleLock(false);
120     if (MaskBefore) {
121       // Sandbox memory access.
122       unsigned BaseReg = MI.getOperand(AddrIdx).getReg();
123       emitMask(BaseReg, LoadStoreStackMaskReg, STI);
124     }
125     MipsELFStreamer::EmitInstruction(MI, STI);
126     if (MaskAfter) {
127       // Sandbox SP change.
128       unsigned SPReg = MI.getOperand(0).getReg();
129       assert((Mips::SP == SPReg) && "Unexpected stack-pointer register.");
130       emitMask(SPReg, LoadStoreStackMaskReg, STI);
131     }
132     EmitBundleUnlock();
133   }
134 
135 public:
136   /// This function is the one used to emit instruction data into the ELF
137   /// streamer.  We override it to mask dangerous instructions.
EmitInstruction(const MCInst & Inst,const MCSubtargetInfo & STI)138   void EmitInstruction(const MCInst &Inst,
139                        const MCSubtargetInfo &STI) override {
140     // Sandbox indirect jumps.
141     if (isIndirectJump(Inst)) {
142       if (PendingCall)
143         report_fatal_error("Dangerous instruction in branch delay slot!");
144       sandboxIndirectJump(Inst, STI);
145       return;
146     }
147 
148     // Sandbox loads, stores and SP changes.
149     unsigned AddrIdx;
150     bool IsStore;
151     bool IsMemAccess = isBasePlusOffsetMemoryAccess(Inst.getOpcode(), &AddrIdx,
152                                                     &IsStore);
153     bool IsSPFirstOperand = isStackPointerFirstOperand(Inst);
154     if (IsMemAccess || IsSPFirstOperand) {
155       bool MaskBefore = (IsMemAccess
156                          && baseRegNeedsLoadStoreMask(Inst.getOperand(AddrIdx)
157                                                           .getReg()));
158       bool MaskAfter = IsSPFirstOperand && !IsStore;
159       if (MaskBefore || MaskAfter) {
160         if (PendingCall)
161           report_fatal_error("Dangerous instruction in branch delay slot!");
162         sandboxLoadStoreStackChange(Inst, AddrIdx, STI, MaskBefore, MaskAfter);
163         return;
164       }
165       // fallthrough
166     }
167 
168     // Sandbox calls by aligning call and branch delay to the bundle end.
169     // For indirect calls, emit the mask before the call.
170     bool IsIndirectCall;
171     if (isCall(Inst, &IsIndirectCall)) {
172       if (PendingCall)
173         report_fatal_error("Dangerous instruction in branch delay slot!");
174 
175       // Start the sandboxing sequence by emitting call.
176       EmitBundleLock(true);
177       if (IsIndirectCall) {
178         unsigned TargetReg = Inst.getOperand(1).getReg();
179         emitMask(TargetReg, IndirectBranchMaskReg, STI);
180       }
181       MipsELFStreamer::EmitInstruction(Inst, STI);
182       PendingCall = true;
183       return;
184     }
185     if (PendingCall) {
186       // Finish the sandboxing sequence by emitting branch delay.
187       MipsELFStreamer::EmitInstruction(Inst, STI);
188       EmitBundleUnlock();
189       PendingCall = false;
190       return;
191     }
192 
193     // None of the sandboxing applies, just emit the instruction.
194     MipsELFStreamer::EmitInstruction(Inst, STI);
195   }
196 };
197 
198 } // end anonymous namespace
199 
200 namespace llvm {
201 
isBasePlusOffsetMemoryAccess(unsigned Opcode,unsigned * AddrIdx,bool * IsStore)202 bool isBasePlusOffsetMemoryAccess(unsigned Opcode, unsigned *AddrIdx,
203                                   bool *IsStore) {
204   if (IsStore)
205     *IsStore = false;
206 
207   switch (Opcode) {
208   default:
209     return false;
210 
211   // Load instructions with base address register in position 1.
212   case Mips::LB:
213   case Mips::LBu:
214   case Mips::LH:
215   case Mips::LHu:
216   case Mips::LW:
217   case Mips::LWC1:
218   case Mips::LDC1:
219   case Mips::LL:
220   case Mips::LL_R6:
221   case Mips::LWL:
222   case Mips::LWR:
223     *AddrIdx = 1;
224     return true;
225 
226   // Store instructions with base address register in position 1.
227   case Mips::SB:
228   case Mips::SH:
229   case Mips::SW:
230   case Mips::SWC1:
231   case Mips::SDC1:
232   case Mips::SWL:
233   case Mips::SWR:
234     *AddrIdx = 1;
235     if (IsStore)
236       *IsStore = true;
237     return true;
238 
239   // Store instructions with base address register in position 2.
240   case Mips::SC:
241   case Mips::SC_R6:
242     *AddrIdx = 2;
243     if (IsStore)
244       *IsStore = true;
245     return true;
246   }
247 }
248 
baseRegNeedsLoadStoreMask(unsigned Reg)249 bool baseRegNeedsLoadStoreMask(unsigned Reg) {
250   // The contents of SP and thread pointer register do not require masking.
251   return Reg != Mips::SP && Reg != Mips::T8;
252 }
253 
createMipsNaClELFStreamer(MCContext & Context,MCAsmBackend & TAB,raw_ostream & OS,MCCodeEmitter * Emitter,const MCSubtargetInfo & STI,bool RelaxAll,bool NoExecStack)254 MCELFStreamer *createMipsNaClELFStreamer(MCContext &Context, MCAsmBackend &TAB,
255                                          raw_ostream &OS,
256                                          MCCodeEmitter *Emitter,
257                                          const MCSubtargetInfo &STI,
258                                          bool RelaxAll, bool NoExecStack) {
259   MipsNaClELFStreamer *S = new MipsNaClELFStreamer(Context, TAB, OS, Emitter,
260                                                    STI);
261   if (RelaxAll)
262     S->getAssembler().setRelaxAll(true);
263   if (NoExecStack)
264     S->getAssembler().setNoExecStack(true);
265 
266   // Set bundle-alignment as required by the NaCl ABI for the target.
267   S->EmitBundleAlignMode(MIPS_NACL_BUNDLE_ALIGN);
268 
269   return S;
270 }
271 
272 }
273