/src/binutils-gdb/gas/gen-sframe.c
Line | Count | Source |
1 | | /* gen-sframe.c - Support for generating SFrame section. |
2 | | Copyright (C) 2022-2026 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-internal.h" |
25 | | #include "gen-sframe.h" |
26 | | #include "dw2gencfi.h" |
27 | | #include "leb128.h" |
28 | | |
29 | | #ifdef support_sframe_p |
30 | | |
31 | | #ifndef sizeof_member |
32 | 18 | # define sizeof_member(type, member) (sizeof (((type *)0)->member)) |
33 | | #endif |
34 | | |
35 | | /* SFrame FRE type selection optimization is an optimization for size. |
36 | | |
37 | | There are three flavors of SFrame FRE representation in the binary format: |
38 | | - sframe_frame_row_entry_addr1 where the FRE start address is 1 byte. |
39 | | - sframe_frame_row_entry_addr2 where the FRE start address is 2 bytes. |
40 | | - sframe_frame_row_entry_addr4 where the FRE start address is 4 bytes. |
41 | | |
42 | | Note that in the SFrame format, all SFrame FREs of a function use one |
43 | | single representation. The SFrame FRE type itself is identified via the |
44 | | information in the SFrame FDE function info. |
45 | | |
46 | | Now, to select the minimum required one from the list above, one needs to |
47 | | make a decision based on the size (in bytes) of the function. |
48 | | |
49 | | As a result, for this optimization, some fragments (generated with a new |
50 | | type rs_sframe) for the SFrame section are fixed up later. |
51 | | |
52 | | This optimization (for size) is enabled by default. */ |
53 | | |
54 | | #ifndef SFRAME_FRE_TYPE_SELECTION_OPT |
55 | 8 | # define SFRAME_FRE_TYPE_SELECTION_OPT 1 |
56 | | #endif |
57 | | |
58 | | /* gas emits SFrame Version 3 only at this time. */ |
59 | | typedef sframe_func_desc_idx_v3 sframe_func_desc_idx; |
60 | | |
61 | | /* List of SFrame FDE entries. */ |
62 | | |
63 | | static struct sframe_func_entry *all_sframe_fdes = NULL; |
64 | | |
65 | | /* Tail of the list to add to. */ |
66 | | |
67 | | static struct sframe_func_entry **last_sframe_fde = &all_sframe_fdes; |
68 | | |
69 | | /* Emit a single byte into the current segment. */ |
70 | | |
71 | | static inline void |
72 | | out_one (int byte) |
73 | 36 | { |
74 | 36 | FRAG_APPEND_1_CHAR (byte); |
75 | 36 | } |
76 | | |
77 | | /* Emit a two-byte word into the current segment. */ |
78 | | |
79 | | static inline void |
80 | | out_two (int data) |
81 | 6 | { |
82 | 6 | md_number_to_chars (frag_more (2), data, 2); |
83 | 6 | } |
84 | | |
85 | | /* Emit a four byte word into the current segment. */ |
86 | | |
87 | | static inline void |
88 | | out_four (int data) |
89 | 6 | { |
90 | 6 | md_number_to_chars (frag_more (4), data, 4); |
91 | 6 | } |
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 | 14 | { |
98 | 14 | return dw_fde->start_address; |
99 | 14 | } |
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 | 14 | { |
106 | 14 | return dw_fde->end_address; |
107 | 14 | } |
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 | 3 | { |
113 | | #ifdef tc_fde_entry_extras |
114 | | return (dw_fde->pauth_key == AARCH64_PAUTH_KEY_B); |
115 | | #else |
116 | 3 | return false; |
117 | 3 | #endif |
118 | 3 | } |
119 | | |
120 | | /* Get whether signal frame. */ |
121 | | static bool |
122 | | get_dw_fde_signal_p (const struct fde_entry *dw_fde) |
123 | 3 | { |
124 | 3 | return (dw_fde->signal_frame == 1); |
125 | 3 | } |
126 | | |
127 | | /* SFrame Frame Row Entry (FRE) related functions. */ |
128 | | |
129 | | static void |
130 | | sframe_fre_set_begin_addr (struct sframe_row_entry *fre, symbolS *beginS) |
131 | 7 | { |
132 | 7 | fre->pc_begin = beginS; |
133 | 7 | } |
134 | | |
135 | | static void |
136 | | sframe_fre_set_end_addr (struct sframe_row_entry *fre, symbolS *endS) |
137 | 4 | { |
138 | 4 | fre->pc_end = endS; |
139 | 4 | } |
140 | | |
141 | | static void |
142 | | sframe_fre_set_cfa_base_reg (struct sframe_row_entry *fre, |
143 | | unsigned int cfa_base_reg) |
144 | 4 | { |
145 | 4 | fre->cfa_base_reg = cfa_base_reg; |
146 | 4 | fre->merge_candidate = false; |
147 | 4 | } |
148 | | |
149 | | static offsetT |
150 | | sframe_fre_get_cfa_offset (const struct sframe_row_entry * fre) |
151 | 0 | { |
152 | 0 | offsetT offset = fre->cfa_offset; |
153 | | |
154 | | /* For s390x undo adjustment of CFA offset (to enable 8-bit offsets). */ |
155 | 0 | if (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG) |
156 | 0 | offset = SFRAME_V2_S390X_CFA_OFFSET_DECODE (offset); |
157 | |
|
158 | 0 | return offset; |
159 | 0 | } |
160 | | |
161 | | /* All stack offsets in SFrame stack trace format must be representable as a |
162 | | 1-byte (SFRAME_FRE_DATAWORD_1B), 2-byte (SFRAME_FRE_DATAWORD_2B) or 4-byte |
163 | | (SFRAME_FRE_DATAWORD_4B) value. |
164 | | |
165 | | At the moment, sanity check on CFA offset (only) is performed to address PR |
166 | | gas/33277. Arguably, such updates to ra_offset or fp_offset will only |
167 | | follow after updates to cfa_offset in a real-world, useful program. */ |
168 | | |
169 | | static bool |
170 | | sframe_fre_stack_offset_bound_p (offsetT offset, bool cfa_reg_p) |
171 | 4 | { |
172 | | /* For s390x, CFA offset is adjusted to enable 8-bit offsets. */ |
173 | 4 | if (cfa_reg_p && sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG) |
174 | 0 | offset = SFRAME_V2_S390X_CFA_OFFSET_ENCODE (offset); |
175 | | |
176 | 4 | return (offset >= INT32_MIN && offset <= INT32_MAX); |
177 | 4 | } |
178 | | |
179 | | static void |
180 | | sframe_fre_set_cfa_offset (struct sframe_row_entry *fre, |
181 | | offsetT cfa_offset) |
182 | 4 | { |
183 | | /* For s390x adjust CFA offset to enable 8-bit offsets. */ |
184 | 4 | if (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG) |
185 | 0 | cfa_offset = SFRAME_V2_S390X_CFA_OFFSET_ENCODE (cfa_offset); |
186 | | |
187 | 4 | fre->cfa_offset = cfa_offset; |
188 | 4 | fre->merge_candidate = false; |
189 | 4 | } |
190 | | |
191 | | static void |
192 | | sframe_fre_set_ra_track (struct sframe_row_entry *fre, offsetT ra_offset) |
193 | 0 | { |
194 | 0 | fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK; |
195 | 0 | fre->ra_offset = ra_offset; |
196 | 0 | fre->ra_undefined_p = false; |
197 | 0 | fre->merge_candidate = false; |
198 | 0 | } |
199 | | |
200 | | static void |
201 | | sframe_fre_set_fp_track (struct sframe_row_entry *fre, offsetT fp_offset) |
202 | 0 | { |
203 | 0 | fre->fp_loc = SFRAME_FRE_ELEM_LOC_STACK; |
204 | 0 | fre->fp_offset = fp_offset; |
205 | 0 | fre->merge_candidate = false; |
206 | 0 | } |
207 | | |
208 | | /* Given a signed offset, return the size in bytes needed to represent it. */ |
209 | | |
210 | | static unsigned int |
211 | | get_offset_size_in_bytes (offsetT value) |
212 | 5 | { |
213 | 5 | unsigned int size = 0; |
214 | | |
215 | 5 | if (value == (int8_t)value) |
216 | 5 | size = 1; |
217 | 0 | else if (value == (int16_t)value) |
218 | 0 | size = 2; |
219 | 0 | else if (value == (int32_t)value) |
220 | 0 | size = 4; |
221 | 0 | else |
222 | 0 | return 8; |
223 | | |
224 | 5 | return size; |
225 | 5 | } |
226 | | |
227 | | /* Given an unsigned item, return the size in bytes needed to represent it. */ |
228 | | |
229 | | static unsigned int |
230 | | get_udata_size_in_bytes (unsigned int value) |
231 | 2 | { |
232 | 2 | unsigned int size = 0; |
233 | | |
234 | 2 | if (value <= UINT8_MAX) |
235 | 2 | size = 1; |
236 | 0 | else if (value <= UINT16_MAX) |
237 | 0 | size = 2; |
238 | 0 | else |
239 | 0 | size = 4; |
240 | | |
241 | 2 | return size; |
242 | 2 | } |
243 | 5 | #define SFRAME_FRE_DATAWORD_FUNC_MAP_INDEX_1B 0 /* SFRAME_FRE_DATAWORD_1B. */ |
244 | 0 | #define SFRAME_FRE_DATAWORD_FUNC_MAP_INDEX_2B 1 /* SFRAME_FRE_DATAWORD_2B. */ |
245 | 0 | #define SFRAME_FRE_DATAWORD_FUNC_MAP_INDEX_4B 2 /* SFRAME_FRE_DATAWORD_4B. */ |
246 | 5 | #define SFRAME_FRE_DATAWORD_FUNC_MAP_INDEX_8B 3 /* Not supported in SFrame. */ |
247 | | #define SFRAME_FRE_DATAWORD_FUNC_MAP_INDEX_MAX \ |
248 | 5 | SFRAME_FRE_DATAWORD_FUNC_MAP_INDEX_8B |
249 | | |
250 | | /* Helper struct for mapping FRE data word size to output functions. */ |
251 | | |
252 | | struct sframe_fre_dataword_func_map |
253 | | { |
254 | | unsigned int dataword_size; |
255 | | void (*out_func)(int); |
256 | | }; |
257 | | |
258 | | /* Given an DATAWORD_SIZE, return the size in bytes needed to represent it. */ |
259 | | |
260 | | static unsigned int |
261 | | sframe_fre_dataword_func_map_index (unsigned int dataword_size) |
262 | 5 | { |
263 | 5 | unsigned int idx = SFRAME_FRE_DATAWORD_FUNC_MAP_INDEX_MAX; |
264 | | |
265 | 5 | switch (dataword_size) |
266 | 5 | { |
267 | 5 | case SFRAME_FRE_DATAWORD_1B: |
268 | 5 | idx = SFRAME_FRE_DATAWORD_FUNC_MAP_INDEX_1B; |
269 | 5 | break; |
270 | 0 | case SFRAME_FRE_DATAWORD_2B: |
271 | 0 | idx = SFRAME_FRE_DATAWORD_FUNC_MAP_INDEX_2B; |
272 | 0 | break; |
273 | 0 | case SFRAME_FRE_DATAWORD_4B: |
274 | 0 | idx = SFRAME_FRE_DATAWORD_FUNC_MAP_INDEX_4B; |
275 | 0 | break; |
276 | 0 | default: |
277 | | /* Not supported in SFrame. */ |
278 | 0 | break; |
279 | 5 | } |
280 | | |
281 | 5 | return idx; |
282 | 5 | } |
283 | | |
284 | | /* Mapping from data word size to the output function to emit the value. */ |
285 | | |
286 | | static const |
287 | | struct sframe_fre_dataword_func_map |
288 | | dataword_func_map[SFRAME_FRE_DATAWORD_FUNC_MAP_INDEX_MAX+1] = |
289 | | { |
290 | | { SFRAME_FRE_DATAWORD_1B, out_one }, |
291 | | { SFRAME_FRE_DATAWORD_2B, out_two }, |
292 | | { SFRAME_FRE_DATAWORD_4B, out_four }, |
293 | | { -1, NULL } /* Not Supported in SFrame. */ |
294 | | }; |
295 | | |
296 | | /* SFrame version specific operations access. */ |
297 | | |
298 | | static struct sframe_version_ops sframe_ver_ops; |
299 | | |
300 | | /* SFrame (SFRAME_VERSION_1) set FRE info. */ |
301 | | |
302 | | static unsigned char |
303 | | sframe_v1_set_fre_info (unsigned int cfa_base_reg, unsigned int dataword_count, |
304 | | unsigned int dataword_size, bool mangled_ra_p) |
305 | 5 | { |
306 | 5 | unsigned char fre_info; |
307 | 5 | fre_info = SFRAME_V1_FRE_INFO (cfa_base_reg, dataword_count, dataword_size); |
308 | 5 | fre_info = SFRAME_V1_FRE_INFO_UPDATE_MANGLED_RA_P (mangled_ra_p, fre_info); |
309 | 5 | return fre_info; |
310 | 5 | } |
311 | | |
312 | | /* SFrame (SFRAME_VERSION_3) set function info. */ |
313 | | |
314 | | static unsigned char |
315 | | sframe_v3_set_func_info (unsigned int fde_pc_type, unsigned int fre_type, |
316 | | unsigned int pauth_key, bool signal_p) |
317 | 3 | { |
318 | 3 | unsigned char func_info; |
319 | 3 | func_info = SFRAME_V3_FDE_FUNC_INFO (fde_pc_type, fre_type); |
320 | 3 | func_info = SFRAME_V3_FDE_UPDATE_PAUTH_KEY (pauth_key, func_info); |
321 | 3 | func_info = SFRAME_V3_FDE_UPDATE_SIGNAL_P (signal_p, func_info); |
322 | 3 | return func_info; |
323 | 3 | } |
324 | | |
325 | | /* SFrame version specific operations setup. */ |
326 | | |
327 | | static void |
328 | | sframe_set_version (enum gen_sframe_version flag_ver) |
329 | 3 | { |
330 | 3 | if (flag_ver == GEN_SFRAME_VERSION_3) |
331 | 3 | { |
332 | 3 | sframe_ver_ops.format_version = SFRAME_VERSION_3; |
333 | | /* These operations remain the same for SFRAME_VERSION_3 as fre_info and |
334 | | func_info layout has not changed from SFRAME_VERSION_2 and |
335 | | SFRAME_VERSION_1. */ |
336 | 3 | sframe_ver_ops.set_fre_info = sframe_v1_set_fre_info; |
337 | 3 | sframe_ver_ops.set_func_info = sframe_v3_set_func_info; |
338 | 3 | } |
339 | 3 | } |
340 | | |
341 | | /* SFrame set FRE info. */ |
342 | | |
343 | | static unsigned char |
344 | | sframe_set_fre_info (unsigned int cfa_base_reg, unsigned int dataword_count, |
345 | | unsigned int dataword_size, bool mangled_ra_p) |
346 | 5 | { |
347 | 5 | return sframe_ver_ops.set_fre_info (cfa_base_reg, dataword_count, |
348 | 5 | dataword_size, mangled_ra_p); |
349 | 5 | } |
350 | | |
351 | | /* SFrame set func info. */ |
352 | | |
353 | | static unsigned char |
354 | | sframe_set_func_info (unsigned int fde_type, unsigned int fre_type, |
355 | | unsigned int pauth_key, bool signal_p) |
356 | 3 | { |
357 | 3 | return sframe_ver_ops.set_func_info (fde_type, fre_type, pauth_key, |
358 | 3 | signal_p); |
359 | 3 | } |
360 | | |
361 | | /* Get the number of SFrame FDEs for the current file. */ |
362 | | |
363 | | static unsigned int |
364 | | get_num_sframe_fdes (void); |
365 | | |
366 | | /* Get the number of SFrame frame row entries for the current file. */ |
367 | | |
368 | | static unsigned int |
369 | | get_num_sframe_fres (void); |
370 | | |
371 | | /* Get CFA base register ID as represented in SFrame Frame Row Entry. */ |
372 | | |
373 | | static unsigned int |
374 | | get_fre_base_reg_id (const struct sframe_row_entry *sframe_fre) |
375 | 5 | { |
376 | 5 | unsigned int cfi_insn_cfa_base_reg = sframe_fre->cfa_base_reg; |
377 | 5 | unsigned fre_base_reg = SFRAME_BASE_REG_SP; |
378 | | |
379 | 5 | if (cfi_insn_cfa_base_reg == SFRAME_CFA_FP_REG) |
380 | 0 | fre_base_reg = SFRAME_BASE_REG_FP; |
381 | | |
382 | | /* Only one bit is reserved in SFRAME_VERSION_1. */ |
383 | 5 | gas_assert (fre_base_reg == SFRAME_BASE_REG_SP |
384 | 5 | || fre_base_reg == SFRAME_BASE_REG_FP); |
385 | | |
386 | 5 | return fre_base_reg; |
387 | 5 | } |
388 | | |
389 | | /* Get number of data words necessary for the SFrame Frame Row Entry. */ |
390 | | |
391 | | static unsigned int |
392 | | get_fre_dataword_count (const struct sframe_row_entry *sframe_fre, bool flex_p) |
393 | 5 | { |
394 | | /* For SFRAME_FDE_TYPE_FLEX FDE type, each entity (CFA, FP, RA) may carry up |
395 | | to two data words. */ |
396 | 5 | unsigned int count = flex_p ? 2 : 1; |
397 | | |
398 | | /* CFA data word (or data words when flex_p) must always be present. */ |
399 | 5 | unsigned int fre_dataword_count = count; |
400 | | |
401 | | /* For flexible FDE type, there will be two data words for RA (if RA |
402 | | has a recovery rule applicable). 1 padding data word otherwise. */ |
403 | 5 | if (flex_p) |
404 | 2 | { |
405 | 2 | if (sframe_fre->ra_loc != SFRAME_FRE_ELEM_LOC_NONE) |
406 | 0 | fre_dataword_count += count; |
407 | 2 | else if (sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_NONE) |
408 | 0 | fre_dataword_count += 1; |
409 | 2 | } |
410 | 3 | else if (sframe_ra_tracking_p () |
411 | 0 | && (sframe_fre->ra_loc != SFRAME_FRE_ELEM_LOC_NONE |
412 | | /* For s390x account padding RA data word, if FP without RA |
413 | | saved. */ |
414 | 0 | || (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG |
415 | 0 | && sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_NONE))) |
416 | 0 | fre_dataword_count++; |
417 | | |
418 | 5 | if (sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_NONE) |
419 | 0 | fre_dataword_count += count; |
420 | | |
421 | 5 | return fre_dataword_count; |
422 | 5 | } |
423 | | |
424 | | /* Get the minimum necessary data word size (in bytes) for this |
425 | | SFrame frame row entry. */ |
426 | | |
427 | | static unsigned int |
428 | | sframe_get_fre_dataword_size (const struct sframe_row_entry *sframe_fre, |
429 | | bool flex_p) |
430 | 5 | { |
431 | 5 | unsigned int max_dataword_size = 0; |
432 | 5 | unsigned int cfa_offset_size = 0; |
433 | 5 | unsigned int fp_offset_size = 0; |
434 | 5 | unsigned int ra_offset_size = 0; |
435 | | |
436 | 5 | unsigned int fre_dataword_size = 0; |
437 | | |
438 | | /* What size of data words appear in this frame row entry. */ |
439 | 5 | cfa_offset_size = get_offset_size_in_bytes (sframe_fre->cfa_offset); |
440 | 5 | if (sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK) |
441 | 0 | fp_offset_size = get_offset_size_in_bytes (sframe_fre->fp_offset); |
442 | 5 | if (sframe_ra_tracking_p ()) |
443 | 0 | { |
444 | 0 | if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK) |
445 | 0 | ra_offset_size = get_offset_size_in_bytes (sframe_fre->ra_offset); |
446 | | /* For s390x account padding RA offset, if FP without RA saved. */ |
447 | 0 | else if (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG |
448 | 0 | && sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK) |
449 | 0 | ra_offset_size = get_offset_size_in_bytes (SFRAME_FRE_RA_OFFSET_INVALID); |
450 | 0 | } |
451 | | |
452 | | /* Get the maximum size needed to represent the offsets. */ |
453 | 5 | max_dataword_size = cfa_offset_size; |
454 | 5 | if (fp_offset_size > max_dataword_size) |
455 | 0 | max_dataword_size = fp_offset_size; |
456 | 5 | if (ra_offset_size > max_dataword_size) |
457 | 0 | max_dataword_size = ra_offset_size; |
458 | | |
459 | | /* If flex FDE, account for reg data too. */ |
460 | 5 | if (flex_p) |
461 | 2 | { |
462 | 2 | bool reg_p = (sframe_fre->cfa_base_reg != SFRAME_FRE_REG_INVALID); |
463 | 2 | unsigned int data |
464 | 2 | = SFRAME_V3_FLEX_FDE_CTRLWORD_ENCODE (sframe_fre->cfa_base_reg, |
465 | 2 | sframe_fre->cfa_deref_p, reg_p); |
466 | 2 | unsigned int cfa_control_word_size = get_udata_size_in_bytes (data); |
467 | 2 | if (cfa_control_word_size > max_dataword_size) |
468 | 0 | max_dataword_size = cfa_control_word_size; |
469 | | |
470 | 2 | if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_REG) |
471 | 0 | { |
472 | 0 | data = SFRAME_V3_FLEX_FDE_CTRLWORD_ENCODE (sframe_fre->ra_reg, |
473 | 0 | sframe_fre->ra_deref_p, |
474 | 0 | 1 /* reg_p. */); |
475 | 0 | unsigned ra_control_word_size = get_udata_size_in_bytes (data); |
476 | 0 | if (ra_control_word_size > max_dataword_size) |
477 | 0 | max_dataword_size = ra_control_word_size; |
478 | 0 | } |
479 | | |
480 | 2 | if (sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_REG) |
481 | 0 | { |
482 | 0 | data = SFRAME_V3_FLEX_FDE_CTRLWORD_ENCODE (sframe_fre->fp_reg, |
483 | 0 | sframe_fre->fp_deref_p, |
484 | 0 | 1 /* reg_p. */); |
485 | 0 | unsigned fp_control_word_size = get_udata_size_in_bytes (data); |
486 | 0 | if (fp_control_word_size > max_dataword_size) |
487 | 0 | max_dataword_size = fp_control_word_size; |
488 | 0 | } |
489 | 2 | } |
490 | | |
491 | 5 | gas_assert (max_dataword_size); |
492 | | |
493 | 5 | switch (max_dataword_size) |
494 | 5 | { |
495 | 5 | case 1: |
496 | 5 | fre_dataword_size = SFRAME_FRE_DATAWORD_1B; |
497 | 5 | break; |
498 | 0 | case 2: |
499 | 0 | fre_dataword_size = SFRAME_FRE_DATAWORD_2B; |
500 | 0 | break; |
501 | 0 | case 4: |
502 | 0 | fre_dataword_size = SFRAME_FRE_DATAWORD_4B; |
503 | 0 | break; |
504 | 0 | default: |
505 | | /* FRE data words of size 8 bytes is not supported in SFrame. */ |
506 | 0 | as_fatal (_("SFrame unsupported FRE data word size\n")); |
507 | 0 | break; |
508 | 5 | } |
509 | | |
510 | 5 | return fre_dataword_size; |
511 | 5 | } |
512 | | |
513 | | /* Create a composite expression CEXP (for SFrame FRE start address) such that: |
514 | | |
515 | | exp = <val> OP_absent <width>, where, |
516 | | |
517 | | - <val> and <width> are themselves expressionS. |
518 | | - <val> stores the expression which when evaluated gives the value of the |
519 | | start address offset of the FRE. |
520 | | - <width> stores the expression when evaluated gives the number of bytes |
521 | | needed to encode the start address offset of the FRE. |
522 | | |
523 | | The use of OP_absent as the X_op_symbol helps identify this expression |
524 | | later when fragments are fixed up. */ |
525 | | |
526 | | static void |
527 | | create_fre_start_addr_exp (expressionS *cexp, symbolS *fre_pc_begin, |
528 | | symbolS *fde_start_address, |
529 | | symbolS *fde_end_address) |
530 | 5 | { |
531 | | /* val expression stores the FDE start address offset from the start PC |
532 | | of function. */ |
533 | 5 | expressionS val = { |
534 | 5 | .X_op = O_subtract, |
535 | 5 | .X_add_symbol = fre_pc_begin, |
536 | 5 | .X_op_symbol = fde_start_address, |
537 | 5 | }; |
538 | | |
539 | | /* width expressions stores the size of the function. This is used later |
540 | | to determine the number of bytes to be used to encode the FRE start |
541 | | address of each FRE of the function. */ |
542 | 5 | expressionS width = { |
543 | 5 | .X_op = O_subtract, |
544 | 5 | .X_add_symbol = fde_end_address, |
545 | 5 | .X_op_symbol = fde_start_address, |
546 | 5 | }; |
547 | | |
548 | 5 | *cexp = (expressionS) { |
549 | 5 | .X_op = O_absent, |
550 | 5 | .X_add_symbol = make_expr_symbol (&val), |
551 | 5 | .X_op_symbol = make_expr_symbol (&width) |
552 | 5 | }; |
553 | 5 | } |
554 | | |
555 | | /* Create a composite expression CEXP (for SFrame FDE function info) such that: |
556 | | |
557 | | exp = <rest_of_func_info> OP_modulus <width>, where, |
558 | | |
559 | | - <rest_of_func_info> and <width> are themselves expressionS. |
560 | | - <rest_of_func_info> stores a constant expression where X_add_number is |
561 | | used to stash away the func_info. The upper 4-bits of the func_info are copied |
562 | | back to the resulting byte by the fragment fixup logic. |
563 | | - <width> stores the expression when evaluated gives the size of the |
564 | | function in number of bytes. |
565 | | |
566 | | The use of OP_modulus as the X_op_symbol helps identify this expression |
567 | | later when fragments are fixed up. */ |
568 | | |
569 | | static void |
570 | | create_func_info_exp (expressionS *cexp, symbolS *dw_fde_end_addrS, |
571 | | symbolS *dw_fde_start_addrS, uint8_t func_info) |
572 | 3 | { |
573 | 3 | expressionS width = { |
574 | 3 | .X_op = O_subtract, |
575 | 3 | .X_add_symbol = dw_fde_end_addrS, |
576 | 3 | .X_op_symbol = dw_fde_start_addrS |
577 | 3 | }; |
578 | | |
579 | 3 | expressionS rest_of_func_info = { |
580 | 3 | .X_op = O_constant, |
581 | 3 | .X_add_number = func_info |
582 | 3 | }; |
583 | | |
584 | 3 | *cexp = (expressionS) { |
585 | 3 | .X_op = O_modulus, |
586 | 3 | .X_add_symbol = make_expr_symbol (&rest_of_func_info), |
587 | 3 | .X_op_symbol = make_expr_symbol (&width) |
588 | 3 | }; |
589 | 3 | } |
590 | | |
591 | | static struct sframe_row_entry* |
592 | | sframe_row_entry_new (void) |
593 | 5 | { |
594 | 5 | struct sframe_row_entry *fre = XCNEW (struct sframe_row_entry); |
595 | | /* Reset all regs to SFRAME_FRE_REG_INVALID. A value of 0 may imply a |
596 | | valid register for a supported arch. */ |
597 | 5 | fre->cfa_base_reg = SFRAME_FRE_REG_INVALID; |
598 | 5 | fre->fp_reg = SFRAME_FRE_REG_INVALID; |
599 | 5 | fre->ra_reg = SFRAME_FRE_REG_INVALID; |
600 | 5 | fre->merge_candidate = true; |
601 | | /* Reset the mangled RA status bit to zero by default. We will |
602 | | initialize it in sframe_row_entry_initialize () with the sticky |
603 | | bit if set. */ |
604 | 5 | fre->mangled_ra_p = false; |
605 | | /* Reset the RA undefined status by to zero by default. */ |
606 | 5 | fre->ra_undefined_p = false; |
607 | | |
608 | 5 | return fre; |
609 | 5 | } |
610 | | |
611 | | static void |
612 | | sframe_row_entry_free (struct sframe_row_entry *fre) |
613 | 3 | { |
614 | 8 | while (fre) |
615 | 5 | { |
616 | 5 | struct sframe_row_entry *fre_next = fre->next; |
617 | 5 | XDELETE (fre); |
618 | 5 | fre = fre_next; |
619 | 5 | } |
620 | 3 | } |
621 | | |
622 | | /* Allocate an SFrame FDE. */ |
623 | | |
624 | | static struct sframe_func_entry* |
625 | | sframe_fde_alloc (void) |
626 | 3 | { |
627 | 3 | return XCNEW (struct sframe_func_entry); |
628 | 3 | } |
629 | | |
630 | | /* Free up the SFrame FDE. */ |
631 | | |
632 | | static void |
633 | | sframe_fde_free (struct sframe_func_entry *sframe_fde) |
634 | 3 | { |
635 | 3 | if (sframe_fde == NULL) |
636 | 0 | return; |
637 | | |
638 | 3 | if (sframe_fde->sframe_fres) |
639 | 3 | sframe_row_entry_free (sframe_fde->sframe_fres); |
640 | | |
641 | 3 | XDELETE (sframe_fde); |
642 | 3 | } |
643 | | |
644 | | /* Output the varlen data (SFrame FRE data words) for SFrame FRE object |
645 | | SFRAME_FRE of the SFrame FDE object SFRAME_FDE. Each emitted entry is of |
646 | | size FRE_DATAWORD_SIZE. Write out the data words in order - CFA, RA, FP. */ |
647 | | |
648 | | static unsigned int |
649 | | output_sframe_row_entry_datawords (const struct sframe_func_entry *sframe_fde, |
650 | | const struct sframe_row_entry *sframe_fre, |
651 | | unsigned int fre_dataword_size) |
652 | 5 | { |
653 | 5 | unsigned int fre_write_datawords = 0; |
654 | | |
655 | 5 | unsigned int idx = sframe_fre_dataword_func_map_index (fre_dataword_size); |
656 | 5 | gas_assert (idx < SFRAME_FRE_DATAWORD_FUNC_MAP_INDEX_MAX); |
657 | | |
658 | 5 | if (sframe_fde->fde_flex_p) |
659 | 2 | { |
660 | | /* SFrame FDE of type SFRAME_FDE_TYPE_FLEX. */ |
661 | | /* Output CFA related FRE data words. */ |
662 | 2 | uint32_t reg = sframe_fre->cfa_base_reg; |
663 | 2 | bool deref_p = sframe_fre->cfa_deref_p; |
664 | 2 | uint32_t reg_data |
665 | 2 | = SFRAME_V3_FLEX_FDE_CTRLWORD_ENCODE (reg, deref_p, 1 /* reg_p. */); |
666 | 2 | offsetT offset_data = sframe_fre->cfa_offset; |
667 | 2 | dataword_func_map[idx].out_func (reg_data); |
668 | 2 | dataword_func_map[idx].out_func (offset_data); |
669 | 2 | fre_write_datawords += 2; |
670 | | |
671 | 2 | bool reg_p = false; |
672 | 2 | if (sframe_fre->ra_loc != SFRAME_FRE_ELEM_LOC_NONE) |
673 | 0 | { |
674 | | /* Output RA related FRE data words. */ |
675 | 0 | reg_p = sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_REG; |
676 | 0 | reg = reg_p ? sframe_fre->ra_reg : 0; |
677 | 0 | deref_p = sframe_fre->ra_deref_p; |
678 | 0 | reg_data = SFRAME_V3_FLEX_FDE_CTRLWORD_ENCODE (reg, deref_p, reg_p); |
679 | |
|
680 | 0 | offset_data = sframe_fre->ra_offset; |
681 | 0 | dataword_func_map[idx].out_func (reg_data); |
682 | 0 | dataword_func_map[idx].out_func (offset_data); |
683 | 0 | fre_write_datawords += 2; |
684 | 0 | } |
685 | 2 | else if (sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_NONE) |
686 | 0 | { |
687 | | /* If RA is not in REG/STACK, emit RA padding if there are more |
688 | | data words to follow. Note that, emitting |
689 | | SFRAME_FRE_RA_OFFSET_INVALID is equivalent to emitting |
690 | | SFRAME_V3_FLEX_FDE_CTRLWORD_ENCODE (0, 0, 0). */ |
691 | 0 | dataword_func_map[idx].out_func (SFRAME_FRE_RA_OFFSET_INVALID); |
692 | 0 | fre_write_datawords += 1; |
693 | 0 | } |
694 | | |
695 | 2 | if (sframe_fre->fp_loc != SFRAME_FRE_ELEM_LOC_NONE) |
696 | 0 | { |
697 | | /* Output FP related FRE data words. */ |
698 | 0 | reg_p = sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_REG; |
699 | 0 | reg = reg_p ? sframe_fre->fp_reg : 0; |
700 | 0 | deref_p = sframe_fre->fp_deref_p; |
701 | 0 | reg_data = SFRAME_V3_FLEX_FDE_CTRLWORD_ENCODE (reg, deref_p, reg_p); |
702 | |
|
703 | 0 | offset_data = sframe_fre->fp_offset; |
704 | 0 | dataword_func_map[idx].out_func (reg_data); |
705 | 0 | dataword_func_map[idx].out_func (offset_data); |
706 | 0 | fre_write_datawords += 2; |
707 | 0 | } |
708 | 2 | } |
709 | 3 | else |
710 | 3 | { |
711 | | /* SFrame FDE of type SFRAME_FDE_TYPE_DEFAULT. */ |
712 | | /* Output CFA related FRE data words. */ |
713 | 3 | dataword_func_map[idx].out_func (sframe_fre->cfa_offset); |
714 | 3 | fre_write_datawords++; |
715 | | |
716 | 3 | if (sframe_ra_tracking_p ()) |
717 | 0 | { |
718 | 0 | if (sframe_fre->ra_loc == SFRAME_FRE_ELEM_LOC_STACK) |
719 | 0 | { |
720 | 0 | dataword_func_map[idx].out_func (sframe_fre->ra_offset); |
721 | 0 | fre_write_datawords++; |
722 | 0 | } |
723 | | /* For s390x write padding RA offset, if FP without RA saved. */ |
724 | 0 | else if (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG |
725 | 0 | && sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK) |
726 | 0 | { |
727 | 0 | dataword_func_map[idx].out_func (SFRAME_FRE_RA_OFFSET_INVALID); |
728 | 0 | fre_write_datawords++; |
729 | 0 | } |
730 | 0 | } |
731 | 3 | if (sframe_fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK) |
732 | 0 | { |
733 | 0 | dataword_func_map[idx].out_func (sframe_fre->fp_offset); |
734 | 0 | fre_write_datawords++; |
735 | 0 | } |
736 | 3 | } |
737 | | |
738 | 5 | return fre_write_datawords; |
739 | 5 | } |
740 | | |
741 | | static void |
742 | | output_sframe_row_entry (const struct sframe_func_entry *sframe_fde, |
743 | | const struct sframe_row_entry *sframe_fre) |
744 | 5 | { |
745 | 5 | unsigned char fre_info; |
746 | 5 | unsigned int fre_dataword_count; |
747 | 5 | unsigned int fre_dataword_size; |
748 | 5 | unsigned int fre_base_reg; |
749 | 5 | bool fre_mangled_ra_p; |
750 | 5 | expressionS exp; |
751 | 5 | unsigned int fre_addr_size; |
752 | | |
753 | 5 | unsigned int fre_write_datawords = 0; |
754 | 5 | symbolS *fde_start_addr = get_dw_fde_start_addrS (sframe_fde->dw_fde); |
755 | 5 | symbolS *fde_end_addr = get_dw_fde_end_addrS (sframe_fde->dw_fde); |
756 | 5 | bool flex_p = sframe_fde->fde_flex_p; |
757 | | |
758 | 5 | fre_addr_size = 4; /* 4 bytes by default. FIXME tie it to fre_type? */ |
759 | | |
760 | | /* SFrame FRE Start Address. */ |
761 | 5 | if (SFRAME_FRE_TYPE_SELECTION_OPT) |
762 | 5 | { |
763 | 5 | create_fre_start_addr_exp (&exp, sframe_fre->pc_begin, fde_start_addr, |
764 | 5 | fde_end_addr); |
765 | 5 | frag_grow (fre_addr_size); |
766 | 5 | frag_var (rs_sframe, fre_addr_size, 0, 0, |
767 | 5 | make_expr_symbol (&exp), 0, (char *) frag_now); |
768 | 5 | } |
769 | 0 | else |
770 | 0 | { |
771 | 0 | gas_assert (fde_end_addr); |
772 | 0 | exp = (expressionS) { |
773 | 0 | .X_op = O_subtract, |
774 | 0 | .X_add_symbol = sframe_fre->pc_begin, /* to. */ |
775 | 0 | .X_op_symbol = fde_start_addr /* from. */ |
776 | 0 | }; |
777 | 0 | emit_expr (&exp, fre_addr_size); |
778 | 0 | } |
779 | | |
780 | | /* Create the fre_info using the CFA base register, number of data words and |
781 | | max size of a data word in this FRE. Represent RA undefined as FRE |
782 | | without any data words and all FRE info word fields zeroed. */ |
783 | 5 | if (sframe_fre->ra_undefined_p) |
784 | 0 | { |
785 | 0 | fre_base_reg = 0; |
786 | 0 | fre_dataword_count = 0; |
787 | 0 | fre_dataword_size = 0; |
788 | 0 | fre_mangled_ra_p = 0; |
789 | 0 | } |
790 | 5 | else |
791 | 5 | { |
792 | 5 | fre_base_reg = get_fre_base_reg_id (sframe_fre); |
793 | 5 | fre_dataword_count = get_fre_dataword_count (sframe_fre, flex_p); |
794 | 5 | fre_dataword_size = sframe_get_fre_dataword_size (sframe_fre, flex_p); |
795 | 5 | fre_mangled_ra_p = sframe_fre->mangled_ra_p; |
796 | 5 | } |
797 | | |
798 | | /* Unused for flex FDE. Set to zero. */ |
799 | 5 | if (flex_p) |
800 | 2 | fre_base_reg = 0; |
801 | | |
802 | 5 | fre_info = sframe_set_fre_info (fre_base_reg, fre_dataword_count, |
803 | 5 | fre_dataword_size, fre_mangled_ra_p); |
804 | 5 | out_one (fre_info); |
805 | | |
806 | | /* Represent RA undefined as FRE without any data words. */ |
807 | 5 | if (sframe_fre->ra_undefined_p) |
808 | 0 | return; |
809 | | |
810 | 5 | fre_write_datawords = output_sframe_row_entry_datawords (sframe_fde, |
811 | 5 | sframe_fre, |
812 | 5 | fre_dataword_size); |
813 | | |
814 | | /* Check if the expected number data words have been written out |
815 | | in this FRE. */ |
816 | 5 | gas_assert (fre_write_datawords == fre_dataword_count); |
817 | 5 | } |
818 | | |
819 | | static void |
820 | | output_sframe_funcdesc_idx (symbolS *start_of_fre_section, |
821 | | symbolS *fre_symbol, |
822 | | const struct sframe_func_entry *sframe_fde) |
823 | 3 | { |
824 | 3 | expressionS exp; |
825 | 3 | symbolS *dw_fde_start_addrS, *dw_fde_end_addrS; |
826 | | |
827 | 3 | dw_fde_start_addrS = get_dw_fde_start_addrS (sframe_fde->dw_fde); |
828 | 3 | dw_fde_end_addrS = get_dw_fde_end_addrS (sframe_fde->dw_fde); |
829 | | |
830 | | /* Start address of the function. gas always emits this value with encoding |
831 | | SFRAME_F_FDE_FUNC_START_PCREL. See PR ld/32666. */ |
832 | 3 | exp.X_op = O_subtract; |
833 | 3 | exp.X_add_symbol = dw_fde_start_addrS; /* to location. */ |
834 | 3 | exp.X_op_symbol = symbol_temp_new_now (); /* from location. */ |
835 | 3 | exp.X_add_number = 0; |
836 | 3 | emit_expr (&exp, sizeof_member (sframe_func_desc_idx, |
837 | 3 | sfdi_func_start_offset)); |
838 | | |
839 | | /* Size of the function in bytes. */ |
840 | 3 | exp.X_op = O_subtract; |
841 | 3 | exp.X_add_symbol = dw_fde_end_addrS; |
842 | 3 | exp.X_op_symbol = dw_fde_start_addrS; |
843 | 3 | exp.X_add_number = 0; |
844 | 3 | emit_expr (&exp, sizeof_member (sframe_func_desc_idx, |
845 | 3 | sfdi_func_size)); |
846 | | |
847 | | /* Offset to the function data (attribtues, FREs) in the FRE subsection. */ |
848 | 3 | exp.X_op = O_subtract; |
849 | 3 | exp.X_add_symbol = fre_symbol; /* Minuend. */ |
850 | 3 | exp.X_op_symbol = start_of_fre_section; /* Subtrahend. */ |
851 | 3 | exp.X_add_number = 0; |
852 | 3 | emit_expr (&exp, sizeof_member (sframe_func_desc_idx, |
853 | 3 | sfdi_func_start_fre_off)); |
854 | 3 | } |
855 | | |
856 | | static void |
857 | | output_sframe_func_desc_attr (const struct sframe_func_entry *sframe_fde) |
858 | 3 | { |
859 | 3 | symbolS *dw_fde_start_addrS, *dw_fde_end_addrS; |
860 | 3 | unsigned int pauth_key; |
861 | 3 | bool signal_p; |
862 | | |
863 | 3 | dw_fde_start_addrS = get_dw_fde_start_addrS (sframe_fde->dw_fde); |
864 | 3 | dw_fde_end_addrS = get_dw_fde_end_addrS (sframe_fde->dw_fde); |
865 | | |
866 | | /* Number of FREs must fit uint16_t. */ |
867 | 3 | gas_assert (sframe_fde->num_fres <= UINT16_MAX); |
868 | 3 | out_two (sframe_fde->num_fres); |
869 | | |
870 | | /* SFrame FDE function info. */ |
871 | 3 | unsigned char func_info; |
872 | 3 | pauth_key = (get_dw_fde_pauth_b_key_p (sframe_fde->dw_fde) |
873 | 3 | ? SFRAME_AARCH64_PAUTH_KEY_B : SFRAME_AARCH64_PAUTH_KEY_A); |
874 | 3 | signal_p = get_dw_fde_signal_p (sframe_fde->dw_fde); |
875 | 3 | func_info = sframe_set_func_info (SFRAME_V3_FDE_PCTYPE_INC, |
876 | 3 | SFRAME_FRE_TYPE_ADDR4, |
877 | 3 | pauth_key, signal_p); |
878 | 3 | if (SFRAME_FRE_TYPE_SELECTION_OPT) |
879 | 3 | { |
880 | 3 | expressionS cexp; |
881 | 3 | create_func_info_exp (&cexp, dw_fde_end_addrS, dw_fde_start_addrS, |
882 | 3 | func_info); |
883 | 3 | frag_grow (1); /* Size of func info is unsigned char. */ |
884 | 3 | frag_var (rs_sframe, 1, 0, 0, make_expr_symbol (&cexp), 0, |
885 | 3 | (char *) frag_now); |
886 | 3 | } |
887 | 0 | else |
888 | 0 | out_one (func_info); |
889 | | |
890 | 3 | uint8_t finfo2 = 0; |
891 | 3 | if (sframe_fde->fde_flex_p) |
892 | 1 | finfo2 = SFRAME_V3_SET_FDE_TYPE (finfo2, SFRAME_FDE_TYPE_FLEX); |
893 | 3 | out_one (finfo2); |
894 | | |
895 | | /* Currently, GAS only emits SFrame FDE with PC Type |
896 | | SFRAME_V3_FDE_PCTYPE_INC. Emit repetitive block size of 0. */ |
897 | 3 | out_one (0); |
898 | 3 | } |
899 | | |
900 | | static void |
901 | | output_sframe_internal (void) |
902 | 3 | { |
903 | 3 | expressionS exp; |
904 | 3 | unsigned int i = 0; |
905 | | |
906 | 3 | symbolS *end_of_frame_hdr; |
907 | 3 | symbolS *end_of_frame_section; |
908 | 3 | symbolS *start_of_func_desc_section; |
909 | 3 | symbolS *start_of_fre_section; |
910 | 3 | struct sframe_func_entry *sframe_fde, *sframe_fde_next; |
911 | 3 | struct sframe_row_entry *sframe_fre; |
912 | 3 | unsigned char abi_arch = 0; |
913 | 3 | int fixed_fp_offset = SFRAME_CFA_FIXED_FP_INVALID; |
914 | 3 | int fixed_ra_offset = SFRAME_CFA_FIXED_RA_INVALID; |
915 | | |
916 | | /* The function descriptor entries as dumped by the assembler are not |
917 | | sorted on PCs. Fix for PR ld/32666 requires setting of an additional |
918 | | flag in SFrame Version 2. */ |
919 | 3 | unsigned char sframe_flags = SFRAME_F_FDE_FUNC_START_PCREL; |
920 | | |
921 | 3 | unsigned int num_fdes = get_num_sframe_fdes (); |
922 | 3 | unsigned int num_fres = get_num_sframe_fres (); |
923 | 3 | symbolS **fde_fre_symbols = XNEWVEC (symbolS *, num_fdes); |
924 | 6 | for (i = 0; i < num_fdes; i++) |
925 | 3 | fde_fre_symbols[i] = symbol_temp_make (); |
926 | | |
927 | 3 | end_of_frame_hdr = symbol_temp_make (); |
928 | 3 | start_of_fre_section = symbol_temp_make (); |
929 | 3 | start_of_func_desc_section = symbol_temp_make (); |
930 | 3 | end_of_frame_section = symbol_temp_make (); |
931 | | |
932 | | /* Output the preamble of SFrame section. */ |
933 | 3 | out_two (SFRAME_MAGIC); |
934 | 3 | out_one (SFRAME_VERSION); |
935 | | /* gas must ensure emitted SFrame sections have at least the required flags |
936 | | set. */ |
937 | 3 | gas_assert ((sframe_flags & SFRAME_V2_GNU_AS_LD_ENCODING_FLAGS) |
938 | 3 | == SFRAME_V2_GNU_AS_LD_ENCODING_FLAGS); |
939 | 3 | out_one (sframe_flags); |
940 | | /* abi/arch. */ |
941 | 3 | #ifdef sframe_get_abi_arch |
942 | 3 | abi_arch = sframe_get_abi_arch (); |
943 | 3 | #endif |
944 | 3 | gas_assert (abi_arch); |
945 | 3 | out_one (abi_arch); |
946 | | |
947 | | /* Offset for the FP register from CFA. Neither of the AMD64 or AAPCS64 |
948 | | ABIs have a fixed offset for the FP register from the CFA. This may be |
949 | | useful in future (but not without additional support in the toolchain) |
950 | | for specialized handling/encoding for cases where, for example, |
951 | | -fno-omit-frame-pointer is used. */ |
952 | 3 | out_one (fixed_fp_offset); |
953 | | |
954 | | /* All ABIs participating in SFrame generation must define |
955 | | sframe_ra_tracking_p. |
956 | | When RA tracking (in FREs) is not needed (e.g., AMD64), SFrame assumes |
957 | | the RA is going to be at a fixed offset from CFA. Check that the fixed RA |
958 | | offset is appropriately defined in all cases. */ |
959 | 3 | if (!sframe_ra_tracking_p ()) |
960 | 3 | { |
961 | 3 | fixed_ra_offset = sframe_cfa_ra_offset (); |
962 | 3 | gas_assert (fixed_ra_offset != SFRAME_CFA_FIXED_RA_INVALID); |
963 | 3 | } |
964 | 3 | out_one (fixed_ra_offset); |
965 | | |
966 | | /* None of the AMD64, AARCH64, or s390x ABIs need the auxiliary header. |
967 | | When the need does arise to use this field, the appropriate backend |
968 | | must provide this information. */ |
969 | 3 | out_one (0); /* Auxiliary SFrame header length. */ |
970 | | |
971 | 3 | out_four (num_fdes); /* Number of FDEs. */ |
972 | 3 | out_four (num_fres); /* Number of FREs. */ |
973 | | |
974 | | /* Size of FRE sub-section. */ |
975 | 3 | exp.X_op = O_subtract; |
976 | 3 | exp.X_add_symbol = end_of_frame_section; |
977 | 3 | exp.X_op_symbol = start_of_fre_section; |
978 | 3 | exp.X_add_number = 0; |
979 | 3 | emit_expr (&exp, sizeof_member (sframe_header, sfh_fre_len)); |
980 | | |
981 | | /* Offset of FDE sub-section. */ |
982 | 3 | exp.X_op = O_subtract; |
983 | 3 | exp.X_add_symbol = end_of_frame_hdr; |
984 | 3 | exp.X_op_symbol = start_of_func_desc_section; |
985 | 3 | exp.X_add_number = 0; |
986 | 3 | emit_expr (&exp, sizeof_member (sframe_header, sfh_fdeoff)); |
987 | | |
988 | | /* Offset of FRE sub-section. */ |
989 | 3 | exp.X_op = O_subtract; |
990 | 3 | exp.X_add_symbol = start_of_fre_section; |
991 | 3 | exp.X_op_symbol = end_of_frame_hdr; |
992 | 3 | exp.X_add_number = 0; |
993 | 3 | emit_expr (&exp, sizeof_member (sframe_header, sfh_freoff)); |
994 | | |
995 | 3 | symbol_set_value_now (end_of_frame_hdr); |
996 | 3 | symbol_set_value_now (start_of_func_desc_section); |
997 | | |
998 | | /* Output the SFrame function descriptor entries. */ |
999 | 3 | i = 0; |
1000 | 6 | for (sframe_fde = all_sframe_fdes; sframe_fde; sframe_fde = sframe_fde->next) |
1001 | 3 | { |
1002 | 3 | output_sframe_funcdesc_idx (start_of_fre_section, fde_fre_symbols[i], |
1003 | 3 | sframe_fde); |
1004 | 3 | i++; |
1005 | 3 | } |
1006 | | |
1007 | 3 | symbol_set_value_now (start_of_fre_section); |
1008 | | |
1009 | | /* Output the SFrame FREs. */ |
1010 | 3 | i = 0; |
1011 | 3 | sframe_fde = all_sframe_fdes; |
1012 | | |
1013 | 6 | for (sframe_fde = all_sframe_fdes; sframe_fde; sframe_fde = sframe_fde_next) |
1014 | 3 | { |
1015 | 3 | symbol_set_value_now (fde_fre_symbols[i]); |
1016 | | |
1017 | 3 | output_sframe_func_desc_attr (sframe_fde); |
1018 | | |
1019 | 3 | for (sframe_fre = sframe_fde->sframe_fres; |
1020 | 8 | sframe_fre; |
1021 | 5 | sframe_fre = sframe_fre->next) |
1022 | 5 | { |
1023 | 5 | output_sframe_row_entry (sframe_fde, sframe_fre); |
1024 | 5 | } |
1025 | 3 | i++; |
1026 | 3 | sframe_fde_next = sframe_fde->next; |
1027 | 3 | sframe_fde_free (sframe_fde); |
1028 | 3 | } |
1029 | 3 | all_sframe_fdes = NULL; |
1030 | 3 | last_sframe_fde = &all_sframe_fdes; |
1031 | | |
1032 | 3 | symbol_set_value_now (end_of_frame_section); |
1033 | | |
1034 | 3 | gas_assert (i == num_fdes); |
1035 | | |
1036 | 3 | free (fde_fre_symbols); |
1037 | 3 | fde_fre_symbols = NULL; |
1038 | 3 | } |
1039 | | |
1040 | | static unsigned int |
1041 | | get_num_sframe_fdes (void) |
1042 | 3 | { |
1043 | 3 | struct sframe_func_entry *sframe_fde; |
1044 | 3 | unsigned int total_fdes = 0; |
1045 | | |
1046 | 6 | for (sframe_fde = all_sframe_fdes; sframe_fde ; sframe_fde = sframe_fde->next) |
1047 | 3 | total_fdes++; |
1048 | | |
1049 | 3 | return total_fdes; |
1050 | 3 | } |
1051 | | |
1052 | | /* Get the total number of SFrame row entries across the FDEs. */ |
1053 | | |
1054 | | static unsigned int |
1055 | | get_num_sframe_fres (void) |
1056 | 3 | { |
1057 | 3 | struct sframe_func_entry *sframe_fde; |
1058 | 3 | unsigned int total_fres = 0; |
1059 | | |
1060 | 6 | for (sframe_fde = all_sframe_fdes; sframe_fde ; sframe_fde = sframe_fde->next) |
1061 | 3 | total_fres += sframe_fde->num_fres; |
1062 | | |
1063 | 3 | return total_fres; |
1064 | 3 | } |
1065 | | |
1066 | | /* SFrame translation context functions. */ |
1067 | | |
1068 | | /* Allocate a new SFrame translation context. */ |
1069 | | |
1070 | | static struct sframe_xlate_ctx* |
1071 | | sframe_xlate_ctx_alloc (void) |
1072 | 3 | { |
1073 | 3 | struct sframe_xlate_ctx* xlate_ctx = XCNEW (struct sframe_xlate_ctx); |
1074 | 3 | return xlate_ctx; |
1075 | 3 | } |
1076 | | |
1077 | | /* Initialize the given SFrame translation context. */ |
1078 | | |
1079 | | static void |
1080 | | sframe_xlate_ctx_init (struct sframe_xlate_ctx *xlate_ctx) |
1081 | 3 | { |
1082 | 3 | xlate_ctx->dw_fde = NULL; |
1083 | 3 | xlate_ctx->flex_p = false; |
1084 | 3 | xlate_ctx->first_fre = NULL; |
1085 | 3 | xlate_ctx->last_fre = NULL; |
1086 | 3 | xlate_ctx->cur_fre = NULL; |
1087 | 3 | xlate_ctx->remember_fre = NULL; |
1088 | 3 | xlate_ctx->num_xlate_fres = 0; |
1089 | 3 | } |
1090 | | |
1091 | | /* Cleanup the given SFrame translation context. */ |
1092 | | |
1093 | | static void |
1094 | | sframe_xlate_ctx_cleanup (struct sframe_xlate_ctx *xlate_ctx) |
1095 | 0 | { |
1096 | 0 | sframe_row_entry_free (xlate_ctx->first_fre); |
1097 | 0 | XDELETE (xlate_ctx->remember_fre); |
1098 | 0 | xlate_ctx->remember_fre = NULL; |
1099 | 0 | XDELETE (xlate_ctx->cur_fre); |
1100 | 0 | xlate_ctx->cur_fre = NULL; |
1101 | 0 | } |
1102 | | |
1103 | | /* Transfer the state from the SFrame translation context to the SFrame FDE. */ |
1104 | | |
1105 | | static void |
1106 | | sframe_xlate_ctx_finalize (struct sframe_xlate_ctx *xlate_ctx, |
1107 | | struct sframe_func_entry *sframe_fde) |
1108 | 3 | { |
1109 | 3 | sframe_fde->dw_fde = xlate_ctx->dw_fde; |
1110 | 3 | sframe_fde->fde_flex_p = xlate_ctx->flex_p; |
1111 | 3 | sframe_fde->sframe_fres = xlate_ctx->first_fre; |
1112 | 3 | sframe_fde->num_fres = xlate_ctx->num_xlate_fres; |
1113 | | /* remember_fre is cloned copy of the applicable fre (where necessary). |
1114 | | Since this is not included in the list of sframe_fres, free it. */ |
1115 | 3 | XDELETE (xlate_ctx->remember_fre); |
1116 | 3 | xlate_ctx->remember_fre = NULL; |
1117 | 3 | } |
1118 | | |
1119 | | /* Get the current CFA base register from the scratchpad FRE (cur_fre). |
1120 | | NB: this may return a value of SFRAME_FRE_REG_INVALID. */ |
1121 | | |
1122 | | static unsigned int |
1123 | | sframe_xlate_ctx_get_cur_cfa_reg (const struct sframe_xlate_ctx *xlate_ctx) |
1124 | 0 | { |
1125 | 0 | gas_assert (xlate_ctx && xlate_ctx->cur_fre); |
1126 | | |
1127 | 0 | return xlate_ctx->cur_fre->cfa_base_reg; |
1128 | 0 | } |
1129 | | |
1130 | | /* Add the given FRE in the list of frame row entries in the given FDE |
1131 | | translation context. */ |
1132 | | |
1133 | | static void |
1134 | | sframe_xlate_ctx_add_fre (struct sframe_xlate_ctx *xlate_ctx, |
1135 | | struct sframe_row_entry *fre) |
1136 | 5 | { |
1137 | 5 | gas_assert (xlate_ctx && fre); |
1138 | | |
1139 | | /* Add the frame row entry. */ |
1140 | 5 | if (!xlate_ctx->first_fre) |
1141 | 3 | xlate_ctx->first_fre = fre; |
1142 | 2 | else if (xlate_ctx->last_fre) |
1143 | 2 | xlate_ctx->last_fre->next = fre; |
1144 | | |
1145 | 5 | xlate_ctx->last_fre = fre; |
1146 | | |
1147 | | /* Keep track of the total number of SFrame frame row entries. */ |
1148 | 5 | xlate_ctx->num_xlate_fres++; |
1149 | 5 | } |
1150 | | |
1151 | | /* A SFrame Frame Row Entry is self-sufficient in terms of stack tracing info |
1152 | | for a given PC. It contains information assimilated from multiple CFI |
1153 | | instructions, and hence, a new SFrame FRE is initialized with the data from |
1154 | | the previous known FRE, if any. |
1155 | | |
1156 | | Understandably, not all information (especially the instruction begin |
1157 | | and end boundaries) needs to be relayed. Hence, the caller of this API |
1158 | | must set the pc_begin and pc_end as applicable. */ |
1159 | | |
1160 | | static void |
1161 | | sframe_row_entry_initialize (struct sframe_row_entry *cur_fre, |
1162 | | const struct sframe_row_entry *prev_fre) |
1163 | 2 | { |
1164 | 2 | gas_assert (prev_fre); |
1165 | 2 | cur_fre->cfa_base_reg = prev_fre->cfa_base_reg; |
1166 | 2 | cur_fre->cfa_offset = prev_fre->cfa_offset; |
1167 | 2 | cur_fre->cfa_deref_p = prev_fre->cfa_deref_p; |
1168 | 2 | cur_fre->fp_loc = prev_fre->fp_loc; |
1169 | 2 | cur_fre->fp_reg = prev_fre->fp_reg; |
1170 | 2 | cur_fre->fp_offset = prev_fre->fp_offset; |
1171 | 2 | cur_fre->fp_deref_p = prev_fre->fp_deref_p; |
1172 | 2 | cur_fre->ra_loc = prev_fre->ra_loc; |
1173 | 2 | cur_fre->ra_reg = prev_fre->ra_reg; |
1174 | 2 | cur_fre->ra_offset = prev_fre->ra_offset; |
1175 | 2 | cur_fre->ra_deref_p = prev_fre->ra_deref_p; |
1176 | | /* Treat RA mangling as a sticky bit. It retains its value until another |
1177 | | .cfi_negate_ra_state is seen. */ |
1178 | 2 | cur_fre->mangled_ra_p = prev_fre->mangled_ra_p; |
1179 | | /* Treat RA undefined as a sticky bit. It retains its value until a |
1180 | | .cfi_offset RA, .cfi_register RA, .cfi_restore RA, or .cfi_same_value RA |
1181 | | is seen. */ |
1182 | 2 | cur_fre->ra_undefined_p = prev_fre->ra_undefined_p; |
1183 | 2 | } |
1184 | | |
1185 | | /* Return SFrame register name for SP, FP, and RA, or NULL if other. */ |
1186 | | |
1187 | | static const char * |
1188 | | sframe_register_name (unsigned int reg) |
1189 | 0 | { |
1190 | 0 | if (reg == SFRAME_CFA_SP_REG) |
1191 | 0 | return "SP"; |
1192 | 0 | else if (reg == SFRAME_CFA_FP_REG) |
1193 | 0 | return "FP"; |
1194 | 0 | else if (reg == SFRAME_CFA_RA_REG) |
1195 | 0 | return "RA"; |
1196 | 0 | else |
1197 | 0 | return NULL; |
1198 | 0 | } |
1199 | | |
1200 | | /* Translate DW_CFA_advance_loc into SFrame context. |
1201 | | Return SFRAME_XLATE_OK if success. */ |
1202 | | |
1203 | | static int |
1204 | | sframe_xlate_do_advance_loc (struct sframe_xlate_ctx *xlate_ctx, |
1205 | | const struct cfi_insn_data *cfi_insn) |
1206 | 4 | { |
1207 | 4 | struct sframe_row_entry *last_fre = xlate_ctx->last_fre; |
1208 | | /* Get the scratchpad FRE currently being updated as the cfi_insn's |
1209 | | get interpreted. This FRE eventually gets linked in into the |
1210 | | list of FREs for the specific function. */ |
1211 | 4 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1212 | | |
1213 | 4 | if (cur_fre) |
1214 | 4 | { |
1215 | 4 | if (!cur_fre->merge_candidate) |
1216 | 2 | { |
1217 | 2 | sframe_fre_set_end_addr (cur_fre, cfi_insn->u.ll.lab2); |
1218 | | |
1219 | 2 | sframe_xlate_ctx_add_fre (xlate_ctx, cur_fre); |
1220 | 2 | last_fre = xlate_ctx->last_fre; |
1221 | | |
1222 | 2 | xlate_ctx->cur_fre = sframe_row_entry_new (); |
1223 | 2 | cur_fre = xlate_ctx->cur_fre; |
1224 | | |
1225 | 2 | if (last_fre) |
1226 | 2 | sframe_row_entry_initialize (cur_fre, last_fre); |
1227 | 2 | } |
1228 | 2 | else |
1229 | 2 | { |
1230 | 2 | sframe_fre_set_end_addr (last_fre, cfi_insn->u.ll.lab2); |
1231 | 2 | gas_assert (last_fre->merge_candidate == false); |
1232 | 2 | } |
1233 | 4 | } |
1234 | 0 | else |
1235 | 0 | { |
1236 | 0 | xlate_ctx->cur_fre = sframe_row_entry_new (); |
1237 | 0 | cur_fre = xlate_ctx->cur_fre; |
1238 | 0 | } |
1239 | | |
1240 | 4 | gas_assert (cur_fre); |
1241 | 4 | sframe_fre_set_begin_addr (cur_fre, cfi_insn->u.ll.lab2); |
1242 | | |
1243 | 4 | return SFRAME_XLATE_OK; |
1244 | 4 | } |
1245 | | |
1246 | | /* Translate DW_CFA_def_cfa into SFrame context. |
1247 | | Return SFRAME_XLATE_OK if success. */ |
1248 | | |
1249 | | static int |
1250 | | sframe_xlate_do_def_cfa (struct sframe_xlate_ctx *xlate_ctx, |
1251 | | const struct cfi_insn_data *cfi_insn) |
1252 | | |
1253 | 4 | { |
1254 | | /* Get the scratchpad FRE. This FRE will eventually get linked in. */ |
1255 | 4 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1256 | 4 | if (!cur_fre) |
1257 | 3 | { |
1258 | 3 | xlate_ctx->cur_fre = sframe_row_entry_new (); |
1259 | 3 | cur_fre = xlate_ctx->cur_fre; |
1260 | 3 | sframe_fre_set_begin_addr (cur_fre, |
1261 | 3 | get_dw_fde_start_addrS (xlate_ctx->dw_fde)); |
1262 | 3 | } |
1263 | | |
1264 | 4 | offsetT offset = cfi_insn->u.ri.offset; |
1265 | 4 | bool bound_p = sframe_fre_stack_offset_bound_p (offset, true); |
1266 | 4 | if (!bound_p) |
1267 | 0 | { |
1268 | 0 | as_warn (_("no SFrame FDE emitted; " |
1269 | 0 | ".cfi_def_cfa with unsupported offset value")); |
1270 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
1271 | 0 | } |
1272 | | |
1273 | | /* Define the current CFA rule to use the provided register and |
1274 | | offset. Typically, the CFA rule uses SP/FP based CFA. However, with |
1275 | | SFrame V3 specification, if the CFA register is not FP/SP, SFrame FDE type |
1276 | | SFRAME_FDE_TYPE_FLEX type may be used. |
1277 | | |
1278 | | GAS uses the hook sframe_support_flex_fde_p () to determine if SFrame FDE |
1279 | | of type SFRAME_FDE_TYPE_FLEX can be emitted for the specific target. |
1280 | | Non-SP/FP based CFA may be seen for: |
1281 | | - AMD64 (e.g., DRAP, stack alignment), or |
1282 | | - s390x, where this may be seen for (GCC) generated code for static stack |
1283 | | clash protection. */ |
1284 | 4 | if (cfi_insn->u.ri.reg != SFRAME_CFA_SP_REG |
1285 | 1 | && cfi_insn->u.ri.reg != SFRAME_CFA_FP_REG) |
1286 | 1 | { |
1287 | 1 | if (!sframe_support_flex_fde_p ()) |
1288 | 0 | { |
1289 | 0 | as_warn (_("no SFrame FDE emitted; " |
1290 | 0 | "non-SP/FP register %u in .cfi_def_cfa"), |
1291 | 0 | cfi_insn->u.ri.reg); |
1292 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
1293 | 0 | } |
1294 | 1 | else |
1295 | 1 | xlate_ctx->flex_p = true; |
1296 | 1 | } |
1297 | | |
1298 | 4 | sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.ri.reg); |
1299 | 4 | sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.ri.offset); |
1300 | 4 | cur_fre->merge_candidate = false; |
1301 | 4 | cur_fre->cfa_deref_p = false; |
1302 | | |
1303 | 4 | return SFRAME_XLATE_OK; |
1304 | 4 | } |
1305 | | |
1306 | | /* Translate DW_CFA_def_cfa_register into SFrame context. |
1307 | | Return SFRAME_XLATE_OK if success. */ |
1308 | | |
1309 | | static int |
1310 | | sframe_xlate_do_def_cfa_register (struct sframe_xlate_ctx *xlate_ctx, |
1311 | | const struct cfi_insn_data *cfi_insn) |
1312 | 0 | { |
1313 | 0 | const struct sframe_row_entry *last_fre = xlate_ctx->last_fre; |
1314 | | /* Get the scratchpad FRE. This FRE will eventually get linked in. */ |
1315 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1316 | |
|
1317 | 0 | gas_assert (cur_fre); |
1318 | | /* Define the current CFA rule to use the provided register (but to |
1319 | | keep the old offset). However, if the register is not FP/SP, |
1320 | | skip creating SFrame stack trace info for the function. */ |
1321 | 0 | if (cfi_insn->u.r != SFRAME_CFA_SP_REG |
1322 | 0 | && cfi_insn->u.r != SFRAME_CFA_FP_REG) |
1323 | 0 | { |
1324 | 0 | if (!sframe_support_flex_fde_p ()) |
1325 | 0 | { |
1326 | 0 | as_warn (_("no SFrame FDE emitted; " |
1327 | 0 | "non-SP/FP register %u in .cfi_def_cfa_register"), |
1328 | 0 | cfi_insn->u.ri.reg); |
1329 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
1330 | 0 | } |
1331 | 0 | else |
1332 | | /* Currently, SFRAME_FDE_TYPE_FLEX is generated for AMD64 only. */ |
1333 | 0 | xlate_ctx->flex_p = true; |
1334 | 0 | } |
1335 | | |
1336 | 0 | sframe_fre_set_cfa_base_reg (cur_fre, cfi_insn->u.r); |
1337 | 0 | if (last_fre) |
1338 | 0 | sframe_fre_set_cfa_offset (cur_fre, sframe_fre_get_cfa_offset (last_fre)); |
1339 | 0 | cur_fre->cfa_deref_p = false; |
1340 | |
|
1341 | 0 | cur_fre->merge_candidate = false; |
1342 | |
|
1343 | 0 | return SFRAME_XLATE_OK; |
1344 | 0 | } |
1345 | | |
1346 | | /* Translate DW_CFA_def_cfa_offset into SFrame context. |
1347 | | Return SFRAME_XLATE_OK if success. */ |
1348 | | |
1349 | | static int |
1350 | | sframe_xlate_do_def_cfa_offset (struct sframe_xlate_ctx *xlate_ctx, |
1351 | | const struct cfi_insn_data *cfi_insn) |
1352 | 0 | { |
1353 | | /* The scratchpad FRE currently being updated with each cfi_insn |
1354 | | being interpreted. This FRE eventually gets linked in into the |
1355 | | list of FREs for the specific function. */ |
1356 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1357 | 0 | unsigned int cur_cfa_reg = sframe_xlate_ctx_get_cur_cfa_reg (xlate_ctx); |
1358 | |
|
1359 | 0 | gas_assert (cur_fre); |
1360 | | /* Define the current CFA rule to use the provided offset (but to keep |
1361 | | the old register). However, if the old register is not FP/SP, |
1362 | | skip creating SFrame stack trace info for the function. */ |
1363 | 0 | if (cur_cfa_reg == SFRAME_CFA_FP_REG || cur_cfa_reg == SFRAME_CFA_SP_REG) |
1364 | 0 | { |
1365 | 0 | if (sframe_fre_stack_offset_bound_p (cfi_insn->u.i, true)) |
1366 | 0 | { |
1367 | 0 | sframe_fre_set_cfa_offset (cur_fre, cfi_insn->u.i); |
1368 | 0 | cur_fre->merge_candidate = false; |
1369 | 0 | } |
1370 | 0 | else |
1371 | 0 | { |
1372 | 0 | as_warn (_("no SFrame FDE emitted; " |
1373 | 0 | ".cfi_def_cfa_offset with unsupported offset value")); |
1374 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
1375 | 0 | } |
1376 | 0 | } |
1377 | 0 | else |
1378 | 0 | { |
1379 | | /* No CFA base register in effect. Non-SP/FP CFA base register should |
1380 | | not occur, as sframe_xlate_do_def_cfa[_register] would detect this. */ |
1381 | 0 | as_warn (_("no SFrame FDE emitted; " |
1382 | 0 | ".cfi_def_cfa_offset without CFA base register in effect")); |
1383 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
1384 | 0 | } |
1385 | | |
1386 | 0 | return SFRAME_XLATE_OK; |
1387 | 0 | } |
1388 | | |
1389 | | /* Translate DW_CFA_offset into SFrame context. |
1390 | | Return SFRAME_XLATE_OK if success. */ |
1391 | | |
1392 | | static int |
1393 | | sframe_xlate_do_offset (struct sframe_xlate_ctx *xlate_ctx, |
1394 | | const struct cfi_insn_data *cfi_insn) |
1395 | 6 | { |
1396 | | /* The scratchpad FRE currently being updated with each cfi_insn |
1397 | | being interpreted. This FRE eventually gets linked in into the |
1398 | | list of FREs for the specific function. */ |
1399 | 6 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1400 | 6 | gas_assert (cur_fre); |
1401 | | |
1402 | | /* For ABIs not tracking RA, the return address is expected to be in a |
1403 | | specific location. Explicit manourvering to a different offset (than the |
1404 | | default offset) is non-representable in SFrame, unless flex FDE generation |
1405 | | is supported for the ABI. */ |
1406 | 6 | if (!sframe_support_flex_fde_p () && !sframe_ra_tracking_p () |
1407 | 0 | && cfi_insn->u.ri.reg == SFRAME_CFA_RA_REG |
1408 | 0 | && cfi_insn->u.ri.offset != sframe_cfa_ra_offset ()) |
1409 | 0 | { |
1410 | 0 | as_warn (_("no SFrame FDE emitted; %s register %u in .cfi_offset"), |
1411 | 0 | sframe_register_name (cfi_insn->u.ri.reg), cfi_insn->u.ri.reg); |
1412 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1413 | 0 | } |
1414 | | |
1415 | | /* Change the rule for the register indicated by the register number to |
1416 | | be the specified offset. */ |
1417 | | /* Ignore SP reg, as it can be recovered from the CFA tracking info. */ |
1418 | 6 | if (cfi_insn->u.ri.reg == SFRAME_CFA_FP_REG) |
1419 | 0 | { |
1420 | 0 | sframe_fre_set_fp_track (cur_fre, cfi_insn->u.ri.offset); |
1421 | 0 | cur_fre->fp_reg = SFRAME_FRE_REG_INVALID; |
1422 | 0 | cur_fre->fp_deref_p = true; |
1423 | 0 | cur_fre->merge_candidate = false; |
1424 | 0 | } |
1425 | | /* Either the ABI has enabled RA tracking, in which case we must process the |
1426 | | DW_CFA_offset opcode for REG_RA like usual. Or if the ABI has not enabled |
1427 | | RA tracking, but flex FDE generation is supported, distinguish between |
1428 | | whether its time to reset the RA tracking state or not. */ |
1429 | 6 | else if (cfi_insn->u.ri.reg == SFRAME_CFA_RA_REG) |
1430 | 3 | { |
1431 | 3 | if (!sframe_ra_tracking_p () |
1432 | 3 | && cfi_insn->u.ri.offset == sframe_cfa_ra_offset ()) |
1433 | 3 | { |
1434 | | /* Reset RA tracking info, if fixed offset. */ |
1435 | 3 | cur_fre->ra_reg = SFRAME_FRE_REG_INVALID; |
1436 | 3 | cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_NONE; |
1437 | 3 | cur_fre->ra_deref_p = false; |
1438 | 3 | cur_fre->merge_candidate = false; |
1439 | 3 | } |
1440 | 0 | else |
1441 | 0 | { |
1442 | 0 | sframe_fre_set_ra_track (cur_fre, cfi_insn->u.ri.offset); |
1443 | 0 | cur_fre->ra_reg = SFRAME_FRE_REG_INVALID; |
1444 | 0 | cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_STACK; |
1445 | 0 | cur_fre->ra_deref_p = true; |
1446 | 0 | cur_fre->merge_candidate = false; |
1447 | |
|
1448 | 0 | if (!sframe_ra_tracking_p () && sframe_support_flex_fde_p ()) |
1449 | 0 | xlate_ctx->flex_p = true; |
1450 | 0 | } |
1451 | 3 | } |
1452 | | |
1453 | | /* Skip all other registers. */ |
1454 | 6 | return SFRAME_XLATE_OK; |
1455 | 6 | } |
1456 | | |
1457 | | /* Translate DW_CFA_val_offset into SFrame context. |
1458 | | Return SFRAME_XLATE_OK if success. |
1459 | | |
1460 | | When CFI_ESC_P is true, the CFI_INSN is hand-crafted using CFI_escape |
1461 | | data. See sframe_xlate_do_escape_val_offset. */ |
1462 | | |
1463 | | static int |
1464 | | sframe_xlate_do_val_offset (const struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED, |
1465 | | const struct cfi_insn_data *cfi_insn, |
1466 | | bool cfi_esc_p) |
1467 | 0 | { |
1468 | | /* Previous value of register is CFA + offset. However, if the specified |
1469 | | register is not interesting (SP, FP, or RA reg), the current |
1470 | | DW_CFA_val_offset instruction can be safely skipped without sacrificing |
1471 | | the asynchronicity of stack trace information. */ |
1472 | 0 | if (cfi_insn->u.ri.reg == SFRAME_CFA_FP_REG |
1473 | 0 | || (sframe_ra_tracking_p () && cfi_insn->u.ri.reg == SFRAME_CFA_RA_REG) |
1474 | | /* Ignore SP reg, if offset matches assumed default rule. */ |
1475 | 0 | || (cfi_insn->u.ri.reg == SFRAME_CFA_SP_REG |
1476 | 0 | && ((sframe_get_abi_arch () != SFRAME_ABI_S390X_ENDIAN_BIG |
1477 | 0 | && cfi_insn->u.ri.offset != 0) |
1478 | 0 | || (sframe_get_abi_arch () == SFRAME_ABI_S390X_ENDIAN_BIG |
1479 | 0 | && cfi_insn->u.ri.offset != SFRAME_S390X_SP_VAL_OFFSET)))) |
1480 | 0 | { |
1481 | 0 | as_warn (_("no SFrame FDE emitted; %s with %s reg %u"), |
1482 | 0 | cfi_esc_p ? ".cfi_escape DW_CFA_val_offset" : ".cfi_val_offset", |
1483 | 0 | sframe_register_name (cfi_insn->u.ri.reg), cfi_insn->u.ri.reg); |
1484 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1485 | 0 | } |
1486 | | |
1487 | | /* Safe to skip. */ |
1488 | 0 | return SFRAME_XLATE_OK; |
1489 | 0 | } |
1490 | | |
1491 | | /* Translate DW_CFA_register into SFrame context. |
1492 | | |
1493 | | This opcode indicates: Previous value of register1 is register2. This is |
1494 | | not representable using FDE type SFRAME_FDE_TYPE_DEFAULT. Hence, if |
1495 | | flexible FDE is not enabled for the ABI/arch, detect the use of registers |
1496 | | interesting to SFrame (FP, RA for this opcode), and skip FDE generation |
1497 | | while warning the user. Same applies for SP, except that it needs special |
1498 | | handling for s390. |
1499 | | |
1500 | | Return SFRAME_XLATE_OK if success. */ |
1501 | | |
1502 | | static int |
1503 | | sframe_xlate_do_register (struct sframe_xlate_ctx *xlate_ctx, |
1504 | | const struct cfi_insn_data *cfi_insn) |
1505 | 0 | { |
1506 | 0 | if (sframe_support_flex_fde_p ()) |
1507 | 0 | { |
1508 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1509 | |
|
1510 | 0 | if (cfi_insn->u.rr.reg1 == SFRAME_CFA_FP_REG) |
1511 | 0 | { |
1512 | 0 | sframe_fre_set_fp_track (cur_fre, 0); |
1513 | 0 | cur_fre->fp_loc = SFRAME_FRE_ELEM_LOC_REG; |
1514 | 0 | cur_fre->fp_reg = cfi_insn->u.rr.reg2; |
1515 | 0 | cur_fre->fp_deref_p = false; |
1516 | 0 | cur_fre->merge_candidate = false; |
1517 | 0 | xlate_ctx->flex_p = true; |
1518 | 0 | return SFRAME_XLATE_OK; |
1519 | 0 | } |
1520 | 0 | else if (cfi_insn->u.rr.reg1 == SFRAME_CFA_RA_REG) |
1521 | 0 | { |
1522 | 0 | sframe_fre_set_ra_track (cur_fre, 0); |
1523 | 0 | cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_REG; |
1524 | 0 | cur_fre->ra_reg = cfi_insn->u.rr.reg2; |
1525 | 0 | cur_fre->ra_deref_p = false; |
1526 | 0 | cur_fre->merge_candidate = false; |
1527 | 0 | xlate_ctx->flex_p = true; |
1528 | 0 | return SFRAME_XLATE_OK; |
1529 | 0 | } |
1530 | | /* Recovering REG_SP from an alternate register is not represented in |
1531 | | SFrame. Fallthrough if SFRAME_CFA_SP_REG and error out. */ |
1532 | 0 | } |
1533 | | |
1534 | 0 | if (cfi_insn->u.rr.reg1 == SFRAME_CFA_RA_REG |
1535 | | /* SFrame does not track SP explicitly. */ |
1536 | 0 | || (cfi_insn->u.rr.reg1 == SFRAME_CFA_SP_REG |
1537 | 0 | && sframe_get_abi_arch () != SFRAME_ABI_S390X_ENDIAN_BIG) |
1538 | 0 | || cfi_insn->u.rr.reg1 == SFRAME_CFA_FP_REG) |
1539 | 0 | { |
1540 | 0 | as_warn (_("no SFrame FDE emitted; %s register %u in .cfi_register"), |
1541 | 0 | sframe_register_name (cfi_insn->u.rr.reg1), cfi_insn->u.rr.reg1); |
1542 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1543 | 0 | } |
1544 | | |
1545 | | /* Safe to skip. */ |
1546 | 0 | return SFRAME_XLATE_OK; |
1547 | 0 | } |
1548 | | |
1549 | | /* Translate DW_CFA_remember_state into SFrame context. |
1550 | | Return SFRAME_XLATE_OK if success. */ |
1551 | | |
1552 | | static int |
1553 | | sframe_xlate_do_remember_state (struct sframe_xlate_ctx *xlate_ctx) |
1554 | 0 | { |
1555 | 0 | const struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1556 | | |
1557 | | /* If there is no FRE state to remember, nothing to do here. Return |
1558 | | early with non-zero error code, this will cause no SFrame stack trace |
1559 | | info for the function involved. */ |
1560 | 0 | if (!cur_fre) |
1561 | 0 | { |
1562 | 0 | as_warn (_("no SFrame FDE emitted; " |
1563 | 0 | ".cfi_remember_state without prior SFrame FRE state")); |
1564 | 0 | return SFRAME_XLATE_ERR_INVAL; |
1565 | 0 | } |
1566 | | |
1567 | 0 | if (!xlate_ctx->remember_fre) |
1568 | 0 | xlate_ctx->remember_fre = sframe_row_entry_new (); |
1569 | 0 | sframe_row_entry_initialize (xlate_ctx->remember_fre, cur_fre); |
1570 | |
|
1571 | 0 | return SFRAME_XLATE_OK; |
1572 | 0 | } |
1573 | | |
1574 | | /* Translate DW_CFA_restore_state into SFrame context. |
1575 | | Return SFRAME_XLATE_OK if success. */ |
1576 | | |
1577 | | static int |
1578 | | sframe_xlate_do_restore_state (struct sframe_xlate_ctx *xlate_ctx) |
1579 | 0 | { |
1580 | | /* The scratchpad FRE currently being updated with each cfi_insn |
1581 | | being interpreted. This FRE eventually gets linked in into the |
1582 | | list of FREs for the specific function. */ |
1583 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1584 | |
|
1585 | 0 | gas_assert (xlate_ctx->remember_fre); |
1586 | 0 | gas_assert (cur_fre && cur_fre->merge_candidate); |
1587 | | |
1588 | | /* Get the CFA state from the DW_CFA_remember_state insn. */ |
1589 | 0 | sframe_row_entry_initialize (cur_fre, xlate_ctx->remember_fre); |
1590 | | /* The PC boundaries of the current SFrame FRE are updated |
1591 | | via other machinery. */ |
1592 | 0 | cur_fre->merge_candidate = false; |
1593 | 0 | return SFRAME_XLATE_OK; |
1594 | 0 | } |
1595 | | |
1596 | | /* Translate DW_CFA_restore into SFrame context. |
1597 | | Return SFRAME_XLATE_OK if success. */ |
1598 | | |
1599 | | static int |
1600 | | sframe_xlate_do_restore (struct sframe_xlate_ctx *xlate_ctx, |
1601 | | const struct cfi_insn_data *cfi_insn) |
1602 | 0 | { |
1603 | 0 | struct sframe_row_entry *cie_fre = xlate_ctx->first_fre; |
1604 | | /* The scratchpad FRE currently being updated with each cfi_insn |
1605 | | being interpreted. This FRE eventually gets linked in into the |
1606 | | list of FREs for the specific function. */ |
1607 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1608 | | |
1609 | | /* PR gas/33170. It is valid to have a: |
1610 | | .cfi_restore N |
1611 | | even at the entry of a function; in which case cie_fre is not yet setup. |
1612 | | Point cie_fre to cur_fre, and let the machinery proceed to update |
1613 | | merge_candidate as usual. */ |
1614 | 0 | if (cie_fre == NULL) |
1615 | 0 | cie_fre = cur_fre; |
1616 | | |
1617 | | /* Change the rule for the indicated register to the rule assigned to |
1618 | | it by the initial_instructions in the CIE. SFrame FREs track only CFA |
1619 | | and FP / RA for backtracing purposes; skip the other .cfi_restore |
1620 | | directives. */ |
1621 | 0 | if (cfi_insn->u.r == SFRAME_CFA_FP_REG) |
1622 | 0 | { |
1623 | 0 | gas_assert (cur_fre); |
1624 | 0 | cur_fre->fp_loc = cie_fre->fp_loc; |
1625 | 0 | cur_fre->fp_offset = cie_fre->fp_offset; |
1626 | 0 | cur_fre->merge_candidate = false; |
1627 | 0 | } |
1628 | 0 | else if (sframe_ra_tracking_p () |
1629 | 0 | && cfi_insn->u.r == SFRAME_CFA_RA_REG) |
1630 | 0 | { |
1631 | 0 | gas_assert (cur_fre); |
1632 | 0 | cur_fre->ra_loc = cie_fre->ra_loc; |
1633 | 0 | cur_fre->ra_offset = cie_fre->ra_offset; |
1634 | 0 | cur_fre->ra_undefined_p = cie_fre->ra_undefined_p; |
1635 | 0 | cur_fre->merge_candidate = false; |
1636 | 0 | } |
1637 | 0 | return SFRAME_XLATE_OK; |
1638 | 0 | } |
1639 | | |
1640 | | /* Translate DW_CFA_AARCH64_negate_ra_state into SFrame context. |
1641 | | Return SFRAME_XLATE_OK if success. */ |
1642 | | |
1643 | | static int |
1644 | | sframe_xlate_do_aarch64_negate_ra_state (struct sframe_xlate_ctx *xlate_ctx, |
1645 | | const struct cfi_insn_data *cfi_insn ATTRIBUTE_UNUSED) |
1646 | 0 | { |
1647 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1648 | |
|
1649 | 0 | gas_assert (cur_fre); |
1650 | | /* Toggle the mangled RA status bit. */ |
1651 | 0 | cur_fre->mangled_ra_p = !cur_fre->mangled_ra_p; |
1652 | 0 | cur_fre->merge_candidate = false; |
1653 | |
|
1654 | 0 | return SFRAME_XLATE_OK; |
1655 | 0 | } |
1656 | | |
1657 | | /* Translate DW_CFA_AARCH64_negate_ra_state_with_pc into SFrame context. |
1658 | | Return SFRAME_XLATE_OK if success. */ |
1659 | | |
1660 | | static int |
1661 | | sframe_xlate_do_aarch64_negate_ra_state_with_pc (struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED, |
1662 | | const struct cfi_insn_data *cfi_insn ATTRIBUTE_UNUSED) |
1663 | 0 | { |
1664 | 0 | as_warn (_("no SFrame FDE emitted; .cfi_negate_ra_state_with_pc")); |
1665 | | /* The used signing method should be encoded inside the FDE in SFrame v3. |
1666 | | For now, PAuth_LR extension is not supported with SFrame. */ |
1667 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1668 | 0 | } |
1669 | | |
1670 | | /* Translate DW_CFA_GNU_window_save into SFrame context. |
1671 | | DW_CFA_GNU_window_save is a DWARF Sparc extension, but is multiplexed with a |
1672 | | directive of DWARF AArch64 extension: DW_CFA_AARCH64_negate_ra_state. |
1673 | | The AArch64 backend of GCC 14 and older versions was emitting mistakenly the |
1674 | | Sparc CFI directive (.cfi_window_save). From GCC 15, the AArch64 backend |
1675 | | only emits .cfi_negate_ra_state. For backward compatibility, the handler for |
1676 | | .cfi_window_save needs to check whether the directive was used in a AArch64 |
1677 | | ABI context or not. |
1678 | | Return SFRAME_XLATE_OK if success. */ |
1679 | | |
1680 | | static int |
1681 | | sframe_xlate_do_gnu_window_save (struct sframe_xlate_ctx *xlate_ctx, |
1682 | | const struct cfi_insn_data *cfi_insn) |
1683 | 0 | { |
1684 | 0 | unsigned char abi_arch = sframe_get_abi_arch (); |
1685 | | |
1686 | | /* Translate DW_CFA_AARCH64_negate_ra_state into SFrame context. */ |
1687 | 0 | if (abi_arch == SFRAME_ABI_AARCH64_ENDIAN_BIG |
1688 | 0 | || abi_arch == SFRAME_ABI_AARCH64_ENDIAN_LITTLE) |
1689 | 0 | return sframe_xlate_do_aarch64_negate_ra_state (xlate_ctx, cfi_insn); |
1690 | | |
1691 | 0 | as_warn (_("no SFrame FDE emitted; .cfi_window_save")); |
1692 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
1693 | 0 | } |
1694 | | |
1695 | | /* Translate a DWARF sleb128 offset in the CFI escape data E to an offsetT. |
1696 | | Update the value in OFFSET if success (and return SFRAME_XLATE_OK). |
1697 | | Return SFRAME_XLATE_ERR_INVAL if error. */ |
1698 | | |
1699 | | static int |
1700 | | sframe_xlate_escape_sleb128_to_offsetT (const struct cfi_escape_data *e, |
1701 | | offsetT *offset) |
1702 | 0 | { |
1703 | 0 | gas_assert (e->type == CFI_ESC_byte || e->type == CFI_ESC_sleb128); |
1704 | | /* Read the offset. */ |
1705 | 0 | if (e->type == CFI_ESC_byte) |
1706 | 0 | { |
1707 | | /* The user/compiler may provide an sleb128 encoded data of a single byte |
1708 | | length (DWARF offset of DW_OP_bregN is sleb128). On a big-endian |
1709 | | host, the endianness of data itself needs to be accommodated then. To |
1710 | | keep it simple, gather the LSB, and translate it to int64. */ |
1711 | 0 | unsigned char sleb_data = e->exp.X_add_number & 0xff; |
1712 | 0 | const unsigned char *buf_start = (const unsigned char *)&sleb_data; |
1713 | 0 | const unsigned char *buf_end = buf_start + 1; |
1714 | 0 | int64_t value = 0; |
1715 | 0 | size_t read = read_sleb128_to_int64 (buf_start, buf_end, &value); |
1716 | | /* In case of bogus input (highest bit erroneously set, e.g., 0x80), |
1717 | | gracefully exit. */ |
1718 | 0 | if (!read) |
1719 | 0 | return SFRAME_XLATE_ERR_INVAL; |
1720 | 0 | *offset = (offsetT) value; |
1721 | 0 | } |
1722 | 0 | else |
1723 | | /* offset must be CFI_ESC_sleb128. */ |
1724 | 0 | *offset = e->exp.X_add_number; |
1725 | | |
1726 | 0 | return SFRAME_XLATE_OK; |
1727 | 0 | } |
1728 | | |
1729 | | /* Handle DW_CFA_def_cfa_expression in .cfi_escape. |
1730 | | |
1731 | | As with sframe_xlate_do_cfi_escape, the intent of this function is to detect |
1732 | | only the simple-to-process but common cases. All other CFA escape |
1733 | | expressions continue to be inadmissible (no SFrame FDE emitted). |
1734 | | |
1735 | | Sets CALLER_WARN_P for skipped cases (and returns SFRAME_XLATE_OK) where the |
1736 | | caller must warn. The caller then must also set |
1737 | | SFRAME_XLATE_ERR_NOTREPRESENTED for their callers. */ |
1738 | | |
1739 | | static int |
1740 | | sframe_xlate_do_escape_cfa_expr (struct sframe_xlate_ctx *xlate_ctx, |
1741 | | const struct cfi_insn_data *cfi_insn, |
1742 | | bool *caller_warn_p) |
1743 | 0 | { |
1744 | 0 | const struct cfi_escape_data *e = cfi_insn->u.esc; |
1745 | 0 | const struct cfi_escape_data *e_offset = NULL; |
1746 | 0 | int err = SFRAME_XLATE_OK; |
1747 | 0 | unsigned int opcode1, opcode2; |
1748 | 0 | offsetT offset = 0; |
1749 | 0 | unsigned int reg = SFRAME_FRE_REG_INVALID; |
1750 | 0 | unsigned int i = 0; |
1751 | 0 | bool x86_cfa_deref_p = false; |
1752 | | |
1753 | | /* Check roughly for an expression like so: |
1754 | | DW_CFA_def_cfa_expression (DW_OP_breg6 (rbp): -8; DW_OP_deref). */ |
1755 | 0 | #define CFI_ESC_NUM_EXP 4 |
1756 | 0 | offsetT items[CFI_ESC_NUM_EXP] = {0}; |
1757 | 0 | while (e->next) |
1758 | 0 | { |
1759 | 0 | e = e->next; |
1760 | | /* Bounds check, must be constant, no relocs. */ |
1761 | 0 | if (i >= CFI_ESC_NUM_EXP |
1762 | 0 | || e->exp.X_op != O_constant |
1763 | 0 | || e->reloc != TC_PARSE_CONS_RETURN_NONE) |
1764 | 0 | goto warn_and_exit; |
1765 | | /* Other checks based on index i. |
1766 | | - For item[2], allow byte OR sleb128. |
1767 | | - items at index 0, 1, and 3: Must be byte. */ |
1768 | 0 | if (i == 2 && (e->type != CFI_ESC_byte && e->type != CFI_ESC_sleb128)) |
1769 | 0 | goto warn_and_exit; |
1770 | 0 | else if (i != 2 && e->type != CFI_ESC_byte) |
1771 | 0 | goto warn_and_exit; |
1772 | | /* Block length (items[0]) of 3 in DWARF expr. */ |
1773 | 0 | if (i == 1 && items[0] != 3) |
1774 | 0 | goto warn_and_exit; |
1775 | | |
1776 | 0 | if (i == 2) |
1777 | 0 | e_offset = e; |
1778 | |
|
1779 | 0 | items[i] = e->exp.X_add_number; |
1780 | 0 | i++; |
1781 | 0 | } |
1782 | | |
1783 | 0 | if (i != CFI_ESC_NUM_EXP) |
1784 | 0 | goto warn_and_exit; |
1785 | 0 | #undef CFI_ESC_NUM_EXP |
1786 | | |
1787 | 0 | err = sframe_xlate_escape_sleb128_to_offsetT (e_offset, &offset); |
1788 | 0 | if (err == SFRAME_XLATE_ERR_INVAL) |
1789 | 0 | goto warn_and_exit; |
1790 | | |
1791 | 0 | opcode1 = items[1]; |
1792 | 0 | opcode2 = items[3]; |
1793 | | /* DW_OP_breg6 is rbp. FIXME - this stub can be enhanced to handle more |
1794 | | regs. */ |
1795 | 0 | if (sframe_get_abi_arch () == SFRAME_ABI_AMD64_ENDIAN_LITTLE |
1796 | 0 | && sframe_support_flex_fde_p () |
1797 | 0 | && opcode1 == DW_OP_breg6 && opcode2 == DW_OP_deref) |
1798 | 0 | { |
1799 | 0 | x86_cfa_deref_p = true; |
1800 | 0 | reg = SFRAME_CFA_FP_REG; |
1801 | 0 | } |
1802 | |
|
1803 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1804 | 0 | gas_assert (cur_fre); |
1805 | | |
1806 | | /* Handle the specific CFA expression mentioned above. */ |
1807 | 0 | if (x86_cfa_deref_p |
1808 | 0 | && sframe_fre_stack_offset_bound_p (offset, false) |
1809 | 0 | && reg != SFRAME_FRE_REG_INVALID) |
1810 | 0 | { |
1811 | 0 | xlate_ctx->flex_p = true; |
1812 | 0 | sframe_fre_set_cfa_base_reg (cur_fre, reg); |
1813 | 0 | sframe_fre_set_cfa_offset (cur_fre, offset); |
1814 | 0 | cur_fre->cfa_deref_p = true; |
1815 | 0 | cur_fre->merge_candidate = false; |
1816 | | /* Done handling here. */ |
1817 | 0 | caller_warn_p = false; |
1818 | |
|
1819 | 0 | return err; |
1820 | 0 | } |
1821 | | /* Any other CFA expression may not be safe to skip. Fall through to |
1822 | | warn_and_exit. */ |
1823 | | |
1824 | 0 | warn_and_exit: |
1825 | 0 | *caller_warn_p = true; |
1826 | 0 | return err; |
1827 | 0 | } |
1828 | | |
1829 | | /* Handle DW_CFA_expression in .cfi_escape. |
1830 | | |
1831 | | As with sframe_xlate_do_cfi_escape, the intent of this function is to detect |
1832 | | only the simple-to-process but common cases, where skipping over the escape |
1833 | | expr data does not affect correctness of the SFrame stack trace data. |
1834 | | |
1835 | | Sets CALLER_WARN_P for skipped cases (and returns SFRAME_XLATE_OK) where the |
1836 | | caller must warn. The caller then must also set |
1837 | | SFRAME_XLATE_ERR_NOTREPRESENTED for their callers. */ |
1838 | | |
1839 | | static int |
1840 | | sframe_xlate_do_escape_expr (struct sframe_xlate_ctx *xlate_ctx, |
1841 | | const struct cfi_insn_data *cfi_insn, |
1842 | | bool *caller_warn_p) |
1843 | 0 | { |
1844 | 0 | const struct cfi_escape_data *e = cfi_insn->u.esc; |
1845 | 0 | const struct cfi_escape_data *e_offset = NULL; |
1846 | 0 | int err = SFRAME_XLATE_OK; |
1847 | 0 | offsetT offset = 0; |
1848 | 0 | unsigned int i = 0; |
1849 | | |
1850 | | /* Check roughly for an expression |
1851 | | DW_CFA_expression: r1 (rdx) (DW_OP_bregN (reg): OFFSET). */ |
1852 | 0 | #define CFI_ESC_NUM_EXP 4 |
1853 | 0 | offsetT items[CFI_ESC_NUM_EXP] = {0}; |
1854 | 0 | while (e->next) |
1855 | 0 | { |
1856 | 0 | e = e->next; |
1857 | | /* Bounds check, must be constant, no relocs. */ |
1858 | 0 | if (i >= CFI_ESC_NUM_EXP |
1859 | 0 | || e->exp.X_op != O_constant |
1860 | 0 | || e->reloc != TC_PARSE_CONS_RETURN_NONE) |
1861 | 0 | goto warn_and_exit; |
1862 | | /* Other checks based on index i. |
1863 | | - For item[3], allow byte OR sleb128. |
1864 | | - items at index 0, 1, and 2: Must be byte. */ |
1865 | 0 | if (i == 3 && (e->type != CFI_ESC_byte && e->type != CFI_ESC_sleb128)) |
1866 | 0 | goto warn_and_exit; |
1867 | 0 | else if (i != 3 && e->type != CFI_ESC_byte) |
1868 | 0 | goto warn_and_exit; |
1869 | | /* Block length (items[1]) of 2 in DWARF expr. */ |
1870 | 0 | if (i == 2 && items[1] != 2) |
1871 | 0 | goto warn_and_exit; |
1872 | | |
1873 | 0 | if (i == 3) |
1874 | 0 | e_offset = e; |
1875 | |
|
1876 | 0 | items[i] = e->exp.X_add_number; |
1877 | 0 | i++; |
1878 | 0 | } |
1879 | | |
1880 | 0 | if (i <= CFI_ESC_NUM_EXP - 1) |
1881 | 0 | goto warn_and_exit; |
1882 | 0 | #undef CFI_ESC_NUM_EXP |
1883 | | |
1884 | 0 | err = sframe_xlate_escape_sleb128_to_offsetT (e_offset, &offset); |
1885 | 0 | if (err == SFRAME_XLATE_ERR_INVAL) |
1886 | 0 | goto warn_and_exit; |
1887 | | |
1888 | | /* reg operand to DW_CFA_expression is ULEB128. For the purpose at hand, |
1889 | | however, the register value will be less than 128 (CFI_ESC_NUM_EXP set |
1890 | | to 4). See an extended comment in sframe_xlate_do_escape_expr for why |
1891 | | reading ULEB is okay to skip without sacrificing correctness. */ |
1892 | 0 | unsigned int reg = items[0]; |
1893 | |
|
1894 | 0 | unsigned opcode = items[2]; |
1895 | 0 | unsigned int fp_base_reg = SFRAME_FRE_REG_INVALID; |
1896 | 0 | bool x86_fp_deref_p = true; |
1897 | |
|
1898 | 0 | if (sframe_get_abi_arch () == SFRAME_ABI_AMD64_ENDIAN_LITTLE |
1899 | 0 | && sframe_support_flex_fde_p () |
1900 | 0 | && opcode == DW_OP_breg6) |
1901 | 0 | { |
1902 | 0 | x86_fp_deref_p = true; |
1903 | 0 | fp_base_reg = SFRAME_CFA_FP_REG; |
1904 | 0 | } |
1905 | |
|
1906 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
1907 | 0 | gas_assert (cur_fre); |
1908 | | |
1909 | 0 | if (x86_fp_deref_p |
1910 | 0 | && reg == SFRAME_CFA_FP_REG |
1911 | 0 | && sframe_fre_stack_offset_bound_p (offset, false)) |
1912 | 0 | { |
1913 | 0 | xlate_ctx->flex_p = true; |
1914 | 0 | sframe_fre_set_fp_track (cur_fre, offset); |
1915 | 0 | cur_fre->fp_loc = SFRAME_FRE_ELEM_LOC_REG; |
1916 | 0 | cur_fre->fp_reg = fp_base_reg; |
1917 | 0 | cur_fre->fp_deref_p = true; |
1918 | 0 | cur_fre->merge_candidate = false; |
1919 | 0 | } |
1920 | 0 | else if (reg == SFRAME_CFA_SP_REG || reg == SFRAME_CFA_FP_REG |
1921 | 0 | || (sframe_ra_tracking_p () && reg == SFRAME_CFA_RA_REG) |
1922 | 0 | || reg == sframe_xlate_ctx_get_cur_cfa_reg (xlate_ctx)) |
1923 | 0 | { |
1924 | 0 | as_warn (_("no SFrame FDE emitted; " |
1925 | 0 | ".cfi_escape DW_CFA_expression with %s reg %u"), |
1926 | 0 | sframe_register_name (reg), reg); |
1927 | 0 | err = SFRAME_XLATE_ERR_NOTREPRESENTED; |
1928 | 0 | } |
1929 | | /* else safe to skip, so continue to return SFRAME_XLATE_OK. */ |
1930 | |
|
1931 | 0 | return err; |
1932 | | |
1933 | 0 | warn_and_exit: |
1934 | 0 | *caller_warn_p = true; |
1935 | 0 | return err; |
1936 | 0 | } |
1937 | | |
1938 | | /* Handle DW_CFA_val_offset in .cfi_escape. |
1939 | | |
1940 | | As with sframe_xlate_do_cfi_escape, the intent of this function is to detect |
1941 | | only the simple-to-process but common cases, where skipping over the escape |
1942 | | expr data does not affect correctness of the SFrame stack trace data. |
1943 | | |
1944 | | Sets CALLER_WARN_P for skipped cases (and returns SFRAME_XLATE_OK) where the |
1945 | | caller must warn. The caller then must also set |
1946 | | SFRAME_XLATE_ERR_NOTREPRESENTED for their callers. */ |
1947 | | |
1948 | | static int |
1949 | | sframe_xlate_do_escape_val_offset (const struct sframe_xlate_ctx *xlate_ctx, |
1950 | | const struct cfi_insn_data *cfi_insn, |
1951 | | bool *caller_warn_p) |
1952 | 0 | { |
1953 | 0 | const struct cfi_escape_data *e = cfi_insn->u.esc; |
1954 | 0 | int err = SFRAME_XLATE_OK; |
1955 | 0 | unsigned int i = 0; |
1956 | 0 | unsigned int reg; |
1957 | 0 | offsetT offset; |
1958 | | |
1959 | | /* Check for (DW_CFA_val_offset reg scaled_offset) sequence. */ |
1960 | 0 | #define CFI_ESC_NUM_EXP 2 |
1961 | 0 | offsetT items[CFI_ESC_NUM_EXP] = {0}; |
1962 | 0 | while (e->next) |
1963 | 0 | { |
1964 | 0 | e = e->next; |
1965 | 0 | if (i >= CFI_ESC_NUM_EXP || e->exp.X_op != O_constant |
1966 | 0 | || e->type != CFI_ESC_byte |
1967 | 0 | || e->reloc != TC_PARSE_CONS_RETURN_NONE) |
1968 | 0 | goto warn_and_exit; |
1969 | 0 | items[i] = e->exp.X_add_number; |
1970 | 0 | i++; |
1971 | 0 | } |
1972 | 0 | if (i <= CFI_ESC_NUM_EXP - 1) |
1973 | 0 | goto warn_and_exit; |
1974 | | |
1975 | | /* Both arguments to DW_CFA_val_offset are ULEB128. Especially with APX (on |
1976 | | x86) we're going to see DWARF register numbers above 127, for the extended |
1977 | | GPRs. And large enough stack frames would also require multi-byte offset |
1978 | | representation. However, since we limit our focus on cases when |
1979 | | CFI_ESC_NUM_EXP is 2, reading ULEB can be skipped. IOW, although not |
1980 | | ideal, SFrame FDE generation in case of an APX register in |
1981 | | DW_CFA_val_offset is being skipped (PS: this does _not_ mean incorrect |
1982 | | SFrame stack trace data). |
1983 | | |
1984 | | Recall that the intent here is to check for simple and prevalent cases, |
1985 | | when feasible. */ |
1986 | | |
1987 | 0 | reg = items[0]; |
1988 | 0 | offset = items[1]; |
1989 | 0 | #undef CFI_ESC_NUM_EXP |
1990 | | |
1991 | | /* Invoke sframe_xlate_do_val_offset itself for checking. */ |
1992 | 0 | struct cfi_insn_data temp = { |
1993 | 0 | .insn = DW_CFA_val_offset, |
1994 | 0 | .u = { |
1995 | 0 | .ri = { |
1996 | 0 | .reg = reg, |
1997 | 0 | .offset = offset * DWARF2_CIE_DATA_ALIGNMENT |
1998 | 0 | } |
1999 | 0 | } |
2000 | 0 | }; |
2001 | 0 | err = sframe_xlate_do_val_offset (xlate_ctx, &temp, true); |
2002 | 0 | return err; |
2003 | | |
2004 | 0 | warn_and_exit: |
2005 | 0 | *caller_warn_p = true; |
2006 | 0 | return err; |
2007 | 0 | } |
2008 | | |
2009 | | /* Handle DW_CFA_GNU_args_size in .cfi_escape. |
2010 | | |
2011 | | The purpose of DW_CFA_GNU_args_size is to adjust SP when performing stack |
2012 | | unwinding for exception handling. For stack tracing needs, |
2013 | | DW_CFA_GNU_args_size can be ignored, when CFA is FP-based. This is because |
2014 | | if the topmost frame is that of the catch block, the SP has been restored to |
2015 | | correct value by exception handling logic. From this point of interest in |
2016 | | the catch block now, stack tracing intends to go backwards to the caller |
2017 | | frame. If CFA restoration does not need SP, DW_CFA_GNU_args_size can be |
2018 | | ignored for stack tracing. |
2019 | | |
2020 | | Continue to warn and not emit SFrame FDE if CFA is SP based. The pattern |
2021 | | where the CFA is SP based and there is a DW_CFA_GNU_args_size for |
2022 | | SP-adjustment is not entirely clear. |
2023 | | |
2024 | | Sets CALLER_WARN_P for skipped cases (and returns SFRAME_XLATE_OK) where the |
2025 | | caller must warn. The caller then must also set |
2026 | | SFRAME_XLATE_ERR_NOTREPRESENTED for their callers. */ |
2027 | | |
2028 | | static int |
2029 | | sframe_xlate_do_escape_gnu_args_size (const struct sframe_xlate_ctx *xlate_ctx, |
2030 | | const struct cfi_insn_data *cfi_insn, |
2031 | | bool *caller_warn_p) |
2032 | 0 | { |
2033 | 0 | const struct cfi_escape_data *e = cfi_insn->u.esc; |
2034 | 0 | unsigned int i = 0; |
2035 | | |
2036 | | /* Check for (DW_CFA_GNU_args_size offset) sequence. */ |
2037 | 0 | #define CFI_ESC_NUM_EXP 1 |
2038 | 0 | offsetT items[CFI_ESC_NUM_EXP] = {0}; |
2039 | 0 | while (e->next) |
2040 | 0 | { |
2041 | 0 | e = e->next; |
2042 | 0 | if (i >= CFI_ESC_NUM_EXP || e->exp.X_op != O_constant |
2043 | 0 | || e->type != CFI_ESC_byte |
2044 | 0 | || e->reloc != TC_PARSE_CONS_RETURN_NONE) |
2045 | 0 | goto warn_and_exit; |
2046 | 0 | items[i] = e->exp.X_add_number; |
2047 | 0 | i++; |
2048 | 0 | } |
2049 | 0 | if (i == 0) |
2050 | 0 | goto warn_and_exit; |
2051 | | |
2052 | 0 | #undef CFI_ESC_NUM_EXP |
2053 | | |
2054 | 0 | offsetT offset = items[0]; |
2055 | |
|
2056 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
2057 | 0 | gas_assert (cur_fre); |
2058 | | /* If CFA is FP based, safe to skip. */ |
2059 | 0 | if (offset == 0 |
2060 | 0 | || sframe_xlate_ctx_get_cur_cfa_reg (xlate_ctx) == SFRAME_CFA_FP_REG) |
2061 | 0 | return SFRAME_XLATE_OK; |
2062 | | |
2063 | 0 | warn_and_exit: |
2064 | 0 | *caller_warn_p = true; |
2065 | 0 | return SFRAME_XLATE_OK; |
2066 | 0 | } |
2067 | | |
2068 | | /* Handle CFI_escape in SFrame context. |
2069 | | |
2070 | | .cfi_escape CFI directive allows the user to add arbitrary data to the |
2071 | | unwind info. DWARF expressions commonly follow after CFI_escape (fake CFI) |
2072 | | DWARF opcode. One might also use CFI_escape to add OS-specific CFI opcodes |
2073 | | even. |
2074 | | |
2075 | | Complex unwind info added using .cfi_escape directive _may_ be of no |
2076 | | consequence for SFrame when the affected registers are not SP, FP, RA or |
2077 | | CFA. The challenge in confirming the afore-mentioned is that it needs full |
2078 | | parsing (and validation) of the data presented after .cfi_escape. Here we |
2079 | | take a case-by-case approach towards skipping _some_ instances of |
2080 | | .cfi_escape: skip those that can be *easily* determined to be harmless in |
2081 | | the context of SFrame stack trace information. |
2082 | | |
2083 | | This function partially processes data following .cfi_escape and returns |
2084 | | SFRAME_XLATE_OK if OK to skip. */ |
2085 | | |
2086 | | static int |
2087 | | sframe_xlate_do_cfi_escape (struct sframe_xlate_ctx *xlate_ctx, |
2088 | | const struct cfi_insn_data *cfi_insn) |
2089 | 1 | { |
2090 | 1 | const struct cfi_escape_data *e; |
2091 | 1 | bool warn_p = false; |
2092 | 1 | int err = SFRAME_XLATE_OK; |
2093 | 1 | offsetT firstop; |
2094 | | |
2095 | 1 | e = cfi_insn->u.esc; |
2096 | | |
2097 | 1 | if (!e) |
2098 | 0 | return SFRAME_XLATE_ERR_INVAL; |
2099 | | |
2100 | 1 | if (e->exp.X_op != O_constant |
2101 | 1 | || e->type != CFI_ESC_byte |
2102 | 1 | || e->reloc != TC_PARSE_CONS_RETURN_NONE) |
2103 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
2104 | | |
2105 | 1 | firstop = e->exp.X_add_number; |
2106 | 1 | switch (firstop) |
2107 | 1 | { |
2108 | 1 | case DW_CFA_nop: |
2109 | | /* One or more nops together are harmless for SFrame. */ |
2110 | 1 | while (e->next) |
2111 | 0 | { |
2112 | 0 | e = e->next; |
2113 | 0 | if (e->exp.X_op != O_constant || e->exp.X_add_number != DW_CFA_nop |
2114 | 0 | || e->type != CFI_ESC_byte |
2115 | 0 | || e->reloc != TC_PARSE_CONS_RETURN_NONE) |
2116 | 0 | { |
2117 | 0 | warn_p = true; |
2118 | 0 | break; |
2119 | 0 | } |
2120 | 0 | } |
2121 | 1 | break; |
2122 | | |
2123 | 0 | case DW_CFA_def_cfa_expression: |
2124 | 0 | err = sframe_xlate_do_escape_cfa_expr (xlate_ctx, cfi_insn, &warn_p); |
2125 | 0 | break; |
2126 | | |
2127 | 0 | case DW_CFA_expression: |
2128 | 0 | err = sframe_xlate_do_escape_expr (xlate_ctx, cfi_insn, &warn_p); |
2129 | 0 | break; |
2130 | | |
2131 | 0 | case DW_CFA_val_offset: |
2132 | 0 | err = sframe_xlate_do_escape_val_offset (xlate_ctx, cfi_insn, &warn_p); |
2133 | 0 | break; |
2134 | | |
2135 | 0 | case DW_CFA_GNU_args_size: |
2136 | 0 | err = sframe_xlate_do_escape_gnu_args_size (xlate_ctx, cfi_insn, &warn_p); |
2137 | 0 | break; |
2138 | | |
2139 | 0 | default: |
2140 | 0 | warn_p = true; |
2141 | 0 | break; |
2142 | 1 | } |
2143 | | |
2144 | 1 | if (warn_p) |
2145 | 0 | { |
2146 | | /* In all other cases (e.g., DW_CFA_def_cfa_expression or other |
2147 | | OS-specific CFI opcodes), skip inspecting the DWARF expression. |
2148 | | This may impact the asynchronicity due to loss of coverage. |
2149 | | Continue to warn the user and bail out. */ |
2150 | 0 | as_warn (_("no SFrame FDE emitted; .cfi_escape with op (%#lx)"), |
2151 | 0 | (unsigned long)firstop); |
2152 | 0 | err = SFRAME_XLATE_ERR_NOTREPRESENTED; |
2153 | 0 | } |
2154 | | |
2155 | 1 | return err; |
2156 | 1 | } |
2157 | | |
2158 | | /* Translate DW_CFA_undefined into SFrame context. |
2159 | | |
2160 | | DW_CFA_undefined op indicates that from now on, the previous value of |
2161 | | register can’t be restored anymore. In DWARF, for the return address (RA) |
2162 | | register, this indicates to an unwinder that there is no return address |
2163 | | and the unwind is complete. |
2164 | | |
2165 | | In SFrame, represent the use of the RA register with DW_CFA_undefined as |
2166 | | SFrame FRE without any trailing FRE data words. Stack tracers can use this |
2167 | | as indication that an outermost frame has been reached and the stack trace |
2168 | | is complete. The use of other registers of interest with DW_CFA_undefined |
2169 | | cannot be represented in SFrame. Therefore skip generating an SFrame FDE. |
2170 | | |
2171 | | Return SFRAME_XLATE_OK if success. */ |
2172 | | |
2173 | | static int |
2174 | | sframe_xlate_do_cfi_undefined (const struct sframe_xlate_ctx *xlate_ctx ATTRIBUTE_UNUSED, |
2175 | | const struct cfi_insn_data *cfi_insn) |
2176 | 3 | { |
2177 | 3 | if (cfi_insn->u.r == SFRAME_CFA_FP_REG |
2178 | 3 | || cfi_insn->u.r == SFRAME_CFA_SP_REG) |
2179 | 0 | { |
2180 | 0 | as_warn (_("no SFrame FDE emitted; %s reg %u in .cfi_undefined"), |
2181 | 0 | sframe_register_name (cfi_insn->u.r), cfi_insn->u.r); |
2182 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; /* Not represented. */ |
2183 | 0 | } |
2184 | 3 | else if (cfi_insn->u.r == SFRAME_CFA_RA_REG) |
2185 | 0 | { |
2186 | | /* Represent RA undefined (i.e. outermost frame) as FRE without any |
2187 | | data words. */ |
2188 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
2189 | |
|
2190 | 0 | gas_assert (cur_fre); |
2191 | | /* Set RA undefined status bit. */ |
2192 | 0 | cur_fre->ra_undefined_p = true; |
2193 | 0 | cur_fre->merge_candidate = false; |
2194 | 0 | } |
2195 | | |
2196 | 3 | return SFRAME_XLATE_OK; |
2197 | 3 | } |
2198 | | |
2199 | | /* Translate DW_CFA_same_value into SFrame context. |
2200 | | |
2201 | | DW_CFA_same_value op indicates that current value of register is the same as |
2202 | | in the previous frame, i.e. no restoration needed. In SFrame stack trace |
2203 | | format, the handling is done similar to DW_CFA_restore. |
2204 | | |
2205 | | For SFRAME_CFA_RA_REG, if RA-tracking is enabled, reset the SFrame FRE state |
2206 | | for REG_RA to indicate that register does not need restoration. P.S.: Even |
2207 | | though resetting just REG_RA may be contradicting the AArch64 ABI (as Frame |
2208 | | Record contains for FP and LR), sframe_xlate_do_same_value () does not |
2209 | | detect the case and assumes the users' DW_CFA_same_value SFRAME_CFA_RA_REG |
2210 | | has a sound reason. For ABIs, where RA-tracking is disabled, handle it |
2211 | | similar to DW_CFA_restore: ignore the directive, it is safe to skip. The |
2212 | | reasoning is similar to that for DW_CFA_restore: if such a restoration was |
2213 | | meant to be of any consequence, there must have been the necessary CFI |
2214 | | directives for updating the CFA rule too such that the recovered RA from |
2215 | | stack is valid. |
2216 | | |
2217 | | SFrame based stacktracers will implement CFA-based SP recovery for all ABIs: |
2218 | | SP for previous frame is based on the applicable CFA-rule. There is no |
2219 | | representation in SFrame to indicate "no restoration needed" for REG_SP, |
2220 | | when going to the previous frame. That said, if DW_CFA_same_value is seen |
2221 | | for SFRAME_CFA_SP_REG, handle it similar to DW_CFA_restore: ignore the |
2222 | | directive, it is safe to skip. The reasoning is similar to that for |
2223 | | DW_CFA_restore: if such a restoration was meant to be of any consequence, |
2224 | | there must have been the necessary CFI directives for updating the CFA rule |
2225 | | too. The latter will be duly processed by the SFrame generation code, as |
2226 | | expected. |
2227 | | |
2228 | | For SFRAME_CFA_FP_REG, reset the state of the current FRE to indicate that |
2229 | | the value is the same as previous frame. |
2230 | | |
2231 | | Return SFRAME_XLATE_OK if success. */ |
2232 | | |
2233 | | static int |
2234 | | sframe_xlate_do_same_value (const struct sframe_xlate_ctx *xlate_ctx, |
2235 | | const struct cfi_insn_data *cfi_insn) |
2236 | 0 | { |
2237 | 0 | struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre; |
2238 | |
|
2239 | 0 | if (sframe_ra_tracking_p () && cfi_insn->u.r == SFRAME_CFA_RA_REG) |
2240 | 0 | { |
2241 | 0 | cur_fre->ra_loc = SFRAME_FRE_ELEM_LOC_NONE; |
2242 | 0 | cur_fre->ra_offset = 0; |
2243 | 0 | cur_fre->ra_undefined_p = false; |
2244 | 0 | cur_fre->merge_candidate = false; |
2245 | 0 | } |
2246 | 0 | else if (cfi_insn->u.r == SFRAME_CFA_FP_REG) |
2247 | 0 | { |
2248 | 0 | cur_fre->fp_loc = SFRAME_FRE_ELEM_LOC_NONE; |
2249 | 0 | cur_fre->fp_offset = 0; |
2250 | 0 | cur_fre->merge_candidate = false; |
2251 | 0 | } |
2252 | | |
2253 | | /* Safe to skip. */ |
2254 | 0 | return SFRAME_XLATE_OK; |
2255 | 0 | } |
2256 | | |
2257 | | /* Returns the DWARF call frame instruction name or fake CFI name for the |
2258 | | specified CFI opcode, or NULL if the value is not recognized. */ |
2259 | | |
2260 | | static const char * |
2261 | | sframe_get_cfi_name (int cfi_opc) |
2262 | 0 | { |
2263 | 0 | const char *cfi_name; |
2264 | |
|
2265 | 0 | switch (cfi_opc) |
2266 | 0 | { |
2267 | | /* Fake CFI type; outside the byte range of any real CFI insn. */ |
2268 | | /* See gas/dw2gencfi.h. */ |
2269 | 0 | case CFI_adjust_cfa_offset: |
2270 | 0 | cfi_name = "CFI_adjust_cfa_offset"; |
2271 | 0 | break; |
2272 | 0 | case CFI_return_column: |
2273 | 0 | cfi_name = "CFI_return_column"; |
2274 | 0 | break; |
2275 | 0 | case CFI_rel_offset: |
2276 | 0 | cfi_name = "CFI_rel_offset"; |
2277 | 0 | break; |
2278 | 0 | case CFI_escape: |
2279 | 0 | cfi_name = "CFI_escape"; |
2280 | 0 | break; |
2281 | 0 | case CFI_signal_frame: |
2282 | 0 | cfi_name = "CFI_signal_frame"; |
2283 | 0 | break; |
2284 | 0 | case CFI_val_encoded_addr: |
2285 | 0 | cfi_name = "CFI_val_encoded_addr"; |
2286 | 0 | break; |
2287 | 0 | case CFI_label: |
2288 | 0 | cfi_name = "CFI_label"; |
2289 | 0 | break; |
2290 | 0 | default: |
2291 | 0 | cfi_name = get_DW_CFA_name (cfi_opc); |
2292 | 0 | } |
2293 | | |
2294 | 0 | return cfi_name; |
2295 | 0 | } |
2296 | | |
2297 | | /* Process CFI_INSN and update the translation context with the FRE |
2298 | | information. |
2299 | | |
2300 | | Returns an error code (sframe_xlate_err) if CFI_INSN is not successfully |
2301 | | processed. */ |
2302 | | |
2303 | | static int |
2304 | | sframe_do_cfi_insn (struct sframe_xlate_ctx *xlate_ctx, |
2305 | | const struct cfi_insn_data *cfi_insn) |
2306 | 18 | { |
2307 | 18 | int err = 0; |
2308 | | |
2309 | | /* Atleast one cfi_insn per FDE is expected. */ |
2310 | 18 | gas_assert (cfi_insn); |
2311 | 18 | int op = cfi_insn->insn; |
2312 | | |
2313 | 18 | switch (op) |
2314 | 18 | { |
2315 | 4 | case DW_CFA_advance_loc: |
2316 | 4 | err = sframe_xlate_do_advance_loc (xlate_ctx, cfi_insn); |
2317 | 4 | break; |
2318 | 4 | case DW_CFA_def_cfa: |
2319 | 4 | err = sframe_xlate_do_def_cfa (xlate_ctx, cfi_insn); |
2320 | 4 | break; |
2321 | 0 | case DW_CFA_def_cfa_register: |
2322 | 0 | err = sframe_xlate_do_def_cfa_register (xlate_ctx, cfi_insn); |
2323 | 0 | break; |
2324 | 0 | case DW_CFA_def_cfa_offset: |
2325 | 0 | err = sframe_xlate_do_def_cfa_offset (xlate_ctx, cfi_insn); |
2326 | 0 | break; |
2327 | 6 | case DW_CFA_offset: |
2328 | 6 | err = sframe_xlate_do_offset (xlate_ctx, cfi_insn); |
2329 | 6 | break; |
2330 | 0 | case DW_CFA_val_offset: |
2331 | 0 | err = sframe_xlate_do_val_offset (xlate_ctx, cfi_insn, false); |
2332 | 0 | break; |
2333 | 0 | case DW_CFA_remember_state: |
2334 | 0 | err = sframe_xlate_do_remember_state (xlate_ctx); |
2335 | 0 | break; |
2336 | 0 | case DW_CFA_restore_state: |
2337 | 0 | err = sframe_xlate_do_restore_state (xlate_ctx); |
2338 | 0 | break; |
2339 | 0 | case DW_CFA_restore: |
2340 | 0 | err = sframe_xlate_do_restore (xlate_ctx, cfi_insn); |
2341 | 0 | break; |
2342 | | /* DW_CFA_AARCH64_negate_ra_state is multiplexed with |
2343 | | DW_CFA_GNU_window_save. */ |
2344 | 0 | case DW_CFA_GNU_window_save: |
2345 | 0 | err = sframe_xlate_do_gnu_window_save (xlate_ctx, cfi_insn); |
2346 | 0 | break; |
2347 | 0 | case DW_CFA_AARCH64_negate_ra_state_with_pc: |
2348 | 0 | err = sframe_xlate_do_aarch64_negate_ra_state_with_pc (xlate_ctx, cfi_insn); |
2349 | 0 | break; |
2350 | 0 | case DW_CFA_register: |
2351 | 0 | err = sframe_xlate_do_register (xlate_ctx, cfi_insn); |
2352 | 0 | break; |
2353 | 1 | case CFI_escape: |
2354 | 1 | err = sframe_xlate_do_cfi_escape (xlate_ctx, cfi_insn); |
2355 | 1 | break; |
2356 | 3 | case DW_CFA_undefined: |
2357 | 3 | err = sframe_xlate_do_cfi_undefined (xlate_ctx, cfi_insn); |
2358 | 3 | break; |
2359 | 0 | case DW_CFA_same_value: |
2360 | 0 | err = sframe_xlate_do_same_value (xlate_ctx, cfi_insn); |
2361 | 0 | break; |
2362 | 0 | default: |
2363 | | /* Other skipped operations may, however, impact the asynchronicity. */ |
2364 | 0 | { |
2365 | 0 | const char *cfi_name = sframe_get_cfi_name (op); |
2366 | |
|
2367 | 0 | if (!cfi_name) |
2368 | 0 | cfi_name = _("(unknown)"); |
2369 | 0 | as_warn (_("no SFrame FDE emitted; CFI insn %s (%#x)"), |
2370 | 0 | cfi_name, op); |
2371 | 0 | err = SFRAME_XLATE_ERR_NOTREPRESENTED; |
2372 | 0 | } |
2373 | 18 | } |
2374 | | |
2375 | | /* Any error will cause no SFrame FDE later. The user has already been |
2376 | | warned. */ |
2377 | 18 | return err; |
2378 | 18 | } |
2379 | | |
2380 | | |
2381 | | static int |
2382 | | sframe_do_fde (struct sframe_xlate_ctx *xlate_ctx, |
2383 | | const struct fde_entry *dw_fde) |
2384 | 3 | { |
2385 | 3 | const struct cfi_insn_data *cfi_insn; |
2386 | 3 | int err = SFRAME_XLATE_OK; |
2387 | | |
2388 | 3 | xlate_ctx->dw_fde = dw_fde; |
2389 | | |
2390 | | /* SFrame format cannot represent a non-default DWARF return column reg. */ |
2391 | 3 | if (xlate_ctx->dw_fde->return_column != DWARF2_DEFAULT_RETURN_COLUMN) |
2392 | 0 | { |
2393 | 0 | as_warn (_("no SFrame FDE emitted; non-default RA register %u"), |
2394 | 0 | xlate_ctx->dw_fde->return_column); |
2395 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
2396 | 0 | } |
2397 | | |
2398 | | /* Iterate over the CFIs and create SFrame FREs. */ |
2399 | 21 | for (cfi_insn = dw_fde->data; cfi_insn; cfi_insn = cfi_insn->next) |
2400 | 18 | { |
2401 | | /* Translate each CFI, and buffer the state in translation context. */ |
2402 | 18 | err = sframe_do_cfi_insn (xlate_ctx, cfi_insn); |
2403 | 18 | if (err != SFRAME_XLATE_OK) |
2404 | 0 | { |
2405 | | /* Skip generating SFrame stack trace info for the function if any |
2406 | | offending CFI is encountered by sframe_do_cfi_insn (). Warning |
2407 | | message already printed by sframe_do_cfi_insn (). */ |
2408 | 0 | return err; /* Return the error code. */ |
2409 | 0 | } |
2410 | 18 | } |
2411 | | |
2412 | | /* Link in the scratchpad FRE that the last few CFI insns helped create. */ |
2413 | 3 | if (xlate_ctx->cur_fre) |
2414 | 3 | { |
2415 | 3 | sframe_xlate_ctx_add_fre (xlate_ctx, xlate_ctx->cur_fre); |
2416 | 3 | xlate_ctx->cur_fre = NULL; |
2417 | 3 | } |
2418 | | /* Designate the end of the last SFrame FRE. */ |
2419 | 3 | if (xlate_ctx->last_fre) |
2420 | 3 | { |
2421 | 3 | xlate_ctx->last_fre->pc_end |
2422 | 3 | = get_dw_fde_end_addrS (xlate_ctx->dw_fde); |
2423 | 3 | } |
2424 | | |
2425 | | /* Number of FREs must fit uint16_t. Check now, and do not emit the SFrame |
2426 | | FDE if it doesnt fit (although, it is not expected to happen for |
2427 | | real-world, useful programs). The approach of truncating the FDE and |
2428 | | emitting multiple SFrame FDEs instead, is not a clearly preferable |
2429 | | handling either. Its a divergence from the model where an SFrame FDE |
2430 | | encodes stack trace data between a .cfi_startproc and .cfi_endproc pair. |
2431 | | Further, some components (linkers, stack tracers) want to associate the |
2432 | | Start PC of a function to a known symbol in the file? */ |
2433 | 3 | if (xlate_ctx->num_xlate_fres > UINT16_MAX) |
2434 | 0 | { |
2435 | 0 | as_warn (_("no SFrame FDE emitted; Number of FREs exceeds UINT16_MAX")); |
2436 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
2437 | 0 | } |
2438 | | |
2439 | | /* ABI/arch except s390x cannot represent FP without RA saved. */ |
2440 | 3 | if (sframe_ra_tracking_p () |
2441 | 0 | && sframe_get_abi_arch () != SFRAME_ABI_S390X_ENDIAN_BIG) |
2442 | 0 | { |
2443 | 0 | struct sframe_row_entry *fre; |
2444 | | |
2445 | | /* Iterate over the scratchpad FREs and validate them. */ |
2446 | 0 | for (fre = xlate_ctx->first_fre; fre; fre = fre->next) |
2447 | 0 | { |
2448 | | /* SFrame format cannot represent FP on stack without RA on stack. */ |
2449 | 0 | if (fre->ra_loc != SFRAME_FRE_ELEM_LOC_STACK |
2450 | 0 | && fre->fp_loc == SFRAME_FRE_ELEM_LOC_STACK) |
2451 | 0 | { |
2452 | 0 | as_warn (_("no SFrame FDE emitted; FP without RA on stack")); |
2453 | 0 | return SFRAME_XLATE_ERR_NOTREPRESENTED; |
2454 | 0 | } |
2455 | 0 | } |
2456 | 0 | } |
2457 | | |
2458 | 3 | return SFRAME_XLATE_OK; |
2459 | 3 | } |
2460 | | |
2461 | | /* Create SFrame stack trace info for all functions. |
2462 | | |
2463 | | This function consumes the already generated DWARF FDEs (by dw2gencfi) and |
2464 | | generates data which is later emitted as stack trace information encoded in |
2465 | | the SFrame format. */ |
2466 | | |
2467 | | static void |
2468 | | create_sframe_all (void) |
2469 | 3 | { |
2470 | 3 | struct fde_entry *dw_fde = NULL; |
2471 | 3 | struct sframe_func_entry *sframe_fde = NULL; |
2472 | | |
2473 | 3 | struct sframe_xlate_ctx *xlate_ctx = sframe_xlate_ctx_alloc (); |
2474 | | |
2475 | 6 | for (dw_fde = all_fde_data; dw_fde ; dw_fde = dw_fde->next) |
2476 | 3 | { |
2477 | 3 | sframe_fde = sframe_fde_alloc (); |
2478 | | /* Initialize the translation context with information anew. */ |
2479 | 3 | sframe_xlate_ctx_init (xlate_ctx); |
2480 | | |
2481 | | /* Process and link SFrame FDEs if no error. */ |
2482 | 3 | int err = sframe_do_fde (xlate_ctx, dw_fde); |
2483 | 3 | if (err && get_dw_fde_signal_p (dw_fde)) |
2484 | 0 | { |
2485 | 0 | sframe_xlate_ctx_cleanup (xlate_ctx); |
2486 | 0 | xlate_ctx->flex_p = false; |
2487 | 0 | err = SFRAME_XLATE_OK; |
2488 | 0 | } |
2489 | | |
2490 | 3 | if (err) |
2491 | 0 | { |
2492 | 0 | sframe_xlate_ctx_cleanup (xlate_ctx); |
2493 | 0 | sframe_fde_free (sframe_fde); |
2494 | 0 | } |
2495 | 3 | else |
2496 | 3 | { |
2497 | | /* All done. Transfer the state from the SFrame translation |
2498 | | context to the SFrame FDE. */ |
2499 | 3 | sframe_xlate_ctx_finalize (xlate_ctx, sframe_fde); |
2500 | 3 | *last_sframe_fde = sframe_fde; |
2501 | 3 | last_sframe_fde = &sframe_fde->next; |
2502 | 3 | } |
2503 | 3 | } |
2504 | | |
2505 | 3 | XDELETE (xlate_ctx); |
2506 | 3 | } |
2507 | | |
2508 | | void |
2509 | | output_sframe (segT sframe_seg) |
2510 | 3 | { |
2511 | 3 | (void) sframe_seg; |
2512 | | |
2513 | | /* Currently only SFRAME_VERSION_3 can be emitted. */ |
2514 | 3 | gas_assert (flag_gen_sframe_version == GEN_SFRAME_VERSION_3); |
2515 | | /* Setup the version specific access functions. */ |
2516 | 3 | sframe_set_version (flag_gen_sframe_version); |
2517 | | |
2518 | | /* Process all fdes and create SFrame stack trace information. */ |
2519 | 3 | create_sframe_all (); |
2520 | | |
2521 | 3 | output_sframe_internal (); |
2522 | 3 | } |
2523 | | |
2524 | | #else /* support_sframe_p */ |
2525 | | |
2526 | | void |
2527 | | output_sframe (segT sframe_seg ATTRIBUTE_UNUSED) |
2528 | | { |
2529 | | } |
2530 | | |
2531 | | #endif /* support_sframe_p */ |