/src/binutils-gdb/gas/scfi.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* scfi.c - Support for synthesizing DWARF CFI for hand-written asm. |
2 | | Copyright (C) 2023-2025 Free Software Foundation, Inc. |
3 | | |
4 | | This file is part of GAS, the GNU Assembler. |
5 | | |
6 | | GAS is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3, or (at your option) |
9 | | any later version. |
10 | | |
11 | | GAS is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with GAS; see the file COPYING. If not, write to the Free |
18 | | Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
19 | | 02110-1301, USA. */ |
20 | | |
21 | | #include "as.h" |
22 | | #include "scfi.h" |
23 | | #include "subsegs.h" |
24 | | #include "scfidw2gen.h" |
25 | | #include "dw2gencfi.h" |
26 | | |
27 | | #if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN) |
28 | | |
29 | | /* Beyond the target defined number of registers to be tracked |
30 | | (SCFI_MAX_REG_ID), keep the next register ID, in sequence, for REG_CFA. */ |
31 | 0 | #define REG_CFA (SCFI_MAX_REG_ID+1) |
32 | | /* Define the total number of registers being tracked. |
33 | | Used as index into an array of cfi_reglocS. Note that a ginsn may carry a |
34 | | register number greater than MAX_NUM_SCFI_REGS, e.g., for the ginsns |
35 | | corresponding to push fs/gs in AMD64. */ |
36 | 0 | #define MAX_NUM_SCFI_REGS (REG_CFA+1) |
37 | | |
38 | 0 | #define REG_INVALID ((unsigned int)-1) |
39 | | |
40 | | enum cfi_reglocstate |
41 | | { |
42 | | CFI_UNDEFINED, |
43 | | CFI_IN_REG, |
44 | | CFI_ON_STACK |
45 | | }; |
46 | | |
47 | | /* Location at which CFI register is saved. |
48 | | |
49 | | A CFI register (callee-saved registers, RA/LR) are always an offset from |
50 | | the CFA. REG_CFA itself, however, may have REG_SP or REG_FP as base |
51 | | register. Hence, keep the base reg ID and offset per tracked register. */ |
52 | | |
53 | | struct cfi_regloc |
54 | | { |
55 | | /* Base reg ID (DWARF register number). */ |
56 | | unsigned int base; |
57 | | /* Location as offset from the CFA. */ |
58 | | offsetT offset; |
59 | | /* Current state of the CFI register. */ |
60 | | enum cfi_reglocstate state; |
61 | | }; |
62 | | |
63 | | typedef struct cfi_regloc cfi_reglocS; |
64 | | |
65 | | struct scfi_op_data |
66 | | { |
67 | | const char *name; |
68 | | }; |
69 | | |
70 | | typedef struct scfi_op_data scfi_op_dataS; |
71 | | |
72 | | /* SCFI operation. |
73 | | |
74 | | An SCFI operation represents a single atomic change to the SCFI state. |
75 | | This can also be understood as an abstraction for what eventually gets |
76 | | emitted as a DWARF CFI operation. */ |
77 | | |
78 | | struct scfi_op |
79 | | { |
80 | | /* An SCFI op updates the state of either the CFA or other tracked |
81 | | (callee-saved, REG_SP etc) registers. 'reg' is in the DWARF register |
82 | | number space and must be strictly less than MAX_NUM_SCFI_REGS. */ |
83 | | unsigned int reg; |
84 | | /* Location of the reg. */ |
85 | | cfi_reglocS loc; |
86 | | /* DWARF CFI opcode. */ |
87 | | uint32_t dw2cfi_op; |
88 | | /* Some SCFI ops, e.g., for CFI_label, may need to carry additional data. */ |
89 | | scfi_op_dataS *op_data; |
90 | | /* A linked list. */ |
91 | | struct scfi_op *next; |
92 | | }; |
93 | | |
94 | | /* SCFI State - accumulated unwind information at a PC. |
95 | | |
96 | | SCFI state is the accumulated unwind information encompassing: |
97 | | - REG_SP, REG_FP, |
98 | | - RA, and |
99 | | - all callee-saved registers. |
100 | | |
101 | | Note that SCFI_MAX_REG_ID is target/ABI dependent and is provided by the |
102 | | backends. The backend must also identify the DWARF register numbers for |
103 | | the REG_SP, and REG_FP registers. */ |
104 | | |
105 | | struct scfi_state |
106 | | { |
107 | | cfi_reglocS regs[MAX_NUM_SCFI_REGS]; |
108 | | cfi_reglocS scratch[MAX_NUM_SCFI_REGS]; |
109 | | /* Current stack size. */ |
110 | | offsetT stack_size; |
111 | | /* Whether the stack size is known. |
112 | | Stack size may become untraceable depending on the specific stack |
113 | | manipulation machine instruction, e.g., rsp = rsp op reg instruction |
114 | | makes the stack size untraceable. */ |
115 | | bool traceable_p; |
116 | | }; |
117 | | |
118 | | /* Initialize a new SCFI op. */ |
119 | | |
120 | | static scfi_opS * |
121 | | init_scfi_op (void) |
122 | 0 | { |
123 | 0 | scfi_opS *op = XCNEW (scfi_opS); |
124 | |
|
125 | 0 | return op; |
126 | 0 | } |
127 | | |
128 | | /* Free the SCFI ops, given the HEAD of the list. */ |
129 | | |
130 | | void |
131 | | scfi_ops_cleanup (scfi_opS **head) |
132 | 0 | { |
133 | 0 | scfi_opS *op; |
134 | 0 | scfi_opS *next; |
135 | |
|
136 | 0 | if (!head || !*head) |
137 | 0 | return; |
138 | | |
139 | 0 | op = *head; |
140 | 0 | next = op->next; |
141 | |
|
142 | 0 | while (op) |
143 | 0 | { |
144 | 0 | free (op->op_data); |
145 | 0 | free (op); |
146 | 0 | op = next; |
147 | 0 | next = op ? op->next : NULL; |
148 | 0 | } |
149 | |
|
150 | 0 | free (head); |
151 | 0 | } |
152 | | |
153 | | /* Compare two SCFI states. */ |
154 | | |
155 | | static int |
156 | | cmp_scfi_state (scfi_stateS *state1, scfi_stateS *state2) |
157 | 0 | { |
158 | 0 | int ret; |
159 | |
|
160 | 0 | if (!state1 || !state2) |
161 | 0 | return 1; |
162 | | |
163 | | /* Skip comparing the scratch[] value of registers. The user visible |
164 | | unwind information is derived from the regs[] from the SCFI state. */ |
165 | 0 | ret = memcmp (state1->regs, state2->regs, |
166 | 0 | sizeof (cfi_reglocS) * MAX_NUM_SCFI_REGS); |
167 | | |
168 | | /* For user functions which perform dynamic stack allocation, after switching |
169 | | t REG_FP based CFA tracking, it is perfectly possible to have stack usage |
170 | | in some control flows. Further, the different control flows may even not |
171 | | have the same idea of CFA tracking (likely later necessitating generation |
172 | | of .cfi_remember_state / .cfi_restore_state pair). */ |
173 | 0 | ret |= state1->regs[REG_CFA].base != state2->regs[REG_CFA].base; |
174 | |
|
175 | 0 | if (!ret && state1->regs[REG_CFA].base == REG_SP) |
176 | 0 | ret |= state1->stack_size != state2->stack_size; |
177 | |
|
178 | 0 | ret |= state1->traceable_p != state2->traceable_p; |
179 | |
|
180 | 0 | return ret; |
181 | 0 | } |
182 | | |
183 | | #if 0 |
184 | | static void |
185 | | scfi_state_update_reg (scfi_stateS *state, uint32_t dst, uint32_t base, |
186 | | int32_t offset) |
187 | | { |
188 | | if (dst >= MAX_NUM_SCFI_REGS) |
189 | | return; |
190 | | |
191 | | state->regs[dst].base = base; |
192 | | state->regs[dst].offset = offset; |
193 | | } |
194 | | #endif |
195 | | |
196 | | /* Update the SCFI state of REG as available on execution stack at OFFSET |
197 | | from REG_CFA (BASE). |
198 | | |
199 | | Note that BASE must be REG_CFA, because any other base (REG_SP, REG_FP) |
200 | | is by definition transitory in the function. */ |
201 | | |
202 | | static void |
203 | | scfi_state_save_reg (scfi_stateS *state, unsigned int reg, unsigned int base, |
204 | | offsetT offset) |
205 | 0 | { |
206 | 0 | if (reg >= MAX_NUM_SCFI_REGS) |
207 | 0 | return; |
208 | | |
209 | 0 | gas_assert (base == REG_CFA); |
210 | | |
211 | 0 | state->regs[reg].base = base; |
212 | 0 | state->regs[reg].offset = offset; |
213 | 0 | state->regs[reg].state = CFI_ON_STACK; |
214 | 0 | } |
215 | | |
216 | | static void |
217 | | scfi_state_restore_reg (scfi_stateS *state, unsigned int reg) |
218 | 0 | { |
219 | 0 | if (reg >= MAX_NUM_SCFI_REGS) |
220 | 0 | return; |
221 | | |
222 | | /* Sanity check. See Rule 4. */ |
223 | 0 | gas_assert (state->regs[reg].state == CFI_ON_STACK); |
224 | 0 | gas_assert (state->regs[reg].base == REG_CFA); |
225 | | |
226 | | /* PS: the register may still be on stack much after the restore. Reset the |
227 | | SCFI state to CFI_UNDEFINED, however, to indicate that the most updated |
228 | | source of value is register itself from here onwards. */ |
229 | 0 | state->regs[reg].base = 0; |
230 | 0 | state->regs[reg].offset = 0; |
231 | 0 | state->regs[reg].state = CFI_UNDEFINED; |
232 | 0 | } |
233 | | |
234 | | /* Identify if the given GAS instruction GINSN saves a register |
235 | | (of interest) on stack. */ |
236 | | |
237 | | static bool |
238 | | ginsn_scfi_save_reg_p (ginsnS *ginsn, scfi_stateS *state) |
239 | 0 | { |
240 | 0 | bool save_reg_p = false; |
241 | 0 | struct ginsn_src *src; |
242 | 0 | struct ginsn_dst *dst; |
243 | |
|
244 | 0 | src = ginsn_get_src1 (ginsn); |
245 | 0 | dst = ginsn_get_dst (ginsn); |
246 | | |
247 | | /* The first save to stack of callee-saved register is deemed as |
248 | | register save. */ |
249 | 0 | if (!ginsn_track_reg_p (ginsn_get_src_reg (src), GINSN_GEN_SCFI) |
250 | 0 | || state->regs[ginsn_get_src_reg (src)].state == CFI_ON_STACK) |
251 | 0 | return save_reg_p; |
252 | | |
253 | | /* A register save insn may be an indirect mov. */ |
254 | 0 | if (ginsn->type == GINSN_TYPE_MOV |
255 | 0 | && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT |
256 | 0 | && (ginsn_get_dst_reg (dst) == REG_SP |
257 | 0 | || (ginsn_get_dst_reg (dst) == REG_FP |
258 | 0 | && state->regs[REG_CFA].base == REG_FP))) |
259 | 0 | save_reg_p = true; |
260 | | /* or an explicit store to stack. */ |
261 | 0 | else if (ginsn->type == GINSN_TYPE_STORE |
262 | 0 | && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT |
263 | 0 | && ginsn_get_dst_reg (dst) == REG_SP) |
264 | 0 | save_reg_p = true; |
265 | |
|
266 | 0 | return save_reg_p; |
267 | 0 | } |
268 | | |
269 | | /* Identify if the given GAS instruction GINSN restores a register |
270 | | (of interest) on stack. */ |
271 | | |
272 | | static bool |
273 | | ginsn_scfi_restore_reg_p (ginsnS *ginsn, scfi_stateS *state) |
274 | 0 | { |
275 | 0 | bool restore_reg_p = false; |
276 | 0 | struct ginsn_dst *dst; |
277 | 0 | struct ginsn_src *src1; |
278 | |
|
279 | 0 | dst = ginsn_get_dst (ginsn); |
280 | 0 | src1 = ginsn_get_src1 (ginsn); |
281 | |
|
282 | 0 | if (!ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI)) |
283 | 0 | return restore_reg_p; |
284 | | |
285 | | /* A register restore insn may be an indirect mov... */ |
286 | 0 | if (ginsn->type == GINSN_TYPE_MOV |
287 | 0 | && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT |
288 | 0 | && (ginsn_get_src_reg (src1) == REG_SP |
289 | 0 | || (ginsn_get_src_reg (src1) == REG_FP |
290 | 0 | && state->regs[REG_CFA].base == REG_FP))) |
291 | 0 | restore_reg_p = true; |
292 | | /* ...or an explicit load from stack. */ |
293 | 0 | else if (ginsn->type == GINSN_TYPE_LOAD |
294 | 0 | && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT |
295 | 0 | && ginsn_get_src_reg (src1) == REG_SP) |
296 | 0 | restore_reg_p = true; |
297 | |
|
298 | 0 | return restore_reg_p; |
299 | 0 | } |
300 | | |
301 | | /* Append the SCFI operation OP to the list of SCFI operations in the |
302 | | given GINSN. */ |
303 | | |
304 | | static int |
305 | | ginsn_append_scfi_op (ginsnS *ginsn, scfi_opS *op) |
306 | 0 | { |
307 | 0 | scfi_opS *sop; |
308 | |
|
309 | 0 | if (!ginsn || !op) |
310 | 0 | return 1; |
311 | | |
312 | 0 | if (!ginsn->scfi_ops) |
313 | 0 | { |
314 | 0 | ginsn->scfi_ops = XCNEW (scfi_opS *); |
315 | 0 | *ginsn->scfi_ops = op; |
316 | 0 | } |
317 | 0 | else |
318 | 0 | { |
319 | | /* Add to tail. Most ginsns have a single SCFI operation, |
320 | | so this traversal for every insertion is acceptable for now. */ |
321 | 0 | sop = *ginsn->scfi_ops; |
322 | 0 | while (sop->next) |
323 | 0 | sop = sop->next; |
324 | |
|
325 | 0 | sop->next = op; |
326 | 0 | } |
327 | 0 | ginsn->num_scfi_ops++; |
328 | |
|
329 | 0 | return 0; |
330 | 0 | } |
331 | | |
332 | | static void |
333 | | scfi_op_add_def_cfa_reg (scfi_stateS *state, ginsnS *ginsn, unsigned int reg) |
334 | 0 | { |
335 | 0 | scfi_opS *op = NULL; |
336 | |
|
337 | 0 | state->regs[REG_CFA].base = reg; |
338 | |
|
339 | 0 | op = init_scfi_op (); |
340 | |
|
341 | 0 | op->dw2cfi_op = DW_CFA_def_cfa_register; |
342 | 0 | op->reg = REG_CFA; |
343 | 0 | op->loc = state->regs[REG_CFA]; |
344 | |
|
345 | 0 | ginsn_append_scfi_op (ginsn, op); |
346 | 0 | } |
347 | | |
348 | | static void |
349 | | scfi_op_add_cfa_offset_inc (scfi_stateS *state, ginsnS *ginsn, offsetT num) |
350 | 0 | { |
351 | 0 | scfi_opS *op = NULL; |
352 | |
|
353 | 0 | state->regs[REG_CFA].offset -= num; |
354 | |
|
355 | 0 | op = init_scfi_op (); |
356 | |
|
357 | 0 | op->dw2cfi_op = DW_CFA_def_cfa_offset; |
358 | 0 | op->reg = REG_CFA; |
359 | 0 | op->loc = state->regs[REG_CFA]; |
360 | |
|
361 | 0 | ginsn_append_scfi_op (ginsn, op); |
362 | 0 | } |
363 | | |
364 | | static void |
365 | | scfi_op_add_cfa_offset_dec (scfi_stateS *state, ginsnS *ginsn, offsetT num) |
366 | 0 | { |
367 | 0 | scfi_opS *op = NULL; |
368 | |
|
369 | 0 | state->regs[REG_CFA].offset += num; |
370 | |
|
371 | 0 | op = init_scfi_op (); |
372 | |
|
373 | 0 | op->dw2cfi_op = DW_CFA_def_cfa_offset; |
374 | 0 | op->reg = REG_CFA; |
375 | 0 | op->loc = state->regs[REG_CFA]; |
376 | |
|
377 | 0 | ginsn_append_scfi_op (ginsn, op); |
378 | 0 | } |
379 | | |
380 | | static void |
381 | | scfi_op_add_def_cfa (scfi_stateS *state, ginsnS *ginsn, unsigned int reg, |
382 | | offsetT num) |
383 | 0 | { |
384 | 0 | scfi_opS *op = NULL; |
385 | |
|
386 | 0 | state->regs[REG_CFA].base = reg; |
387 | 0 | state->regs[REG_CFA].offset = num; |
388 | |
|
389 | 0 | op = init_scfi_op (); |
390 | |
|
391 | 0 | op->dw2cfi_op = DW_CFA_def_cfa; |
392 | 0 | op->reg = REG_CFA; |
393 | 0 | op->loc = state->regs[REG_CFA]; |
394 | |
|
395 | 0 | ginsn_append_scfi_op (ginsn, op); |
396 | 0 | } |
397 | | |
398 | | static void |
399 | | scfi_op_add_cfi_offset (scfi_stateS *state, ginsnS *ginsn, unsigned int reg) |
400 | 0 | { |
401 | 0 | scfi_opS *op = NULL; |
402 | |
|
403 | 0 | op = init_scfi_op (); |
404 | |
|
405 | 0 | op->dw2cfi_op = DW_CFA_offset; |
406 | 0 | op->reg = reg; |
407 | 0 | op->loc = state->regs[reg]; |
408 | |
|
409 | 0 | ginsn_append_scfi_op (ginsn, op); |
410 | 0 | } |
411 | | |
412 | | static void |
413 | | scfi_op_add_cfa_restore (ginsnS *ginsn, unsigned int reg) |
414 | 0 | { |
415 | 0 | scfi_opS *op = NULL; |
416 | |
|
417 | 0 | op = init_scfi_op (); |
418 | |
|
419 | 0 | op->dw2cfi_op = DW_CFA_restore; |
420 | 0 | op->reg = reg; |
421 | 0 | op->loc.base = REG_INVALID; |
422 | 0 | op->loc.offset = 0; |
423 | |
|
424 | 0 | ginsn_append_scfi_op (ginsn, op); |
425 | 0 | } |
426 | | |
427 | | static void |
428 | | scfi_op_add_cfi_remember_state (ginsnS *ginsn) |
429 | 0 | { |
430 | 0 | scfi_opS *op = NULL; |
431 | |
|
432 | 0 | op = init_scfi_op (); |
433 | |
|
434 | 0 | op->dw2cfi_op = DW_CFA_remember_state; |
435 | |
|
436 | 0 | ginsn_append_scfi_op (ginsn, op); |
437 | 0 | } |
438 | | |
439 | | static void |
440 | | scfi_op_add_cfi_restore_state (ginsnS *ginsn) |
441 | 0 | { |
442 | 0 | scfi_opS *op = NULL; |
443 | |
|
444 | 0 | op = init_scfi_op (); |
445 | |
|
446 | 0 | op->dw2cfi_op = DW_CFA_restore_state; |
447 | | |
448 | | /* FIXME - add to the beginning of the scfi_ops. */ |
449 | 0 | ginsn_append_scfi_op (ginsn, op); |
450 | 0 | } |
451 | | |
452 | | void |
453 | | scfi_op_add_cfi_label (ginsnS *ginsn, const char *name) |
454 | 0 | { |
455 | 0 | scfi_opS *op = NULL; |
456 | |
|
457 | 0 | op = init_scfi_op (); |
458 | 0 | op->dw2cfi_op = CFI_label; |
459 | 0 | op->op_data = XCNEW (scfi_op_dataS); |
460 | 0 | op->op_data->name = name; |
461 | |
|
462 | 0 | ginsn_append_scfi_op (ginsn, op); |
463 | 0 | } |
464 | | |
465 | | void |
466 | | scfi_op_add_signal_frame (ginsnS *ginsn) |
467 | 0 | { |
468 | 0 | scfi_opS *op = NULL; |
469 | |
|
470 | 0 | op = init_scfi_op (); |
471 | 0 | op->dw2cfi_op = CFI_signal_frame; |
472 | |
|
473 | 0 | ginsn_append_scfi_op (ginsn, op); |
474 | 0 | } |
475 | | |
476 | | static int |
477 | | verify_heuristic_traceable_reg_fp (ginsnS *ginsn, scfi_stateS *state) |
478 | 0 | { |
479 | | /* The function uses this variable to issue error to user right away. */ |
480 | 0 | int fp_traceable_p = 0; |
481 | 0 | enum ginsn_type gtype; |
482 | 0 | struct ginsn_src *src1; |
483 | 0 | struct ginsn_src *src2; |
484 | 0 | struct ginsn_dst *dst; |
485 | 0 | unsigned int src1_reg; |
486 | 0 | unsigned int dst_reg; |
487 | 0 | enum ginsn_src_type src1_type; |
488 | 0 | enum ginsn_src_type src2_type; |
489 | 0 | enum ginsn_dst_type dst_type; |
490 | |
|
491 | 0 | gtype = ginsn->type; |
492 | |
|
493 | 0 | src1 = ginsn_get_src1 (ginsn); |
494 | 0 | src2 = ginsn_get_src2 (ginsn); |
495 | 0 | dst = ginsn_get_dst (ginsn); |
496 | |
|
497 | 0 | src1_reg = ginsn_get_src_reg (src1); |
498 | 0 | dst_reg = ginsn_get_dst_reg (dst); |
499 | |
|
500 | 0 | src1_type = ginsn_get_src_type (src1); |
501 | 0 | src2_type = ginsn_get_src_type (src2); |
502 | 0 | dst_type = ginsn_get_dst_type (dst); |
503 | | |
504 | | /* Stack manipulation can be done in a variety of ways. A program may |
505 | | allocate stack statically or may perform dynamic stack allocation in |
506 | | the prologue. |
507 | | |
508 | | The SCFI machinery in GAS is based on some heuristics: |
509 | | |
510 | | - Rule 3 If the base register for CFA tracking is REG_FP, the program |
511 | | must not clobber REG_FP, unless it is for switch to REG_SP based CFA |
512 | | tracking (via say, a pop %rbp in X86). */ |
513 | | |
514 | | /* Check all applicable instructions with dest REG_FP, when the CFA base |
515 | | register is REG_FP. */ |
516 | 0 | if (state->regs[REG_CFA].base == REG_FP |
517 | 0 | && (dst_type == GINSN_DST_REG || dst_type == GINSN_DST_INDIRECT) |
518 | 0 | && dst_reg == REG_FP) |
519 | 0 | { |
520 | | /* Excuse the add/sub with imm usage: They are OK. */ |
521 | 0 | if ((gtype == GINSN_TYPE_ADD || gtype == GINSN_TYPE_SUB) |
522 | 0 | && src1_type == GINSN_SRC_REG && src1_reg == REG_FP |
523 | 0 | && src2_type == GINSN_SRC_IMM) |
524 | 0 | fp_traceable_p = 0; |
525 | | /* REG_FP restore is OK too. */ |
526 | 0 | else if (ginsn->type == GINSN_TYPE_LOAD) |
527 | 0 | fp_traceable_p = 0; |
528 | | /* mov's to memory with REG_FP base do not make REG_FP untraceable. */ |
529 | 0 | else if (dst_type == GINSN_DST_INDIRECT |
530 | 0 | && (gtype == GINSN_TYPE_MOV || gtype == GINSN_TYPE_STORE)) |
531 | 0 | fp_traceable_p = 0; |
532 | | /* Manipulations of the values possibly on stack are OK too. */ |
533 | 0 | else if ((gtype == GINSN_TYPE_ADD || gtype == GINSN_TYPE_SUB |
534 | 0 | || gtype == GINSN_TYPE_AND) |
535 | 0 | && dst_type == GINSN_DST_INDIRECT) |
536 | 0 | fp_traceable_p = 0; |
537 | | /* All other ginsns with REG_FP as destination make REG_FP not |
538 | | traceable. */ |
539 | 0 | else |
540 | 0 | fp_traceable_p = 1; |
541 | 0 | } |
542 | |
|
543 | 0 | if (fp_traceable_p) |
544 | 0 | as_bad_where (ginsn->file, ginsn->line, |
545 | 0 | _("SCFI: usage of REG_FP as scratch not supported")); |
546 | |
|
547 | 0 | return fp_traceable_p; |
548 | 0 | } |
549 | | |
550 | | static int |
551 | | verify_heuristic_traceable_stack_manipulation (ginsnS *ginsn, |
552 | | scfi_stateS *state) |
553 | 0 | { |
554 | | /* The function uses this variable to issue error to user right away. */ |
555 | 0 | int sp_untraceable_p = 0; |
556 | 0 | bool possibly_untraceable = false; |
557 | 0 | enum ginsn_type gtype; |
558 | 0 | struct ginsn_dst *dst; |
559 | 0 | struct ginsn_src *src1; |
560 | 0 | struct ginsn_src *src2; |
561 | 0 | unsigned int src1_reg; |
562 | 0 | unsigned int dst_reg; |
563 | 0 | enum ginsn_src_type src1_type; |
564 | 0 | enum ginsn_src_type src2_type; |
565 | 0 | enum ginsn_dst_type dst_type; |
566 | |
|
567 | 0 | gtype = ginsn->type; |
568 | |
|
569 | 0 | src1 = ginsn_get_src1 (ginsn); |
570 | 0 | src2 = ginsn_get_src2 (ginsn); |
571 | 0 | dst = ginsn_get_dst (ginsn); |
572 | |
|
573 | 0 | src1_reg = ginsn_get_src_reg (src1); |
574 | 0 | dst_reg = ginsn_get_dst_reg (dst); |
575 | |
|
576 | 0 | src1_type = ginsn_get_src_type (src1); |
577 | 0 | src2_type = ginsn_get_src_type (src2); |
578 | 0 | dst_type = ginsn_get_dst_type (dst); |
579 | | |
580 | | /* Stack manipulation can be done in a variety of ways. A program may |
581 | | allocate stack statically in prologue or may need to do dynamic stack |
582 | | allocation. |
583 | | |
584 | | The SCFI machinery in GAS is based on some heuristics: |
585 | | |
586 | | - Rule 1 The base register for CFA tracking may be either REG_SP or |
587 | | REG_FP. |
588 | | |
589 | | - Rule 2 If the base register for CFA tracking is REG_SP, the precise |
590 | | amount of stack usage (and hence, the value of rsp) must be known at |
591 | | all times. */ |
592 | |
|
593 | 0 | if (gtype == GINSN_TYPE_MOV |
594 | 0 | && dst_type == GINSN_DST_REG && dst_reg == REG_SP |
595 | | /* Exclude mov %rbp, %rsp from this check. */ |
596 | 0 | && src1_type == GINSN_SRC_REG && src1_reg != REG_FP) |
597 | 0 | { |
598 | | /* A previous mov %rsp, %reg must have been seen earlier for this to be |
599 | | an OK for stack manipulation. */ |
600 | 0 | if (state->scratch[src1_reg].base != REG_CFA |
601 | 0 | || state->scratch[src1_reg].state != CFI_IN_REG) |
602 | 0 | possibly_untraceable = true; |
603 | 0 | } |
604 | | /* Check add/sub/and insn usage when CFA base register is REG_SP. |
605 | | Any stack size manipulation, including stack realignment is not allowed |
606 | | if CFA base register is REG_SP. */ |
607 | 0 | else if (dst_type == GINSN_DST_REG && dst_reg == REG_SP |
608 | 0 | && (((gtype == GINSN_TYPE_ADD || gtype == GINSN_TYPE_SUB) |
609 | 0 | && src2_type != GINSN_SRC_IMM) |
610 | 0 | || gtype == GINSN_TYPE_AND || gtype == GINSN_TYPE_OTHER)) |
611 | 0 | possibly_untraceable = true; |
612 | | /* If a register save operation is seen when REG_SP is untraceable, |
613 | | CFI cannot be synthesized for register saves, hence bail out. */ |
614 | 0 | else if (ginsn_scfi_save_reg_p (ginsn, state) && !state->traceable_p) |
615 | 0 | { |
616 | 0 | sp_untraceable_p = 1; |
617 | | /* If, however, the register save is an REG_FP-based, indirect mov |
618 | | like: mov reg, disp(%rbp) and CFA base register is REG_BP, |
619 | | untraceable REG_SP is not a problem. */ |
620 | 0 | if (gtype == GINSN_TYPE_MOV && state->regs[REG_CFA].base == REG_FP |
621 | 0 | && dst_type == GINSN_DST_INDIRECT && dst_reg == REG_FP) |
622 | 0 | sp_untraceable_p = 0; |
623 | 0 | } |
624 | 0 | else if (ginsn_scfi_restore_reg_p (ginsn, state) && !state->traceable_p) |
625 | 0 | { |
626 | 0 | if (gtype == GINSN_TYPE_MOV && dst_type == GINSN_DST_INDIRECT |
627 | 0 | && (src1_reg == REG_SP |
628 | 0 | || (src1_reg == REG_FP && state->regs[REG_CFA].base != REG_FP))) |
629 | 0 | sp_untraceable_p = 1; |
630 | 0 | } |
631 | |
|
632 | 0 | if (possibly_untraceable) |
633 | 0 | { |
634 | | /* See Rule 2. For SP-based CFA, this makes CFA tracking not possible. |
635 | | Propagate now to caller. */ |
636 | 0 | if (state->regs[REG_CFA].base == REG_SP) |
637 | 0 | sp_untraceable_p = 1; |
638 | 0 | else if (state->traceable_p) |
639 | 0 | { |
640 | | /* An extension of Rule 2. |
641 | | For FP-based CFA, this may be a problem *if* certain specific |
642 | | changes to the SCFI state are seen beyond this point, e.g., |
643 | | register save / restore from stack. */ |
644 | 0 | gas_assert (state->regs[REG_CFA].base == REG_FP); |
645 | | /* Simply make a note in the SCFI state object for now and |
646 | | continue. Indicate an error when register save / restore |
647 | | for callee-saved registers is seen. */ |
648 | 0 | sp_untraceable_p = 0; |
649 | 0 | state->traceable_p = false; |
650 | 0 | } |
651 | 0 | } |
652 | | |
653 | 0 | if (sp_untraceable_p) |
654 | 0 | as_bad_where (ginsn->file, ginsn->line, |
655 | 0 | _("SCFI: unsupported stack manipulation pattern")); |
656 | |
|
657 | 0 | return sp_untraceable_p; |
658 | 0 | } |
659 | | |
660 | | static int |
661 | | verify_heuristic_symmetrical_restore_reg (scfi_stateS *state, ginsnS* ginsn) |
662 | 0 | { |
663 | 0 | int sym_restore = true; |
664 | 0 | offsetT expected_offset = 0; |
665 | 0 | struct ginsn_src *src1; |
666 | 0 | struct ginsn_dst *dst; |
667 | 0 | unsigned int reg; |
668 | | |
669 | | /* Rule 4: Save and Restore of callee-saved registers must be symmetrical. |
670 | | It is expected that value of the saved register is restored correctly. |
671 | | E.g., |
672 | | push reg1 |
673 | | push reg2 |
674 | | ... |
675 | | body of func which uses reg1 , reg2 as scratch, |
676 | | and may be even spills them to stack. |
677 | | ... |
678 | | pop reg2 |
679 | | pop reg1 |
680 | | It is difficult to verify the Rule 4 in all cases. For the SCFI machinery, |
681 | | it is difficult to separate prologue-epilogue from the body of the function |
682 | | |
683 | | Hence, the SCFI machinery at this time, should only warn on an asymetrical |
684 | | restore. */ |
685 | 0 | src1 = ginsn_get_src1 (ginsn); |
686 | 0 | dst = ginsn_get_dst (ginsn); |
687 | 0 | reg = ginsn_get_dst_reg (dst); |
688 | | |
689 | | /* For non callee-saved registers, calling the API is meaningless. */ |
690 | 0 | if (!ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI)) |
691 | 0 | return sym_restore; |
692 | | |
693 | | /* The register must have been saved on stack, for sure. */ |
694 | 0 | gas_assert (state->regs[reg].state == CFI_ON_STACK); |
695 | 0 | gas_assert (state->regs[reg].base == REG_CFA); |
696 | | |
697 | 0 | if ((ginsn->type == GINSN_TYPE_MOV |
698 | 0 | || ginsn->type == GINSN_TYPE_LOAD) |
699 | 0 | && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT |
700 | 0 | && (ginsn_get_src_reg (src1) == REG_SP |
701 | 0 | || (ginsn_get_src_reg (src1) == REG_FP |
702 | 0 | && state->regs[REG_CFA].base == REG_FP))) |
703 | 0 | { |
704 | | /* mov disp(%rsp), reg. */ |
705 | | /* mov disp(%rbp), reg. */ |
706 | 0 | expected_offset = (((ginsn_get_src_reg (src1) == REG_SP) |
707 | 0 | ? -state->stack_size |
708 | 0 | : state->regs[REG_FP].offset) |
709 | 0 | + ginsn_get_src_disp (src1)); |
710 | 0 | } |
711 | |
|
712 | 0 | sym_restore = (expected_offset == state->regs[reg].offset); |
713 | |
|
714 | 0 | return sym_restore; |
715 | 0 | } |
716 | | |
717 | | /* Perform symbolic execution of the GINSN and update its list of scfi_ops. |
718 | | scfi_ops are later used to directly generate the DWARF CFI directives. |
719 | | Also update the SCFI state object STATE for the caller. */ |
720 | | |
721 | | static int |
722 | | gen_scfi_ops (ginsnS *ginsn, scfi_stateS *state) |
723 | 0 | { |
724 | 0 | int ret = 0; |
725 | 0 | offsetT offset; |
726 | 0 | struct ginsn_src *src1; |
727 | 0 | struct ginsn_src *src2; |
728 | 0 | struct ginsn_dst *dst; |
729 | 0 | unsigned int src1_reg; |
730 | 0 | unsigned int dst_reg; |
731 | 0 | enum ginsn_src_type src1_type; |
732 | 0 | enum ginsn_src_type src2_type; |
733 | 0 | enum ginsn_dst_type dst_type; |
734 | |
|
735 | 0 | if (!ginsn || !state) |
736 | 0 | ret = 1; |
737 | | |
738 | | /* For the first ginsn (of type GINSN_TYPE_SYMBOL) in the gbb, generate |
739 | | the SCFI op with DW_CFA_def_cfa. Note that the register and offset are |
740 | | target-specific. */ |
741 | 0 | if (GINSN_F_FUNC_BEGIN_P (ginsn)) |
742 | 0 | { |
743 | 0 | scfi_op_add_def_cfa (state, ginsn, REG_SP, SCFI_INIT_CFA_OFFSET); |
744 | 0 | state->stack_size += SCFI_INIT_CFA_OFFSET; |
745 | 0 | return ret; |
746 | 0 | } |
747 | | |
748 | 0 | src1 = ginsn_get_src1 (ginsn); |
749 | 0 | src2 = ginsn_get_src2 (ginsn); |
750 | 0 | dst = ginsn_get_dst (ginsn); |
751 | |
|
752 | 0 | src1_reg = ginsn_get_src_reg (src1); |
753 | 0 | dst_reg = ginsn_get_dst_reg (dst); |
754 | |
|
755 | 0 | src1_type = ginsn_get_src_type (src1); |
756 | 0 | src2_type = ginsn_get_src_type (src2); |
757 | 0 | dst_type = ginsn_get_dst_type (dst); |
758 | |
|
759 | 0 | ret = verify_heuristic_traceable_stack_manipulation (ginsn, state); |
760 | 0 | if (ret) |
761 | 0 | return ret; |
762 | | |
763 | 0 | ret = verify_heuristic_traceable_reg_fp (ginsn, state); |
764 | 0 | if (ret) |
765 | 0 | return ret; |
766 | | |
767 | 0 | switch (dst_type) |
768 | 0 | { |
769 | 0 | case GINSN_DST_REG: |
770 | 0 | switch (ginsn->type) |
771 | 0 | { |
772 | 0 | case GINSN_TYPE_MOV: |
773 | 0 | if (src1_type == GINSN_SRC_REG && src1_reg == REG_SP |
774 | 0 | && dst_type == GINSN_DST_REG && dst_reg == REG_FP |
775 | 0 | && state->regs[REG_CFA].base == REG_SP) |
776 | 0 | { |
777 | | /* mov %rsp, %rbp. */ |
778 | 0 | scfi_op_add_def_cfa_reg (state, ginsn, dst_reg); |
779 | 0 | } |
780 | 0 | else if (src1_type == GINSN_SRC_REG && src1_reg == REG_FP |
781 | 0 | && dst_type == GINSN_DST_REG && dst_reg == REG_SP |
782 | 0 | && state->regs[REG_CFA].base == REG_FP) |
783 | 0 | { |
784 | | /* mov %rbp, %rsp. */ |
785 | 0 | state->stack_size = -state->regs[REG_FP].offset; |
786 | 0 | scfi_op_add_def_cfa_reg (state, ginsn, dst_reg); |
787 | 0 | state->traceable_p = true; |
788 | 0 | } |
789 | 0 | else if (src1_type == GINSN_SRC_INDIRECT |
790 | 0 | && (src1_reg == REG_SP || src1_reg == REG_FP) |
791 | 0 | && ginsn_track_reg_p (dst_reg, GINSN_GEN_SCFI)) |
792 | 0 | { |
793 | | /* mov disp(%rsp), reg. */ |
794 | | /* mov disp(%rbp), reg. */ |
795 | 0 | if (verify_heuristic_symmetrical_restore_reg (state, ginsn)) |
796 | 0 | { |
797 | 0 | scfi_state_restore_reg (state, dst_reg); |
798 | 0 | scfi_op_add_cfa_restore (ginsn, dst_reg); |
799 | 0 | } |
800 | 0 | else |
801 | 0 | as_warn_where (ginsn->file, ginsn->line, |
802 | 0 | _("SCFI: asymetrical register restore")); |
803 | 0 | } |
804 | 0 | else if (src1_type == GINSN_SRC_REG && src1_reg == REG_SP |
805 | 0 | && dst_type == GINSN_DST_REG) |
806 | 0 | { |
807 | | /* mov %rsp, %reg. */ |
808 | | /* The value of rsp is taken directly from state->stack_size. |
809 | | IMP: The workflow in gen_scfi_ops must keep it updated. |
810 | | PS: Not taking the value from state->scratch[REG_SP] is |
811 | | intentional. */ |
812 | 0 | state->scratch[dst_reg].base = REG_CFA; |
813 | 0 | state->scratch[dst_reg].offset = -state->stack_size; |
814 | 0 | state->scratch[dst_reg].state = CFI_IN_REG; |
815 | 0 | } |
816 | 0 | else if (src1_type == GINSN_SRC_REG |
817 | 0 | && dst_type == GINSN_DST_REG && dst_reg == REG_SP) |
818 | 0 | { |
819 | | /* mov %reg, %rsp. */ |
820 | | /* Keep the value of REG_SP updated. */ |
821 | 0 | if (state->scratch[src1_reg].state == CFI_IN_REG) |
822 | 0 | { |
823 | 0 | state->stack_size = -state->scratch[src1_reg].offset; |
824 | 0 | state->traceable_p = true; |
825 | 0 | } |
826 | | # if 0 |
827 | | scfi_state_update_reg (state, ginsn_get_dst_reg (dst), |
828 | | state->scratch[ginsn_get_src_reg (src1)].base, |
829 | | state->scratch[ginsn_get_src_reg (src1)].offset); |
830 | | #endif |
831 | |
|
832 | 0 | } |
833 | 0 | break; |
834 | 0 | case GINSN_TYPE_SUB: |
835 | 0 | if (src1_type == GINSN_SRC_REG && src1_reg == REG_SP |
836 | 0 | && dst_type == GINSN_DST_REG && dst_reg == REG_SP |
837 | 0 | && src2_type == GINSN_SRC_IMM) |
838 | 0 | { |
839 | | /* Stack inc/dec offset, when generated due to stack push and pop is |
840 | | target-specific. Use the value encoded in the ginsn. */ |
841 | 0 | state->stack_size += ginsn_get_src_imm (src2); |
842 | 0 | if (state->regs[REG_CFA].base == REG_SP) |
843 | 0 | { |
844 | | /* push reg. */ |
845 | 0 | scfi_op_add_cfa_offset_dec (state, ginsn, ginsn_get_src_imm (src2)); |
846 | 0 | } |
847 | 0 | } |
848 | 0 | break; |
849 | 0 | case GINSN_TYPE_ADD: |
850 | 0 | if (src1_type == GINSN_SRC_REG && src1_reg == REG_SP |
851 | 0 | && dst_type == GINSN_DST_REG && dst_reg == REG_SP |
852 | 0 | && src2_type == GINSN_SRC_IMM) |
853 | 0 | { |
854 | | /* Stack inc/dec offset is target-specific. Use the value |
855 | | encoded in the ginsn. */ |
856 | 0 | state->stack_size -= ginsn_get_src_imm (src2); |
857 | | /* pop %reg affects CFA offset only if CFA is currently |
858 | | stack-pointer based. */ |
859 | 0 | if (state->regs[REG_CFA].base == REG_SP) |
860 | 0 | { |
861 | 0 | scfi_op_add_cfa_offset_inc (state, ginsn, ginsn_get_src_imm (src2)); |
862 | 0 | } |
863 | 0 | } |
864 | 0 | else if (src1_type == GINSN_SRC_REG && src1_reg == REG_FP |
865 | 0 | && dst_type == GINSN_DST_REG && dst_reg == REG_SP |
866 | 0 | && state->regs[REG_CFA].base == REG_FP) |
867 | 0 | { |
868 | | /* FIXME - what is this for ? */ |
869 | 0 | state->stack_size = 0 - (state->regs[REG_FP].offset + ginsn_get_src_imm (src2)); |
870 | 0 | } |
871 | 0 | break; |
872 | 0 | case GINSN_TYPE_LOAD: |
873 | | /* If this is a load from stack. */ |
874 | 0 | if (src1_type == GINSN_SRC_INDIRECT |
875 | 0 | && ((src1_reg == REG_FP && state->regs[REG_CFA].base == REG_FP) |
876 | 0 | || src1_reg == REG_SP)) |
877 | | |
878 | 0 | { |
879 | | /* pop %rbp when CFA tracking is REG_FP based. */ |
880 | 0 | if (dst_reg == REG_FP && state->regs[REG_CFA].base == REG_FP) |
881 | 0 | { |
882 | 0 | scfi_op_add_def_cfa_reg (state, ginsn, REG_SP); |
883 | 0 | if (state->regs[REG_CFA].offset != state->stack_size) |
884 | 0 | scfi_op_add_cfa_offset_inc (state, ginsn, |
885 | 0 | (state->regs[REG_CFA].offset - state->stack_size)); |
886 | 0 | } |
887 | 0 | if (ginsn_track_reg_p (dst_reg, GINSN_GEN_SCFI)) |
888 | 0 | { |
889 | 0 | if (verify_heuristic_symmetrical_restore_reg (state, ginsn)) |
890 | 0 | { |
891 | 0 | scfi_state_restore_reg (state, dst_reg); |
892 | 0 | scfi_op_add_cfa_restore (ginsn, dst_reg); |
893 | 0 | } |
894 | 0 | else |
895 | 0 | as_warn_where (ginsn->file, ginsn->line, |
896 | 0 | _("SCFI: asymetrical register restore")); |
897 | 0 | } |
898 | 0 | } |
899 | 0 | break; |
900 | 0 | default: |
901 | 0 | break; |
902 | 0 | } |
903 | 0 | break; |
904 | | |
905 | 0 | case GINSN_DST_INDIRECT: |
906 | | /* Some operations with an indirect access to memory (or even to stack) |
907 | | may still be uninteresting for SCFI purpose (e.g, addl %edx, -32(%rsp) |
908 | | in x86). In case of x86_64, these can neither be a register |
909 | | save / unsave, nor can alter the stack size. |
910 | | PS: This condition may need to be revisited for other arches. */ |
911 | 0 | if (ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB |
912 | 0 | || ginsn->type == GINSN_TYPE_AND) |
913 | 0 | break; |
914 | 0 | gas_assert (ginsn->type == GINSN_TYPE_MOV |
915 | 0 | || ginsn->type == GINSN_TYPE_STORE |
916 | 0 | || ginsn->type == GINSN_TYPE_LOAD); |
917 | | /* mov reg, disp(%rbp) */ |
918 | | /* mov reg, disp(%rsp) */ |
919 | 0 | if (ginsn_scfi_save_reg_p (ginsn, state)) |
920 | 0 | { |
921 | 0 | if (dst_reg == REG_SP) |
922 | 0 | { |
923 | | /* mov reg, disp(%rsp) */ |
924 | 0 | offset = 0 - state->stack_size + ginsn_get_dst_disp (dst); |
925 | 0 | scfi_state_save_reg (state, src1_reg, REG_CFA, offset); |
926 | 0 | scfi_op_add_cfi_offset (state, ginsn, src1_reg); |
927 | 0 | } |
928 | 0 | else if (dst_reg == REG_FP) |
929 | 0 | { |
930 | 0 | gas_assert (state->regs[REG_CFA].base == REG_FP); |
931 | | /* mov reg, disp(%rbp) */ |
932 | 0 | offset = 0 - state->regs[REG_CFA].offset + ginsn_get_dst_disp (dst); |
933 | 0 | scfi_state_save_reg (state, src1_reg, REG_CFA, offset); |
934 | 0 | scfi_op_add_cfi_offset (state, ginsn, src1_reg); |
935 | 0 | } |
936 | 0 | } |
937 | 0 | break; |
938 | | |
939 | 0 | default: |
940 | | /* Skip GINSN_DST_UNKNOWN and GINSN_DST_MEM as they are uninteresting |
941 | | currently for SCFI. */ |
942 | 0 | break; |
943 | 0 | } |
944 | | |
945 | 0 | return ret; |
946 | 0 | } |
947 | | |
948 | | /* Recursively perform forward flow of the (unwind information) SCFI STATE |
949 | | starting at basic block GBB. |
950 | | |
951 | | The core of forward flow process takes the SCFI state at the entry of a bb |
952 | | and updates it incrementally as per the semantics of each ginsn in the bb. |
953 | | |
954 | | Returns error code, if any. */ |
955 | | |
956 | | static int |
957 | | forward_flow_scfi_state (gcfgS *gcfg, gbbS *gbb, scfi_stateS *state) |
958 | 0 | { |
959 | 0 | ginsnS *ginsn; |
960 | 0 | gbbS *prev_bb; |
961 | 0 | gedgeS *gedge = NULL; |
962 | 0 | int ret = 0; |
963 | |
|
964 | 0 | if (gbb->visited) |
965 | 0 | { |
966 | | /* Check that the SCFI state is the same as previous. */ |
967 | 0 | ret = cmp_scfi_state (state, gbb->entry_state); |
968 | 0 | if (ret) |
969 | 0 | as_bad (_("SCFI: Bad CFI propagation perhaps")); |
970 | 0 | return ret; |
971 | 0 | } |
972 | | |
973 | 0 | gbb->visited = true; |
974 | |
|
975 | 0 | gbb->entry_state = XCNEW (scfi_stateS); |
976 | 0 | memcpy (gbb->entry_state, state, sizeof (scfi_stateS)); |
977 | | |
978 | | /* Perform symbolic execution of each ginsn in the gbb and update the |
979 | | scfi_ops list of each ginsn (and also update the STATE object). */ |
980 | 0 | bb_for_each_insn(gbb, ginsn) |
981 | 0 | { |
982 | 0 | ret = gen_scfi_ops (ginsn, state); |
983 | 0 | if (ret) |
984 | 0 | goto fail; |
985 | 0 | } |
986 | | |
987 | 0 | gbb->exit_state = XCNEW (scfi_stateS); |
988 | 0 | memcpy (gbb->exit_state, state, sizeof (scfi_stateS)); |
989 | | |
990 | | /* Forward flow the SCFI state. Currently, we process the next basic block |
991 | | in DFS order. But any forward traversal order should be fine. */ |
992 | 0 | prev_bb = gbb; |
993 | 0 | if (gbb->num_out_gedges) |
994 | 0 | { |
995 | 0 | bb_for_each_edge(gbb, gedge) |
996 | 0 | { |
997 | 0 | gbb = gedge->dst_bb; |
998 | | /* Ensure that the state is the one from the exit of the prev bb. */ |
999 | 0 | memcpy (state, prev_bb->exit_state, sizeof (scfi_stateS)); |
1000 | 0 | if (gbb->visited) |
1001 | 0 | { |
1002 | 0 | ret = cmp_scfi_state (gbb->entry_state, state); |
1003 | 0 | if (ret) |
1004 | 0 | goto fail; |
1005 | 0 | } |
1006 | | |
1007 | 0 | if (!gedge->visited) |
1008 | 0 | { |
1009 | 0 | gedge->visited = true; |
1010 | | |
1011 | | /* Entry SCFI state for the destination bb of the edge is the |
1012 | | same as the exit SCFI state of the source bb of the edge. */ |
1013 | 0 | memcpy (state, prev_bb->exit_state, sizeof (scfi_stateS)); |
1014 | 0 | ret = forward_flow_scfi_state (gcfg, gbb, state); |
1015 | 0 | if (ret) |
1016 | 0 | goto fail; |
1017 | 0 | } |
1018 | 0 | } |
1019 | 0 | } |
1020 | | |
1021 | 0 | return 0; |
1022 | | |
1023 | 0 | fail: |
1024 | |
|
1025 | 0 | if (gedge) |
1026 | 0 | gedge->visited = true; |
1027 | 0 | return 1; |
1028 | 0 | } |
1029 | | |
1030 | | static int |
1031 | | backward_flow_scfi_state (const symbolS *func ATTRIBUTE_UNUSED, gcfgS *gcfg) |
1032 | 0 | { |
1033 | 0 | gbbS **prog_order_bbs; |
1034 | 0 | gbbS **restore_bbs; |
1035 | 0 | gbbS *current_bb; |
1036 | 0 | gbbS *prev_bb; |
1037 | 0 | gbbS *dst_bb; |
1038 | 0 | ginsnS *ginsn; |
1039 | 0 | gedgeS *gedge = NULL; |
1040 | |
|
1041 | 0 | int ret = 0; |
1042 | 0 | uint64_t i, j; |
1043 | | |
1044 | | /* Basic blocks in reverse program order. */ |
1045 | 0 | prog_order_bbs = XCNEWVEC (gbbS *, gcfg->num_gbbs); |
1046 | | /* Basic blocks for which CFI remember op needs to be generated. */ |
1047 | 0 | restore_bbs = XCNEWVEC (gbbS *, gcfg->num_gbbs); |
1048 | |
|
1049 | 0 | gcfg_get_bbs_in_prog_order (gcfg, prog_order_bbs); |
1050 | |
|
1051 | 0 | i = gcfg->num_gbbs - 1; |
1052 | | /* Traverse in reverse program order. */ |
1053 | 0 | while (i > 0) |
1054 | 0 | { |
1055 | 0 | current_bb = prog_order_bbs[i]; |
1056 | 0 | prev_bb = prog_order_bbs[i-1]; |
1057 | 0 | if (cmp_scfi_state (prev_bb->exit_state, current_bb->entry_state)) |
1058 | 0 | { |
1059 | | /* Candidate for .cfi_restore_state found. */ |
1060 | 0 | ginsn = bb_get_first_ginsn (current_bb); |
1061 | 0 | scfi_op_add_cfi_restore_state (ginsn); |
1062 | | /* Memorize current_bb now to find location for its remember state |
1063 | | later. */ |
1064 | 0 | restore_bbs[i] = current_bb; |
1065 | 0 | } |
1066 | 0 | else |
1067 | 0 | { |
1068 | 0 | bb_for_each_edge (current_bb, gedge) |
1069 | 0 | { |
1070 | 0 | dst_bb = gedge->dst_bb; |
1071 | 0 | for (j = 0; j < gcfg->num_gbbs; j++) |
1072 | 0 | if (restore_bbs[j] == dst_bb) |
1073 | 0 | { |
1074 | 0 | ginsn = bb_get_last_ginsn (current_bb); |
1075 | 0 | scfi_op_add_cfi_remember_state (ginsn); |
1076 | | /* Remove the memorised restore_bb from the list. */ |
1077 | 0 | restore_bbs[j] = NULL; |
1078 | 0 | break; |
1079 | 0 | } |
1080 | 0 | } |
1081 | 0 | } |
1082 | 0 | i--; |
1083 | 0 | } |
1084 | | |
1085 | | /* All .cfi_restore_state pseudo-ops must have a corresponding |
1086 | | .cfi_remember_state by now. */ |
1087 | 0 | for (j = 0; j < gcfg->num_gbbs; j++) |
1088 | 0 | if (restore_bbs[j] != NULL) |
1089 | 0 | { |
1090 | 0 | ret = 1; |
1091 | 0 | break; |
1092 | 0 | } |
1093 | |
|
1094 | 0 | free (restore_bbs); |
1095 | 0 | free (prog_order_bbs); |
1096 | |
|
1097 | 0 | return ret; |
1098 | 0 | } |
1099 | | |
1100 | | /* Synthesize DWARF CFI for a function. */ |
1101 | | |
1102 | | int |
1103 | | scfi_synthesize_dw2cfi (const symbolS *func, gcfgS *gcfg, gbbS *root_bb) |
1104 | 0 | { |
1105 | 0 | int ret; |
1106 | 0 | scfi_stateS *init_state; |
1107 | |
|
1108 | 0 | init_state = XCNEW (scfi_stateS); |
1109 | 0 | init_state->traceable_p = true; |
1110 | | |
1111 | | /* Traverse the input GCFG and perform forward flow of information. |
1112 | | Update the scfi_op(s) per ginsn. */ |
1113 | 0 | ret = forward_flow_scfi_state (gcfg, root_bb, init_state); |
1114 | 0 | if (ret) |
1115 | 0 | { |
1116 | 0 | as_bad (_("SCFI: forward pass failed for func '%s'"), S_GET_NAME (func)); |
1117 | 0 | goto end; |
1118 | 0 | } |
1119 | | |
1120 | 0 | ret = backward_flow_scfi_state (func, gcfg); |
1121 | 0 | if (ret) |
1122 | 0 | { |
1123 | 0 | as_bad (_("SCFI: backward pass failed for func '%s'"), S_GET_NAME (func)); |
1124 | 0 | goto end; |
1125 | 0 | } |
1126 | | |
1127 | 0 | end: |
1128 | 0 | free (init_state); |
1129 | 0 | return ret; |
1130 | 0 | } |
1131 | | |
1132 | | static int |
1133 | | handle_scfi_dot_cfi (ginsnS *ginsn) |
1134 | 0 | { |
1135 | 0 | scfi_opS *op; |
1136 | | |
1137 | | /* Nothing to do. */ |
1138 | 0 | if (!ginsn->scfi_ops) |
1139 | 0 | return 0; |
1140 | | |
1141 | 0 | op = *ginsn->scfi_ops; |
1142 | 0 | if (!op) |
1143 | 0 | goto bad; |
1144 | | |
1145 | 0 | while (op) |
1146 | 0 | { |
1147 | 0 | switch (op->dw2cfi_op) |
1148 | 0 | { |
1149 | 0 | case DW_CFA_def_cfa_register: |
1150 | 0 | scfi_dot_cfi (DW_CFA_def_cfa_register, op->loc.base, 0, 0, NULL, |
1151 | 0 | ginsn->sym); |
1152 | 0 | break; |
1153 | 0 | case DW_CFA_def_cfa_offset: |
1154 | 0 | scfi_dot_cfi (DW_CFA_def_cfa_offset, op->loc.base, 0, |
1155 | 0 | op->loc.offset, NULL, ginsn->sym); |
1156 | 0 | break; |
1157 | 0 | case DW_CFA_def_cfa: |
1158 | 0 | scfi_dot_cfi (DW_CFA_def_cfa, op->loc.base, 0, op->loc.offset, |
1159 | 0 | NULL, ginsn->sym); |
1160 | 0 | break; |
1161 | 0 | case DW_CFA_offset: |
1162 | 0 | scfi_dot_cfi (DW_CFA_offset, op->reg, 0, op->loc.offset, NULL, |
1163 | 0 | ginsn->sym); |
1164 | 0 | break; |
1165 | 0 | case DW_CFA_restore: |
1166 | 0 | scfi_dot_cfi (DW_CFA_restore, op->reg, 0, 0, NULL, ginsn->sym); |
1167 | 0 | break; |
1168 | 0 | case DW_CFA_remember_state: |
1169 | 0 | scfi_dot_cfi (DW_CFA_remember_state, 0, 0, 0, NULL, ginsn->sym); |
1170 | 0 | break; |
1171 | 0 | case DW_CFA_restore_state: |
1172 | 0 | scfi_dot_cfi (DW_CFA_restore_state, 0, 0, 0, NULL, ginsn->sym); |
1173 | 0 | break; |
1174 | 0 | case CFI_label: |
1175 | 0 | scfi_dot_cfi (CFI_label, 0, 0, 0, op->op_data->name, ginsn->sym); |
1176 | 0 | free ((char *) op->op_data->name); |
1177 | 0 | break; |
1178 | 0 | case CFI_signal_frame: |
1179 | 0 | scfi_dot_cfi (CFI_signal_frame, 0, 0, 0, NULL, ginsn->sym); |
1180 | 0 | break; |
1181 | 0 | default: |
1182 | 0 | goto bad; |
1183 | 0 | break; |
1184 | 0 | } |
1185 | 0 | op = op->next; |
1186 | 0 | } |
1187 | | |
1188 | 0 | return 0; |
1189 | 0 | bad: |
1190 | 0 | as_bad (_("SCFI: Invalid DWARF CFI opcode data")); |
1191 | 0 | return 1; |
1192 | 0 | } |
1193 | | |
1194 | | /* Emit Synthesized DWARF CFI. */ |
1195 | | |
1196 | | int |
1197 | | scfi_emit_dw2cfi (const symbolS *func) |
1198 | 0 | { |
1199 | 0 | struct frch_ginsn_data *frch_gdata; |
1200 | 0 | ginsnS* ginsn = NULL; |
1201 | |
|
1202 | 0 | frch_gdata = frchain_now->frch_ginsn_data; |
1203 | 0 | ginsn = frch_gdata->gins_rootP; |
1204 | |
|
1205 | 0 | while (ginsn) |
1206 | 0 | { |
1207 | 0 | switch (ginsn->type) |
1208 | 0 | { |
1209 | 0 | case GINSN_TYPE_SYMBOL: |
1210 | | /* .cfi_startproc and .cfi_endproc pseudo-ops. */ |
1211 | 0 | if (GINSN_F_FUNC_BEGIN_P (ginsn)) |
1212 | 0 | { |
1213 | 0 | scfi_dot_cfi_startproc (frch_gdata->start_addr); |
1214 | 0 | break; |
1215 | 0 | } |
1216 | 0 | else if (GINSN_F_FUNC_END_P (ginsn)) |
1217 | 0 | { |
1218 | 0 | scfi_dot_cfi_endproc (ginsn->sym); |
1219 | 0 | break; |
1220 | 0 | } |
1221 | | /* Fall through. */ |
1222 | 0 | case GINSN_TYPE_ADD: |
1223 | 0 | case GINSN_TYPE_AND: |
1224 | 0 | case GINSN_TYPE_CALL: |
1225 | 0 | case GINSN_TYPE_JUMP: |
1226 | 0 | case GINSN_TYPE_JUMP_COND: |
1227 | 0 | case GINSN_TYPE_MOV: |
1228 | 0 | case GINSN_TYPE_LOAD: |
1229 | 0 | case GINSN_TYPE_PHANTOM: |
1230 | 0 | case GINSN_TYPE_STORE: |
1231 | 0 | case GINSN_TYPE_SUB: |
1232 | 0 | case GINSN_TYPE_OTHER: |
1233 | 0 | case GINSN_TYPE_RETURN: |
1234 | | |
1235 | | /* For all other SCFI ops, invoke the handler. */ |
1236 | 0 | if (ginsn->scfi_ops) |
1237 | 0 | handle_scfi_dot_cfi (ginsn); |
1238 | 0 | break; |
1239 | | |
1240 | 0 | default: |
1241 | | /* No other GINSN_TYPE_* expected. */ |
1242 | 0 | as_bad (_("SCFI: bad ginsn for func '%s'"), |
1243 | 0 | S_GET_NAME (func)); |
1244 | 0 | break; |
1245 | 0 | } |
1246 | 0 | ginsn = ginsn->next; |
1247 | 0 | } |
1248 | 0 | return 0; |
1249 | 0 | } |
1250 | | |
1251 | | #else |
1252 | | |
1253 | | int |
1254 | | scfi_emit_dw2cfi (const symbolS *func ATTRIBUTE_UNUSED) |
1255 | | { |
1256 | | as_bad (_("SCFI: unsupported for target")); |
1257 | | return 1; |
1258 | | } |
1259 | | |
1260 | | int |
1261 | | scfi_synthesize_dw2cfi (const symbolS *func ATTRIBUTE_UNUSED, |
1262 | | gcfgS *gcfg ATTRIBUTE_UNUSED, |
1263 | | gbbS *root_bb ATTRIBUTE_UNUSED) |
1264 | | { |
1265 | | as_bad (_("SCFI: unsupported for target")); |
1266 | | return 1; |
1267 | | } |
1268 | | |
1269 | | #endif /* defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN). */ |