/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-2023 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 | | /* By default, use 32-bit relocations from .sframe into .text. */ |
30 | | #ifndef SFRAME_RELOC_SIZE |
31 | 0 | # define SFRAME_RELOC_SIZE 4 |
32 | | #endif |
33 | | |
34 | | /* Whether frame row entries track RA. |
35 | | |
36 | | A target may not need return address tracking for stack tracing. If it |
37 | | does need the same, SFRAME_CFA_RA_REG must be defined with the return |
38 | | address register number. */ |
39 | | |
40 | | #if defined (sframe_ra_tracking_p) && defined (SFRAME_CFA_RA_REG) |
41 | | # ifndef SFRAME_FRE_RA_TRACKING |
42 | | # define SFRAME_FRE_RA_TRACKING 1 |
43 | | # endif |
44 | | #endif |
45 | | |
46 | | /* SFrame FRE type selection optimization is an optimization for size. |
47 | | |
48 | | There are three flavors of SFrame FRE representation in the binary format: |
49 | | - sframe_frame_row_entry_addr1 where the FRE start address is 1 byte. |
50 | | - sframe_frame_row_entry_addr2 where the FRE start address is 2 bytes. |
51 | | - sframe_frame_row_entry_addr4 where the FRE start address is 4 bytes. |
52 | | |
53 | | Note that in the SFrame format, all SFrame FREs of a function use one |
54 | | single representation. The SFrame FRE type itself is identified via the |
55 | | information in the SFrame FDE function info. |
56 | | |
57 | | Now, to select the minimum required one from the list above, one needs to |
58 | | make a decision based on the size (in bytes) of the function. |
59 | | |
60 | | As a result, for this optimization, some fragments (generated with a new |
61 | | type rs_sframe) for the SFrame section are fixed up later. |
62 | | |
63 | | This optimization (for size) is enabled by default. */ |
64 | | |
65 | | #ifndef SFRAME_FRE_TYPE_SELECTION_OPT |
66 | | # define SFRAME_FRE_TYPE_SELECTION_OPT 1 |
67 | | #endif |
68 | | |
69 | | /* Emit a single byte into the current segment. */ |
70 | | |
71 | | static inline void |
72 | | out_one (int byte) |
73 | 0 | { |
74 | 0 | FRAG_APPEND_1_CHAR (byte); |
75 | 0 | } |
76 | | |
77 | | /* Emit a two-byte word into the current segment. */ |
78 | | |
79 | | static inline void |
80 | | out_two (int data) |
81 | 0 | { |
82 | 0 | md_number_to_chars (frag_more (2), data, 2); |
83 | 0 | } |
84 | | |
85 | | /* Emit a four byte word into the current segment. */ |
86 | | |
87 | | static inline void |
88 | | out_four (int data) |
89 | 0 | { |
90 | 0 | md_number_to_chars (frag_more (4), data, 4); |
91 | 0 | } |
92 | | |
93 | | /* Get the start address symbol from the DWARF FDE. */ |
94 | | |
95 | | static symbolS* |
96 | | get_dw_fde_start_addrS (const struct fde_entry *dw_fde) |
97 | 0 | { |
98 | 0 | return dw_fde->start_address; |
99 | 0 | } |
100 | | |
101 | | /* Get the start address symbol from the DWARF FDE. */ |
102 | | |
103 | | static symbolS* |
104 | | get_dw_fde_end_addrS (const struct fde_entry *dw_fde) |
105 | 0 | { |
106 | 0 | return dw_fde->end_address; |
107 | 0 | } |
108 | | |
109 | | /* Get whether PAUTH B key is used. */ |
110 | | static bool |
111 | | get_dw_fde_pauth_b_key_p (const struct fde_entry *dw_fde ATTRIBUTE_UNUSED) |
112 | 0 | { |
113 | | #ifdef tc_fde_entry_extras |
114 | | return (dw_fde->pauth_key == AARCH64_PAUTH_KEY_B); |
115 | | #else |
116 | 0 | return false; |
117 | 0 | #endif |
118 | 0 | } |
119 | | |
120 | | /* SFrame Frame Row Entry (FRE) related functions. */ |
121 | | |
122 | | static void |
123 | | sframe_fre_set_begin_addr (struct sframe_row_entry *fre, symbolS *beginS) |
124 | 0 | { |
125 | 0 | fre->pc_begin = beginS; |
126 | 0 | } |
127 | | |
128 | | static void |
129 | | sframe_fre_set_end_addr (struct sframe_row_entry *fre, symbolS *endS) |
130 | 0 | { |
131 | 0 | fre->pc_end = endS; |
132 | 0 | } |
133 | | |
134 | | static void |
135 | | sframe_fre_set_cfa_base_reg (struct sframe_row_entry *fre, |
136 | | unsigned int cfa_base_reg) |
137 | 0 | { |
138 | 0 | fre->cfa_base_reg = cfa_base_reg; |
139 | 0 | fre->merge_candidate = false; |
140 | 0 | } |
141 | | |
142 | | static void |
143 | | sframe_fre_set_cfa_offset (struct sframe_row_entry *fre, |
144 | | offsetT cfa_offset) |
145 | 0 | { |
146 | 0 | fre->cfa_offset = cfa_offset; |
147 | 0 | fre->merge_candidate = false; |
148 | 0 | } |
149 | | |
150 | | #ifdef SFRAME_FRE_RA_TRACKING |
151 | | static void |
152 | | sframe_fre_set_ra_track (struct sframe_row_entry *fre, offsetT ra_offset) |
153 | | { |
154 | | fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK; |
155 | | fre->ra_offset = ra_offset; |
156 | | fre->merge_candidate = false; |
157 | | } |
158 | | #endif |
159 | | |
160 | | static void |
161 | | sframe_fre_set_bp_track (struct sframe_row_entry *fre, offsetT bp_offset) |
162 | 0 | { |
163 | 0 | fre->bp_loc = SFRAME_FRE_ELEM_LOC_STACK; |
164 | 0 | fre->bp_offset = bp_offset; |
165 | 0 | fre->merge_candidate = false; |
166 | 0 | } |
167 | | |
168 | | /* All stack offset values within an FRE are uniformly encoded in the same |
169 | | number of bytes. The size of the stack offset values will, however, vary |
170 | | across FREs. */ |
171 | | |
172 | 0 | #define VALUE_8BIT 0x7f |
173 | 0 | #define VALUE_16BIT 0x7fff |
174 | 0 | #define VALUE_32BIT 0x7fffffff |
175 | 0 | #define VALUE_64BIT 0x7fffffffffffffff |
176 | | |
177 | | /* Given a signed offset, return the size in bytes needed to represent it. */ |
178 | | |
179 | | static unsigned int |
180 | | get_offset_size_in_bytes (offsetT value) |
181 | 0 | { |
182 | 0 | unsigned int size = 0; |
183 | |
|
184 | 0 | if (value <= VALUE_8BIT && value >= (offsetT) -VALUE_8BIT) |
185 | 0 | size = 1; |
186 | 0 | else if (value <= VALUE_16BIT && value >= (offsetT) -VALUE_16BIT) |
187 | 0 | size = 2; |
188 | 0 | else if (value <= VALUE_32BIT && value >= (offsetT) -VALUE_32BIT) |
189 | 0 | size = 4; |
190 | 0 | else if ((sizeof (offsetT) > 4) && (value <= (offsetT) VALUE_64BIT |
191 | 0 | && value >= (offsetT) -VALUE_64BIT)) |
192 | 0 | size = 8; |
193 | |
|
194 | 0 | return size; |
195 | 0 | } |
196 | | |
197 | 0 | #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_1B 0 /* SFRAME_FRE_OFFSET_1B. */ |
198 | 0 | #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_2B 1 /* SFRAME_FRE_OFFSET_2B. */ |
199 | 0 | #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_4B 2 /* SFRAME_FRE_OFFSET_4B. */ |
200 | 0 | #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_8B 3 /* Not supported in SFrame. */ |
201 | 0 | #define SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_8B |
202 | | |
203 | | /* Helper struct for mapping offset size to output functions. */ |
204 | | |
205 | | struct sframe_fre_offset_func_map |
206 | | { |
207 | | unsigned int offset_size; |
208 | | void (*out_func)(int); |
209 | | }; |
210 | | |
211 | | /* Given an OFFSET_SIZE, return the size in bytes needed to represent it. */ |
212 | | |
213 | | static unsigned int |
214 | | sframe_fre_offset_func_map_index (unsigned int offset_size) |
215 | 0 | { |
216 | 0 | unsigned int idx = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX; |
217 | |
|
218 | 0 | switch (offset_size) |
219 | 0 | { |
220 | 0 | case SFRAME_FRE_OFFSET_1B: |
221 | 0 | idx = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_1B; |
222 | 0 | break; |
223 | 0 | case SFRAME_FRE_OFFSET_2B: |
224 | 0 | idx = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_2B; |
225 | 0 | break; |
226 | 0 | case SFRAME_FRE_OFFSET_4B: |
227 | 0 | idx = SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_4B; |
228 | 0 | break; |
229 | 0 | default: |
230 | | /* Not supported in SFrame. */ |
231 | 0 | break; |
232 | 0 | } |
233 | | |
234 | 0 | return idx; |
235 | 0 | } |
236 | | |
237 | | /* Mapping from offset size to the output function to emit the value. */ |
238 | | |
239 | | static const |
240 | | struct sframe_fre_offset_func_map |
241 | | fre_offset_func_map[SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX+1] = |
242 | | { |
243 | | { SFRAME_FRE_OFFSET_1B, out_one }, |
244 | | { SFRAME_FRE_OFFSET_2B, out_two }, |
245 | | { SFRAME_FRE_OFFSET_4B, out_four }, |
246 | | { -1, NULL } /* Not Supported in SFrame. */ |
247 | | }; |
248 | | |
249 | | /* SFrame version specific operations access. */ |
250 | | |
251 | | static struct sframe_version_ops sframe_ver_ops; |
252 | | |
253 | | /* SFrame (SFRAME_VERSION_1) set FRE info. */ |
254 | | |
255 | | static unsigned char |
256 | | sframe_v1_set_fre_info (unsigned int base_reg, unsigned int num_offsets, |
257 | | unsigned int offset_size, bool mangled_ra_p) |
258 | 0 | { |
259 | 0 | unsigned char fre_info; |
260 | 0 | fre_info = SFRAME_V1_FRE_INFO (base_reg, num_offsets, offset_size); |
261 | 0 | fre_info = SFRAME_V1_FRE_INFO_UPDATE_MANGLED_RA_P (mangled_ra_p, fre_info); |
262 | 0 | return fre_info; |
263 | 0 | } |
264 | | |
265 | | /* SFrame (SFRAME_VERSION_1) set function info. */ |
266 | | static unsigned char |
267 | | sframe_v1_set_func_info (unsigned int fde_type, unsigned int fre_type, |
268 | | unsigned int pauth_key) |
269 | 0 | { |
270 | 0 | unsigned char func_info; |
271 | 0 | func_info = SFRAME_V1_FUNC_INFO (fde_type, fre_type); |
272 | 0 | func_info = SFRAME_V1_FUNC_INFO_UPDATE_PAUTH_KEY (pauth_key, func_info); |
273 | 0 | return func_info; |
274 | 0 | } |
275 | | |
276 | | /* SFrame version specific operations setup. */ |
277 | | |
278 | | static void |
279 | | sframe_set_version (uint32_t sframe_version ATTRIBUTE_UNUSED) |
280 | 0 | { |
281 | 0 | sframe_ver_ops.format_version = SFRAME_VERSION_1; |
282 | |
|
283 | 0 | sframe_ver_ops.set_fre_info = sframe_v1_set_fre_info; |
284 | |
|
285 | 0 | sframe_ver_ops.set_func_info = sframe_v1_set_func_info; |
286 | 0 | } |
287 | | |
288 | | /* SFrame set FRE info. */ |
289 | | |
290 | | static unsigned char |
291 | | sframe_set_fre_info (unsigned int base_reg, unsigned int num_offsets, |
292 | | unsigned int offset_size, bool mangled_ra_p) |
293 | 0 | { |
294 | 0 | return sframe_ver_ops.set_fre_info (base_reg, num_offsets, |
295 | 0 | offset_size, mangled_ra_p); |
296 | 0 | } |
297 | | |
298 | | /* SFrame set func info. */ |
299 | | |
300 | | static unsigned char |
301 | | sframe_set_func_info (unsigned int fde_type, unsigned int fre_type, |
302 | | unsigned int pauth_key) |
303 | 0 | { |
304 | 0 | return sframe_ver_ops.set_func_info (fde_type, fre_type, pauth_key); |
305 | 0 | } |
306 | | |
307 | | /* Get the number of SFrame FDEs for the current file. */ |
308 | | |
309 | | static unsigned int |
310 | | get_num_sframe_fdes (void); |
311 | | |
312 | | /* Get the number of SFrame frame row entries for the current file. */ |
313 | | |
314 | | static unsigned int |
315 | | get_num_sframe_fres (void); |
316 | | |
317 | | /* Get CFA base register ID as represented in SFrame Frame Row Entry. */ |
318 | | |
319 | | static unsigned int |
320 | | get_fre_base_reg_id (struct sframe_row_entry *sframe_fre) |
321 | 0 | { |
322 | 0 | unsigned int cfi_insn_cfa_base_reg = sframe_fre->cfa_base_reg; |
323 | 0 | unsigned fre_base_reg = SFRAME_BASE_REG_SP; |
324 | |
|
325 | 0 | if (cfi_insn_cfa_base_reg == SFRAME_CFA_FP_REG) |
326 | 0 | fre_base_reg = SFRAME_BASE_REG_FP; |
327 | | |
328 | | /* Only one bit is reserved in SFRAME_VERSION_1. */ |
329 | 0 | gas_assert (fre_base_reg == SFRAME_BASE_REG_SP |
330 | 0 | || fre_base_reg == SFRAME_BASE_REG_FP); |
331 | | |
332 | 0 | return fre_base_reg; |
333 | 0 | } |
334 | | |
335 | | /* Get number of offsets necessary for the SFrame Frame Row Entry. */ |
336 | | |
337 | | static unsigned int |
338 | | get_fre_num_offsets (struct sframe_row_entry *sframe_fre) |
339 | 0 | { |
340 | | /* Atleast 1 must always be present (to recover CFA). */ |
341 | 0 | unsigned int fre_num_offsets = 1; |
342 | |
|
343 | 0 | if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK) |
344 | 0 | fre_num_offsets++; |
345 | | #ifdef SFRAME_FRE_RA_TRACKING |
346 | | if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK) |
347 | | fre_num_offsets++; |
348 | | #endif |
349 | 0 | return fre_num_offsets; |
350 | 0 | } |
351 | | |
352 | | /* Get the minimum necessary offset size (in bytes) for this |
353 | | SFrame frame row entry. */ |
354 | | |
355 | | static unsigned int |
356 | | sframe_get_fre_offset_size (struct sframe_row_entry *sframe_fre) |
357 | 0 | { |
358 | 0 | unsigned int max_offset_size = 0; |
359 | 0 | unsigned int cfa_offset_size = 0; |
360 | 0 | unsigned int bp_offset_size = 0; |
361 | 0 | unsigned int ra_offset_size = 0; |
362 | |
|
363 | 0 | unsigned int fre_offset_size = 0; |
364 | | |
365 | | /* What size of offsets appear in this frame row entry. */ |
366 | 0 | cfa_offset_size = get_offset_size_in_bytes (sframe_fre->cfa_offset); |
367 | 0 | if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK) |
368 | 0 | bp_offset_size = get_offset_size_in_bytes (sframe_fre->bp_offset); |
369 | | #ifdef SFRAME_FRE_RA_TRACKING |
370 | | if (sframe_ra_tracking_p () |
371 | | && sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK) |
372 | | ra_offset_size = get_offset_size_in_bytes (sframe_fre->ra_offset); |
373 | | #endif |
374 | | |
375 | | /* Get the maximum size needed to represent the offsets. */ |
376 | 0 | max_offset_size = cfa_offset_size; |
377 | 0 | if (bp_offset_size > max_offset_size) |
378 | 0 | max_offset_size = bp_offset_size; |
379 | 0 | if (ra_offset_size > max_offset_size) |
380 | 0 | max_offset_size = ra_offset_size; |
381 | |
|
382 | 0 | gas_assert (max_offset_size); |
383 | | |
384 | 0 | switch (max_offset_size) |
385 | 0 | { |
386 | 0 | case 1: |
387 | 0 | fre_offset_size = SFRAME_FRE_OFFSET_1B; |
388 | 0 | break; |
389 | 0 | case 2: |
390 | 0 | fre_offset_size = SFRAME_FRE_OFFSET_2B; |
391 | 0 | break; |
392 | 0 | case 4: |
393 | 0 | fre_offset_size = SFRAME_FRE_OFFSET_4B; |
394 | 0 | break; |
395 | 0 | default: |
396 | | /* Offset of size 8 bytes is not supported in SFrame format |
397 | | version 1. */ |
398 | 0 | as_fatal (_("SFrame unsupported offset value\n")); |
399 | 0 | break; |
400 | 0 | } |
401 | | |
402 | 0 | return fre_offset_size; |
403 | 0 | } |
404 | | |
405 | | #if SFRAME_FRE_TYPE_SELECTION_OPT |
406 | | |
407 | | /* Create a composite exression CEXP (for SFrame FRE start address) such that: |
408 | | |
409 | | exp = <val> OP_absent <width>, where, |
410 | | |
411 | | - <val> and <width> are themselves expressionS. |
412 | | - <val> stores the expression which when evaluated gives the value of the |
413 | | start address offset of the FRE. |
414 | | - <width> stores the expression when evaluated gives the number of bytes |
415 | | needed to encode the start address offset of the FRE. |
416 | | |
417 | | The use of OP_absent as the X_op_symbol helps identify this expression |
418 | | later when fragments are fixed up. */ |
419 | | |
420 | | static void |
421 | | create_fre_start_addr_exp (expressionS *cexp, symbolS *fre_pc_begin, |
422 | | symbolS *fde_start_address, |
423 | | symbolS *fde_end_address) |
424 | 0 | { |
425 | 0 | expressionS val; |
426 | 0 | expressionS width; |
427 | | |
428 | | /* val expression stores the FDE start address offset from the start PC |
429 | | of function. */ |
430 | 0 | val.X_op = O_subtract; |
431 | 0 | val.X_add_symbol = fre_pc_begin; |
432 | 0 | val.X_op_symbol = fde_start_address; |
433 | 0 | val.X_add_number = 0; |
434 | | |
435 | | /* width expressions stores the size of the function. This is used later |
436 | | to determine the number of bytes to be used to encode the FRE start |
437 | | address of each FRE of the function. */ |
438 | 0 | width.X_op = O_subtract; |
439 | 0 | width.X_add_symbol = fde_end_address; |
440 | 0 | width.X_op_symbol = fde_start_address; |
441 | 0 | width.X_add_number = 0; |
442 | |
|
443 | 0 | cexp->X_op = O_absent; |
444 | 0 | cexp->X_add_symbol = make_expr_symbol (&val); |
445 | 0 | cexp->X_op_symbol = make_expr_symbol (&width); |
446 | 0 | cexp->X_add_number = 0; |
447 | 0 | } |
448 | | |
449 | | /* Create a composite exression CEXP (for SFrame FDE function info) such that: |
450 | | |
451 | | exp = <rest_of_func_info> OP_modulus <width>, where, |
452 | | |
453 | | - <rest_of_func_info> and <width> are themselves expressionS. |
454 | | - <rest_of_func_info> stores a constant expression where X_add_number is |
455 | | used to stash away the func_info. The upper 4-bits of the func_info are copied |
456 | | back to the resulting byte by the fragment fixup logic. |
457 | | - <width> stores the expression when evaluated gives the size of the |
458 | | funtion in number of bytes. |
459 | | |
460 | | The use of OP_modulus as the X_op_symbol helps identify this expression |
461 | | later when fragments are fixed up. */ |
462 | | |
463 | | static void |
464 | | create_func_info_exp (expressionS *cexp, symbolS *dw_fde_end_addrS, |
465 | | symbolS *dw_fde_start_addrS, uint8_t func_info) |
466 | 0 | { |
467 | 0 | expressionS width; |
468 | 0 | expressionS rest_of_func_info; |
469 | |
|
470 | 0 | width.X_op = O_subtract; |
471 | 0 | width.X_add_symbol = dw_fde_end_addrS; |
472 | 0 | width.X_op_symbol = dw_fde_start_addrS; |
473 | 0 | width.X_add_number = 0; |
474 | |
|
475 | 0 | rest_of_func_info.X_op = O_constant; |
476 | 0 | rest_of_func_info.X_add_number = func_info; |
477 | |
|
478 | 0 | cexp->X_op = O_modulus; |
479 | 0 | cexp->X_add_symbol = make_expr_symbol (&rest_of_func_info); |
480 | 0 | cexp->X_op_symbol = make_expr_symbol (&width); |
481 | 0 | cexp->X_add_number = 0; |
482 | 0 | } |
483 | | |
484 | | #endif |
485 | | |
486 | | static void |
487 | | output_sframe_row_entry (symbolS *fde_start_addr, |
488 | | symbolS *fde_end_addr, |
489 | | struct sframe_row_entry *sframe_fre) |
490 | 0 | { |
491 | 0 | unsigned char fre_info; |
492 | 0 | unsigned int fre_num_offsets; |
493 | 0 | unsigned int fre_offset_size; |
494 | 0 | unsigned int fre_base_reg; |
495 | 0 | expressionS exp; |
496 | 0 | unsigned int fre_addr_size; |
497 | |
|
498 | 0 | unsigned int idx = 0; |
499 | 0 | unsigned int fre_write_offsets = 0; |
500 | |
|
501 | 0 | fre_addr_size = 4; /* 4 bytes by default. FIXME tie it to fre_type? */ |
502 | | |
503 | | /* SFrame FRE Start Address. */ |
504 | 0 | #if SFRAME_FRE_TYPE_SELECTION_OPT |
505 | 0 | create_fre_start_addr_exp (&exp, sframe_fre->pc_begin, fde_start_addr, |
506 | 0 | fde_end_addr); |
507 | 0 | frag_grow (fre_addr_size); |
508 | 0 | frag_var (rs_sframe, fre_addr_size, 0, (relax_substateT) 0, |
509 | 0 | make_expr_symbol (&exp), 0, (char *) frag_now); |
510 | | #else |
511 | | gas_assert (fde_end_addr); |
512 | | exp.X_op = O_subtract; |
513 | | exp.X_add_symbol = sframe_fre->pc_begin; /* to. */ |
514 | | exp.X_op_symbol = fde_start_addr; /* from. */ |
515 | | exp.X_add_number = 0; |
516 | | emit_expr (&exp, fre_addr_size); |
517 | | #endif |
518 | | |
519 | | /* Create the fre_info using the CFA base register, number of offsets and max |
520 | | size of offset in this frame row entry. */ |
521 | 0 | fre_base_reg = get_fre_base_reg_id (sframe_fre); |
522 | 0 | fre_num_offsets = get_fre_num_offsets (sframe_fre); |
523 | 0 | fre_offset_size = sframe_get_fre_offset_size (sframe_fre); |
524 | 0 | fre_info = sframe_set_fre_info (fre_base_reg, fre_num_offsets, |
525 | 0 | fre_offset_size, sframe_fre->mangled_ra_p); |
526 | 0 | out_one (fre_info); |
527 | |
|
528 | 0 | idx = sframe_fre_offset_func_map_index (fre_offset_size); |
529 | 0 | gas_assert (idx < SFRAME_FRE_OFFSET_FUNC_MAP_INDEX_MAX); |
530 | | |
531 | | /* Write out the offsets in order - cfa, bp, ra. */ |
532 | 0 | fre_offset_func_map[idx].out_func (sframe_fre->cfa_offset); |
533 | 0 | fre_write_offsets++; |
534 | |
|
535 | | #ifdef SFRAME_FRE_RA_TRACKING |
536 | | if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK) |
537 | | { |
538 | | fre_offset_func_map[idx].out_func (sframe_fre->ra_offset); |
539 | | fre_write_offsets++; |
540 | | } |
541 | | #endif |
542 | 0 | if (sframe_fre->bp_loc == SFRAME_FRE_ELEM_LOC_STACK) |
543 | 0 | { |
544 | 0 | fre_offset_func_map[idx].out_func (sframe_fre->bp_offset); |
545 | 0 | fre_write_offsets++; |
546 | 0 | } |
547 | | |
548 | | /* Check if the expected number offsets have been written out |
549 | | in this FRE. */ |
550 | 0 | gas_assert (fre_write_offsets == fre_num_offsets); |
551 | 0 | } |
552 | | |
553 | | static void |
554 | | output_sframe_funcdesc (symbolS *start_of_fre_section, |
555 | | symbolS *fre_symbol, |
556 | | struct sframe_func_entry *sframe_fde) |
557 | 0 | { |
558 | 0 | expressionS exp; |
559 | 0 | unsigned int addr_size; |
560 | 0 | symbolS *dw_fde_start_addrS, *dw_fde_end_addrS; |
561 | 0 | unsigned int pauth_key; |
562 | |
|
563 | 0 | addr_size = SFRAME_RELOC_SIZE; |
564 | 0 | dw_fde_start_addrS = get_dw_fde_start_addrS (sframe_fde->dw_fde); |
565 | 0 | dw_fde_end_addrS = get_dw_fde_end_addrS (sframe_fde->dw_fde); |
566 | | |
567 | | /* Start address of the function. */ |
568 | 0 | exp.X_op = O_subtract; |
569 | 0 | exp.X_add_symbol = dw_fde_start_addrS; /* to location. */ |
570 | 0 | exp.X_op_symbol = symbol_temp_new_now (); /* from location. */ |
571 | 0 | exp.X_add_number = 0; |
572 | 0 | emit_expr (&exp, addr_size); |
573 | | |
574 | | /* Size of the function in bytes. */ |
575 | 0 | exp.X_op = O_subtract; |
576 | 0 | exp.X_add_symbol = dw_fde_end_addrS; |
577 | 0 | exp.X_op_symbol = dw_fde_start_addrS; |
578 | 0 | exp.X_add_number = 0; |
579 | 0 | emit_expr (&exp, addr_size); |
580 | | |
581 | | /* Offset to the first frame row entry. */ |
582 | 0 | exp.X_op = O_subtract; |
583 | 0 | exp.X_add_symbol = fre_symbol; /* Minuend. */ |
584 | 0 | exp.X_op_symbol = start_of_fre_section; /* Subtrahend. */ |
585 | 0 | exp.X_add_number = 0; |
586 | 0 | emit_expr (&exp, addr_size); |
587 | | |
588 | | /* Number of FREs. */ |
589 | 0 | out_four (sframe_fde->num_fres); |
590 | | |
591 | | /* SFrame FDE function info. */ |
592 | 0 | unsigned char func_info; |
593 | 0 | pauth_key = (get_dw_fde_pauth_b_key_p (sframe_fde->dw_fde) |
594 | 0 | ? SFRAME_AARCH64_PAUTH_KEY_B : SFRAME_AARCH64_PAUTH_KEY_A); |
595 | 0 | func_info = sframe_set_func_info (SFRAME_FDE_TYPE_PCINC, |
596 | 0 | SFRAME_FRE_TYPE_ADDR4, |
597 | 0 | pauth_key); |
598 | 0 | #if SFRAME_FRE_TYPE_SELECTION_OPT |
599 | 0 | expressionS cexp; |
600 | 0 | create_func_info_exp (&cexp, dw_fde_end_addrS, dw_fde_start_addrS, |
601 | 0 | func_info); |
602 | 0 | frag_grow (1); /* Size of func info is unsigned char. */ |
603 | 0 | frag_var (rs_sframe, 1, 0, (relax_substateT) 0, |
604 | 0 | make_expr_symbol (&cexp), 0, (char *) frag_now); |
605 | | #else |
606 | | out_one (func_info); |
607 | | #endif |
608 | 0 | } |
609 | | |
610 | | static void |
611 | | output_sframe_internal (void) |
612 | 0 | { |
613 | 0 | expressionS exp; |
614 | 0 | unsigned int i = 0; |
615 | |
|
616 | 0 | symbolS *end_of_frame_hdr; |
617 | 0 | symbolS *end_of_frame_section; |
618 | 0 | symbolS *start_of_func_desc_section; |
619 | 0 | symbolS *start_of_fre_section; |
620 | 0 | struct sframe_func_entry *sframe_fde; |
621 | 0 | struct sframe_row_entry *sframe_fre; |
622 | 0 | unsigned char abi_arch = 0; |
623 | 0 | int fixed_bp_offset = SFRAME_CFA_FIXED_FP_INVALID; |
624 | 0 | int fixed_ra_offset = SFRAME_CFA_FIXED_RA_INVALID; |
625 | 0 | unsigned int addr_size; |
626 | |
|
627 | 0 | addr_size = SFRAME_RELOC_SIZE; |
628 | | |
629 | | /* The function desciptor entries as dumped by the assembler are not |
630 | | sorted on PCs. */ |
631 | 0 | unsigned char sframe_flags = 0; |
632 | 0 | sframe_flags |= !SFRAME_F_FDE_SORTED; |
633 | |
|
634 | 0 | unsigned int num_fdes = get_num_sframe_fdes (); |
635 | 0 | unsigned int num_fres = get_num_sframe_fres (); |
636 | 0 | symbolS **fre_symbols = XNEWVEC (symbolS *, num_fres); |
637 | 0 | for (i = 0; i < num_fres; i++) |
638 | 0 | fre_symbols[i] = symbol_temp_make (); |
639 | |
|
640 | 0 | end_of_frame_hdr = symbol_temp_make (); |
641 | 0 | start_of_fre_section = symbol_temp_make (); |
642 | 0 | start_of_func_desc_section = symbol_temp_make (); |
643 | 0 | end_of_frame_section = symbol_temp_make (); |
644 | | |
645 | | /* Output the preamble of SFrame section. */ |
646 | 0 | out_two (SFRAME_MAGIC); |
647 | 0 | out_one (SFRAME_VERSION); |
648 | 0 | out_one (sframe_flags); |
649 | | /* abi/arch. */ |
650 | 0 | #ifdef sframe_get_abi_arch |
651 | 0 | abi_arch = sframe_get_abi_arch (); |
652 | 0 | #endif |
653 | 0 | gas_assert (abi_arch); |
654 | 0 | out_one (abi_arch); |
655 | | |
656 | | /* Offset for the BP register from CFA. Neither of the AMD64 or AAPCS64 |
657 | | ABIs have a fixed offset for the BP register from the CFA. This may be |
658 | | useful in future (but not without additional support in the toolchain) |
659 | | for specialized handling/encoding for cases where, for example, |
660 | | -fno-omit-frame-pointer is used. */ |
661 | 0 | out_one (fixed_bp_offset); |
662 | | |
663 | | /* Offset for the return address from CFA is fixed for some ABIs |
664 | | (e.g., AMD64), output a SFRAME_CFA_FIXED_RA_INVALID otherwise. */ |
665 | 0 | #ifdef sframe_ra_tracking_p |
666 | 0 | if (!sframe_ra_tracking_p ()) |
667 | 0 | fixed_ra_offset = sframe_cfa_ra_offset (); |
668 | 0 | #endif |
669 | 0 | out_one (fixed_ra_offset); |
670 | | |
671 | | /* None of the AMD64, or AARCH64 ABIs need the auxilliary header. |
672 | | When the need does arise to use this field, the appropriate backend |
673 | | must provide this information. */ |
674 | 0 | out_one (0); /* Auxilliary SFrame header length. */ |
675 | |
|
676 | 0 | out_four (num_fdes); /* Number of FDEs. */ |
677 | 0 | out_four (num_fres); /* Number of FREs. */ |
678 | | |
679 | | /* FRE sub-section len. */ |
680 | 0 | exp.X_op = O_subtract; |
681 | 0 | exp.X_add_symbol = end_of_frame_section; |
682 | 0 | exp.X_op_symbol = start_of_fre_section; |
683 | 0 | exp.X_add_number = 0; |
684 | 0 | emit_expr (&exp, addr_size); |
685 | | |
686 | | /* Offset of Function Index sub-section. */ |
687 | 0 | exp.X_op = O_subtract; |
688 | 0 | exp.X_add_symbol = end_of_frame_hdr; |
689 | 0 | exp.X_op_symbol = start_of_func_desc_section; |
690 | 0 | exp.X_add_number = 0; |
691 | 0 | emit_expr (&exp, addr_size); |
692 | | |
693 | | /* Offset of FRE sub-section. */ |
694 | 0 | exp.X_op = O_subtract; |
695 | 0 | exp.X_add_symbol = start_of_fre_section; |
696 | 0 | exp.X_op_symbol = end_of_frame_hdr; |
697 | 0 | exp.X_add_number = 0; |
698 | 0 | emit_expr (&exp, addr_size); |
699 | |
|
700 | 0 | symbol_set_value_now (end_of_frame_hdr); |
701 | 0 | symbol_set_value_now (start_of_func_desc_section); |
702 | | |
703 | | /* Output the SFrame function descriptor entries. */ |
704 | 0 | i = 0; |
705 | 0 | for (sframe_fde = all_sframe_fdes; sframe_fde; sframe_fde = sframe_fde->next) |
706 | 0 | { |
707 | 0 | output_sframe_funcdesc (start_of_fre_section, |
708 | 0 | fre_symbols[i], sframe_fde); |
709 | 0 | i += sframe_fde->num_fres; |
710 | 0 | } |
711 | |
|
712 | 0 | symbol_set_value_now (start_of_fre_section); |
713 | | |
714 | | /* Output the SFrame FREs. */ |
715 | 0 | i = 0; |
716 | 0 | sframe_fde = all_sframe_fdes; |
717 | |
|
718 | 0 | for (sframe_fde = all_sframe_fdes; sframe_fde; sframe_fde = sframe_fde->next) |
719 | 0 | { |
720 | 0 | for (sframe_fre = sframe_fde->sframe_fres; |
721 | 0 | sframe_fre; |
722 | 0 | sframe_fre = sframe_fre->next) |
723 | 0 | { |
724 | 0 | symbol_set_value_now (fre_symbols[i]); |
725 | 0 | output_sframe_row_entry (get_dw_fde_start_addrS (sframe_fde->dw_fde), |
726 | 0 | get_dw_fde_end_addrS (sframe_fde->dw_fde), |
727 | 0 | sframe_fre); |
728 | 0 | i++; |
729 | 0 | } |
730 | 0 | } |
731 | |
|
732 | 0 | symbol_set_value_now (end_of_frame_section); |
733 | |
|
734 | 0 | gas_assert (i == num_fres); |
735 | | |
736 | 0 | free (fre_symbols); |
737 | 0 | fre_symbols = NULL; |
738 | 0 | } |
739 | | |
740 | | /* List of SFrame FDE entries. */ |
741 | | |
742 | | struct sframe_func_entry *all_sframe_fdes; |
743 | | |
744 | | /* Tail of the list to add to. */ |
745 | | |
746 | | static struct sframe_func_entry **last_sframe_fde = &all_sframe_fdes; |
747 | | |
748 | | static unsigned int |
749 | | get_num_sframe_fdes (void) |
750 | 0 | { |
751 | 0 | struct sframe_func_entry *sframe_fde; |
752 | 0 | unsigned int total_fdes = 0; |
753 | |
|
754 | 0 | for (sframe_fde = all_sframe_fdes; sframe_fde ; sframe_fde = sframe_fde->next) |
755 | 0 | total_fdes++; |
756 | |
|
757 | 0 | return total_fdes; |
758 | 0 | } |
759 | | |
760 | | /* Get the total number of SFrame row entries across the FDEs. */ |
761 | | |
762 | | static unsigned int |
763 | | get_num_sframe_fres (void) |
764 | 0 | { |
765 | 0 | struct sframe_func_entry *sframe_fde; |
766 | 0 | unsigned int total_fres = 0; |
767 | |
|
768 | 0 | for (sframe_fde = all_sframe_fdes; sframe_fde ; sframe_fde = sframe_fde->next) |
769 | 0 | total_fres += sframe_fde->num_fres; |
770 | |
|
771 | 0 | return total_fres; |
772 | 0 | } |
773 | | |
774 | | /* Allocate an SFrame FDE. */ |
775 | | |
776 | | static struct sframe_func_entry* |
777 | | sframe_fde_alloc (void) |
778 | 0 | { |
779 | 0 | struct sframe_func_entry *sframe_fde = XCNEW (struct sframe_func_entry); |
780 | 0 | return sframe_fde; |
781 | 0 | } |
782 | | |
783 | | /* Link the SFrame FDE in. */ |
784 | | |
785 | | static int |
786 | | sframe_fde_link (struct sframe_func_entry *sframe_fde) |
787 | 0 | { |
788 | 0 | *last_sframe_fde = sframe_fde; |
789 | 0 | last_sframe_fde = &sframe_fde->next; |
790 | |
|
791 | 0 | return 0; |
792 | 0 | } |
793 | | |
794 | | /* Free up the SFrame FDE. */ |
795 | | |
796 | | static void |
797 | | sframe_fde_free (struct sframe_func_entry *sframe_fde) |
798 | 0 | { |
799 | 0 | XDELETE (sframe_fde); |
800 | 0 | sframe_fde = NULL; |
801 | 0 | } |
802 | | |
803 | | /* SFrame translation context functions. */ |
804 | | |
805 | | /* Allocate a new SFrame translation context. */ |
806 | | |
807 | | static struct sframe_xlate_ctx* |
808 | | sframe_xlate_ctx_alloc (void) |
809 | 0 | { |
810 | 0 | struct sframe_xlate_ctx* xlate_ctx = XCNEW (struct sframe_xlate_ctx); |
811 | 0 | return xlate_ctx; |
812 | 0 | } |
813 | | |
814 | | /* Initialize the given SFrame translation context. */ |
815 | | |
816 | | static void |
817 | | sframe_xlate_ctx_init (struct sframe_xlate_ctx *xlate_ctx) |
818 | 0 | { |
819 | 0 | xlate_ctx->dw_fde = NULL; |
820 | 0 | xlate_ctx->first_fre = NULL; |
821 | 0 | xlate_ctx->last_fre = NULL; |
822 | 0 | xlate_ctx->cur_fre = NULL; |
823 | 0 | xlate_ctx->remember_fre = NULL; |
824 | 0 | xlate_ctx->num_xlate_fres = 0; |
825 | 0 | } |
826 | | |
827 | | /* Cleanup the given SFrame translation context. */ |
828 | | |
829 | | static void |
830 | | sframe_xlate_ctx_cleanup (struct sframe_xlate_ctx *xlate_ctx) |
831 | 0 | { |
832 | 0 | struct sframe_row_entry *fre, *fre_next; |
833 | |
|
834 | 0 | if (xlate_ctx->num_xlate_fres) |
835 | 0 | { |
836 | 0 | fre = xlate_ctx->first_fre; |
837 | 0 | while (fre) |
838 | 0 | { |
839 | 0 | fre_next = fre->next; |
840 | 0 | XDELETE (fre); |
841 | 0 | fre = fre_next; |
842 | 0 | } |
843 | 0 | } |
844 | |
|
845 | 0 | sframe_xlate_ctx_init (xlate_ctx); |
846 | 0 | } |
847 | | |
848 | | /* Transfer the state from the SFrame translation context to the SFrame FDE. */ |
849 | | |
850 | | static void |
851 | | sframe_xlate_ctx_finalize (struct sframe_xlate_ctx *xlate_ctx, |
852 | | struct sframe_func_entry *sframe_fde) |
853 | 0 | { |
854 | 0 | sframe_fde->dw_fde = xlate_ctx->dw_fde; |
855 | 0 | sframe_fde->sframe_fres = xlate_ctx->first_fre; |
856 | 0 | sframe_fde->num_fres = xlate_ctx->num_xlate_fres; |
857 | 0 | } |
858 | | |
859 | | static struct sframe_row_entry* |
860 | | sframe_row_entry_new (void) |
861 | 0 | { |
862 | 0 | struct sframe_row_entry *fre = XCNEW (struct sframe_row_entry); |
863 | | /* Reset cfa_base_reg to -1. A value of 0 will imply some valid register |
864 | | for the supported arches. */ |
865 | 0 | fre->cfa_base_reg = -1; |
866 | 0 | fre->merge_candidate = true; |
867 | | /* Reset the mangled RA status bit to zero by default. We will initialize it in |
868 | | sframe_row_entry_initialize () with the sticky bit if set. */ |
869 | 0 | fre->mangled_ra_p = false; |
870 | |
|
871 | 0 | return fre; |
872 | 0 | } |
873 | | |
874 | | /* Add the given FRE in the list of frame row entries in the given FDE |
875 | | translation context. */ |
876 | | |
877 | | static void |
878 | | sframe_xlate_ctx_add_fre (struct sframe_xlate_ctx *xlate_ctx, |
879 | | struct sframe_row_entry *fre) |
880 | 0 | { |
881 | 0 | gas_assert (xlate_ctx && fre); |
882 | | |
883 | | /* Add the frame row entry. */ |
884 | 0 | if (!xlate_ctx->first_fre) |
885 | 0 | xlate_ctx->first_fre = fre; |
886 | 0 | else if (xlate_ctx->last_fre) |
887 | 0 | xlate_ctx->last_fre->next = fre; |
888 | |
|
889 | 0 | xlate_ctx->last_fre = fre; |
890 | | |
891 | | /* Keep track of the total number of SFrame frame row entries. */ |
892 | 0 | xlate_ctx->num_xlate_fres++; |
893 | 0 | } |
894 | | |
895 | | /* A SFrame Frame Row Entry is self-sufficient in terms of stack tracing info |
896 | | for a given PC. It contains information assimilated from multiple CFI |
897 | | instructions, and hence, a new SFrame FRE is initialized with the data from |
898 | | the previous known FRE, if any. |
899 | | |
900 | | Understandably, not all information (especially the instruction begin |
901 | | and end boundaries) needs to be relayed. Hence, the caller of this API |
902 | | must set the pc_begin and pc_end as applicable. */ |
903 | | |
904 | | static void |
905 | | sframe_row_entry_initialize (struct sframe_row_entry *cur_fre, |
906 | | struct sframe_row_entry *prev_fre) |
907 | 0 | { |
908 | 0 | gas_assert (prev_fre); |
909 | 0 | cur_fre->cfa_base_reg = prev_fre->cfa_base_reg; |
910 | 0 | cur_fre->cfa_offset = prev_fre->cfa_offset; |
911 | 0 | cur_fre->bp_loc = prev_fre->bp_loc; |
912 | 0 | cur_fre->bp_offset = prev_fre->bp_offset; |
913 | 0 | cur_fre->ra_loc = prev_fre->ra_loc; |
914 | 0 | cur_fre->ra_offset = prev_fre->ra_offset; |
915 | | /* Treat RA mangling as a sticky bit. It retains its value until another |
916 | | .cfi_negate_ra_state is seen. */ |
917 | 0 | cur_fre->mangled_ra_p = prev_fre->mangled_ra_p; |
918 | 0 | } |
919 | | |
920 | | /* Translate DW_CFA_advance_loc into SFrame context. |
921 | | Return SFRAME_XLATE_OK if success. */ |
922 | | |
923 | | static int |
924 | | sframe_xlate_do_advance_loc (struct sframe_xlate_ctx *xlate_ctx, |
925 | | struct cfi_insn_data *cfi_insn) |
926 | 0 | { |
927 | 0 | struct sframe_row_entry *last_fre = xlate_ctx->last_fre; |
928 | | /* Get the scratchpad FRE currently being updated as the cfi_insn's |
929 | | get interpreted. This FRE eventually gets linked in into the |
930 | | list of FREs for the specific function. */ |
931 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
932 | |
|
933 | 0 | if (cur_fre) |
934 | 0 | { |
935 | 0 | if (!cur_fre->merge_candidate) |
936 | 0 | { |
937 | 0 | sframe_fre_set_end_addr (cur_fre, cfi_insn->u.ll.lab2); |
938 | |
|
939 | 0 | sframe_xlate_ctx_add_fre (xlate_ctx, cur_fre); |
940 | 0 | last_fre = xlate_ctx->last_fre; |
941 | |
|
942 | 0 | xlate_ctx->cur_fre = sframe_row_entry_new (); |
943 | 0 | cur_fre = xlate_ctx->cur_fre; |
944 | |
|
945 | 0 | if (last_fre) |
946 | 0 | sframe_row_entry_initialize (cur_fre, last_fre); |
947 | 0 | } |
948 | 0 | else |
949 | 0 | { |
950 | 0 | sframe_fre_set_end_addr (last_fre, cfi_insn->u.ll.lab2); |
951 | 0 | gas_assert (last_fre->merge_candidate == false); |
952 | 0 | } |
953 | 0 | } |
954 | 0 | else |
955 | 0 | { |
956 | 0 | xlate_ctx->cur_fre = sframe_row_entry_new (); |
957 | 0 | cur_fre = xlate_ctx->cur_fre; |
958 | 0 | } |
959 | | |
960 | 0 | gas_assert (cur_fre); |
961 | 0 | sframe_fre_set_begin_addr (cur_fre, cfi_insn->u.ll.lab2); |
962 | |
|
963 | 0 | return SFRAME_XLATE_OK; |
964 | 0 | } |
965 | | |
966 | | /* Translate DW_CFA_def_cfa into SFrame context. |
967 | | Return SFRAME_XLATE_OK if success. */ |
968 | | |
969 | | static int |
970 | | sframe_xlate_do_def_cfa (struct sframe_xlate_ctx *xlate_ctx, |
971 | | struct cfi_insn_data *cfi_insn) |
972 | | |
973 | 0 | { |
974 | | /* Get the scratchpad FRE. This FRE will eventually get linked in. */ |
975 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
976 | 0 | if (!cur_fre) |
977 | 0 | { |
978 | 0 | xlate_ctx->cur_fre = sframe_row_entry_new (); |
979 | 0 | cur_fre = xlate_ctx->cur_fre; |
980 | 0 | sframe_fre_set_begin_addr (cur_fre, |
981 | 0 | get_dw_fde_start_addrS (xlate_ctx->dw_fde)); |
982 | 0 | } |
983 | | /* Define the current CFA rule to use the provided register and |
984 | | offset. */ |
985 | 0 | sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg); |
986 | 0 | sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.ri.offset); |
987 | 0 | cur_fre->merge_candidate = false; |
988 | |
|
989 | 0 | return SFRAME_XLATE_OK; |
990 | 0 | } |
991 | | |
992 | | /* Translate DW_CFA_def_cfa_register into SFrame context. |
993 | | Return SFRAME_XLATE_OK if success. */ |
994 | | |
995 | | static int |
996 | | sframe_xlate_do_def_cfa_register (struct sframe_xlate_ctx *xlate_ctx, |
997 | | struct cfi_insn_data *cfi_insn) |
998 | 0 | { |
999 | 0 | struct sframe_row_entry *last_fre = xlate_ctx->last_fre; |
1000 | | /* Get the scratchpad FRE. This FRE will eventually get linked in. */ |
1001 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1002 | 0 | gas_assert (cur_fre); |
1003 | | /* Define the current CFA rule to use the provided register (but to |
1004 | | keep the old offset). */ |
1005 | 0 | sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg); |
1006 | 0 | sframe_fre_set_cfa_offset (cur_fre, last_fre->cfa_offset); |
1007 | 0 | cur_fre->merge_candidate = false; |
1008 | |
|
1009 | 0 | return SFRAME_XLATE_OK; |
1010 | 0 | } |
1011 | | |
1012 | | /* Translate DW_CFA_def_cfa_offset into SFrame context. |
1013 | | Return SFRAME_XLATE_OK if success. */ |
1014 | | |
1015 | | static int |
1016 | | sframe_xlate_do_def_cfa_offset (struct sframe_xlate_ctx *xlate_ctx, |
1017 | | struct cfi_insn_data *cfi_insn) |
1018 | 0 | { |
1019 | | /* The scratchpad FRE currently being updated with each cfi_insn |
1020 | | being interpreted. This FRE eventually gets linked in into the |
1021 | | list of FREs for the specific function. */ |
1022 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1023 | |
|
1024 | 0 | gas_assert (cur_fre); |
1025 | | /* Define the current CFA rule to use the provided offset (but to keep |
1026 | | the old register). However, if the old register is not FP/SP, |
1027 | | skip creating SFrame stack trace info for the function. */ |
1028 | 0 | if ((cur_fre->cfa_base_reg == SFRAME_CFA_FP_REG) |
1029 | 0 | || (cur_fre->cfa_base_reg == SFRAME_CFA_SP_REG)) |
1030 | 0 | { |
1031 | 0 | sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.i); |
1032 | 0 | cur_fre->merge_candidate = false; |
1033 | 0 | } |
1034 | 0 | else |
1035 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
1036 | | |
1037 | 0 | return SFRAME_XLATE_OK; |
1038 | 0 | } |
1039 | | |
1040 | | /* Translate DW_CFA_offset into SFrame context. |
1041 | | Return SFRAME_XLATE_OK if success. */ |
1042 | | |
1043 | | static int |
1044 | | sframe_xlate_do_offset (struct sframe_xlate_ctx *xlate_ctx, |
1045 | | struct cfi_insn_data *cfi_insn) |
1046 | 0 | { |
1047 | | /* The scratchpad FRE currently being updated with each cfi_insn |
1048 | | being interpreted. This FRE eventually gets linked in into the |
1049 | | list of FREs for the specific function. */ |
1050 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1051 | |
|
1052 | 0 | gas_assert (cur_fre); |
1053 | | /* Change the rule for the register indicated by the register number to |
1054 | | be the specified offset. */ |
1055 | 0 | if (cfi_insn->u.r == SFRAME_CFA_FP_REG) |
1056 | 0 | { |
1057 | 0 | gas_assert (!cur_fre->base_reg); |
1058 | 0 | sframe_fre_set_bp_track (cur_fre, cfi_insn->u.ri.offset); |
1059 | 0 | cur_fre->merge_candidate = false; |
1060 | 0 | } |
1061 | | #ifdef SFRAME_FRE_RA_TRACKING |
1062 | | else if (sframe_ra_tracking_p () |
1063 | | && cfi_insn->u.r == SFRAME_CFA_RA_REG) |
1064 | | { |
1065 | | sframe_fre_set_ra_track (cur_fre, cfi_insn->u.ri.offset); |
1066 | | cur_fre->merge_candidate = false; |
1067 | | } |
1068 | | #endif |
1069 | | /* This is used to track changes to non-rsp registers, skip all others |
1070 | | except FP / RA for now. */ |
1071 | 0 | return SFRAME_XLATE_OK; |
1072 | 0 | } |
1073 | | |
1074 | | /* Translate DW_CFA_val_offset into SFrame context. |
1075 | | Return SFRAME_XLATE_OK if success. */ |
1076 | | |
1077 | | static int |
1078 | | sframe_xlate_do_val_offset (struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED, |
1079 | | struct cfi_insn_data *cfi_insn) |
1080 | 0 | { |
1081 | | /* Previous value of register is CFA + offset. However, if the specified |
1082 | | register is not interesting (FP or RA reg), the current DW_CFA_val_offset |
1083 | | instruction can be safely skipped without sacrificing the asynchonicity of |
1084 | | stack trace information. */ |
1085 | 0 | if (cfi_insn->u.r == SFRAME_CFA_FP_REG) |
1086 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1087 | | #ifdef SFRAME_FRE_RA_TRACKING |
1088 | | else if (sframe_ra_tracking_p () |
1089 | | && cfi_insn->u.r == SFRAME_CFA_RA_REG) |
1090 | | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1091 | | #endif |
1092 | | |
1093 | | /* Safe to skip. */ |
1094 | 0 | return SFRAME_XLATE_OK; |
1095 | 0 | } |
1096 | | |
1097 | | /* Translate DW_CFA_remember_state into SFrame context. |
1098 | | Return SFRAME_XLATE_OK if success. */ |
1099 | | |
1100 | | static int |
1101 | | sframe_xlate_do_remember_state (struct sframe_xlate_ctx *xlate_ctx) |
1102 | 0 | { |
1103 | 0 | struct sframe_row_entry *last_fre = xlate_ctx->last_fre; |
1104 | | |
1105 | | /* If there is no FRE state to remember, nothing to do here. Return |
1106 | | early with non-zero error code, this will cause no SFrame stack trace |
1107 | | info for the function involved. */ |
1108 | 0 | if (!last_fre) |
1109 | 0 | return SFRAME_XLATE_ERR_INVAL; |
1110 | | |
1111 | 0 | if (!xlate_ctx->remember_fre) |
1112 | 0 | xlate_ctx->remember_fre = sframe_row_entry_new (); |
1113 | 0 | sframe_row_entry_initialize (xlate_ctx->remember_fre, last_fre); |
1114 | |
|
1115 | 0 | return SFRAME_XLATE_OK; |
1116 | 0 | } |
1117 | | |
1118 | | /* Translate DW_CFA_restore_state into SFrame context. |
1119 | | Return SFRAME_XLATE_OK if success. */ |
1120 | | |
1121 | | static int |
1122 | | sframe_xlate_do_restore_state (struct sframe_xlate_ctx *xlate_ctx) |
1123 | 0 | { |
1124 | | /* The scratchpad FRE currently being updated with each cfi_insn |
1125 | | being interpreted. This FRE eventually gets linked in into the |
1126 | | list of FREs for the specific function. */ |
1127 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1128 | |
|
1129 | 0 | gas_assert (xlate_ctx->remember_fre); |
1130 | 0 | gas_assert (cur_fre && cur_fre->merge_candidate); |
1131 | | |
1132 | | /* Get the CFA state from the DW_CFA_remember_state insn. */ |
1133 | 0 | sframe_row_entry_initialize (cur_fre, xlate_ctx->remember_fre); |
1134 | | /* The PC boundaries of the current SFrame FRE are updated |
1135 | | via other machinery. */ |
1136 | 0 | cur_fre->merge_candidate = false; |
1137 | 0 | return SFRAME_XLATE_OK; |
1138 | 0 | } |
1139 | | |
1140 | | /* Translate DW_CFA_restore into SFrame context. |
1141 | | Return SFRAME_XLATE_OK if success. */ |
1142 | | |
1143 | | static int |
1144 | | sframe_xlate_do_restore (struct sframe_xlate_ctx *xlate_ctx, |
1145 | | struct cfi_insn_data *cfi_insn) |
1146 | 0 | { |
1147 | 0 | struct sframe_row_entry *cie_fre = xlate_ctx->first_fre; |
1148 | | /* The scratchpad FRE currently being updated with each cfi_insn |
1149 | | being interpreted. This FRE eventually gets linked in into the |
1150 | | list of FREs for the specific function. */ |
1151 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1152 | | |
1153 | | /* Change the rule for the indicated register to the rule assigned to |
1154 | | it by the initial_instructions in the CIE. */ |
1155 | 0 | gas_assert (cie_fre); |
1156 | | /* SFrame FREs track only CFA and FP / RA for backtracing purposes; |
1157 | | skip the other .cfi_restore directives. */ |
1158 | 0 | if (cfi_insn->u.r == SFRAME_CFA_FP_REG) |
1159 | 0 | { |
1160 | 0 | gas_assert (cur_fre); |
1161 | 0 | cur_fre->bp_loc = cie_fre->bp_loc; |
1162 | 0 | cur_fre->bp_offset = cie_fre->bp_offset; |
1163 | 0 | cur_fre->merge_candidate = false; |
1164 | 0 | } |
1165 | | #ifdef SFRAME_FRE_RA_TRACKING |
1166 | | else if (sframe_ra_tracking_p () |
1167 | | && cfi_insn->u.r == SFRAME_CFA_RA_REG) |
1168 | | { |
1169 | | gas_assert (cur_fre); |
1170 | | cur_fre->ra_loc = cie_fre->ra_loc; |
1171 | | cur_fre->ra_offset = cie_fre->ra_offset; |
1172 | | cur_fre->merge_candidate = false; |
1173 | | } |
1174 | | #endif |
1175 | 0 | return SFRAME_XLATE_OK; |
1176 | 0 | } |
1177 | | |
1178 | | /* Translate DW_CFA_GNU_window_save into SFrame context. |
1179 | | Return SFRAME_XLATE_OK if success. */ |
1180 | | |
1181 | | static int |
1182 | | sframe_xlate_do_gnu_window_save (struct sframe_xlate_ctx *xlate_ctx, |
1183 | | struct cfi_insn_data *cfi_insn ATTRIBUTE_UNUSED) |
1184 | 0 | { |
1185 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1186 | |
|
1187 | 0 | gas_assert (cur_fre); |
1188 | | /* Toggle the mangled RA status bit. */ |
1189 | 0 | cur_fre->mangled_ra_p = !cur_fre->mangled_ra_p; |
1190 | 0 | cur_fre->merge_candidate = false; |
1191 | |
|
1192 | 0 | return SFRAME_XLATE_OK; |
1193 | 0 | } |
1194 | | |
1195 | | /* Process CFI_INSN and update the translation context with the FRE |
1196 | | information. |
1197 | | |
1198 | | Returns an error code (sframe_xlate_err) if CFI_INSN is not successfully |
1199 | | processed. */ |
1200 | | |
1201 | | static int |
1202 | | sframe_do_cfi_insn (struct sframe_xlate_ctx *xlate_ctx, |
1203 | | struct cfi_insn_data *cfi_insn) |
1204 | 0 | { |
1205 | 0 | int err = 0; |
1206 | | |
1207 | | /* Atleast one cfi_insn per FDE is expected. */ |
1208 | 0 | gas_assert (cfi_insn); |
1209 | 0 | int op = cfi_insn->insn; |
1210 | |
|
1211 | 0 | switch (op) |
1212 | 0 | { |
1213 | 0 | case DW_CFA_advance_loc: |
1214 | 0 | err = sframe_xlate_do_advance_loc (xlate_ctx, cfi_insn); |
1215 | 0 | break; |
1216 | 0 | case DW_CFA_def_cfa: |
1217 | 0 | err = sframe_xlate_do_def_cfa (xlate_ctx, cfi_insn); |
1218 | 0 | break; |
1219 | 0 | case DW_CFA_def_cfa_register: |
1220 | 0 | err = sframe_xlate_do_def_cfa_register (xlate_ctx, cfi_insn); |
1221 | 0 | break; |
1222 | 0 | case DW_CFA_def_cfa_offset: |
1223 | 0 | err = sframe_xlate_do_def_cfa_offset (xlate_ctx, cfi_insn); |
1224 | 0 | break; |
1225 | 0 | case DW_CFA_offset: |
1226 | 0 | err = sframe_xlate_do_offset (xlate_ctx, cfi_insn); |
1227 | 0 | break; |
1228 | 0 | case DW_CFA_val_offset: |
1229 | 0 | err = sframe_xlate_do_val_offset (xlate_ctx, cfi_insn); |
1230 | 0 | break; |
1231 | 0 | case DW_CFA_remember_state: |
1232 | 0 | err = sframe_xlate_do_remember_state (xlate_ctx); |
1233 | 0 | break; |
1234 | 0 | case DW_CFA_restore_state: |
1235 | 0 | err = sframe_xlate_do_restore_state (xlate_ctx); |
1236 | 0 | break; |
1237 | 0 | case DW_CFA_restore: |
1238 | 0 | err = sframe_xlate_do_restore (xlate_ctx, cfi_insn); |
1239 | 0 | break; |
1240 | | /* DW_CFA_AARCH64_negate_ra_state is multiplexed with |
1241 | | DW_CFA_GNU_window_save. */ |
1242 | 0 | case DW_CFA_GNU_window_save: |
1243 | 0 | err = sframe_xlate_do_gnu_window_save (xlate_ctx, cfi_insn); |
1244 | 0 | break; |
1245 | 0 | case DW_CFA_undefined: |
1246 | 0 | case DW_CFA_same_value: |
1247 | 0 | break; |
1248 | 0 | default: |
1249 | 0 | { |
1250 | | /* Other CFI opcodes are not processed at this time. |
1251 | | These do not impact the coverage of the basic stack tracing |
1252 | | information as conveyed in the SFrame format. |
1253 | | - DW_CFA_register, |
1254 | | - ... |
1255 | | |
1256 | | Following skipped operations do, however, impact the asynchronicity: |
1257 | | - CFI_escape */ |
1258 | |
|
1259 | 0 | err = SFRAME_XLATE_ERR_NOTREPRESENTED; |
1260 | | // printf (_("SFrame Unsupported or unknown Dwarf CFI number: %#x\n"), op); |
1261 | 0 | } |
1262 | 0 | } |
1263 | | |
1264 | 0 | return err; |
1265 | 0 | } |
1266 | | |
1267 | | |
1268 | | static int |
1269 | | sframe_do_fde (struct sframe_xlate_ctx *xlate_ctx, |
1270 | | const struct fde_entry *dw_fde) |
1271 | 0 | { |
1272 | 0 | struct cfi_insn_data *cfi_insn; |
1273 | 0 | int err = SFRAME_XLATE_OK; |
1274 | |
|
1275 | 0 | xlate_ctx->dw_fde = dw_fde; |
1276 | | |
1277 | | /* If the return column is not RIP, SFrame format cannot represent it. */ |
1278 | 0 | if (xlate_ctx->dw_fde->return_column != DWARF2_DEFAULT_RETURN_COLUMN) |
1279 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
1280 | | |
1281 | | /* Iterate over the CFIs and create SFrame FREs. */ |
1282 | 0 | for (cfi_insn = dw_fde->data; cfi_insn; cfi_insn = cfi_insn->next) |
1283 | 0 | { |
1284 | | /* Translate each CFI, and buffer the state in translation context. */ |
1285 | 0 | err = sframe_do_cfi_insn (xlate_ctx, cfi_insn); |
1286 | 0 | if (err != SFRAME_XLATE_OK) |
1287 | 0 | { |
1288 | | /* Skip generating SFrame stack trace info for the function if any |
1289 | | offending CFI is encountered by sframe_do_cfi_insn (). */ |
1290 | 0 | return err; /* Return the error code. */ |
1291 | 0 | } |
1292 | 0 | } |
1293 | | |
1294 | | /* No errors encountered. */ |
1295 | | |
1296 | | /* Link in the scratchpad FRE that the last few CFI insns helped create. */ |
1297 | 0 | if (xlate_ctx->cur_fre) |
1298 | 0 | { |
1299 | 0 | sframe_xlate_ctx_add_fre (xlate_ctx, xlate_ctx->cur_fre); |
1300 | 0 | xlate_ctx->cur_fre = NULL; |
1301 | 0 | } |
1302 | | /* Designate the end of the last SFrame FRE. */ |
1303 | 0 | if (xlate_ctx->last_fre) |
1304 | 0 | { |
1305 | 0 | xlate_ctx->last_fre->pc_end |
1306 | 0 | = get_dw_fde_end_addrS (xlate_ctx->dw_fde); |
1307 | 0 | } |
1308 | |
|
1309 | 0 | return SFRAME_XLATE_OK; |
1310 | 0 | } |
1311 | | |
1312 | | /* Create SFrame stack trace info for all functions. |
1313 | | |
1314 | | This function consumes the already generated DWARF FDEs (by dw2gencfi) and |
1315 | | generates data which is later emitted as stack trace information encoded in |
1316 | | the SFrame format. */ |
1317 | | |
1318 | | static void |
1319 | | create_sframe_all (void) |
1320 | 0 | { |
1321 | 0 | struct fde_entry *dw_fde = NULL; |
1322 | 0 | struct sframe_func_entry *sframe_fde = NULL; |
1323 | |
|
1324 | 0 | struct sframe_xlate_ctx *xlate_ctx = sframe_xlate_ctx_alloc (); |
1325 | |
|
1326 | 0 | for (dw_fde = all_fde_data; dw_fde ; dw_fde = dw_fde->next) |
1327 | 0 | { |
1328 | 0 | sframe_fde = sframe_fde_alloc (); |
1329 | | /* Initialize the translation context with information anew. */ |
1330 | 0 | sframe_xlate_ctx_init (xlate_ctx); |
1331 | | |
1332 | | /* Process and link SFrame FDEs if no error. Also skip adding an SFrame |
1333 | | FDE if it does not contain any SFrame FREs. There is little use of an |
1334 | | SFrame FDE if there is no stack tracing information for the |
1335 | | function. */ |
1336 | 0 | int err = sframe_do_fde (xlate_ctx, dw_fde); |
1337 | 0 | if (err || xlate_ctx->num_xlate_fres == 0) |
1338 | 0 | { |
1339 | 0 | sframe_xlate_ctx_cleanup (xlate_ctx); |
1340 | 0 | sframe_fde_free (sframe_fde); |
1341 | 0 | } |
1342 | 0 | else |
1343 | 0 | { |
1344 | | /* All done. Transfer the state from the SFrame translation |
1345 | | context to the SFrame FDE. */ |
1346 | 0 | sframe_xlate_ctx_finalize (xlate_ctx, sframe_fde); |
1347 | 0 | sframe_fde_link (sframe_fde); |
1348 | 0 | } |
1349 | 0 | } |
1350 | 0 | } |
1351 | | |
1352 | | void |
1353 | | output_sframe (segT sframe_seg) |
1354 | 0 | { |
1355 | 0 | (void) sframe_seg; |
1356 | | |
1357 | | /* Setup the version specific access functions. */ |
1358 | 0 | sframe_set_version (SFRAME_VERSION_1); |
1359 | | |
1360 | | /* Process all fdes and create SFrame stack trace information. */ |
1361 | 0 | create_sframe_all (); |
1362 | |
|
1363 | 0 | output_sframe_internal (); |
1364 | 0 | } |
1365 | | |
1366 | | #else /* support_sframe_p */ |
1367 | | |
1368 | | void |
1369 | | output_sframe (segT sframe_seg ATTRIBUTE_UNUSED) |
1370 | | { |
1371 | | } |
1372 | | |
1373 | | #endif /* support_sframe_p */ |