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