1 /****************************************************************************
2  *                                                                          *
3  *                         GNAT COMPILER COMPONENTS                         *
4  *                                                                          *
5  *                             S I G T R A M P                              *
6  *                                                                          *
7  *                         Asm Implementation File                          *
8  *                                                                          *
9  *           Copyright (C) 2015-2019, Free Software Foundation, Inc.        *
10  *                                                                          *
11  * GNAT is free software;  you can  redistribute it  and/or modify it under *
12  * terms of the  GNU General Public License as published  by the Free Soft- *
13  * ware  Foundation;  either version 3,  or (at your option) any later ver- *
14  * sion.  GNAT is distributed in the hope that it will be useful, but WITH- *
15  * OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY *
16  * or FITNESS FOR A PARTICULAR PURPOSE.                                     *
17  *                                                                          *
18  * As a special exception under Section 7 of GPL version 3, you are granted *
19  * additional permissions described in the GCC Runtime Library Exception,   *
20  * version 3.1, as published by the Free Software Foundation.               *
21  *                                                                          *
22  * In particular,  you can freely  distribute your programs  built with the *
23  * GNAT Pro compiler, including any required library run-time units,  using *
24  * any licensing terms  of your choosing.  See the AdaCore Software License *
25  * for full details.                                                        *
26  *                                                                          *
27  * GNAT was originally developed  by the GNAT team at  New York University. *
28  * Extensive contributions were provided by Ada Core Technologies Inc.      *
29  *                                                                          *
30  ****************************************************************************/
31 
32 /****************************************************
33  * ARM64/IOS version of the __gnat_sigtramp service *
34  ****************************************************/
35 
36 #include <sys/ucontext.h>
37 
38 #include "sigtramp.h"
39 /* See sigtramp.h for a general explanation of functionality.  */
40 
41 /* ----------------------
42    -- General comments --
43    ----------------------
44 
45    Unfortunately the libunwind library used on this platform comes with severe
46    limitations that make the implementation convoluted:
47 
48      1. At each step, the stack pointer register SP is restored with the CFA.
49 	This makes it impossible to set the CFA to an arbitrary value, for
50 	example to the address of the context saved on the stack, which means
51 	that the simple CFI directives cannot be used for the registers.
52 
53      2. For the ARM64 architecture (and only it), DWARF expressions are not
54 	supported to compute the CFA.  Only DW_CFA_def_cfa is supported, which
55 	means that the CFA (modulo offset) must be loaded into a register.
56 
57      3. The return column cannot be changed (30 for the ARM64 architecture).
58 	Since column 30 is that of the LR register, this makes it impossible
59 	to restore both the LR register and the PC.
60 
61    Therefore we need 2 distinct call-saved registers in the trampoline and
62    we resort to manual encoding of CFI byte sequences.  */
63 
64 /* -----------------------------------------
65    -- Protypes for our internal asm stubs --
66    -----------------------------------------
67 
68    Even though our symbols will remain local, the prototype claims "extern"
69    and not "static" to prevent compiler complaints about a symbol used but
70    never defined.  */
71 
72 /* sigtramp stub providing unwind info for common registers.  */
73 
74 extern void __gnat_sigtramp_common
75   (int signo, void *siginfo, void *sigcontext,
76    __sigtramphandler_t * handler);
77 
78 void __gnat_sigtramp (int signo, void *si, void *ucontext,
79                       __sigtramphandler_t * handler)
80      __attribute__((optimize(2)));
81 
__gnat_sigtramp(int signo,void * si,void * ucontext,__sigtramphandler_t * handler)82 void __gnat_sigtramp (int signo, void *si, void *ucontext,
83                       __sigtramphandler_t * handler)
84 {
85   mcontext_t mcontext = ((ucontext_t *) ucontext)->uc_mcontext;
86 
87   __gnat_sigtramp_common (signo, si, mcontext, handler);
88 }
89 
90 /* asm string construction helpers.  */
91 
92 #define STR(TEXT) #TEXT
93 /* stringify expanded TEXT, surrounding it with double quotes.  */
94 
95 #define S(E) STR(E)
96 /* stringify E, which will resolve as text but may contain macros
97    still to be expanded.  */
98 
99 /* asm (TEXT) outputs <tab>TEXT. These facilitate the output of
100    multiline contents:  */
101 #define TAB(S) "\t" S
102 #define CR(S)  S "\n"
103 
104 #undef TCR
105 #define TCR(S) TAB(CR(S))
106 
107 /* Offset in uc_mcontext of the __ss structure containing the registers.  */
108 #define UC_MCONTEXT_SS 16
109 
110 #define CFA_REG  19
111 #define BASE_REG 20
112 
113 #define DW_CFA_def_cfa    0x0c
114 #define DW_CFA_expression 0x10
115 
116 #define DW_OP_breg(n)     0x70+(n)
117 
118 #define REG_REGNO_GR(n)   n
119 #define REG_REGNO_PC      30
120 
121 /* The first byte of the SLEB128 value of the offset.  */
122 #define REG_OFFSET_GR(n)         (UC_MCONTEXT_SS + n * 8)
123 #define REG_OFFSET_LONG_GR(n)    (UC_MCONTEXT_SS + n * 8 + 128)
124 #define REG_OFFSET_LONG128_GR(n) (UC_MCONTEXT_SS + (n - 16) * 8 + 128)
125 #define REG_OFFSET_LONG256_GR(n) (UC_MCONTEXT_SS + (n - 32) * 8 + 128)
126 
127 #define REG_OFFSET_LONG256_PC    REG_OFFSET_LONG256_GR(32)
128 
129 #define CFI_DEF_CFA \
130   TCR(".cfi_def_cfa " S(CFA_REG) ", 0")
131 
132 /* We need 4 variants depending on the offset: 0+, 64+, 128+, 256+.  */
133 #define COMMON_CFI(REG) \
134   ".cfi_escape " S(DW_CFA_expression) "," S(REG_REGNO_##REG) ",2," \
135   S(DW_OP_breg(BASE_REG)) "," S(REG_OFFSET_##REG)
136 
137 #define COMMON_LONG_CFI(REG) \
138   ".cfi_escape " S(DW_CFA_expression) "," S(REG_REGNO_##REG) ",3," \
139   S(DW_OP_breg(BASE_REG)) "," S(REG_OFFSET_LONG_##REG) ",0"
140 
141 #define COMMON_LONG128_CFI(REG) \
142   ".cfi_escape " S(DW_CFA_expression) "," S(REG_REGNO_##REG) ",3," \
143   S(DW_OP_breg(BASE_REG)) "," S(REG_OFFSET_LONG128_##REG) ",1"
144 
145 #define COMMON_LONG256_CFI(REG) \
146   ".cfi_escape " S(DW_CFA_expression) "," S(REG_REGNO_##REG) ",3," \
147   S(DW_OP_breg(BASE_REG)) "," S(REG_OFFSET_LONG256_##REG) ",2"
148 
149 #define CFI_COMMON_REGS \
150   CR("# CFI for common registers\n") \
151   TCR(COMMON_CFI(GR(0)))  \
152   TCR(COMMON_CFI(GR(1)))  \
153   TCR(COMMON_CFI(GR(2)))  \
154   TCR(COMMON_CFI(GR(3)))  \
155   TCR(COMMON_CFI(GR(4)))  \
156   TCR(COMMON_CFI(GR(5)))  \
157   TCR(COMMON_LONG_CFI(GR(6)))  \
158   TCR(COMMON_LONG_CFI(GR(7)))  \
159   TCR(COMMON_LONG_CFI(GR(8)))  \
160   TCR(COMMON_LONG_CFI(GR(9)))  \
161   TCR(COMMON_LONG_CFI(GR(10))) \
162   TCR(COMMON_LONG_CFI(GR(11))) \
163   TCR(COMMON_LONG_CFI(GR(12))) \
164   TCR(COMMON_LONG_CFI(GR(13))) \
165   TCR(COMMON_LONG128_CFI(GR(14))) \
166   TCR(COMMON_LONG128_CFI(GR(15))) \
167   TCR(COMMON_LONG128_CFI(GR(16))) \
168   TCR(COMMON_LONG128_CFI(GR(17))) \
169   TCR(COMMON_LONG128_CFI(GR(18))) \
170   TCR(COMMON_LONG128_CFI(GR(19))) \
171   TCR(COMMON_LONG128_CFI(GR(20))) \
172   TCR(COMMON_LONG128_CFI(GR(21))) \
173   TCR(COMMON_LONG128_CFI(GR(22))) \
174   TCR(COMMON_LONG128_CFI(GR(23))) \
175   TCR(COMMON_LONG128_CFI(GR(24))) \
176   TCR(COMMON_LONG128_CFI(GR(25))) \
177   TCR(COMMON_LONG128_CFI(GR(26))) \
178   TCR(COMMON_LONG128_CFI(GR(27))) \
179   TCR(COMMON_LONG128_CFI(GR(28))) \
180   TCR(COMMON_LONG128_CFI(GR(29))) \
181   TCR(COMMON_LONG256_CFI(PC))
182 
183 /* Trampoline body block
184    ---------------------  */
185 
186 #define SIGTRAMP_BODY \
187   TCR("stp fp, lr, [sp, #-32]!") \
188   TCR("stp x" S(CFA_REG) ", x" S(BASE_REG) ", [sp, #16]") \
189   TCR("mov fp, sp") \
190   TCR("# Load the saved value of the stack pointer as CFA") \
191   TCR("ldr x" S(CFA_REG) ", [x2, #" S(REG_OFFSET_GR(31)) "]") \
192   TCR("# Use x" S(BASE_REG) " as base register for the CFI") \
193   TCR("mov x" S(BASE_REG) ", x2") \
194   TCR("# Call the handler") \
195   TCR("blr x3") \
196   TCR("# Release our frame and return (should never get here!).") \
197   TCR("ldp x" S(CFA_REG) ", x" S(BASE_REG)" , [sp, #16]") \
198   TCR("ldp fp, lr, [sp], 32") \
199   TCR("ret")
200 
201 /* -----------------------------
202    -- Symbol definition block --
203    ----------------------------- */
204 
205 #define SIGTRAMP_START(SYM) \
206   CR("# " S(SYM) " signal trampoline") \
207   CR(S(SYM) ":") \
208   TCR(".cfi_startproc") \
209   TCR(".cfi_signal_frame")
210 
211 /* ------------------------------
212    -- Symbol termination block --
213    ------------------------------ */
214 
215 #define SIGTRAMP_END(SYM) \
216   TCR(".cfi_endproc")
217 
218 /*----------------------------
219   -- And now, the real code --
220   ---------------------------- */
221 
222 asm(".text\n"
223     TCR(".align 2"));
224 
225 /* sigtramp stub for common registers.  */
226 
227 #define TRAMP_COMMON ___gnat_sigtramp_common
228 
229 asm (SIGTRAMP_START(TRAMP_COMMON));
230 asm (CFI_DEF_CFA);
231 asm (CFI_COMMON_REGS);
232 asm (SIGTRAMP_BODY);
233 asm (SIGTRAMP_END(TRAMP_COMMON));
234