1// Copyright (c) 2012, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14//     * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30// A minimalistic implementation of getcontext() to be used by
31// Google Breakpad when getcontext() is not available in libc.
32
33#include "common/linux/ucontext_constants.h"
34
35/* int getcontext (ucontext_t *ucp) */
36
37#if defined(__arm__)
38
39  .text
40  .global breakpad_getcontext
41  .hidden breakpad_getcontext
42  .type breakpad_getcontext, #function
43  .align 0
44  .fnstart
45breakpad_getcontext:
46
47  /* First, save r4-r11 */
48  add   r1, r0, #(MCONTEXT_GREGS_OFFSET + 4*4)
49  stm   r1, {r4-r11}
50
51  /* r12 is a scratch register, don't save it */
52
53  /* Save sp and lr explicitly. */
54  /* - sp can't be stored with stmia in Thumb-2 */
55  /* - STM instructions that store sp and pc are deprecated in ARM */
56  str   sp, [r0, #(MCONTEXT_GREGS_OFFSET + 13*4)]
57  str   lr, [r0, #(MCONTEXT_GREGS_OFFSET + 14*4)]
58
59  /* Save the caller's address in 'pc' */
60  str   lr, [r0, #(MCONTEXT_GREGS_OFFSET + 15*4)]
61
62  /* Save ucontext_t* pointer across next call */
63  mov   r4, r0
64
65  /* Call sigprocmask(SIG_BLOCK, NULL, &(ucontext->uc_sigmask)) */
66  mov   r0, #0  /* SIG_BLOCK */
67  mov   r1, #0  /* NULL */
68  add   r2, r4, #UCONTEXT_SIGMASK_OFFSET
69  bl    sigprocmask(PLT)
70
71  /* Intentionally do not save the FPU state here. This is because on
72   * Linux/ARM, one should instead use ptrace(PTRACE_GETFPREGS) or
73   * ptrace(PTRACE_GETVFPREGS) to get it.
74   *
75   * Note that a real implementation of getcontext() would need to save
76   * this here to allow setcontext()/swapcontext() to work correctly.
77   */
78
79  /* Restore the values of r4 and lr */
80  mov   r0, r4
81  ldr   lr, [r0, #(MCONTEXT_GREGS_OFFSET + 14*4)]
82  ldr   r4, [r0, #(MCONTEXT_GREGS_OFFSET +  4*4)]
83
84  /* Return 0 */
85  mov   r0, #0
86  bx    lr
87
88  .fnend
89  .size breakpad_getcontext, . - breakpad_getcontext
90
91#elif defined(__aarch64__)
92
93#define  _NSIG                       64
94#define  __NR_rt_sigprocmask         135
95
96  .text
97  .global breakpad_getcontext
98  .hidden breakpad_getcontext
99  .type breakpad_getcontext, #function
100  .align 4
101  .cfi_startproc
102breakpad_getcontext:
103
104  /* The saved context will return to the getcontext() call point
105     with a return value of 0 */
106  str     xzr,      [x0, MCONTEXT_GREGS_OFFSET +  0 * REGISTER_SIZE]
107
108  stp     x18, x19, [x0, MCONTEXT_GREGS_OFFSET + 18 * REGISTER_SIZE]
109  stp     x20, x21, [x0, MCONTEXT_GREGS_OFFSET + 20 * REGISTER_SIZE]
110  stp     x22, x23, [x0, MCONTEXT_GREGS_OFFSET + 22 * REGISTER_SIZE]
111  stp     x24, x25, [x0, MCONTEXT_GREGS_OFFSET + 24 * REGISTER_SIZE]
112  stp     x26, x27, [x0, MCONTEXT_GREGS_OFFSET + 26 * REGISTER_SIZE]
113  stp     x28, x29, [x0, MCONTEXT_GREGS_OFFSET + 28 * REGISTER_SIZE]
114  str     x30,      [x0, MCONTEXT_GREGS_OFFSET + 30 * REGISTER_SIZE]
115
116  /* Place LR into the saved PC, this will ensure that when
117     switching to this saved context with setcontext() control
118     will pass back to the caller of getcontext(), we have
119     already arranged to return the appropriate return value in x0
120     above.  */
121  str     x30, [x0, MCONTEXT_PC_OFFSET]
122
123  /* Save the current SP */
124  mov     x2, sp
125  str     x2, [x0, MCONTEXT_SP_OFFSET]
126
127  /* Initialize the pstate.  */
128  str     xzr, [x0, MCONTEXT_PSTATE_OFFSET]
129
130  /* Figure out where to place the first context extension
131     block.  */
132  add     x2, x0, #MCONTEXT_EXTENSION_OFFSET
133
134  /* Write the context extension fpsimd header.  */
135  mov     w3, #(FPSIMD_MAGIC & 0xffff)
136  movk    w3, #(FPSIMD_MAGIC >> 16), lsl #16
137  str     w3, [x2, #FPSIMD_CONTEXT_MAGIC_OFFSET]
138  mov     w3, #FPSIMD_CONTEXT_SIZE
139  str     w3, [x2, #FPSIMD_CONTEXT_SIZE_OFFSET]
140
141  /* Fill in the FP SIMD context.  */
142  add     x3, x2, #(FPSIMD_CONTEXT_VREGS_OFFSET + 8 * SIMD_REGISTER_SIZE)
143  stp     d8,  d9, [x3], #(2 * SIMD_REGISTER_SIZE)
144  stp     d10, d11, [x3], #(2 * SIMD_REGISTER_SIZE)
145  stp     d12, d13, [x3], #(2 * SIMD_REGISTER_SIZE)
146  stp     d14, d15, [x3], #(2 * SIMD_REGISTER_SIZE)
147
148  add     x3, x2, FPSIMD_CONTEXT_FPSR_OFFSET
149
150  mrs     x4, fpsr
151  str     w4, [x3]
152
153  mrs     x4, fpcr
154  str     w4, [x3, FPSIMD_CONTEXT_FPCR_OFFSET - FPSIMD_CONTEXT_FPSR_OFFSET]
155
156  /* Write the termination context extension header.  */
157  add     x2, x2, #FPSIMD_CONTEXT_SIZE
158
159  str     xzr, [x2, #FPSIMD_CONTEXT_MAGIC_OFFSET]
160  str     xzr, [x2, #FPSIMD_CONTEXT_SIZE_OFFSET]
161
162  /* Grab the signal mask */
163  /* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
164  add     x2, x0, #UCONTEXT_SIGMASK_OFFSET
165  mov     x0, #0  /* SIG_BLOCK */
166  mov     x1, #0  /* NULL */
167  mov     x3, #(_NSIG / 8)
168  mov     x8, #__NR_rt_sigprocmask
169  svc     0
170
171  /* Return x0 for success */
172  mov     x0, 0
173  ret
174
175  .cfi_endproc
176  .size breakpad_getcontext, . - breakpad_getcontext
177
178#elif defined(__i386__)
179
180  .text
181  .global breakpad_getcontext
182  .hidden breakpad_getcontext
183  .align 4
184  .type breakpad_getcontext, @function
185
186breakpad_getcontext:
187
188  movl 4(%esp), %eax   /* eax = uc */
189
190  /* Save register values */
191  movl %ecx, MCONTEXT_ECX_OFFSET(%eax)
192  movl %edx, MCONTEXT_EDX_OFFSET(%eax)
193  movl %ebx, MCONTEXT_EBX_OFFSET(%eax)
194  movl %edi, MCONTEXT_EDI_OFFSET(%eax)
195  movl %esi, MCONTEXT_ESI_OFFSET(%eax)
196  movl %ebp, MCONTEXT_EBP_OFFSET(%eax)
197
198  movl (%esp), %edx   /* return address */
199  lea  4(%esp), %ecx  /* exclude return address from stack */
200  mov  %edx, MCONTEXT_EIP_OFFSET(%eax)
201  mov  %ecx, MCONTEXT_ESP_OFFSET(%eax)
202
203  xorl %ecx, %ecx
204  movw %fs, %cx
205  mov  %ecx, MCONTEXT_FS_OFFSET(%eax)
206
207  movl $0, MCONTEXT_EAX_OFFSET(%eax)
208
209  /* Save floating point state to fpregstate, then update
210   * the fpregs pointer to point to it */
211  leal UCONTEXT_FPREGS_MEM_OFFSET(%eax), %ecx
212  fnstenv (%ecx)
213  fldenv  (%ecx)
214  mov %ecx, UCONTEXT_FPREGS_OFFSET(%eax)
215
216  /* Save signal mask: sigprocmask(SIGBLOCK, NULL, &uc->uc_sigmask) */
217  leal UCONTEXT_SIGMASK_OFFSET(%eax), %edx
218  xorl %ecx, %ecx
219  push %edx   /* &uc->uc_sigmask */
220  push %ecx   /* NULL */
221  push %ecx   /* SIGBLOCK == 0 on i386 */
222  call sigprocmask@PLT
223  addl $12, %esp
224
225  movl $0, %eax
226  ret
227
228  .size breakpad_getcontext, . - breakpad_getcontext
229
230#elif defined(__mips__)
231
232// This implementation is inspired by implementation of getcontext in glibc.
233#include <asm-mips/asm.h>
234#include <asm-mips/regdef.h>
235#if _MIPS_SIM == _ABIO32
236#include <asm-mips/fpregdef.h>
237#endif
238
239// from asm-mips/asm.h
240#if _MIPS_SIM == _ABIO32
241#define ALSZ 7
242#define ALMASK ~7
243#define SZREG 4
244#else // _MIPS_SIM != _ABIO32
245#define ALSZ 15
246#define ALMASK ~15
247#define SZREG 8
248#endif
249
250#include <asm/unistd.h> // for __NR_rt_sigprocmask
251
252#define _NSIG8 128 / 8
253#define SIG_BLOCK 1
254
255
256  .text
257LOCALS_NUM = 1 // save gp on stack
258FRAME_SIZE = ((LOCALS_NUM * SZREG) + ALSZ) & ALMASK
259
260GP_FRAME_OFFSET = FRAME_SIZE - (1 * SZREG)
261MCONTEXT_REG_SIZE = 8
262
263#if _MIPS_SIM == _ABIO32
264
265NESTED (breakpad_getcontext, FRAME_SIZE, ra)
266  .mask	0x00000000, 0
267  .fmask 0x00000000, 0
268
269  .set noreorder
270  .cpload t9
271  .set reorder
272
273  move a2, sp
274#define _SP a2
275
276  addiu sp, -FRAME_SIZE
277  .cprestore GP_FRAME_OFFSET
278
279  sw s0, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
280  sw s1, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
281  sw s2, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
282  sw s3, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
283  sw s4, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
284  sw s5, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
285  sw s6, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
286  sw s7, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
287  sw _SP, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
288  sw fp, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
289  sw ra, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
290  sw ra, MCONTEXT_PC_OFFSET(a0)
291
292#ifdef __mips_hard_float
293  s.d fs0, (20 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
294  s.d fs1, (22 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
295  s.d fs2, (24 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
296  s.d fs3, (26 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
297  s.d fs4, (28 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
298  s.d fs5, (30 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
299
300  cfc1 v1, fcr31
301  sw v1, MCONTEXT_FPC_CSR(a0)
302#endif  // __mips_hard_float
303
304  /* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
305  li a3, _NSIG8
306  addu a2, a0, UCONTEXT_SIGMASK_OFFSET
307  move a1, zero
308  li a0, SIG_BLOCK
309  li v0, __NR_rt_sigprocmask
310  syscall
311
312  addiu sp, FRAME_SIZE
313  jr ra
314
315END (breakpad_getcontext)
316#else
317
318#ifndef NESTED
319/*
320 * NESTED - declare nested routine entry point
321 */
322#define NESTED(symbol, framesize, rpc)  \
323    .globl  symbol;                     \
324    .align  2;                          \
325    .type symbol,@function;             \
326    .ent  symbol,0;                     \
327symbol:   .frame  sp, framesize, rpc;
328#endif
329
330/*
331 * END - mark end of function
332 */
333#ifndef END
334# define END(function)                  \
335    .end  function;                     \
336    .size function,.-function
337#endif
338
339/* int getcontext (ucontext_t *ucp) */
340
341NESTED (breakpad_getcontext, FRAME_SIZE, ra)
342  .mask   0x10000000, 0
343  .fmask  0x00000000, 0
344
345  move  a2, sp
346#define _SP a2
347  move  a3, gp
348#define _GP a3
349
350  daddiu sp, -FRAME_SIZE
351  .cpsetup $25, GP_FRAME_OFFSET, breakpad_getcontext
352
353  /* Store a magic flag.  */
354  li  v1, 1
355  sd v1, (0 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)  /* zero */
356
357  sd s0, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
358  sd s1, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
359  sd s2, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
360  sd s3, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
361  sd s4, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
362  sd s5, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
363  sd s6, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
364  sd s7, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
365  sd _GP, (28 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
366  sd _SP, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
367  sd s8, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
368  sd ra, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
369  sd ra, MCONTEXT_PC_OFFSET(a0)
370
371#ifdef __mips_hard_float
372  s.d $f24, (24 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
373  s.d $f25, (25 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
374  s.d $f26, (26 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
375  s.d $f27, (27 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
376  s.d $f28, (28 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
377  s.d $f29, (29 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
378  s.d $f30, (30 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
379  s.d $f31, (31 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
380
381  cfc1  v1, $31
382  sw  v1, MCONTEXT_FPC_CSR(a0)
383#endif /* __mips_hard_float */
384
385/* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
386  li  a3, _NSIG8
387  daddu a2, a0, UCONTEXT_SIGMASK_OFFSET
388  move  a1, zero
389  li  a0, SIG_BLOCK
390
391  li  v0, __NR_rt_sigprocmask
392  syscall
393
394  .cpreturn
395  daddiu sp, FRAME_SIZE
396  move  v0, zero
397  jr  ra
398
399END (breakpad_getcontext)
400#endif // _MIPS_SIM == _ABIO32
401
402#elif defined(__x86_64__)
403/* The x64 implementation of breakpad_getcontext was derived in part
404   from the implementation of libunwind which requires the following
405   notice. */
406/* libunwind - a platform-independent unwind library
407   Copyright (C) 2008 Google, Inc
408	Contributed by Paul Pluzhnikov <ppluzhnikov@google.com>
409   Copyright (C) 2010 Konstantin Belousov <kib@freebsd.org>
410
411This file is part of libunwind.
412
413Permission is hereby granted, free of charge, to any person obtaining
414a copy of this software and associated documentation files (the
415"Software"), to deal in the Software without restriction, including
416without limitation the rights to use, copy, modify, merge, publish,
417distribute, sublicense, and/or sell copies of the Software, and to
418permit persons to whom the Software is furnished to do so, subject to
419the following conditions:
420
421The above copyright notice and this permission notice shall be
422included in all copies or substantial portions of the Software.
423
424THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
425EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
426MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
427NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
428LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
429OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
430WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
431
432  .text
433  .global breakpad_getcontext
434  .hidden breakpad_getcontext
435  .align 4
436  .type breakpad_getcontext, @function
437
438breakpad_getcontext:
439  .cfi_startproc
440
441  /* Callee saved: RBX, RBP, R12-R15  */
442  movq %r12, MCONTEXT_GREGS_R12(%rdi)
443  movq %r13, MCONTEXT_GREGS_R13(%rdi)
444  movq %r14, MCONTEXT_GREGS_R14(%rdi)
445  movq %r15, MCONTEXT_GREGS_R15(%rdi)
446  movq %rbp, MCONTEXT_GREGS_RBP(%rdi)
447  movq %rbx, MCONTEXT_GREGS_RBX(%rdi)
448
449  /* Save argument registers (not strictly needed, but setcontext
450     restores them, so don't restore garbage).  */
451  movq %r8,  MCONTEXT_GREGS_R8(%rdi)
452  movq %r9,  MCONTEXT_GREGS_R9(%rdi)
453  movq %rdi, MCONTEXT_GREGS_RDI(%rdi)
454  movq %rsi, MCONTEXT_GREGS_RSI(%rdi)
455  movq %rdx, MCONTEXT_GREGS_RDX(%rdi)
456  movq %rax, MCONTEXT_GREGS_RAX(%rdi)
457  movq %rcx, MCONTEXT_GREGS_RCX(%rdi)
458
459  /* Save fp state (not needed, except for setcontext not
460     restoring garbage).  */
461  leaq MCONTEXT_FPREGS_MEM(%rdi),%r8
462  movq %r8, MCONTEXT_FPREGS_PTR(%rdi)
463  fnstenv (%r8)
464  stmxcsr FPREGS_OFFSET_MXCSR(%r8)
465
466  leaq 8(%rsp), %rax /* exclude this call.  */
467  movq %rax, MCONTEXT_GREGS_RSP(%rdi)
468
469  movq 0(%rsp), %rax
470  movq %rax, MCONTEXT_GREGS_RIP(%rdi)
471
472  /* Save signal mask: sigprocmask(SIGBLOCK, NULL, &uc->uc_sigmask) */
473  leaq UCONTEXT_SIGMASK_OFFSET(%rdi), %rdx  // arg3
474  xorq %rsi, %rsi  // arg2 NULL
475  xorq %rdi, %rdi  // arg1 SIGBLOCK == 0
476  call sigprocmask@PLT
477
478  /* Always return 0 for success, even if sigprocmask failed. */
479  xorl %eax, %eax
480  ret
481  .cfi_endproc
482  .size breakpad_getcontext, . - breakpad_getcontext
483
484#else
485#error "This file has not been ported for your CPU!"
486#endif
487