aboutsummaryrefslogtreecommitdiff
path: root/lib/Target/ARM/MCTargetDesc/ARMMCNaCl.cpp
blob: cd25c865d9c923b1d6137abb7cfd8224d5ed2ddb (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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
//=== ARMMCNaCl.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 "arm-mc-nacl"

#include "MCTargetDesc/ARMBaseInfo.h"
#include "MCTargetDesc/ARMMCExpr.h"
#include "MCTargetDesc/ARMMCTargetDesc.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"

using namespace llvm;

namespace llvm {
  cl::opt<bool> FlagSfiZeroMask("sfi-zero-mask");
}

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

static void EmitBICMask(MCStreamer &Out,
                        unsigned Addr, int64_t  Pred, unsigned Mask) {
  // bic\Pred \Addr, \Addr, #Mask
  MCInst BICInst;
  BICInst.setOpcode(ARM::BICri);
  BICInst.addOperand(MCOperand::CreateReg(Addr)); // rD
  BICInst.addOperand(MCOperand::CreateReg(Addr)); // rS
  if (FlagSfiZeroMask) {
    BICInst.addOperand(MCOperand::CreateImm(0)); // imm
  } else {
    BICInst.addOperand(MCOperand::CreateImm(Mask)); // imm
  }
  BICInst.addOperand(MCOperand::CreateImm(Pred));  // predicate
  BICInst.addOperand(MCOperand::CreateReg(ARM::CPSR)); // CPSR
  BICInst.addOperand(MCOperand::CreateReg(0)); // flag out
  Out.EmitInstruction(BICInst);
}

static void EmitTST(MCStreamer &Out, unsigned Reg) {
  // tst \reg, #\MASK typically 0xc0000000
  const unsigned Mask = 0xC0000000;
  MCInst TSTInst;
  TSTInst.setOpcode(ARM::TSTri);
  TSTInst.addOperand(MCOperand::CreateReg(Reg));  // rS
  if (FlagSfiZeroMask) {
    TSTInst.addOperand(MCOperand::CreateImm(0)); // imm
  } else {
    TSTInst.addOperand(MCOperand::CreateImm(Mask)); // imm
  }
  TSTInst.addOperand(MCOperand::CreateImm((int64_t)ARMCC::AL)); // Always
  TSTInst.addOperand(MCOperand::CreateImm(0)); // flag out
  Out.EmitInstruction(TSTInst);
}


// 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 &&
         (ARM::SFI_NOP_IF_AT_BUNDLE_END == Saved[0].getOpcode()) &&
         (ARM::SFI_DATA_MASK == Saved[2].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering");

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

  Out.EmitBundleLock(false);
  Out.EmitInstruction(Saved[1]);
  EmitBICMask(Out, Addr, Pred, 0xC0000000);
  Out.EmitBundleUnlock();
}

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

static void EmitIndirectGuardCall(int I, MCInst Saved[],
                                  MCStreamer &Out) {
  // sfi_indirect_call_preamble link cond=
  //   sfi_nops_to_force_slot2
  //   sfi_code_mask \link \cond
  assert(I == 2 && (ARM::SFI_GUARD_INDIRECT_CALL == Saved[0].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering SFI_GUARD_CALL");
  unsigned Reg = Saved[0].getOperand(0).getReg();
  int64_t Pred = Saved[0].getOperand(2).getImm();
  Out.EmitBundleLock(true);
  EmitBICMask(Out, Reg, Pred, 0xC000000F);
  Out.EmitInstruction(Saved[1]);
  Out.EmitBundleUnlock();
}

static void EmitIndirectGuardJmp(int I, MCInst Saved[], MCStreamer &Out) {
  //  sfi_indirect_jump_preamble link cond=
  //   sfi_nop_if_at_bundle_end
  //   sfi_code_mask \link \cond
  assert(I == 2 && (ARM::SFI_GUARD_INDIRECT_JMP == Saved[0].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering SFI_GUARD_CALL");
  unsigned Reg = Saved[0].getOperand(0).getReg();
  int64_t Pred = Saved[0].getOperand(2).getImm();

  Out.EmitBundleLock(false);
  EmitBICMask(Out, Reg, Pred, 0xC000000F);
  Out.EmitInstruction(Saved[1]);
  Out.EmitBundleUnlock();
}

static void EmitGuardReturn(int I, MCInst Saved[], MCStreamer &Out) {
  // sfi_return_preamble reg cond=
  //    sfi_nop_if_at_bundle_end
  //    sfi_code_mask \reg \cond
  assert(I == 2 && (ARM::SFI_GUARD_RETURN == Saved[0].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering SFI_GUARD_RETURN");
  int64_t Pred = Saved[0].getOperand(0).getImm();

  Out.EmitBundleLock(false);
  EmitBICMask(Out, ARM::LR, Pred, 0xC000000F);
  Out.EmitInstruction(Saved[1]);
  Out.EmitBundleUnlock();
}

static void EmitGuardLoadOrStore(int I, MCInst Saved[], MCStreamer &Out) {
  // sfi_store_preamble reg cond ---->
  //    sfi_nop_if_at_bundle_end
  //    sfi_data_mask \reg, \cond
  assert(I == 2 && (ARM::SFI_GUARD_LOADSTORE == Saved[0].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering SFI_GUARD_RETURN");
  unsigned Reg = Saved[0].getOperand(0).getReg();
  int64_t Pred = Saved[0].getOperand(2).getImm();

  Out.EmitBundleLock(false);
  EmitBICMask(Out, Reg, Pred, 0xC0000000);
  Out.EmitInstruction(Saved[1]);
  Out.EmitBundleUnlock();
}

static void EmitGuardLoadOrStoreTst(int I, MCInst Saved[], MCStreamer &Out) {
  // sfi_cstore_preamble reg -->
  //   sfi_nop_if_at_bundle_end
  //   sfi_data_tst \reg
  assert(I == 2 && (ARM::SFI_GUARD_LOADSTORE_TST == Saved[0].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering");
  unsigned Reg = Saved[0].getOperand(0).getReg();

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

// This is ONLY used for loads into the stack pointer.
static void EmitGuardSpLoad(int I, MCInst Saved[], MCStreamer &Out) {
  assert(I == 4 &&
         (ARM::SFI_GUARD_SP_LOAD == Saved[0].getOpcode()) &&
         (ARM::SFI_NOP_IF_AT_BUNDLE_END == Saved[1].getOpcode()) &&
         (ARM::SFI_DATA_MASK == Saved[3].getOpcode()) &&
         "Unexpected SFI Pseudo while lowering");

  unsigned AddrReg = Saved[0].getOperand(0).getReg();
  unsigned SpReg = Saved[3].getOperand(0).getReg();
  int64_t  Pred = Saved[3].getOperand(2).getImm();
  assert((ARM::SP == SpReg) && "Unexpected register at stack guard");

  Out.EmitBundleLock(false);
  EmitBICMask(Out, AddrReg, Pred, 0xC0000000);
  Out.EmitInstruction(Saved[2]);
  EmitBICMask(Out, SpReg, Pred, 0xC0000000);
  Out.EmitBundleUnlock();
}

namespace llvm {

// CustomExpandInstNaClARM -
//   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.
bool CustomExpandInstNaClARM(const MCInst &Inst, MCStreamer &Out) {
  // 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
  // instructions.
  //
  // When a SFI pseudo is detected, it is saved. Then, the saved SFI
  // pseudo and the very next instructions (their amount depending on the kind
  // of the SFI pseudo) are used as arguments to the Emit*() functions in
  // this file.
  //
  // Some static data is used to preserve state accross calls (TODO: can this
  // be lifted into a proper state object?)
  //
  // Saved:      the saved instructions (starting with the SFI_ pseudo).
  // SavedCount: the amount of saved instructions required for the SFI pseudo
  //             that's being expanded.
  // I:          the index of the currently saved instruction - used to track
  //             where in Saved to insert the instruction and how many more
  //             remain.
  //
  const int MaxSaved = 4;
  static MCInst Saved[MaxSaved];
  static int SaveCount  = 0;
  static int I = 0;

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

  // Protect against recursive execution. If RecurseGuard == true, it means
  // we're already in the process of expanding a custom instruction, and we
  // don't need to run recursively on anything generated by such an expansion.
  static bool RecurseGuard = false;
  if (RecurseGuard)
    return false;

  DEBUG(dbgs()