/src/binutils-gdb/gas/gen-sframe.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* gen-sframe.c - Support for generating SFrame section. |
2 | | Copyright (C) 2022-2025 Free Software Foundation, Inc. |
3 | | |
4 | | This file is part of GAS, the GNU Assembler. |
5 | | |
6 | | GAS is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3, or (at your option) |
9 | | any later version. |
10 | | |
11 | | GAS is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with GAS; see the file COPYING. If not, write to the Free |
18 | | Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
19 | | 02110-1301, USA. */ |
20 | | |
21 | | #include "as.h" |
22 | | #include "subsegs.h" |
23 | | #include "sframe.h" |
24 | | #include "gen-sframe.h" |
25 | | #include "dw2gencfi.h" |
26 | | |
27 | | #ifdef support_sframe_p |
28 | | |
29 | | #ifndef sizeof_member |
30 | 6 | # define sizeof_member(type, member) (sizeof (((type *)0)->member)) |
31 | | #endif |
32 | | |
33 | | /* SFrame FRE type selection optimization is an optimization for size. |
34 | | |
35 | | There are three flavors of SFrame FRE representation in the binary format: |
36 | | - sframe_frame_row_entry_addr1 where the FRE start address is 1 byte. |
37 | | - sframe_frame_row_entry_addr2 where the FRE start address is 2 bytes. |
38 | | - sframe_frame_row_entry_addr4 where the FRE start address is 4 bytes. |
39 | | |
40 | | Note that in the SFrame format, all SFrame FREs of a function use one |
41 | | single representation. The SFrame FRE type itself is identified via the |
42 | | information in the SFrame FDE function info. |
43 | | |
44 | | Now, to select the minimum required one from the list above, one needs to |
45 | | make a decision based on the size (in bytes) of the function. |
46 | | |
47 | | As a result, for this optimization, some fragments (generated with a new |
48 | | type rs_sframe) for the SFrame section are fixed up later. |
49 | | |
50 | | This optimization (for size) is enabled by default. */ |
51 | | |
52 | | #ifndef SFRAME_FRE_TYPE_SELECTION_OPT |
53 | | # define SFRAME_FRE_TYPE_SELECTION_OPT 1 |
54 | | #endif |
55 | | |
56 | | /* List of SFrame FDE entries. */ |
57 | | |
58 | | static struct sframe_func_entry *all_sframe_fdes = NULL; |
59 | | |
60 | | /* Tail of the list to add to. */ |
61 | | |
62 | | static struct sframe_func_entry **last_sframe_fde = &all_sframe_fdes; |
63 | | |
64 | | /* Emit a single byte into the current segment. */ |
65 | | |
66 | | static inline void |
67 | | out_one (int byte) |
68 | 9 | { |
69 | 9 | FRAG_APPEND_1_CHAR (byte); |
70 | 9 | } |
71 | | |
72 | | /* Emit a two-byte word into the current segment. */ |
73 | | |
74 | | static inline void |
75 | | out_two (int data) |
76 | 2 | { |
77 | 2 | md_number_to_chars (frag_more (2), data, 2); |
78 | 2 | } |
79 | | |
80 | | /* Emit a four byte word into the current segment. */ |
81 | | |
82 | | static inline void |
83 | | out_four (int data) |
84 | 3 | { |
85 | 3 | md_number_to_chars (frag_more (4), data, 4); |
86 | 3 | } |
87 | | |
88 | | /* Get the start address symbol from the DWARF FDE. */ |
89 | | |
90 | | static symbolS* |
91 | | get_dw_fde_start_addrS (const struct fde_entry *dw_fde) |
92 | 6 | { |
93 | 6 | return dw_fde->start_address; |
94 | 6 | } |
95 | | |
96 | | /* Get the start address symbol from the DWARF FDE. */ |
97 | | |
98 | | static symbolS* |
99 | | get_dw_fde_end_addrS (const struct fde_entry *dw_fde) |
100 | 3 | { |
101 | 3 | return dw_fde->end_address; |
102 | 3 | } |
103 | | |
104 | | /* Get whether PAUTH B key is used. */ |
105 | | static bool |
106 | | get_dw_fde_pauth_b_key_p (const struct fde_entry *dw_fde ATTRIBUTE_UNUSED) |
107 | 1 | { |
108 | | #ifdef tc_fde_entry_extras |
109 | | return (dw_fde->pauth_key == AARCH64_PAUTH_KEY_B); |
110 | | #else |
111 | 1 | return false; |
112 | 1 | #endif |
113 | 1 | } |
114 | | |
115 | | /* SFrame Frame Row Entry (FRE) related functions. */ |
116 | | |
117 | | static void |
118 | | sframe_fre_set_begin_addr (struct sframe_row_entry *fre, symbolS *beginS) |
119 | 8 | { |
120 | 8 | fre->pc_begin = beginS; |
121 | 8 | } |
122 | | |
123 | | static void |
124 | | sframe_fre_set_end_addr (struct sframe_row_entry *fre, symbolS *endS) |
125 | 4 | { |
126 | 4 | fre->pc_end = endS; |
127 | 4 | } |
128 | | |
129 | | static void |
130 | | sframe_fre_set_cfa_base_reg (struct sframe_row_entry *fre, |
131 | | unsigned int cfa_base_reg) |
132 | 4 | { |
133 | 4 | fre->cfa_base_reg = cfa_base_reg; |
134 | 4 | fre->merge_candidate = false; |
135 | 4 | } |
136 | | |
137 | | static void |
138 | | sframe_fre_set_cfa_offset (struct sframe_row_entry *fre, |
139 | | offsetT cfa_offset) |
140 | 4 | { |
141 | 4 | fre->cfa_offset = cfa_offset; |
142 | 4 | fre->merge_candidate = false; |
143 | 4 | } |
144 | | |
145 | | static void |
146 | | sframe_fre_set_ra_track (struct sframe_row_entry *fre, offsetT ra_offset) |
147 | 0 | { |
148 | 0 | fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK; |
149 | 0 | fre->ra_offset = ra_offset; |
150 | 0 | fre->merge_candidate = false; |
151 | 0 | } |
152 | | |
153 | | static void |
154 | | sframe_fre_set_bp_track (struct sframe_row_entry *fre, offsetT bp_offset) |
155 | 0 | { |
156 | 0 | fre->bp_loc = SFRAME_FRE_ELEM_LOC_STACK; |
157 | 0 | fre->bp_offset = bp_offset; |
158 | 0 | fre->merge_candidate = false; |
159 | 0 | } |
160 | | |
161 | | /* All stack offset values within an FRE are uniformly encoded in the same |
162 | | number of bytes. The size of the stack offset values will, however, vary |
163 | | across FREs. */ |
164 | | |
165 | 3 | #define VALUE_8BIT 0x7f |
166 | 0 | #define VALUE_16BIT 0x7fff |
167 | 0 | #define VALUE_32BIT 0x7fffffff |
168 | 0 | #define VALUE_64BIT 0x7fffffffffffffff |
169 | | |
170 | | /* Given a signed offset, return the size in bytes needed to represent it. */ |
171 | | |
172 | | static unsigned int |
173 | | get_offset_size_in_bytes (offsetT value) |
174 | 1 | { |
175 | 1 | unsigned int size = 0; |
176 | | |
177 | 1 | if (value <= VALUE_8BIT && value >= (offsetT) -VALUE_8BIT) |
178 | 1 | size = 1; |
179 | 0 | else if (value <= VALUE_16BIT && value >= (offsetT) -VALUE_16BIT) |
180 | 0 | size = 2; |
181 | 0 | else if (value <= VALUE_32BIT && value >= (offsetT) -VALUE_32BIT) |
182 | 0 | size = 4; |
183 | 0 | else if ((sizeof (offsetT) > 4) && (value <= (offsetT) VALUE_64BIT |
184 | 0 | && value >= (offsetT) -VALUE_64BIT)) |
185 | 0 | size = 8; |
186 | | |
187 | 1 | return size; |
188 | 1 | } |
189 | | |
190 | 1 | #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_1B 0 /* SFRAME_FRE_OFFSET_1B. */ |
191 | 0 | #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_2B 1 /* SFRAME_FRE_OFFSET_2B. */ |
192 | 0 | #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_4B 2 /* SFRAME_FRE_OFFSET_4B. */ |
193 | 1 | #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_8B 3 /* Not supported in SFrame. */ |
194 | 1 | #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_8B |
195 | | |
196 | | /* Helper struct for mapping offset size to output functions. */ |
197 | | |
198 | | struct sframe_fre_offset_func_map |
199 | | { |
200 | | unsigned int offset_size; |
201 | | void (*out_func)(int); |
202 | | }; |
203 | | |
204 | | /* Given an OFFSET_SIZE, return the size in bytes needed to represent it. */ |
205 | | |
206 | | static unsigned int |
207 | | sframe_fre_offset_func_map_index (unsigned int offset_size) |
208 | 1 | { |
209 | 1 | unsigned int idx = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX; |
210 | | |
211 | 1 | switch (offset_size) |
212 | 1 | { |
213 | 1 | case SFRAME_FRE_OFFSET_1B: |
214 | 1 | idx = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_1B; |
215 | 1 | break; |
216 | 0 | case SFRAME_FRE_OFFSET_2B: |
217 | 0 | idx = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_2B; |
218 | 0 | break; |
219 | 0 | case SFRAME_FRE_OFFSET_4B: |
220 | 0 | idx = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_4B; |
221 | 0 | break; |
222 | 0 | default: |
223 | | /* Not supported in SFrame. */ |
224 | 0 | break; |
225 | 1 | } |
226 | | |
227 | 1 | return idx; |
228 | 1 | } |
229 | | |
230 | | /* Mapping from offset size to the output function to emit the value. */ |
231 | | |
232 | | static const |
233 | | struct sframe_fre_offset_func_map |
234 | | fre_offset_func_map[SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX+1] = |
235 | | { |
236 | | { SFRAME_FRE_OFFSET_1B, out_one }, |
237 | | { SFRAME_FRE_OFFSET_2B, out_two }, |
238 | | { SFRAME_FRE_OFFSET_4B, out_four }, |
239 | | { -1, NULL } /* Not Supported in SFrame. */ |
240 | | }; |
241 | | |
242 | | /* SFrame version specific operations access. */ |
243 | | |
244 | | static struct sframe_version_ops sframe_ver_ops; |
245 | | |
246 | | /* SFrame (SFRAME_VERSION_1) set FRE info. */ |
247 | | |
248 | | static unsigned char |
249 | | sframe_v1_set_fre_info (unsigned int base_reg, unsigned int num_offsets, |
250 | | unsigned int offset_size, bool mangled_ra_p) |
251 | 1 | { |
252 | 1 | unsigned char fre_info; |
253 | 1 | fre_info = SFRAME_V1_FRE_INFO (base_reg, num_offsets, offset_size); |
254 | 1 | fre_info = SFRAME_V1_FRE_INFO_UPDATE_MANGLED_RA_P (mangled_ra_p, fre_info); |
255 | 1 | return fre_info; |
256 | 1 | } |
257 | | |
258 | | /* SFrame (SFRAME_VERSION_1) set function info. */ |
259 | | static unsigned char |
260 | | sframe_v1_set_func_info (unsigned int fde_type, unsigned int fre_type, |
261 | | unsigned int pauth_key) |
262 | 1 | { |
263 | 1 | unsigned char func_info; |
264 | 1 | func_info = SFRAME_V1_FUNC_INFO (fde_type, fre_type); |
265 | 1 | func_info = SFRAME_V1_FUNC_INFO_UPDATE_PAUTH_KEY (pauth_key, func_info); |
266 | 1 | return func_info; |
267 | 1 | } |
268 | | |
269 | | /* SFrame version specific operations setup. */ |
270 | | |
271 | | static void |
272 | | sframe_set_version (uint32_t sframe_version ATTRIBUTE_UNUSED) |
273 | 1 | { |
274 | 1 | sframe_ver_ops.format_version = SFRAME_VERSION_2; |
275 | | |
276 | | /* These operations remain the same for SFRAME_VERSION_2 as fre_info and |
277 | | func_info have not changed from SFRAME_VERSION_1. */ |
278 | | |
279 | 1 | sframe_ver_ops.set_fre_info = sframe_v1_set_fre_info; |
280 | | |
281 | 1 | sframe_ver_ops.set_func_info = sframe_v1_set_func_info; |
282 | 1 | } |
283 | | |
284 | | /* SFrame set FRE info. */ |
285 | | |
286 | | static unsigned char |
287 | | sframe_set_fre_info (unsigned int base_reg, unsigned int num_offsets, |
288 | | unsigned int offset_size, bool mangled_ra_p) |
289 | 1 | { |
290 | 1 | return sframe_ver_ops.set_fre_info (base_reg, num_offsets, |
291 | 1 | offset_size, mangled_ra_p); |
292 | 1 | } |
293 | | |
294 | | /* SFrame set func info. */ |
295 | | |
296 | | static unsigned char |
297 | | sframe_set_func_info (unsigned int fde_type, unsigned int fre_type, |
298 | | unsigned int pauth_key) |
299 | 1 | { |
300 | 1 | return sframe_ver_ops.set_func_info (fde_type, fre_type, pauth_key); |
301 | 1 | } |
302 | | |
303 | | /* Get the number of SFrame FDEs for the current file. */ |
304 | | |
305 | | static unsigned int |
306 | | get_num_sframe_fdes (void); |
307 | | |
308 | | /* Get the number of SFrame frame row entries for the current file. */ |
309 | | |
310 | | static unsigned int |
311 | | get_num_sframe_fres (void); |
312 | | |
313 | | /* Get CFA base register ID as represented in SFrame Frame Row Entry. */ |
314 | | |
315 | | static unsigned int |
316 | | get_fre_base_reg_id (struct sframe_row_entry *sframe_fre) |
317 | 1 | { |
318 | 1 | unsigned int cfi_insn_cfa_base_reg = sframe_fre->cfa_base_reg; |
319 | 1 | unsigned fre_base_reg = SFRAME_BASE_REG_SP; |
320 | | |
321 | 1 | if (cfi_insn_cfa_base_reg == SFRAME_CFA_FP_REG) |
322 | 0 | fre_base_reg = SFRAME_BASE_REG_FP; |
323 | | |
324 | | /* Only one bit is reserved in SFRAME_VERSION_1. */ |
325 | 1 | gas_assert (fre_base_reg == SFRAME_BASE_REG_SP |
326 | 1 | || fre_base_reg == SFRAME_BASE_REG_FP); |
327 | | |
328 | 1 | return fre_base_reg; |
329 | 1 | } |
330 | | |
331 | | /* Get number of offsets necessary for the SFrame Frame Row Entry. */ |
332 | | |
333 | | static unsigned int |
334 | | get_fre_num_offsets (struct sframe_row_entry *sframe_fre) |
335 | 1 | { |
336 | | /* Atleast 1 must always be present (to recover CFA). */ |
337 | 1 | unsigned int fre_num_offsets = 1; |
338 | | |
339 | 1 | if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK) |
340 | 0 | fre_num_offsets++; |
341 | 1 | if (sframe_ra_tracking_p () |
342 | 1 | && sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK) |
343 | 0 | fre_num_offsets++; |
344 | 1 | return fre_num_offsets; |
345 | 1 | } |
346 | | |
347 | | /* Get the minimum necessary offset size (in bytes) for this |
348 | | SFrame frame row entry. */ |
349 | | |
350 | | static unsigned int |
351 | | sframe_get_fre_offset_size (struct sframe_row_entry *sframe_fre) |
352 | 1 | { |
353 | 1 | unsigned int max_offset_size = 0; |
354 | 1 | unsigned int cfa_offset_size = 0; |
355 | 1 | unsigned int bp_offset_size = 0; |
356 | 1 | unsigned int ra_offset_size = 0; |
357 | | |
358 | 1 | unsigned int fre_offset_size = 0; |
359 | | |
360 | | /* What size of offsets appear in this frame row entry. */ |
361 | 1 | cfa_offset_size = get_offset_size_in_bytes (sframe_fre->cfa_offset); |
362 | 1 | if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK) |
363 | 0 | bp_offset_size = get_offset_size_in_bytes (sframe_fre->bp_offset); |
364 | 1 | if (sframe_ra_tracking_p () |
365 | 1 | && sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK) |
366 | 0 | ra_offset_size = get_offset_size_in_bytes (sframe_fre->ra_offset); |
367 | | |
368 | | /* Get the maximum size needed to represent the offsets. */ |
369 | 1 | max_offset_size = cfa_offset_size; |
370 | 1 | if (bp_offset_size > max_offset_size) |
371 | 0 | max_offset_size = bp_offset_size; |
372 | 1 | if (ra_offset_size > max_offset_size) |
373 | 0 | max_offset_size = ra_offset_size; |
374 | | |
375 | 1 | gas_assert (max_offset_size); |
376 | | |
377 | 1 | switch (max_offset_size) |
378 | 1 | { |
379 | 1 | case 1: |
380 | 1 | fre_offset_size = SFRAME_FRE_OFFSET_1B; |
381 | 1 | break; |
382 | 0 | case 2: |
383 | 0 | fre_offset_size = SFRAME_FRE_OFFSET_2B; |
384 | 0 | break; |
385 | 0 | case 4: |
386 | 0 | fre_offset_size = SFRAME_FRE_OFFSET_4B; |
387 | 0 | break; |
388 | 0 | default: |
389 | | /* Offset of size 8 bytes is not supported in SFrame format |
390 | | version 1. */ |
391 | 0 | as_fatal (_("SFrame unsupported offset value\n")); |
392 | 0 | break; |
393 | 1 | } |
394 | | |
395 | 1 | return fre_offset_size; |
396 | 1 | } |
397 | | |
398 | | #if SFRAME_FRE_TYPE_SELECTION_OPT |
399 | | |
400 | | /* Create a composite expression CEXP (for SFrame FRE start address) such that: |
401 | | |
402 | | exp = <val> OP_absent <width>, where, |
403 | | |
404 | | - <val> and <width> are themselves expressionS. |
405 | | - <val> stores the expression which when evaluated gives the value of the |
406 | | start address offset of the FRE. |
407 | | - <width> stores the expression when evaluated gives the number of bytes |
408 | | needed to encode the start address offset of the FRE. |
409 | | |
410 | | The use of OP_absent as the X_op_symbol helps identify this expression |
411 | | later when fragments are fixed up. */ |
412 | | |
413 | | static void |
414 | | create_fre_start_addr_exp (expressionS *cexp, symbolS *fre_pc_begin, |
415 | | symbolS *fde_start_address, |
416 | | symbolS *fde_end_address) |
417 | 1 | { |
418 | 1 | expressionS val; |
419 | 1 | expressionS width; |
420 | | |
421 | | /* val expression stores the FDE start address offset from the start PC |
422 | | of function. */ |
423 | 1 | val.X_op = O_subtract; |
424 | 1 | val.X_add_symbol = fre_pc_begin; |
425 | 1 | val.X_op_symbol = fde_start_address; |
426 | 1 | val.X_add_number = 0; |
427 | | |
428 | | /* width expressions stores the size of the function. This is used later |
429 | | to determine the number of bytes to be used to encode the FRE start |
430 | | address of each FRE of the function. */ |
431 | 1 | width.X_op = O_subtract; |
432 | 1 | width.X_add_symbol = fde_end_address; |
433 | 1 | width.X_op_symbol = fde_start_address; |
434 | 1 | width.X_add_number = 0; |
435 | | |
436 | 1 | cexp->X_op = O_absent; |
437 | 1 | cexp->X_add_symbol = make_expr_symbol (&val); |
438 | 1 | cexp->X_op_symbol = make_expr_symbol (&width); |
439 | 1 | cexp->X_add_number = 0; |
440 | 1 | } |
441 | | |
442 | | /* Create a composite expression CEXP (for SFrame FDE function info) such that: |
443 | | |
444 | | exp = <rest_of_func_info> OP_modulus <width>, where, |
445 | | |
446 | | - <rest_of_func_info> and <width> are themselves expressionS. |
447 | | - <rest_of_func_info> stores a constant expression where X_add_number is |
448 | | used to stash away the func_info. The upper 4-bits of the func_info are copied |
449 | | back to the resulting byte by the fragment fixup logic. |
450 | | - <width> stores the expression when evaluated gives the size of the |
451 | | function in number of bytes. |
452 | | |
453 | | The use of OP_modulus as the X_op_symbol helps identify this expression |
454 | | later when fragments are fixed up. */ |
455 | | |
456 | | static void |
457 | | create_func_info_exp (expressionS *cexp, symbolS *dw_fde_end_addrS, |
458 | | symbolS *dw_fde_start_addrS, uint8_t func_info) |
459 | 1 | { |
460 | 1 | expressionS width; |
461 | 1 | expressionS rest_of_func_info; |
462 | | |
463 | 1 | width.X_op = O_subtract; |
464 | 1 | width.X_add_symbol = dw_fde_end_addrS; |
465 | 1 | width.X_op_symbol = dw_fde_start_addrS; |
466 | 1 | width.X_add_number = 0; |
467 | | |
468 | 1 | rest_of_func_info.X_op = O_constant; |
469 | 1 | rest_of_func_info.X_add_number = func_info; |
470 | | |
471 | 1 | cexp->X_op = O_modulus; |
472 | 1 | cexp->X_add_symbol = make_expr_symbol (&rest_of_func_info); |
473 | 1 | cexp->X_op_symbol = make_expr_symbol (&width); |
474 | 1 | cexp->X_add_number = 0; |
475 | 1 | } |
476 | | |
477 | | #endif |
478 | | |
479 | | static struct sframe_row_entry* |
480 | | sframe_row_entry_new (void) |
481 | 7 | { |
482 | 7 | struct sframe_row_entry *fre = XCNEW (struct sframe_row_entry); |
483 | | /* Reset cfa_base_reg to -1. A value of 0 will imply some valid register |
484 | | for the supported arches. */ |
485 | 7 | fre->cfa_base_reg = SFRAME_FRE_BASE_REG_INVAL; |
486 | 7 | fre->merge_candidate = true; |
487 | | /* Reset the mangled RA status bit to zero by default. We will |
488 | | initialize it in sframe_row_entry_initialize () with the sticky |
489 | | bit if set. */ |
490 | 7 | fre->mangled_ra_p = false; |
491 | | |
492 | 7 | return fre; |
493 | 7 | } |
494 | | |
495 | | static void |
496 | | sframe_row_entry_free (struct sframe_row_entry *fre) |
497 | 7 | { |
498 | 11 | while (fre) |
499 | 4 | { |
500 | 4 | struct sframe_row_entry *fre_next = fre->next; |
501 | 4 | XDELETE (fre); |
502 | 4 | fre = fre_next; |
503 | 4 | } |
504 | 7 | } |
505 | | |
506 | | /* Allocate an SFrame FDE. */ |
507 | | |
508 | | static struct sframe_func_entry* |
509 | | sframe_fde_alloc (void) |
510 | 4 | { |
511 | 4 | return XCNEW (struct sframe_func_entry); |
512 | 4 | } |
513 | | |
514 | | /* Free up the SFrame FDE. */ |
515 | | |
516 | | static void |
517 | | sframe_fde_free (struct sframe_func_entry *sframe_fde) |
518 | 4 | { |
519 | 4 | sframe_row_entry_free (sframe_fde->sframe_fres); |
520 | 4 | XDELETE (sframe_fde); |
521 | 4 | } |
522 | | |
523 | | static void |
524 | | output_sframe_row_entry (symbolS *fde_start_addr, |
525 | | symbolS *fde_end_addr, |
526 | | struct sframe_row_entry *sframe_fre) |
527 | 1 | { |
528 | 1 | unsigned char fre_info; |
529 | 1 | unsigned int fre_num_offsets; |
530 | 1 | unsigned int fre_offset_size; |
531 | 1 | unsigned int fre_base_reg; |
532 | 1 | expressionS exp; |
533 | 1 | unsigned int fre_addr_size; |
534 | | |
535 | 1 | unsigned int idx = 0; |
536 | 1 | unsigned int fre_write_offsets = 0; |
537 | | |
538 | 1 | fre_addr_size = 4; /* 4 bytes by default. FIXME tie it to fre_type? */ |
539 | | |
540 | | /* SFrame FRE Start Address. */ |
541 | 1 | #if SFRAME_FRE_TYPE_SELECTION_OPT |
542 | 1 | create_fre_start_addr_exp (&exp, sframe_fre->pc_begin, fde_start_addr, |
543 | 1 | fde_end_addr); |
544 | 1 | frag_grow (fre_addr_size); |
545 | 1 | frag_var (rs_sframe, fre_addr_size, 0, (relax_substateT) 0, |
546 | 1 | make_expr_symbol (&exp), 0, (char *) frag_now); |
547 | | #else |
548 | | gas_assert (fde_end_addr); |
549 | | exp.X_op = O_subtract; |
550 | | exp.X_add_symbol = sframe_fre->pc_begin; /* to. */ |
551 | | exp.X_op_symbol = fde_start_addr; /* from. */ |
552 | | exp.X_add_number = 0; |
553 | | emit_expr (&exp, fre_addr_size); |
554 | | #endif |
555 | | |
556 | | /* Create the fre_info using the CFA base register, number of offsets and max |
557 | | size of offset in this frame row entry. */ |
558 | 1 | fre_base_reg = get_fre_base_reg_id (sframe_fre); |
559 | 1 | fre_num_offsets = get_fre_num_offsets (sframe_fre); |
560 | 1 | fre_offset_size = sframe_get_fre_offset_size (sframe_fre); |
561 | 1 | fre_info = sframe_set_fre_info (fre_base_reg, fre_num_offsets, |
562 | 1 | fre_offset_size, sframe_fre->mangled_ra_p); |
563 | 1 | out_one (fre_info); |
564 | | |
565 | 1 | idx = sframe_fre_offset_func_map_index (fre_offset_size); |
566 | 1 | gas_assert (idx < SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX); |
567 | | |
568 | | /* Write out the offsets in order - cfa, bp, ra. */ |
569 | 1 | fre_offset_func_map[idx].out_func (sframe_fre->cfa_offset); |
570 | 1 | fre_write_offsets++; |
571 | | |
572 | 1 | if (sframe_ra_tracking_p () |
573 | 1 | && sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK) |
574 | 0 | { |
575 | 0 | fre_offset_func_map[idx].out_func (sframe_fre->ra_offset); |
576 | 0 | fre_write_offsets++; |
577 | 0 | } |
578 | 1 | if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK) |
579 | 0 | { |
580 | 0 | fre_offset_func_map[idx].out_func (sframe_fre->bp_offset); |
581 | 0 | fre_write_offsets++; |
582 | 0 | } |
583 | | |
584 | | /* Check if the expected number offsets have been written out |
585 | | in this FRE. */ |
586 | 1 | gas_assert (fre_write_offsets == fre_num_offsets); |
587 | 1 | } |
588 | | |
589 | | static void |
590 | | output_sframe_funcdesc (symbolS *start_of_fre_section, |
591 | | symbolS *fre_symbol, |
592 | | struct sframe_func_entry *sframe_fde) |
593 | 1 | { |
594 | 1 | expressionS exp; |
595 | 1 | symbolS *dw_fde_start_addrS, *dw_fde_end_addrS; |
596 | 1 | unsigned int pauth_key; |
597 | | |
598 | 1 | dw_fde_start_addrS = get_dw_fde_start_addrS (sframe_fde->dw_fde); |
599 | 1 | dw_fde_end_addrS = get_dw_fde_end_addrS (sframe_fde->dw_fde); |
600 | | |
601 | | /* Start address of the function. */ |
602 | 1 | exp.X_op = O_subtract; |
603 | 1 | exp.X_add_symbol = dw_fde_start_addrS; /* to location. */ |
604 | 1 | exp.X_op_symbol = symbol_temp_new_now (); /* from location. */ |
605 | 1 | exp.X_add_number = 0; |
606 | 1 | emit_expr (&exp, sizeof_member (sframe_func_desc_entry, |
607 | 1 | sfde_func_start_address)); |
608 | | |
609 | | /* Size of the function in bytes. */ |
610 | 1 | exp.X_op = O_subtract; |
611 | 1 | exp.X_add_symbol = dw_fde_end_addrS; |
612 | 1 | exp.X_op_symbol = dw_fde_start_addrS; |
613 | 1 | exp.X_add_number = 0; |
614 | 1 | emit_expr (&exp, sizeof_member (sframe_func_desc_entry, |
615 | 1 | sfde_func_size)); |
616 | | |
617 | | /* Offset to the first frame row entry. */ |
618 | 1 | exp.X_op = O_subtract; |
619 | 1 | exp.X_add_symbol = fre_symbol; /* Minuend. */ |
620 | 1 | exp.X_op_symbol = start_of_fre_section; /* Subtrahend. */ |
621 | 1 | exp.X_add_number = 0; |
622 | 1 | emit_expr (&exp, sizeof_member (sframe_func_desc_entry, |
623 | 1 | sfde_func_start_fre_off)); |
624 | | |
625 | | /* Number of FREs. */ |
626 | 1 | out_four (sframe_fde->num_fres); |
627 | | |
628 | | /* SFrame FDE function info. */ |
629 | 1 | unsigned char func_info; |
630 | 1 | pauth_key = (get_dw_fde_pauth_b_key_p (sframe_fde->dw_fde) |
631 | 1 | ? SFRAME_AARCH64_PAUTH_KEY_B : SFRAME_AARCH64_PAUTH_KEY_A); |
632 | 1 | func_info = sframe_set_func_info (SFRAME_FDE_TYPE_PCINC, |
633 | 1 | SFRAME_FRE_TYPE_ADDR4, |
634 | 1 | pauth_key); |
635 | 1 | #if SFRAME_FRE_TYPE_SELECTION_OPT |
636 | 1 | expressionS cexp; |
637 | 1 | create_func_info_exp (&cexp, dw_fde_end_addrS, dw_fde_start_addrS, |
638 | 1 | func_info); |
639 | 1 | frag_grow (1); /* Size of func info is unsigned char. */ |
640 | 1 | frag_var (rs_sframe, 1, 0, (relax_substateT) 0, |
641 | 1 | make_expr_symbol (&cexp), 0, (char *) frag_now); |
642 | | #else |
643 | | out_one (func_info); |
644 | | #endif |
645 | 1 | out_one (0); |
646 | 1 | out_two (0); |
647 | 1 | } |
648 | | |
649 | | static void |
650 | | output_sframe_internal (void) |
651 | 1 | { |
652 | 1 | expressionS exp; |
653 | 1 | unsigned int i = 0; |
654 | | |
655 | 1 | symbolS *end_of_frame_hdr; |
656 | 1 | symbolS *end_of_frame_section; |
657 | 1 | symbolS *start_of_func_desc_section; |
658 | 1 | symbolS *start_of_fre_section; |
659 | 1 | struct sframe_func_entry *sframe_fde, *sframe_fde_next; |
660 | 1 | struct sframe_row_entry *sframe_fre; |
661 | 1 | unsigned char abi_arch = 0; |
662 | 1 | int fixed_fp_offset = SFRAME_CFA_FIXED_FP_INVALID; |
663 | 1 | int fixed_ra_offset = SFRAME_CFA_FIXED_RA_INVALID; |
664 | | |
665 | | /* The function descriptor entries as dumped by the assembler are not |
666 | | sorted on PCs. */ |
667 | 1 | unsigned char sframe_flags = 0; |
668 | | |
669 | 1 | unsigned int num_fdes = get_num_sframe_fdes (); |
670 | 1 | unsigned int num_fres = get_num_sframe_fres (); |
671 | 1 | symbolS **fde_fre_symbols = XNEWVEC (symbolS *, num_fdes); |
672 | 2 | for (i = 0; i < num_fdes; i++) |
673 | 1 | fde_fre_symbols[i] = symbol_temp_make (); |
674 | | |
675 | 1 | end_of_frame_hdr = symbol_temp_make (); |
676 | 1 | start_of_fre_section = symbol_temp_make (); |
677 | 1 | start_of_func_desc_section = symbol_temp_make (); |
678 | 1 | end_of_frame_section = symbol_temp_make (); |
679 | | |
680 | | /* Output the preamble of SFrame section. */ |
681 | 1 | out_two (SFRAME_MAGIC); |
682 | 1 | out_one (SFRAME_VERSION); |
683 | 1 | out_one (sframe_flags); |
684 | | /* abi/arch. */ |
685 | 1 | #ifdef sframe_get_abi_arch |
686 | 1 | abi_arch = sframe_get_abi_arch (); |
687 | 1 | #endif |
688 | 1 | gas_assert (abi_arch); |
689 | 1 | out_one (abi_arch); |
690 | | |
691 | | /* Offset for the FP register from CFA. Neither of the AMD64 or AAPCS64 |
692 | | ABIs have a fixed offset for the FP register from the CFA. This may be |
693 | | useful in future (but not without additional support in the toolchain) |
694 | | for specialized handling/encoding for cases where, for example, |
695 | | -fno-omit-frame-pointer is used. */ |
696 | 1 | out_one (fixed_fp_offset); |
697 | | |
698 | | /* All ABIs participating in SFrame generation must define |
699 | | sframe_ra_tracking_p. |
700 | | When RA tracking (in FREs) is not needed (e.g., AMD64), SFrame assumes |
701 | | the RA is going to be at a fixed offset from CFA. Check that the fixed RA |
702 | | offset is appropriately defined in all cases. */ |
703 | 1 | if (!sframe_ra_tracking_p ()) |
704 | 1 | { |
705 | 1 | fixed_ra_offset = sframe_cfa_ra_offset (); |
706 | 1 | gas_assert (fixed_ra_offset != SFRAME_CFA_FIXED_RA_INVALID); |
707 | 1 | } |
708 | 1 | out_one (fixed_ra_offset); |
709 | | |
710 | | /* None of the AMD64, or AARCH64 ABIs need the auxiliary header. |
711 | | When the need does arise to use this field, the appropriate backend |
712 | | must provide this information. */ |
713 | 1 | out_one (0); /* Auxiliary SFrame header length. */ |
714 | | |
715 | 1 | out_four (num_fdes); /* Number of FDEs. */ |
716 | 1 | out_four (num_fres); /* Number of FREs. */ |
717 | | |
718 | | /* Size of FRE sub-section. */ |
719 | 1 | exp.X_op = O_subtract; |
720 | 1 | exp.X_add_symbol = end_of_frame_section; |
721 | 1 | exp.X_op_symbol = start_of_fre_section; |
722 | 1 | exp.X_add_number = 0; |
723 | 1 | emit_expr (&exp, sizeof_member (sframe_header, sfh_fre_len)); |
724 | | |
725 | | /* Offset of FDE sub-section. */ |
726 | 1 | exp.X_op = O_subtract; |
727 | 1 | exp.X_add_symbol = end_of_frame_hdr; |
728 | 1 | exp.X_op_symbol = start_of_func_desc_section; |
729 | 1 | exp.X_add_number = 0; |
730 | 1 | emit_expr (&exp, sizeof_member (sframe_header, sfh_fdeoff)); |
731 | | |
732 | | /* Offset of FRE sub-section. */ |
733 | 1 | exp.X_op = O_subtract; |
734 | 1 | exp.X_add_symbol = start_of_fre_section; |
735 | 1 | exp.X_op_symbol = end_of_frame_hdr; |
736 | 1 | exp.X_add_number = 0; |
737 | 1 | emit_expr (&exp, sizeof_member (sframe_header, sfh_freoff)); |
738 | | |
739 | 1 | symbol_set_value_now (end_of_frame_hdr); |
740 | 1 | symbol_set_value_now (start_of_func_desc_section); |
741 | | |
742 | | /* Output the SFrame function descriptor entries. */ |
743 | 1 | i = 0; |
744 | 2 | for (sframe_fde = all_sframe_fdes; sframe_fde; sframe_fde = sframe_fde->next) |
745 | 1 | { |
746 | 1 | output_sframe_funcdesc (start_of_fre_section, |
747 | 1 | fde_fre_symbols[i], sframe_fde); |
748 | 1 | i++; |
749 | 1 | } |
750 | | |
751 | 1 | symbol_set_value_now (start_of_fre_section); |
752 | | |
753 | | /* Output the SFrame FREs. */ |
754 | 1 | i = 0; |
755 | 1 | sframe_fde = all_sframe_fdes; |
756 | | |
757 | 2 | for (sframe_fde = all_sframe_fdes; sframe_fde; sframe_fde = sframe_fde_next) |
758 | 1 | { |
759 | 1 | symbol_set_value_now (fde_fre_symbols[i]); |
760 | 1 | for (sframe_fre = sframe_fde->sframe_fres; |
761 | 2 | sframe_fre; |
762 | 1 | sframe_fre = sframe_fre->next) |
763 | 1 | { |
764 | 1 | output_sframe_row_entry (get_dw_fde_start_addrS (sframe_fde->dw_fde), |
765 | 1 | get_dw_fde_end_addrS (sframe_fde->dw_fde), |
766 | 1 | sframe_fre); |
767 | 1 | } |
768 | 1 | i++; |
769 | 1 | sframe_fde_next = sframe_fde->next; |
770 | 1 | sframe_fde_free (sframe_fde); |
771 | 1 | } |
772 | 1 | all_sframe_fdes = NULL; |
773 | 1 | last_sframe_fde = &all_sframe_fdes; |
774 | | |
775 | 1 | symbol_set_value_now (end_of_frame_section); |
776 | | |
777 | 1 | gas_assert (i == num_fdes); |
778 | | |
779 | 1 | free (fde_fre_symbols); |
780 | 1 | fde_fre_symbols = NULL; |
781 | 1 | } |
782 | | |
783 | | static unsigned int |
784 | | get_num_sframe_fdes (void) |
785 | 1 | { |
786 | 1 | struct sframe_func_entry *sframe_fde; |
787 | 1 | unsigned int total_fdes = 0; |
788 | | |
789 | 2 | for (sframe_fde = all_sframe_fdes; sframe_fde ; sframe_fde = sframe_fde->next) |
790 | 1 | total_fdes++; |
791 | | |
792 | 1 | return total_fdes; |
793 | 1 | } |
794 | | |
795 | | /* Get the total number of SFrame row entries across the FDEs. */ |
796 | | |
797 | | static unsigned int |
798 | | get_num_sframe_fres (void) |
799 | 1 | { |
800 | 1 | struct sframe_func_entry *sframe_fde; |
801 | 1 | unsigned int total_fres = 0; |
802 | | |
803 | 2 | for (sframe_fde = all_sframe_fdes; sframe_fde ; sframe_fde = sframe_fde->next) |
804 | 1 | total_fres += sframe_fde->num_fres; |
805 | | |
806 | 1 | return total_fres; |
807 | 1 | } |
808 | | |
809 | | /* SFrame translation context functions. */ |
810 | | |
811 | | /* Allocate a new SFrame translation context. */ |
812 | | |
813 | | static struct sframe_xlate_ctx* |
814 | | sframe_xlate_ctx_alloc (void) |
815 | 1 | { |
816 | 1 | struct sframe_xlate_ctx* xlate_ctx = XCNEW (struct sframe_xlate_ctx); |
817 | 1 | return xlate_ctx; |
818 | 1 | } |
819 | | |
820 | | /* Initialize the given SFrame translation context. */ |
821 | | |
822 | | static void |
823 | | sframe_xlate_ctx_init (struct sframe_xlate_ctx *xlate_ctx) |
824 | 4 | { |
825 | 4 | xlate_ctx->dw_fde = NULL; |
826 | 4 | xlate_ctx->first_fre = NULL; |
827 | 4 | xlate_ctx->last_fre = NULL; |
828 | 4 | xlate_ctx->cur_fre = NULL; |
829 | 4 | xlate_ctx->remember_fre = NULL; |
830 | 4 | xlate_ctx->num_xlate_fres = 0; |
831 | 4 | } |
832 | | |
833 | | /* Cleanup the given SFrame translation context. */ |
834 | | |
835 | | static void |
836 | | sframe_xlate_ctx_cleanup (struct sframe_xlate_ctx *xlate_ctx) |
837 | 3 | { |
838 | 3 | sframe_row_entry_free (xlate_ctx->first_fre); |
839 | 3 | XDELETE (xlate_ctx->remember_fre); |
840 | 3 | XDELETE (xlate_ctx->cur_fre); |
841 | 3 | } |
842 | | |
843 | | /* Transfer the state from the SFrame translation context to the SFrame FDE. */ |
844 | | |
845 | | static void |
846 | | sframe_xlate_ctx_finalize (struct sframe_xlate_ctx *xlate_ctx, |
847 | | struct sframe_func_entry *sframe_fde) |
848 | 1 | { |
849 | 1 | sframe_fde->dw_fde = xlate_ctx->dw_fde; |
850 | 1 | sframe_fde->sframe_fres = xlate_ctx->first_fre; |
851 | 1 | sframe_fde->num_fres = xlate_ctx->num_xlate_fres; |
852 | 1 | } |
853 | | |
854 | | /* Add the given FRE in the list of frame row entries in the given FDE |
855 | | translation context. */ |
856 | | |
857 | | static void |
858 | | sframe_xlate_ctx_add_fre (struct sframe_xlate_ctx *xlate_ctx, |
859 | | struct sframe_row_entry *fre) |
860 | 4 | { |
861 | 4 | gas_assert (xlate_ctx && fre); |
862 | | |
863 | | /* Add the frame row entry. */ |
864 | 4 | if (!xlate_ctx->first_fre) |
865 | 4 | xlate_ctx->first_fre = fre; |
866 | 0 | else if (xlate_ctx->last_fre) |
867 | 0 | xlate_ctx->last_fre->next = fre; |
868 | | |
869 | 4 | xlate_ctx->last_fre = fre; |
870 | | |
871 | | /* Keep track of the total number of SFrame frame row entries. */ |
872 | 4 | xlate_ctx->num_xlate_fres++; |
873 | 4 | } |
874 | | |
875 | | /* A SFrame Frame Row Entry is self-sufficient in terms of stack tracing info |
876 | | for a given PC. It contains information assimilated from multiple CFI |
877 | | instructions, and hence, a new SFrame FRE is initialized with the data from |
878 | | the previous known FRE, if any. |
879 | | |
880 | | Understandably, not all information (especially the instruction begin |
881 | | and end boundaries) needs to be relayed. Hence, the caller of this API |
882 | | must set the pc_begin and pc_end as applicable. */ |
883 | | |
884 | | static void |
885 | | sframe_row_entry_initialize (struct sframe_row_entry *cur_fre, |
886 | | struct sframe_row_entry *prev_fre) |
887 | 3 | { |
888 | 3 | gas_assert (prev_fre); |
889 | 3 | cur_fre->cfa_base_reg = prev_fre->cfa_base_reg; |
890 | 3 | cur_fre->cfa_offset = prev_fre->cfa_offset; |
891 | 3 | cur_fre->bp_loc = prev_fre->bp_loc; |
892 | 3 | cur_fre->bp_offset = prev_fre->bp_offset; |
893 | 3 | cur_fre->ra_loc = prev_fre->ra_loc; |
894 | 3 | cur_fre->ra_offset = prev_fre->ra_offset; |
895 | | /* Treat RA mangling as a sticky bit. It retains its value until another |
896 | | .cfi_negate_ra_state is seen. */ |
897 | 3 | cur_fre->mangled_ra_p = prev_fre->mangled_ra_p; |
898 | 3 | } |
899 | | |
900 | | /* Return SFrame register name for SP, FP, and RA, or NULL if other. */ |
901 | | |
902 | | static const char * |
903 | | sframe_register_name (unsigned int reg) |
904 | 0 | { |
905 | 0 | if (reg == SFRAME_CFA_SP_REG) |
906 | 0 | return "SP"; |
907 | 0 | else if (reg == SFRAME_CFA_FP_REG) |
908 | 0 | return "FP"; |
909 | 0 | else if (reg == SFRAME_CFA_RA_REG) |
910 | 0 | return "RA"; |
911 | 0 | else |
912 | 0 | return NULL; |
913 | 0 | } |
914 | | |
915 | | /* Translate DW_CFA_advance_loc into SFrame context. |
916 | | Return SFRAME_XLATE_OK if success. */ |
917 | | |
918 | | static int |
919 | | sframe_xlate_do_advance_loc (struct sframe_xlate_ctx *xlate_ctx, |
920 | | struct cfi_insn_data *cfi_insn) |
921 | 4 | { |
922 | 4 | struct sframe_row_entry *last_fre = xlate_ctx->last_fre; |
923 | | /* Get the scratchpad FRE currently being updated as the cfi_insn's |
924 | | get interpreted. This FRE eventually gets linked in into the |
925 | | list of FREs for the specific function. */ |
926 | 4 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
927 | | |
928 | 4 | if (cur_fre) |
929 | 4 | { |
930 | 4 | if (!cur_fre->merge_candidate) |
931 | 3 | { |
932 | 3 | sframe_fre_set_end_addr (cur_fre, cfi_insn->u.ll.lab2); |
933 | | |
934 | 3 | sframe_xlate_ctx_add_fre (xlate_ctx, cur_fre); |
935 | 3 | last_fre = xlate_ctx->last_fre; |
936 | | |
937 | 3 | xlate_ctx->cur_fre = sframe_row_entry_new (); |
938 | 3 | cur_fre = xlate_ctx->cur_fre; |
939 | | |
940 | 3 | if (last_fre) |
941 | 3 | sframe_row_entry_initialize (cur_fre, last_fre); |
942 | 3 | } |
943 | 1 | else |
944 | 1 | { |
945 | 1 | sframe_fre_set_end_addr (last_fre, cfi_insn->u.ll.lab2); |
946 | 1 | gas_assert (last_fre->merge_candidate == false); |
947 | 1 | } |
948 | 4 | } |
949 | 0 | else |
950 | 0 | { |
951 | 0 | xlate_ctx->cur_fre = sframe_row_entry_new (); |
952 | 0 | cur_fre = xlate_ctx->cur_fre; |
953 | 0 | } |
954 | | |
955 | 4 | gas_assert (cur_fre); |
956 | 4 | sframe_fre_set_begin_addr (cur_fre, cfi_insn->u.ll.lab2); |
957 | | |
958 | 4 | return SFRAME_XLATE_OK; |
959 | 4 | } |
960 | | |
961 | | /* Translate DW_CFA_def_cfa into SFrame context. |
962 | | Return SFRAME_XLATE_OK if success. */ |
963 | | |
964 | | static int |
965 | | sframe_xlate_do_def_cfa (struct sframe_xlate_ctx *xlate_ctx, |
966 | | struct cfi_insn_data *cfi_insn) |
967 | | |
968 | 4 | { |
969 | | /* Get the scratchpad FRE. This FRE will eventually get linked in. */ |
970 | 4 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
971 | 4 | if (!cur_fre) |
972 | 4 | { |
973 | 4 | xlate_ctx->cur_fre = sframe_row_entry_new (); |
974 | 4 | cur_fre = xlate_ctx->cur_fre; |
975 | 4 | sframe_fre_set_begin_addr (cur_fre, |
976 | 4 | get_dw_fde_start_addrS (xlate_ctx->dw_fde)); |
977 | 4 | } |
978 | | /* Define the current CFA rule to use the provided register and |
979 | | offset. However, if the register is not FP/SP, skip creating |
980 | | SFrame stack trace info for the function. */ |
981 | 4 | if (cfi_insn->u.ri.reg != SFRAME_CFA_SP_REG |
982 | 4 | && cfi_insn->u.ri.reg != SFRAME_CFA_FP_REG) |
983 | 0 | { |
984 | 0 | as_warn (_("no SFrame FDE emitted; " |
985 | 0 | "non-SP/FP register %u in .cfi_def_cfa"), |
986 | 0 | cfi_insn->u.ri.reg); |
987 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
988 | 0 | } |
989 | 4 | sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg); |
990 | 4 | sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.ri.offset); |
991 | 4 | cur_fre->merge_candidate = false; |
992 | | |
993 | 4 | return SFRAME_XLATE_OK; |
994 | 4 | } |
995 | | |
996 | | /* Translate DW_CFA_def_cfa_register into SFrame context. |
997 | | Return SFRAME_XLATE_OK if success. */ |
998 | | |
999 | | static int |
1000 | | sframe_xlate_do_def_cfa_register (struct sframe_xlate_ctx *xlate_ctx, |
1001 | | struct cfi_insn_data *cfi_insn) |
1002 | 0 | { |
1003 | 0 | struct sframe_row_entry *last_fre = xlate_ctx->last_fre; |
1004 | | /* Get the scratchpad FRE. This FRE will eventually get linked in. */ |
1005 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1006 | |
|
1007 | 0 | gas_assert (cur_fre); |
1008 | | /* Define the current CFA rule to use the provided register (but to |
1009 | | keep the old offset). However, if the register is not FP/SP, |
1010 | | skip creating SFrame stack trace info for the function. */ |
1011 | 0 | if (cfi_insn->u.r != SFRAME_CFA_SP_REG |
1012 | 0 | && cfi_insn->u.r != SFRAME_CFA_FP_REG) |
1013 | 0 | { |
1014 | 0 | as_warn (_("no SFrame FDE emitted; " |
1015 | 0 | "non-SP/FP register %u in .cfi_def_cfa_register"), |
1016 | 0 | cfi_insn->u.r); |
1017 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1018 | 0 | } |
1019 | 0 | sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.r); |
1020 | 0 | if (last_fre) |
1021 | 0 | sframe_fre_set_cfa_offset (cur_fre, last_fre->cfa_offset); |
1022 | |
|
1023 | 0 | cur_fre->merge_candidate = false; |
1024 | |
|
1025 | 0 | return SFRAME_XLATE_OK; |
1026 | 0 | } |
1027 | | |
1028 | | /* Translate DW_CFA_def_cfa_offset into SFrame context. |
1029 | | Return SFRAME_XLATE_OK if success. */ |
1030 | | |
1031 | | static int |
1032 | | sframe_xlate_do_def_cfa_offset (struct sframe_xlate_ctx *xlate_ctx, |
1033 | | struct cfi_insn_data *cfi_insn) |
1034 | 0 | { |
1035 | | /* The scratchpad FRE currently being updated with each cfi_insn |
1036 | | being interpreted. This FRE eventually gets linked in into the |
1037 | | list of FREs for the specific function. */ |
1038 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1039 | |
|
1040 | 0 | gas_assert (cur_fre); |
1041 | | /* Define the current CFA rule to use the provided offset (but to keep |
1042 | | the old register). However, if the old register is not FP/SP, |
1043 | | skip creating SFrame stack trace info for the function. */ |
1044 | 0 | if ((cur_fre->cfa_base_reg == SFRAME_CFA_FP_REG) |
1045 | 0 | || (cur_fre->cfa_base_reg == SFRAME_CFA_SP_REG)) |
1046 | 0 | { |
1047 | 0 | sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.i); |
1048 | 0 | cur_fre->merge_candidate = false; |
1049 | 0 | } |
1050 | 0 | else |
1051 | 0 | { |
1052 | | /* No CFA base register in effect. Non-SP/FP CFA base register should |
1053 | | not occur, as sframe_xlate_do_def_cfa[_register] would detect this. */ |
1054 | 0 | as_warn (_("no SFrame FDE emitted; " |
1055 | 0 | ".cfi_def_cfa_offset without CFA base register in effect")); |
1056 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
1057 | 0 | } |
1058 | | |
1059 | 0 | return SFRAME_XLATE_OK; |
1060 | 0 | } |
1061 | | |
1062 | | /* Translate DW_CFA_offset into SFrame context. |
1063 | | Return SFRAME_XLATE_OK if success. */ |
1064 | | |
1065 | | static int |
1066 | | sframe_xlate_do_offset (struct sframe_xlate_ctx *xlate_ctx, |
1067 | | struct cfi_insn_data *cfi_insn) |
1068 | 4 | { |
1069 | | /* The scratchpad FRE currently being updated with each cfi_insn |
1070 | | being interpreted. This FRE eventually gets linked in into the |
1071 | | list of FREs for the specific function. */ |
1072 | 4 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1073 | | |
1074 | 4 | gas_assert (cur_fre); |
1075 | | /* Change the rule for the register indicated by the register number to |
1076 | | be the specified offset. */ |
1077 | | /* Ignore SP reg, as it can be recovered from the CFA tracking info. */ |
1078 | 4 | if (cfi_insn->u.ri.reg == SFRAME_CFA_FP_REG) |
1079 | 0 | { |
1080 | 0 | gas_assert (!cur_fre->base_reg); |
1081 | 0 | sframe_fre_set_bp_track (cur_fre, cfi_insn->u.ri.offset); |
1082 | 0 | cur_fre->merge_candidate = false; |
1083 | 0 | } |
1084 | 4 | else if (sframe_ra_tracking_p () |
1085 | 4 | && cfi_insn->u.ri.reg == SFRAME_CFA_RA_REG) |
1086 | 0 | { |
1087 | 0 | sframe_fre_set_ra_track (cur_fre, cfi_insn->u.ri.offset); |
1088 | 0 | cur_fre->merge_candidate = false; |
1089 | 0 | } |
1090 | | /* This is used to track changes to non-rsp registers, skip all others |
1091 | | except FP / RA for now. */ |
1092 | 4 | return SFRAME_XLATE_OK; |
1093 | 4 | } |
1094 | | |
1095 | | /* Translate DW_CFA_val_offset into SFrame context. |
1096 | | Return SFRAME_XLATE_OK if success. |
1097 | | |
1098 | | When CFI_ESC_P is true, the CFI_INSN is hand-crafted using CFI_escape |
1099 | | data. See sframe_xlate_do_escape_val_offset. */ |
1100 | | |
1101 | | static int |
1102 | | sframe_xlate_do_val_offset (const struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED, |
1103 | | const struct cfi_insn_data *cfi_insn, |
1104 | | bool cfi_esc_p) |
1105 | 0 | { |
1106 | | /* Previous value of register is CFA + offset. However, if the specified |
1107 | | register is not interesting (SP, FP, or RA reg), the current |
1108 | | DW_CFA_val_offset instruction can be safely skipped without sacrificing |
1109 | | the asynchronicity of stack trace information. */ |
1110 | 0 | if (cfi_insn->u.ri.reg == SFRAME_CFA_FP_REG |
1111 | 0 | || (sframe_ra_tracking_p () && cfi_insn->u.ri.reg == SFRAME_CFA_RA_REG) |
1112 | | /* Ignore SP reg, if offset matches assumed default rule. */ |
1113 | 0 | || (cfi_insn->u.ri.reg == SFRAME_CFA_SP_REG && cfi_insn->u.ri.offset != 0)) |
1114 | 0 | { |
1115 | 0 | as_warn (_("no SFrame FDE emitted; %s with %s reg %u"), |
1116 | 0 | cfi_esc_p ? ".cfi_escape DW_CFA_val_offset" : ".cfi_val_offset", |
1117 | 0 | sframe_register_name (cfi_insn->u.ri.reg), cfi_insn->u.ri.reg); |
1118 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1119 | 0 | } |
1120 | | |
1121 | | /* Safe to skip. */ |
1122 | 0 | return SFRAME_XLATE_OK; |
1123 | 0 | } |
1124 | | |
1125 | | /* Translate DW_CFA_register into SFrame context. |
1126 | | Return SFRAME_XLATE_OK if success. */ |
1127 | | |
1128 | | static int |
1129 | | sframe_xlate_do_register (struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED, |
1130 | | struct cfi_insn_data *cfi_insn) |
1131 | 0 | { |
1132 | | /* Previous value of register1 is register2. However, if the specified |
1133 | | register1 is not interesting (FP or RA reg), the current DW_CFA_register |
1134 | | instruction can be safely skipped without sacrificing the asynchronicity of |
1135 | | stack trace information. */ |
1136 | 0 | if (cfi_insn->u.rr.reg1 == SFRAME_CFA_FP_REG |
1137 | 0 | || (sframe_ra_tracking_p () && cfi_insn->u.rr.reg1 == SFRAME_CFA_RA_REG) |
1138 | | /* Ignore SP reg, as it can be recovered from the CFA tracking info. */ |
1139 | 0 | ) |
1140 | 0 | { |
1141 | 0 | as_warn (_("no SFrame FDE emitted; %s register %u in .cfi_register"), |
1142 | 0 | sframe_register_name (cfi_insn->u.rr.reg1), cfi_insn->u.rr.reg1); |
1143 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1144 | 0 | } |
1145 | | |
1146 | | /* Safe to skip. */ |
1147 | 0 | return SFRAME_XLATE_OK; |
1148 | 0 | } |
1149 | | |
1150 | | /* Translate DW_CFA_remember_state into SFrame context. |
1151 | | Return SFRAME_XLATE_OK if success. */ |
1152 | | |
1153 | | static int |
1154 | | sframe_xlate_do_remember_state (struct sframe_xlate_ctx *xlate_ctx) |
1155 | 0 | { |
1156 | 0 | struct sframe_row_entry *last_fre = xlate_ctx->last_fre; |
1157 | | |
1158 | | /* If there is no FRE state to remember, nothing to do here. Return |
1159 | | early with non-zero error code, this will cause no SFrame stack trace |
1160 | | info for the function involved. */ |
1161 | 0 | if (!last_fre) |
1162 | 0 | { |
1163 | 0 | as_warn (_("no SFrame FDE emitted; " |
1164 | 0 | ".cfi_remember_state without prior SFrame FRE state")); |
1165 | 0 | return SFRAME_XLATE_ERR_INVAL; |
1166 | 0 | } |
1167 | | |
1168 | 0 | if (!xlate_ctx->remember_fre) |
1169 | 0 | xlate_ctx->remember_fre = sframe_row_entry_new (); |
1170 | 0 | sframe_row_entry_initialize (xlate_ctx->remember_fre, last_fre); |
1171 | |
|
1172 | 0 | return SFRAME_XLATE_OK; |
1173 | 0 | } |
1174 | | |
1175 | | /* Translate DW_CFA_restore_state into SFrame context. |
1176 | | Return SFRAME_XLATE_OK if success. */ |
1177 | | |
1178 | | static int |
1179 | | sframe_xlate_do_restore_state (struct sframe_xlate_ctx *xlate_ctx) |
1180 | 0 | { |
1181 | | /* The scratchpad FRE currently being updated with each cfi_insn |
1182 | | being interpreted. This FRE eventually gets linked in into the |
1183 | | list of FREs for the specific function. */ |
1184 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1185 | |
|
1186 | 0 | gas_assert (xlate_ctx->remember_fre); |
1187 | 0 | gas_assert (cur_fre && cur_fre->merge_candidate); |
1188 | | |
1189 | | /* Get the CFA state from the DW_CFA_remember_state insn. */ |
1190 | 0 | sframe_row_entry_initialize (cur_fre, xlate_ctx->remember_fre); |
1191 | | /* The PC boundaries of the current SFrame FRE are updated |
1192 | | via other machinery. */ |
1193 | 0 | cur_fre->merge_candidate = false; |
1194 | 0 | return SFRAME_XLATE_OK; |
1195 | 0 | } |
1196 | | |
1197 | | /* Translate DW_CFA_restore into SFrame context. |
1198 | | Return SFRAME_XLATE_OK if success. */ |
1199 | | |
1200 | | static int |
1201 | | sframe_xlate_do_restore (struct sframe_xlate_ctx *xlate_ctx, |
1202 | | struct cfi_insn_data *cfi_insn) |
1203 | 0 | { |
1204 | 0 | struct sframe_row_entry *cie_fre = xlate_ctx->first_fre; |
1205 | | /* The scratchpad FRE currently being updated with each cfi_insn |
1206 | | being interpreted. This FRE eventually gets linked in into the |
1207 | | list of FREs for the specific function. */ |
1208 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1209 | | |
1210 | | /* Change the rule for the indicated register to the rule assigned to |
1211 | | it by the initial_instructions in the CIE. */ |
1212 | 0 | gas_assert (cie_fre); |
1213 | | /* SFrame FREs track only CFA and FP / RA for backtracing purposes; |
1214 | | skip the other .cfi_restore directives. */ |
1215 | 0 | if (cfi_insn->u.r == SFRAME_CFA_FP_REG) |
1216 | 0 | { |
1217 | 0 | gas_assert (cur_fre); |
1218 | 0 | cur_fre->bp_loc = cie_fre->bp_loc; |
1219 | 0 | cur_fre->bp_offset = cie_fre->bp_offset; |
1220 | 0 | cur_fre->merge_candidate = false; |
1221 | 0 | } |
1222 | 0 | else if (sframe_ra_tracking_p () |
1223 | 0 | && cfi_insn->u.r == SFRAME_CFA_RA_REG) |
1224 | 0 | { |
1225 | 0 | gas_assert (cur_fre); |
1226 | 0 | cur_fre->ra_loc = cie_fre->ra_loc; |
1227 | 0 | cur_fre->ra_offset = cie_fre->ra_offset; |
1228 | 0 | cur_fre->merge_candidate = false; |
1229 | 0 | } |
1230 | 0 | return SFRAME_XLATE_OK; |
1231 | 0 | } |
1232 | | |
1233 | | /* Translate DW_CFA_AARCH64_negate_ra_state into SFrame context. |
1234 | | Return SFRAME_XLATE_OK if success. */ |
1235 | | |
1236 | | static int |
1237 | | sframe_xlate_do_aarch64_negate_ra_state (struct sframe_xlate_ctx *xlate_ctx, |
1238 | | struct cfi_insn_data *cfi_insn ATTRIBUTE_UNUSED) |
1239 | 0 | { |
1240 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1241 | |
|
1242 | 0 | gas_assert (cur_fre); |
1243 | | /* Toggle the mangled RA status bit. */ |
1244 | 0 | cur_fre->mangled_ra_p = !cur_fre->mangled_ra_p; |
1245 | 0 | cur_fre->merge_candidate = false; |
1246 | |
|
1247 | 0 | return SFRAME_XLATE_OK; |
1248 | 0 | } |
1249 | | |
1250 | | /* Translate DW_CFA_AARCH64_negate_ra_state_with_pc into SFrame context. |
1251 | | Return SFRAME_XLATE_OK if success. */ |
1252 | | |
1253 | | static int |
1254 | | sframe_xlate_do_aarch64_negate_ra_state_with_pc (struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED, |
1255 | | struct cfi_insn_data *cfi_insn ATTRIBUTE_UNUSED) |
1256 | 0 | { |
1257 | 0 | as_warn (_("no SFrame FDE emitted; .cfi_negate_ra_state_with_pc")); |
1258 | | /* The used signing method should be encoded inside the FDE in SFrame v3. |
1259 | | For now, PAuth_LR extension is not supported with SFrame. */ |
1260 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1261 | 0 | } |
1262 | | |
1263 | | /* Translate DW_CFA_GNU_window_save into SFrame context. |
1264 | | DW_CFA_GNU_window_save is a DWARF Sparc extension, but is multiplexed with a |
1265 | | directive of DWARF AArch64 extension: DW_CFA_AARCH64_negate_ra_state. |
1266 | | The AArch64 backend of GCC 14 and older versions was emitting mistakenly the |
1267 | | Sparc CFI directive (.cfi_window_save). From GCC 15, the AArch64 backend |
1268 | | only emits .cfi_negate_ra_state. For backward compatibility, the handler for |
1269 | | .cfi_window_save needs to check whether the directive was used in a AArch64 |
1270 | | ABI context or not. |
1271 | | Return SFRAME_XLATE_OK if success. */ |
1272 | | |
1273 | | static int |
1274 | | sframe_xlate_do_gnu_window_save (struct sframe_xlate_ctx *xlate_ctx, |
1275 | | struct cfi_insn_data *cfi_insn) |
1276 | 3 | { |
1277 | 3 | unsigned char abi_arch = sframe_get_abi_arch (); |
1278 | | |
1279 | | /* Translate DW_CFA_AARCH64_negate_ra_state into SFrame context. */ |
1280 | 3 | if (abi_arch == SFRAME_ABI_AARCH64_ENDIAN_BIG |
1281 | 3 | || abi_arch == SFRAME_ABI_AARCH64_ENDIAN_LITTLE) |
1282 | 0 | return sframe_xlate_do_aarch64_negate_ra_state (xlate_ctx, cfi_insn); |
1283 | | |
1284 | 3 | as_warn (_("no SFrame FDE emitted; .cfi_window_save")); |
1285 | 3 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1286 | 3 | } |
1287 | | |
1288 | | /* Handle DW_CFA_expression in .cfi_escape. |
1289 | | |
1290 | | As with sframe_xlate_do_cfi_escape, the intent of this function is to detect |
1291 | | only the simple-to-process but common cases, where skipping over the escape |
1292 | | expr data does not affect correctness of the SFrame stack trace data. |
1293 | | |
1294 | | Sets CALLER_WARN_P for skipped cases (and returns SFRAME_XLATE_OK) where the |
1295 | | caller must warn. The caller then must also set |
1296 | | SFRAME_XLATE_ERR_NOTREPRESENTED for their callers. */ |
1297 | | |
1298 | | static int |
1299 | | sframe_xlate_do_escape_expr (const struct sframe_xlate_ctx *xlate_ctx, |
1300 | | const struct cfi_insn_data *cfi_insn, |
1301 | | bool *caller_warn_p) |
1302 | 0 | { |
1303 | 0 | const struct cfi_escape_data *e = cfi_insn->u.esc; |
1304 | 0 | int err = SFRAME_XLATE_OK; |
1305 | 0 | unsigned int reg = 0; |
1306 | 0 | unsigned int i = 0; |
1307 | | |
1308 | | /* Check roughly for an expression |
1309 | | DW_CFA_expression: r1 (rdx) (DW_OP_bregN (reg): OFFSET). */ |
1310 | 0 | #define CFI_ESC_NUM_EXP 4 |
1311 | 0 | offsetT items[CFI_ESC_NUM_EXP] = {0}; |
1312 | 0 | while (e->next) |
1313 | 0 | { |
1314 | 0 | e = e->next; |
1315 | 0 | if ((i == 2 && (items[1] != 2)) /* Expected len of 2 in DWARF expr. */ |
1316 | | /* We do not care for the exact values of items[2] and items[3], |
1317 | | so an explicit check for O_constant isnt necessary either. */ |
1318 | 0 | || i >= CFI_ESC_NUM_EXP |
1319 | 0 | || (i < 2 |
1320 | 0 | && (e->exp.X_op != O_constant |
1321 | 0 | || e->type != CFI_ESC_byte |
1322 | 0 | || e->reloc != TC_PARSE_CONS_RETURN_NONE))) |
1323 | 0 | goto warn_and_exit; |
1324 | 0 | items[i] = e->exp.X_add_number; |
1325 | 0 | i++; |
1326 | 0 | } |
1327 | | |
1328 | 0 | if (i <= CFI_ESC_NUM_EXP - 1) |
1329 | 0 | goto warn_and_exit; |
1330 | | |
1331 | | /* reg operand to DW_CFA_expression is ULEB128. For the purpose at hand, |
1332 | | however, the register value will be less than 128 (CFI_ESC_NUM_EXP set |
1333 | | to 4). See an extended comment in sframe_xlate_do_escape_expr for why |
1334 | | reading ULEB is okay to skip without sacrificing correctness. */ |
1335 | 0 | reg = items[0]; |
1336 | 0 | #undef CFI_ESC_NUM_EXP |
1337 | |
|
1338 | 0 | if (reg == SFRAME_CFA_SP_REG || reg == SFRAME_CFA_FP_REG |
1339 | 0 | || (sframe_ra_tracking_p () && reg == SFRAME_CFA_RA_REG) |
1340 | 0 | || reg == xlate_ctx->cur_fre->cfa_base_reg) |
1341 | 0 | { |
1342 | 0 | as_warn (_("no SFrame FDE emitted; " |
1343 | 0 | ".cfi_escape DW_CFA_expression with %s reg %u"), |
1344 | 0 | sframe_register_name (reg), reg); |
1345 | 0 | err = SFRAME_XLATE_ERR_NOTREPRESENTED; |
1346 | 0 | } |
1347 | | /* else safe to skip, so continue to return SFRAME_XLATE_OK. */ |
1348 | |
|
1349 | 0 | return err; |
1350 | | |
1351 | 0 | warn_and_exit: |
1352 | 0 | *caller_warn_p = true; |
1353 | 0 | return err; |
1354 | 0 | } |
1355 | | |
1356 | | /* Handle DW_CFA_val_offset in .cfi_escape. |
1357 | | |
1358 | | As with sframe_xlate_do_cfi_escape, the intent of this function is to detect |
1359 | | only the simple-to-process but common cases, where skipping over the escape |
1360 | | expr data does not affect correctness of the SFrame stack trace data. |
1361 | | |
1362 | | Sets CALLER_WARN_P for skipped cases (and returns SFRAME_XLATE_OK) where the |
1363 | | caller must warn. The caller then must also set |
1364 | | SFRAME_XLATE_ERR_NOTREPRESENTED for their callers. */ |
1365 | | |
1366 | | static int |
1367 | | sframe_xlate_do_escape_val_offset (const struct sframe_xlate_ctx *xlate_ctx, |
1368 | | const struct cfi_insn_data *cfi_insn, |
1369 | | bool *caller_warn_p) |
1370 | 0 | { |
1371 | 0 | const struct cfi_escape_data *e = cfi_insn->u.esc; |
1372 | 0 | int err = SFRAME_XLATE_OK; |
1373 | 0 | unsigned int i = 0; |
1374 | 0 | unsigned int reg; |
1375 | 0 | offsetT offset; |
1376 | | |
1377 | | /* Check for (DW_CFA_val_offset reg scaled_offset) sequence. */ |
1378 | 0 | #define CFI_ESC_NUM_EXP 2 |
1379 | 0 | offsetT items[CFI_ESC_NUM_EXP] = {0}; |
1380 | 0 | while (e->next) |
1381 | 0 | { |
1382 | 0 | e = e->next; |
1383 | 0 | if (i >= CFI_ESC_NUM_EXP || e->exp.X_op != O_constant |
1384 | 0 | || e->type != CFI_ESC_byte |
1385 | 0 | || e->reloc != TC_PARSE_CONS_RETURN_NONE) |
1386 | 0 | goto warn_and_exit; |
1387 | 0 | items[i] = e->exp.X_add_number; |
1388 | 0 | i++; |
1389 | 0 | } |
1390 | 0 | if (i <= CFI_ESC_NUM_EXP - 1) |
1391 | 0 | goto warn_and_exit; |
1392 | | |
1393 | | /* Both arguments to DW_CFA_val_offset are ULEB128. Especially with APX (on |
1394 | | x86) we're going to see DWARF register numbers above 127, for the extended |
1395 | | GPRs. And large enough stack frames would also require multi-byte offset |
1396 | | representation. However, since we limit our focus on cases when |
1397 | | CFI_ESC_NUM_EXP is 2, reading ULEB can be skipped. IOW, although not |
1398 | | ideal, SFrame FDE generation in case of an APX register in |
1399 | | DW_CFA_val_offset is being skipped (PS: this does _not_ mean incorrect |
1400 | | SFrame stack trace data). |
1401 | | |
1402 | | Recall that the intent here is to check for simple and prevalent cases, |
1403 | | when feasible. */ |
1404 | | |
1405 | 0 | reg = items[0]; |
1406 | 0 | offset = items[1]; |
1407 | 0 | #undef CFI_ESC_NUM_EXP |
1408 | | |
1409 | | /* Invoke sframe_xlate_do_val_offset itself for checking. */ |
1410 | 0 | struct cfi_insn_data temp = { |
1411 | 0 | .insn = DW_CFA_val_offset, |
1412 | 0 | .u = { |
1413 | 0 | .ri = { |
1414 | 0 | .reg = reg, |
1415 | 0 | .offset = offset * DWARF2_CIE_DATA_ALIGNMENT |
1416 | 0 | } |
1417 | 0 | } |
1418 | 0 | }; |
1419 | 0 | err = sframe_xlate_do_val_offset (xlate_ctx, &temp, true); |
1420 | 0 | return err; |
1421 | | |
1422 | 0 | warn_and_exit: |
1423 | 0 | *caller_warn_p = true; |
1424 | 0 | return err; |
1425 | 0 | } |
1426 | | |
1427 | | /* Handle CFI_escape in SFrame context. |
1428 | | |
1429 | | .cfi_escape CFI directive allows the user to add arbitrary data to the |
1430 | | unwind info. DWARF expressions commonly follow after CFI_escape (fake CFI) |
1431 | | DWARF opcode. One might also use CFI_escape to add OS-specific CFI opcodes |
1432 | | even. |
1433 | | |
1434 | | Complex unwind info added using .cfi_escape directive _may_ be of no |
1435 | | consequence for SFrame when the affected registers are not SP, FP, RA or |
1436 | | CFA. The challenge in confirming the afore-mentioned is that it needs full |
1437 | | parsing (and validation) of the data presented after .cfi_escape. Here we |
1438 | | take a case-by-case approach towards skipping _some_ instances of |
1439 | | .cfi_escape: skip those that can be *easily* determined to be harmless in |
1440 | | the context of SFrame stack trace information. |
1441 | | |
1442 | | This function partially processes data following .cfi_escape and returns |
1443 | | SFRAME_XLATE_OK if OK to skip. */ |
1444 | | |
1445 | | static int |
1446 | | sframe_xlate_do_cfi_escape (const struct sframe_xlate_ctx *xlate_ctx, |
1447 | | const struct cfi_insn_data *cfi_insn) |
1448 | 0 | { |
1449 | 0 | const struct cfi_escape_data *e; |
1450 | 0 | bool warn_p = false; |
1451 | 0 | int err = SFRAME_XLATE_OK; |
1452 | 0 | offsetT firstop; |
1453 | |
|
1454 | 0 | e = cfi_insn->u.esc; |
1455 | |
|
1456 | 0 | if (!e) |
1457 | 0 | return SFRAME_XLATE_ERR_INVAL; |
1458 | | |
1459 | 0 | if (e->exp.X_op != O_constant |
1460 | 0 | || e->type != CFI_ESC_byte |
1461 | 0 | || e->reloc != TC_PARSE_CONS_RETURN_NONE) |
1462 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
1463 | | |
1464 | 0 | firstop = e->exp.X_add_number; |
1465 | 0 | switch (firstop) |
1466 | 0 | { |
1467 | 0 | case DW_CFA_nop: |
1468 | | /* One or more nops together are harmless for SFrame. */ |
1469 | 0 | while (e->next) |
1470 | 0 | { |
1471 | 0 | e = e->next; |
1472 | 0 | if (e->exp.X_op != O_constant || e->exp.X_add_number != DW_CFA_nop |
1473 | 0 | || e->type != CFI_ESC_byte |
1474 | 0 | || e->reloc != TC_PARSE_CONS_RETURN_NONE) |
1475 | 0 | { |
1476 | 0 | warn_p = true; |
1477 | 0 | break; |
1478 | 0 | } |
1479 | 0 | } |
1480 | 0 | break; |
1481 | | |
1482 | 0 | case DW_CFA_expression: |
1483 | 0 | err = sframe_xlate_do_escape_expr (xlate_ctx, cfi_insn, &warn_p); |
1484 | 0 | break; |
1485 | | |
1486 | 0 | case DW_CFA_val_offset: |
1487 | 0 | err = sframe_xlate_do_escape_val_offset (xlate_ctx, cfi_insn, &warn_p); |
1488 | 0 | break; |
1489 | | |
1490 | | /* FIXME - Also add processing for DW_CFA_GNU_args_size in future? */ |
1491 | | |
1492 | 0 | default: |
1493 | 0 | warn_p = true; |
1494 | 0 | break; |
1495 | 0 | } |
1496 | | |
1497 | 0 | if (warn_p) |
1498 | 0 | { |
1499 | | /* In all other cases (e.g., DW_CFA_def_cfa_expression or other |
1500 | | OS-specific CFI opcodes), skip inspecting the DWARF expression. |
1501 | | This may impact the asynchronicity due to loss of coverage. |
1502 | | Continue to warn the user and bail out. */ |
1503 | 0 | as_warn (_("no SFrame FDE emitted; .cfi_escape with op (%#lx)"), |
1504 | 0 | (unsigned long)firstop); |
1505 | 0 | err = SFRAME_XLATE_ERR_NOTREPRESENTED; |
1506 | 0 | } |
1507 | |
|
1508 | 0 | return err; |
1509 | 0 | } |
1510 | | |
1511 | | /* Translate DW_CFA_undefined into SFrame context. |
1512 | | |
1513 | | DW_CFA_undefined op indicates that from now on, the previous value of |
1514 | | register can’t be restored anymore. In SFrame stack trace, we cannot |
1515 | | represent such a semantic. So, we skip generating an SFrame FDE for this, |
1516 | | when a register of interest is used with DW_CFA_undefined. |
1517 | | |
1518 | | Return SFRAME_XLATE_OK if success. */ |
1519 | | |
1520 | | static int |
1521 | | sframe_xlate_do_cfi_undefined (const struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED, |
1522 | | const struct cfi_insn_data *cfi_insn) |
1523 | 2 | { |
1524 | 2 | if (cfi_insn->u.r == SFRAME_CFA_FP_REG |
1525 | 2 | || cfi_insn->u.r == SFRAME_CFA_RA_REG |
1526 | 2 | || cfi_insn->u.r == SFRAME_CFA_SP_REG) |
1527 | 0 | { |
1528 | 0 | as_warn (_("no SFrame FDE emitted; %s reg %u in .cfi_undefined"), |
1529 | 0 | sframe_register_name (cfi_insn->u.r), cfi_insn->u.r); |
1530 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1531 | 0 | } |
1532 | | |
1533 | | /* Safe to skip. */ |
1534 | 2 | return SFRAME_XLATE_OK; |
1535 | 2 | } |
1536 | | |
1537 | | /* Translate DW_CFA_same_value into SFrame context. |
1538 | | |
1539 | | DW_CFA_same_value op indicates that current value of register is the same as |
1540 | | in the previous frame, i.e. no restoration needed. In SFrame stack trace |
1541 | | format, the handling is done similar to DW_CFA_restore. |
1542 | | |
1543 | | For SFRAME_CFA_RA_REG, if RA-tracking is enabled, reset the SFrame FRE state |
1544 | | for REG_RA to indicate that register does not need restoration. P.S.: Even |
1545 | | though resetting just REG_RA may be contradicting the AArch64 ABI (as Frame |
1546 | | Record contains for FP and LR), sframe_xlate_do_same_value () does not |
1547 | | detect the case and assumes the users' DW_CFA_same_value SFRAME_CFA_RA_REG |
1548 | | has a sound reason. For ABIs, where RA-tracking is disabled, handle it |
1549 | | similar to DW_CFA_restore: ignore the directive, it is safe to skip. The |
1550 | | reasoning is similar to that for DW_CFA_restore: if such a restoration was |
1551 | | meant to be of any consequence, there must have been the necessary CFI |
1552 | | directives for updating the CFA rule too such that the recovered RA from |
1553 | | stack is valid. |
1554 | | |
1555 | | SFrame based stacktracers will implement CFA-based SP recovery for all ABIs: |
1556 | | SP for previous frame is based on the applicable CFA-rule. There is no |
1557 | | representation in SFrame to indicate "no restoration needed" for REG_SP, |
1558 | | when going to the previous frame. That said, if DW_CFA_same_value is seen |
1559 | | for SFRAME_CFA_SP_REG, handle it similar to DW_CFA_restore: ignore the |
1560 | | directive, it is safe to skip. The reasoning is similar to that for |
1561 | | DW_CFA_restore: if such a restoration was meant to be of any consequence, |
1562 | | there must have been the necessary CFI directives for updating the CFA rule |
1563 | | too. The latter will be duly processed by the SFrame generation code, as |
1564 | | expected. |
1565 | | |
1566 | | For SFRAME_CFA_FP_REG, reset the state of the current FRE to indicate that |
1567 | | the value is the same as previous frame. |
1568 | | |
1569 | | Return SFRAME_XLATE_OK if success. */ |
1570 | | |
1571 | | static int |
1572 | | sframe_xlate_do_same_value (const struct sframe_xlate_ctx *xlate_ctx, |
1573 | | const struct cfi_insn_data *cfi_insn) |
1574 | 0 | { |
1575 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1576 | |
|
1577 | 0 | if (sframe_ra_tracking_p () && cfi_insn->u.r == SFRAME_CFA_RA_REG) |
1578 | 0 | { |
1579 | 0 | cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_REG; |
1580 | 0 | cur_fre->ra_offset = 0; |
1581 | 0 | cur_fre->merge_candidate = false; |
1582 | 0 | } |
1583 | 0 | else if (cfi_insn->u.r == SFRAME_CFA_FP_REG) |
1584 | 0 | { |
1585 | 0 | cur_fre->bp_loc = SFRAME_FRE_ELEM_LOC_REG; |
1586 | 0 | cur_fre->bp_offset = 0; |
1587 | 0 | cur_fre->merge_candidate = false; |
1588 | 0 | } |
1589 | | |
1590 | | /* Safe to skip. */ |
1591 | 0 | return SFRAME_XLATE_OK; |
1592 | 0 | } |
1593 | | |
1594 | | /* Returns the DWARF call frame instruction name or fake CFI name for the |
1595 | | specified CFI opcode, or NULL if the value is not recognized. */ |
1596 | | |
1597 | | static const char * |
1598 | | sframe_get_cfi_name (int cfi_opc) |
1599 | 0 | { |
1600 | 0 | const char *cfi_name; |
1601 | |
|
1602 | 0 | switch (cfi_opc) |
1603 | 0 | { |
1604 | | /* Fake CFI type; outside the byte range of any real CFI insn. */ |
1605 | | /* See gas/dw2gencfi.h. */ |
1606 | 0 | case CFI_adjust_cfa_offset: |
1607 | 0 | cfi_name = "CFI_adjust_cfa_offset"; |
1608 | 0 | break; |
1609 | 0 | case CFI_return_column: |
1610 | 0 | cfi_name = "CFI_return_column"; |
1611 | 0 | break; |
1612 | 0 | case CFI_rel_offset: |
1613 | 0 | cfi_name = "CFI_rel_offset"; |
1614 | 0 | break; |
1615 | 0 | case CFI_escape: |
1616 | 0 | cfi_name = "CFI_escape"; |
1617 | 0 | break; |
1618 | 0 | case CFI_signal_frame: |
1619 | 0 | cfi_name = "CFI_signal_frame"; |
1620 | 0 | break; |
1621 | 0 | case CFI_val_encoded_addr: |
1622 | 0 | cfi_name = "CFI_val_encoded_addr"; |
1623 | 0 | break; |
1624 | 0 | case CFI_label: |
1625 | 0 | cfi_name = "CFI_label"; |
1626 | 0 | break; |
1627 | 0 | default: |
1628 | 0 | cfi_name = get_DW_CFA_name (cfi_opc); |
1629 | 0 | } |
1630 | | |
1631 | 0 | return cfi_name; |
1632 | 0 | } |
1633 | | |
1634 | | /* Process CFI_INSN and update the translation context with the FRE |
1635 | | information. |
1636 | | |
1637 | | Returns an error code (sframe_xlate_err) if CFI_INSN is not successfully |
1638 | | processed. */ |
1639 | | |
1640 | | static int |
1641 | | sframe_do_cfi_insn (struct sframe_xlate_ctx *xlate_ctx, |
1642 | | struct cfi_insn_data *cfi_insn) |
1643 | 17 | { |
1644 | 17 | int err = 0; |
1645 | | |
1646 | | /* Atleast one cfi_insn per FDE is expected. */ |
1647 | 17 | gas_assert (cfi_insn); |
1648 | 17 | int op = cfi_insn->insn; |
1649 | | |
1650 | 17 | switch (op) |
1651 | 17 | { |
1652 | 4 | case DW_CFA_advance_loc: |
1653 | 4 | err = sframe_xlate_do_advance_loc (xlate_ctx, cfi_insn); |
1654 | 4 | break; |
1655 | 4 | case DW_CFA_def_cfa: |
1656 | 4 | err = sframe_xlate_do_def_cfa (xlate_ctx, cfi_insn); |
1657 | 4 | break; |
1658 | 0 | case DW_CFA_def_cfa_register: |
1659 | 0 | err = sframe_xlate_do_def_cfa_register (xlate_ctx, cfi_insn); |
1660 | 0 | break; |
1661 | 0 | case DW_CFA_def_cfa_offset: |
1662 | 0 | err = sframe_xlate_do_def_cfa_offset (xlate_ctx, cfi_insn); |
1663 | 0 | break; |
1664 | 4 | case DW_CFA_offset: |
1665 | 4 | err = sframe_xlate_do_offset (xlate_ctx, cfi_insn); |
1666 | 4 | break; |
1667 | 0 | case DW_CFA_val_offset: |
1668 | 0 | err = sframe_xlate_do_val_offset (xlate_ctx, cfi_insn, false); |
1669 | 0 | break; |
1670 | 0 | case DW_CFA_remember_state: |
1671 | 0 | err = sframe_xlate_do_remember_state (xlate_ctx); |
1672 | 0 | break; |
1673 | 0 | case DW_CFA_restore_state: |
1674 | 0 | err = sframe_xlate_do_restore_state (xlate_ctx); |
1675 | 0 | break; |
1676 | 0 | case DW_CFA_restore: |
1677 | 0 | err = sframe_xlate_do_restore (xlate_ctx, cfi_insn); |
1678 | 0 | break; |
1679 | | /* DW_CFA_AARCH64_negate_ra_state is multiplexed with |
1680 | | DW_CFA_GNU_window_save. */ |
1681 | 3 | case DW_CFA_GNU_window_save: |
1682 | 3 | err = sframe_xlate_do_gnu_window_save (xlate_ctx, cfi_insn); |
1683 | 3 | break; |
1684 | 0 | case DW_CFA_AARCH64_negate_ra_state_with_pc: |
1685 | 0 | err = sframe_xlate_do_aarch64_negate_ra_state_with_pc (xlate_ctx, cfi_insn); |
1686 | 0 | break; |
1687 | 0 | case DW_CFA_register: |
1688 | 0 | err = sframe_xlate_do_register (xlate_ctx, cfi_insn); |
1689 | 0 | break; |
1690 | 0 | case CFI_escape: |
1691 | 0 | err = sframe_xlate_do_cfi_escape (xlate_ctx, cfi_insn); |
1692 | 0 | break; |
1693 | 2 | case DW_CFA_undefined: |
1694 | 2 | err = sframe_xlate_do_cfi_undefined (xlate_ctx, cfi_insn); |
1695 | 2 | break; |
1696 | 0 | case DW_CFA_same_value: |
1697 | 0 | err = sframe_xlate_do_same_value (xlate_ctx, cfi_insn); |
1698 | 0 | break; |
1699 | 0 | default: |
1700 | | /* Other skipped operations may, however, impact the asynchronicity. */ |
1701 | 0 | { |
1702 | 0 | const char *cfi_name = sframe_get_cfi_name (op); |
1703 | |
|
1704 | 0 | if (!cfi_name) |
1705 | 0 | cfi_name = _("(unknown)"); |
1706 | 0 | as_warn (_("no SFrame FDE emitted; CFI insn %s (%#x)"), |
1707 | 0 | cfi_name, op); |
1708 | 0 | err = SFRAME_XLATE_ERR_NOTREPRESENTED; |
1709 | 0 | } |
1710 | 17 | } |
1711 | | |
1712 | | /* Any error will cause no SFrame FDE later. The user has already been |
1713 | | warned. */ |
1714 | 17 | return err; |
1715 | 17 | } |
1716 | | |
1717 | | |
1718 | | static int |
1719 | | sframe_do_fde (struct sframe_xlate_ctx *xlate_ctx, |
1720 | | const struct fde_entry *dw_fde) |
1721 | 4 | { |
1722 | 4 | struct cfi_insn_data *cfi_insn; |
1723 | 4 | int err = SFRAME_XLATE_OK; |
1724 | | |
1725 | 4 | xlate_ctx->dw_fde = dw_fde; |
1726 | | |
1727 | | /* SFrame format cannot represent a non-default DWARF return column reg. */ |
1728 | 4 | if (xlate_ctx->dw_fde->return_column != DWARF2_DEFAULT_RETURN_COLUMN) |
1729 | 0 | { |
1730 | 0 | as_warn (_("no SFrame FDE emitted; non-default RA register %u"), |
1731 | 0 | xlate_ctx->dw_fde->return_column); |
1732 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
1733 | 0 | } |
1734 | | |
1735 | | /* Iterate over the CFIs and create SFrame FREs. */ |
1736 | 18 | for (cfi_insn = dw_fde->data; cfi_insn; cfi_insn = cfi_insn->next) |
1737 | 17 | { |
1738 | | /* Translate each CFI, and buffer the state in translation context. */ |
1739 | 17 | err = sframe_do_cfi_insn (xlate_ctx, cfi_insn); |
1740 | 17 | if (err != SFRAME_XLATE_OK) |
1741 | 3 | { |
1742 | | /* Skip generating SFrame stack trace info for the function if any |
1743 | | offending CFI is encountered by sframe_do_cfi_insn (). Warning |
1744 | | message already printed by sframe_do_cfi_insn (). */ |
1745 | 3 | return err; /* Return the error code. */ |
1746 | 3 | } |
1747 | 17 | } |
1748 | | |
1749 | | /* Link in the scratchpad FRE that the last few CFI insns helped create. */ |
1750 | 1 | if (xlate_ctx->cur_fre) |
1751 | 1 | { |
1752 | 1 | sframe_xlate_ctx_add_fre (xlate_ctx, xlate_ctx->cur_fre); |
1753 | 1 | xlate_ctx->cur_fre = NULL; |
1754 | 1 | } |
1755 | | /* Designate the end of the last SFrame FRE. */ |
1756 | 1 | if (xlate_ctx->last_fre) |
1757 | 1 | { |
1758 | 1 | xlate_ctx->last_fre->pc_end |
1759 | 1 | = get_dw_fde_end_addrS (xlate_ctx->dw_fde); |
1760 | 1 | } |
1761 | | |
1762 | 1 | if (sframe_ra_tracking_p ()) |
1763 | 0 | { |
1764 | 0 | struct sframe_row_entry *fre; |
1765 | | |
1766 | | /* Iterate over the scratchpad FREs and validate them. */ |
1767 | 0 | for (fre = xlate_ctx->first_fre; fre; fre = fre->next) |
1768 | 0 | { |
1769 | | /* SFrame format cannot represent FP on stack without RA on stack. */ |
1770 | 0 | if (fre->ra_loc != SFRAME_FRE_ELEM_LOC_STACK |
1771 | 0 | && fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK) |
1772 | 0 | { |
1773 | 0 | as_warn (_("no SFrame FDE emitted; FP without RA on stack")); |
1774 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
1775 | 0 | } |
1776 | 0 | } |
1777 | 0 | } |
1778 | | |
1779 | 1 | return SFRAME_XLATE_OK; |
1780 | 1 | } |
1781 | | |
1782 | | /* Create SFrame stack trace info for all functions. |
1783 | | |
1784 | | This function consumes the already generated DWARF FDEs (by dw2gencfi) and |
1785 | | generates data which is later emitted as stack trace information encoded in |
1786 | | the SFrame format. */ |
1787 | | |
1788 | | static void |
1789 | | create_sframe_all (void) |
1790 | 1 | { |
1791 | 1 | struct fde_entry *dw_fde = NULL; |
1792 | 1 | struct sframe_func_entry *sframe_fde = NULL; |
1793 | | |
1794 | 1 | struct sframe_xlate_ctx *xlate_ctx = sframe_xlate_ctx_alloc (); |
1795 | | |
1796 | 5 | for (dw_fde = all_fde_data; dw_fde ; dw_fde = dw_fde->next) |
1797 | 4 | { |
1798 | 4 | sframe_fde = sframe_fde_alloc (); |
1799 | | /* Initialize the translation context with information anew. */ |
1800 | 4 | sframe_xlate_ctx_init (xlate_ctx); |
1801 | | |
1802 | | /* Process and link SFrame FDEs if no error. Also skip adding an SFrame |
1803 | | FDE if it does not contain any SFrame FREs. There is little use of an |
1804 | | SFrame FDE if there is no stack tracing information for the |
1805 | | function. */ |
1806 | 4 | int err = sframe_do_fde (xlate_ctx, dw_fde); |
1807 | 4 | if (err || xlate_ctx->num_xlate_fres == 0) |
1808 | 3 | { |
1809 | 3 | sframe_xlate_ctx_cleanup (xlate_ctx); |
1810 | 3 | sframe_fde_free (sframe_fde); |
1811 | 3 | } |
1812 | 1 | else |
1813 | 1 | { |
1814 | | /* All done. Transfer the state from the SFrame translation |
1815 | | context to the SFrame FDE. */ |
1816 | 1 | sframe_xlate_ctx_finalize (xlate_ctx, sframe_fde); |
1817 | 1 | *last_sframe_fde = sframe_fde; |
1818 | 1 | last_sframe_fde = &sframe_fde->next; |
1819 | 1 | } |
1820 | 4 | } |
1821 | | |
1822 | 1 | XDELETE (xlate_ctx); |
1823 | 1 | } |
1824 | | |
1825 | | void |
1826 | | output_sframe (segT sframe_seg) |
1827 | 1 | { |
1828 | 1 | (void) sframe_seg; |
1829 | | |
1830 | | /* Setup the version specific access functions. */ |
1831 | 1 | sframe_set_version (SFRAME_VERSION_2); |
1832 | | |
1833 | | /* Process all fdes and create SFrame stack trace information. */ |
1834 | 1 | create_sframe_all (); |
1835 | | |
1836 | 1 | output_sframe_internal (); |
1837 | 1 | } |
1838 | | |
1839 | | #else /* support_sframe_p */ |
1840 | | |
1841 | | void |
1842 | | output_sframe (segT sframe_seg ATTRIBUTE_UNUSED) |
1843 | | { |
1844 | | } |
1845 | | |
1846 | | #endif /* support_sframe_p */ |