summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/test/win64UnwindInfoTests.asm
blob: 4dd5ce76461483b67eb5423e13c067b29cf270a2 (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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at http://mozilla.org/MPL/2.0/.

; Comments indicate stack memory layout during execution.
; For example at the top of a function, where RIP just points to the return
; address, the stack looks like
; rip = [ra]
; And after pushing rax to the stack,
; rip = [rax][ra]
; And then, after allocating 20h bytes on the stack,
; rip = [..20..][rax][ra]
; And then, after pushing a function pointer,
; rip = [pfn][..20..][rax][ra]

include ksamd64.inc

.code

; It helps to add padding between functions so they're not right up against
; each other. Adds clarity to debugging, and gives a bit of leeway when
; searching for symbols (e.g. a function whose last instruction is CALL
; would push a return address that's in the next function.)
PaddingBetweenFunctions macro
  repeat 10h
     int 3
  endm
endm

DoCrash macro
  mov rax, 7
  mov byte ptr [rax], 9
endm

PaddingBetweenFunctions

; There is no rip addressing mode in x64. The only way to get the value
; of rip is to call a function, and pop it from the stack.
WhoCalledMe proc
  pop rax ; rax is now ra
  push rax ; Restore ra so this function can return.
  sub rax, 5 ; Correct for the size of the call instruction
  ret
WhoCalledMe endp

PaddingBetweenFunctions

; Any function that we expect to test against on the stack, we'll need its
; real address. If we use function pointers in C, we'll get the address to jump
; table entries. This bit of code at the beginning of each function will
; return the real address we'd expect to see in stack traces.
;
; rcx (1st arg) = mode
; rax (return)  = address of either NO_MANS_LAND or this function.
;
; When mode is 0, we place the address of NO_MANS_LAND in RAX, for the function
; to use as it wants. This is just for convenience because almost all functions
; here need this address at some point.
;
; When mode is 1, the address of this function is returned.
TestHeader macro
  call WhoCalledMe
  test rcx, rcx
  je continue_test
  ret
continue_test:
  inc rcx
  call x64CrashCFITest_NO_MANS_LAND
  xor rcx, rcx
endm

; The point of this is to add a stack frame to test against.
; void* x64CrashCFITest_Launcher(int getAddress, void* pTestFn)
x64CrashCFITest_Launcher proc frame
  TestHeader

  .endprolog
  call rdx
  ret
x64CrashCFITest_Launcher endp

PaddingBetweenFunctions

; void* x64CrashCFITest_NO_MANS_LAND(uint64_t mode);
; Not meant to be called. Only when mode = 1 in order to return its address.
; Place this function's address on the stack so the stack scanning algorithm
; thinks this is a return address, and places it on the stack trace.
x64CrashCFITest_NO_MANS_LAND proc frame
  TestHeader
  .endprolog
  ret
x64CrashCFITest_NO_MANS_LAND endp

PaddingBetweenFunctions

; Test that we:
; - handle unknown opcodes gracefully
; - fall back to other stack unwind strategies if CFI doesn't work
;
; In order to properly unwind this frame, we'd need to fully support
; SET_FPREG with offsets, plus restoring registers via PUSH_NONVOL.
; To do this, sprinkle the stack with bad return addresses
; and stack pointers.
x64CrashCFITest_UnknownOpcode proc frame
  TestHeader

  push rax
  .allocstack 8

  push rbp
  .pushreg rbp

  push rax
  push rsp
  push rax
  push rsp
  .allocstack 20h
  ; rsp = [rsp][pfn][rsp][pfn][rbp][pfn][ra]

  lea rbp, [rsp+10h]
  .setframe rbp, 10h
  ; rsp = [rsp][pfn] [rsp][pfn][rbp][pfn][ra]
  ; rbp =           ^

  .endprolog

  ; Now modify RSP so measuring stack size from unwind ops will not help
  ; finding the return address.
  push rax
  push rsp
  ; rsp = [rsp][pfn][rsp][pfn] [rsp][pfn][rbp][pfn][ra]

  DoCrash

x64CrashCFITest_UnknownOpcode endp

PaddingBetweenFunctions

; void* x64CrashCFITest_PUSH_NONVOL(uint64_t mode);
;
; Test correct handling of PUSH_NONVOL unwind code.
;
x64CrashCFITest_PUSH_NONVOL proc frame
  TestHeader

  push r10
  .pushreg r10
  push r15
  .pushreg r15
  push rbx
  .pushreg rbx
  push rsi
  .pushreg rsi
  push rbp
  .pushreg rbp
  ; rsp = [rbp][rsi][rbx][r15][r10][ra]

  push rax
  .allocstack 8
  ; rsp = [pfn][rbp][rsi][rbx][r15][r10][ra]

  .endprolog

  DoCrash

x64CrashCFITest_PUSH_NONVOL endp

PaddingBetweenFunctions

; void* x64CrashCFITest_ALLOC_SMALL(uint64_t mode);
;
; Small allocations are between 8bytes and 512kb-8bytes
;
x64CrashCFITest_ALLOC_SMALL proc frame
  TestHeader

  push rax
  push rax
  push rax
  push rax
  .allocstack 20h
  ; rsp = [pfn][pfn][pfn][pfn][ra]

  .endprolog

  DoCrash

x64CrashCFITest_ALLOC_SMALL endp

PaddingBetweenFunctions

; void* x64CrashCFITest_ALLOC_LARGE(uint64_t mode);
;
; Allocations between 512kb and 4gb
; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack
; space for this.
x64CrashCFITest_ALLOC_LARGE proc frame
  TestHeader

  sub rsp, 0a000h
  .allocstack 0a000h
  ; rsp = [..640kb..][ra]

  mov qword ptr [rsp], rax
  ; rsp = [pfn][..640kb-8..][ra]

  .endprolog

  DoCrash

x64CrashCFITest_ALLOC_LARGE endp

PaddingBetweenFunctions

; void* x64CrashCFITest_SAVE_NONVOL(uint64_t mode);
;
; Test correct handling of SAVE_NONVOL unwind code.
;
x64CrashCFITest_SAVE_NONVOL proc frame
  TestHeader

  sub rsp, 30h
  .allocstack 30h
  ; rsp = [..30..][ra]

  mov qword ptr [rsp+28h], r10
  .savereg r10, 28h
  mov qword ptr [rsp+20h], rbp
  .savereg rbp, 20h
  mov qword ptr [rsp+18h], rsi
  .savereg rsi, 18h
  mov qword ptr [rsp+10h], rbx
  .savereg rbx, 10h
  mov qword ptr [rsp+8], r15
  .savereg r15, 8
  ; rsp = [r15][rbx][rsi][rbp][r10][ra]

  mov qword ptr [rsp], rax

  ; rsp = [pfn][r15][rbx][rsi][rbp][r10][ra]

  .endprolog

  DoCrash

x64CrashCFITest_SAVE_NONVOL endp

PaddingBetweenFunctions

; void* x64CrashCFITest_SAVE_NONVOL_FAR(uint64_t mode);
;
; Similar to the test above but adding 640kb to most offsets.
; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack
; space for this.
x64CrashCFITest_SAVE_NONVOL_FAR proc frame
  TestHeader

  sub rsp, 0a0030h
  .allocstack 0a0030h
  ; rsp = [..640k..][..30..][ra]

  mov qword ptr [rsp+28h+0a0000h], r10
  .savereg r10, 28h+0a0000h
  mov qword ptr [rsp+20h+0a0000h], rbp
  .savereg rbp, 20h+0a0000h
  mov qword ptr [rsp+18h+0a0000h], rsi
  .savereg rsi, 18h+0a0000h
  mov qword ptr [rsp+10h+0a0000h], rbx
  .savereg rbx, 10h+0a0000h
  mov qword ptr [rsp+8+0a0000h], r15
  .savereg r15, 8+0a0000h
  ; rsp = [..640k..][..8..][r15][rbx][rsi][rbp][r10][ra]

  mov qword ptr [rsp], rax

  ; rsp = [pfn][..640k..][r15][rbx][rsi][rbp][r10][ra]

  .endprolog

  DoCrash

x64CrashCFITest_SAVE_NONVOL_FAR endp

PaddingBetweenFunctions

; void* x64CrashCFITest_SAVE_XMM128(uint64_t mode);
;
; Test correct handling of SAVE_XMM128 unwind code.
x64CrashCFITest_SAVE_XMM128 proc frame
  TestHeader

  sub rsp, 30h
  .allocstack 30h
  ; rsp = [..30..][ra]

  movdqu [rsp+20h], xmm6
  .savexmm128 xmm6, 20h
  ; rsp = [..20..][xmm6][ra]

  movdqu [rsp+10h], xmm15
  .savexmm128 xmm15, 10h
  ; rsp = [..10..][xmm15][xmm6][ra]

  mov qword ptr [rsp], rax
  ; rsp = [pfn][..8..][xmm15][xmm6][ra]

  .endprolog

  DoCrash

x64CrashCFITest_SAVE_XMM128 endp

PaddingBetweenFunctions

; void* x64CrashCFITest_SAVE_XMM128(uint64_t mode);
;
; Similar to the test above but adding 640kb to most offsets.
; Note: ReserveStackSpace() in nsTestCrasher.cpp pre-allocates stack
; space for this.
x64CrashCFITest_SAVE_XMM128_FAR proc frame
  TestHeader

  sub rsp, 0a0030h
  .allocstack 0a0030h
  ; rsp = [..640kb..][..30..][ra]

  movdqu [rsp+20h+0a0000h], xmm6
  .savexmm128 xmm6, 20h+0a0000h
  ; rsp = [..640kb..][..20..][xmm6][ra]

  movdqu [rsp+10h+0a0000h], xmm6
  .savexmm128 xmm15, 10h+0a0000h
  ; rsp = [..640kb..][..10..][xmm15][xmm6][ra]

  mov qword ptr [rsp], rax
  ; rsp = [pfn][..640kb..][..8..][xmm15][xmm6][ra]

  .endprolog

  DoCrash

x64CrashCFITest_SAVE_XMM128_FAR endp

PaddingBetweenFunctions

; void* x64CrashCFITest_EPILOG(uint64_t mode);
;
; The epilog unwind op will also set the unwind version to 2.
; Test that we don't choke on UWOP_EPILOG or version 2 unwind info.
x64CrashCFITest_EPILOG proc frame
  TestHeader

  push rax
  .allocstack 8
  ; rsp = [pfn][ra]

  .endprolog

  DoCrash

  .beginepilog

  ret

x64CrashCFITest_EPILOG endp

PaddingBetweenFunctions

; Having an EOF symbol at the end of this file contains symbolication to this
; file. So addresses beyond this file don't get mistakenly symbolicated as a
; meaningful function name.
x64CrashCFITest_EOF proc frame
  TestHeader
  .endprolog
  ret
x64CrashCFITest_EOF endp

end