aboutsummaryrefslogtreecommitdiff
path: root/lib/Target/Mips/MCTargetDesc/MipsMCNaCl.cpp
blob: fe0cabc92359120ff83fdf27d4e58ad35dda2045 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//=== MipsMCNaCl.cpp -  Expansion of NaCl pseudo-instructions    --*- C++ -*-=//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "mips-mc-nacl"

#include "MCTargetDesc/MipsBaseInfo.h"
#include "MCTargetDesc/MipsMCTargetDesc.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"

using namespace llvm;

/// Two helper functions for emitting the actual guard instructions

static void EmitMask(MCStreamer &Out,
                        unsigned Addr, unsigned Mask) {
  // and \Addr, \Addr, \Mask
  MCInst MaskInst;
  MaskInst.setOpcode(Mips::AND);
  MaskInst.addOperand(MCOperand::CreateReg(Addr));
  MaskInst.addOperand(MCOperand::CreateReg(Addr));
  MaskInst.addOperand(MCOperand::CreateReg(Mask));
  Out.EmitInstruction(MaskInst);
}

// This is ONLY used for sandboxing stack changes.
// The reason why SFI_NOP_IF_AT_BUNDLE_END gets handled here is that
// it must ensure that the two instructions are in the same bundle.
// It just so happens that the SFI_NOP_IF_AT_BUNDLE_END is always
// emitted in conjunction with a SFI_DATA_MASK
//
static void EmitDataMask(int I, MCInst Saved[], MCStreamer &Out) {
  assert(I == 3 &&
         (Mips::SFI_NOP_IF_AT_BUNDLE_END == Saved[0].getOpcode()) &&
         (Mips::SFI_DATA_MASK == Saved[2].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering");

  unsigned Addr = Saved[2].getOperand(0).getReg();
  unsigned Mask = Saved[2].getOperand(2).getReg();
  assert((Mips::SP == Addr) && "Unexpected register at stack guard");

  Out.EmitBundleLock(false);
  Out.EmitInstruction(Saved[1]);
  EmitMask(Out, Addr, Mask);
  Out.EmitBundleUnlock();
}

static void EmitDirectGuardCall(int I, MCInst Saved[],
                                MCStreamer &Out) {
  // sfi_call_preamble --->
  //   sfi_nops_to_force_slot2
  assert(I == 3 && (Mips::SFI_GUARD_CALL == Saved[0].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering SFI_GUARD_CALL");
  Out.EmitBundleLock(true);
  Out.EmitInstruction(Saved[1]);
  Out.EmitInstruction(Saved[2]);
  Out.EmitBundleUnlock();
}

static void EmitIndirectGuardCall(int I, MCInst Saved[],
                                  MCStreamer &Out) {
  // sfi_indirect_call_preamble link --->
  //   sfi_nops_to_force_slot1
  //   sfi_code_mask \link \link \maskreg
  assert(I == 3 && (Mips::SFI_GUARD_INDIRECT_CALL == Saved[0].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering SFI_GUARD_INDIRECT_CALL");

  unsigned Addr = Saved[0].getOperand(0).getReg();
  unsigned Mask = Saved[0].getOperand(2).getReg();

  Out.EmitBundleLock(true);
  EmitMask(Out, Addr, Mask);
  Out.EmitInstruction(Saved[1]);
  Out.EmitInstruction(Saved[2]);
  Out.EmitBundleUnlock();
}

static void EmitIndirectGuardJmp(int I, MCInst Saved[], MCStreamer &Out) {
  //  sfi_indirect_jump_preamble link --->
  //    sfi_nop_if_at_bundle_end
  //    sfi_code_mask \link \link \maskreg
  assert(I == 2 && (Mips::SFI_GUARD_INDIRECT_JMP == Saved[0].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering SFI_GUARD_INDIRECT_JMP");
  unsigned Addr = Saved[0].getOperand(0).getReg();
  unsigned Mask = Saved[0].getOperand(2).getReg();

  Out.EmitBundleLock(false);
  EmitMask(Out, Addr, Mask);
  Out.EmitInstruction(Saved[1]);
  Out.EmitBundleUnlock();
}

static void EmitGuardReturn(int I, MCInst Saved[], MCStreamer &Out) {
  // sfi_return_preamble reg --->
  //    sfi_nop_if_at_bundle_end
  //    sfi_code_mask \reg \reg \maskreg
  assert(I == 2 && (Mips::SFI_GUARD_RETURN == Saved[0].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering SFI_GUARD_RETURN");
  unsigned Reg = Saved[0].getOperand(0).getReg();
  unsigned Mask = Saved[0].getOperand(2).getReg();

  Out.EmitBundleLock(false);
  EmitMask(Out, Reg, Mask);
  Out.EmitInstruction(Saved[1]);
  Out.EmitBundleUnlock();
}

static void EmitGuardLoadOrStore(int I, MCInst Saved[], MCStreamer &Out) {
  // sfi_load_store_preamble reg --->
  //    sfi_nop_if_at_bundle_end
  //    sfi_data_mask \reg \reg \maskreg
  assert(I == 2 && (Mips::SFI_GUARD_LOADSTORE == Saved[0].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering SFI_GUARD_LOADSTORE");
  unsigned Reg = Saved[0].getOperand(0).getReg();
  unsigned Mask = Saved[0].getOperand(2).getReg();

  Out.EmitBundleLock(false);
  EmitMask(Out, Reg, Mask);
  Out.EmitInstruction(Saved[1]);
  Out.EmitBundleUnlock();
}

namespace llvm {
// CustomExpandInstNaClMips -
//   If Inst is a NaCl pseudo instruction, emits the substitute
//   expansion to the MCStreamer and returns true.
//   Otherwise, returns false.
//
//   NOTE: Each time this function calls Out.EmitInstruction(), it will be
//   called again recursively to rewrite the new instruction being emitted.
//   Care must be taken to ensure that this does not result in an infinite
//   loop. Also, global state must be managed carefully so that it is
//   consistent during recursive calls.
//
//   We need global state to keep track of the explicit prefix (PREFIX_*)
//   instructions. Unfortunately, the assembly parser prefers to generate
//   these instead of combined instructions. At this time, having only
//   one explicit prefix is supported.


bool CustomExpandInstNaClMips(const MCInst &Inst, MCStreamer &Out) {
  const int MaxSaved = 4;
  static MCInst Saved[MaxSaved];
  static int SaveCount  = 0;
  static int I = 0;
  // This routine only executes  if RecurseGuard == 0
  static bool RecurseGuard = false;

  // If we are emitting to .s, just emit all pseudo-instructions directly.
  if (Out.hasRawTextSupport()) {
    return false;
  }

  //No recursive calls allowed;
  if (RecurseGuard) return false;

  unsigned Opc = Inst.getOpcode();

  DEBUG(dbgs() << "CustomExpandInstNaClMips("; Inst.dump(); dbgs() << ")\n");

  // Note: SFI_NOP_IF_AT_BUNDLE_END is only emitted directly as part of
  // a stack guard in conjunction with a SFI_DATA_MASK

  // Logic:
  // This is somewhat convoluted, but in the current model, the SFI
  // guard pseudo instructions occur PRIOR to the actual instruction.
  // So, the bundling/alignment operation has to refer to the FOLLOWING
  // one or two instructions.
  //
  // When a SFI_* pseudo is detected, it is saved. Then, the saved SFI_*
  // pseudo and the very next one or two instructions are used as arguments to
  // the Emit*() functions in this file.  This is the reason why we have a
  // doublely nested switch here.  First, to save the SFI_* pseudo, then to
  // emit it and the next instruction

  // By default, we only need to save two or three instructions

  if ((I == 0) && (SaveCount == 0)) {
    // Base State, no saved instructions.
    // If the current instruction is a SFI instruction, set the SaveCount
    // and fall through.
    switch (Opc) {
    default:
      SaveCount = 0; // Nothing to do.
      return false;  // Handle this Inst elsewhere.
    case Mips::SFI_NOP_IF_AT_BUNDLE_END:
    case Mips::SFI_GUARD_CALL:
    case Mips::SFI_GUARD_INDIRECT_CALL:
      SaveCount = 3;
      break;
    case Mips::SFI_DATA_MASK:
      SaveCount = 0; // Do nothing.
      break;
    case Mips::SFI_GUARD_INDIRECT_JMP:
    case Mips::SFI_GUARD_RETURN:
    case Mips::SFI_GUARD_LOADSTORE:
      SaveCount = 2;
      break;
    }
  }

  if (I < SaveCount) {
    // Othewise, save the current Inst and return
    Saved[I++] = Inst;
    if (I < SaveCount)
      return true;
    // Else fall through to next stat
  }

  if (SaveCount > 0) {
    assert(I == SaveCount && "Bookeeping Error");
    SaveCount = 0; // Reset for next iteration
    // The following calls may call Out.EmitInstruction()
    // which must not again call CustomExpandInst ...
    // So set RecurseGuard = 1;
    RecurseGuard = true;

    switch (Saved[0].getOpcode()) {
    default:  /* No action required */      break;
    case Mips::SFI_NOP_IF_AT_BUNDLE_END:
      EmitDataMask(I, Saved, Out);
      break;
    case Mips::SFI_DATA_MASK:
      assert(0 && "Unexpected NOP_IF_AT_BUNDLE_END as a Saved Inst");
      break;
    case Mips::SFI_GUARD_CALL:
      EmitDirectGuardCall(I, Saved, Out);
      break;
    case Mips::SFI_GUARD_INDIRECT_CALL:
      EmitIndirectGuardCall(I, Saved, Out);
      break;
    case Mips::SFI_GUARD_INDIRECT_JMP:
      EmitIndirectGuardJmp(I, Saved, Out);
      break;
    case Mips::SFI_GUARD_RETURN:
      EmitGuardReturn(I, Saved, Out);
      break;
    case Mips::SFI_GUARD_LOADSTORE:
      EmitGuardLoadOrStore(I, Saved, Out);
      break;
    }
    I = 0; // Reset I for next.
    assert(RecurseGuard && "Illegal Depth");
    RecurseGuard = false;
    return true;
  }
  return false;
}

} // namespace llvm