/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 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 | 0 | state->regs[reg].base = reg; |
227 | 0 | state->regs[reg].offset = 0; |
228 | | /* PS: the register may still be on stack much after the restore, but the |
229 | | SCFI state keeps the state as 'in register'. */ |
230 | 0 | state->regs[reg].state = CFI_IN_REG; |
231 | 0 | } |
232 | | |
233 | | /* Identify if the given GAS instruction GINSN saves a register |
234 | | (of interest) on stack. */ |
235 | | |
236 | | static bool |
237 | | ginsn_scfi_save_reg_p (ginsnS *ginsn, scfi_stateS *state) |
238 | 0 | { |
239 | 0 | bool save_reg_p = false; |
240 | 0 | struct ginsn_src *src; |
241 | 0 | struct ginsn_dst *dst; |
242 | |
|
243 | 0 | src = ginsn_get_src1 (ginsn); |
244 | 0 | dst = ginsn_get_dst (ginsn); |
245 | | |
246 | | /* The first save to stack of callee-saved register is deemed as |
247 | | register save. */ |
248 | 0 | if (!ginsn_track_reg_p (ginsn_get_src_reg (src), GINSN_GEN_SCFI) |
249 | 0 | || state->regs[ginsn_get_src_reg (src)].state == CFI_ON_STACK) |
250 | 0 | return save_reg_p; |
251 | | |
252 | | /* A register save insn may be an indirect mov. */ |
253 | 0 | if (ginsn->type == GINSN_TYPE_MOV |
254 | 0 | && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT |
255 | 0 | && (ginsn_get_dst_reg (dst) == REG_SP |
256 | 0 | || (ginsn_get_dst_reg (dst) == REG_FP |
257 | 0 | && state->regs[REG_CFA].base == REG_FP))) |
258 | 0 | save_reg_p = true; |
259 | | /* or an explicit store to stack. */ |
260 | 0 | else if (ginsn->type == GINSN_TYPE_STORE |
261 | 0 | && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT |
262 | 0 | && ginsn_get_dst_reg (dst) == REG_SP) |
263 | 0 | save_reg_p = true; |
264 | |
|
265 | 0 | return save_reg_p; |
266 | 0 | } |
267 | | |
268 | | /* Identify if the given GAS instruction GINSN restores a register |
269 | | (of interest) on stack. */ |
270 | | |
271 | | static bool |
272 | | ginsn_scfi_restore_reg_p (ginsnS *ginsn, scfi_stateS *state) |
273 | 0 | { |
274 | 0 | bool restore_reg_p = false; |
275 | 0 | struct ginsn_dst *dst; |
276 | 0 | struct ginsn_src *src1; |
277 | |
|
278 | 0 | dst = ginsn_get_dst (ginsn); |
279 | 0 | src1 = ginsn_get_src1 (ginsn); |
280 | |
|
281 | 0 | if (!ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI)) |
282 | 0 | return restore_reg_p; |
283 | | |
284 | | /* A register restore insn may be an indirect mov... */ |
285 | 0 | if (ginsn->type == GINSN_TYPE_MOV |
286 | 0 | && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT |
287 | 0 | && (ginsn_get_src_reg (src1) == REG_SP |
288 | 0 | || (ginsn_get_src_reg (src1) == REG_FP |
289 | 0 | && state->regs[REG_CFA].base == REG_FP))) |
290 | 0 | restore_reg_p = true; |
291 | | /* ...or an explicit load from stack. */ |
292 | 0 | else if (ginsn->type == GINSN_TYPE_LOAD |
293 | 0 | && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT |
294 | 0 | && ginsn_get_src_reg (src1) == REG_SP) |
295 | 0 | restore_reg_p = true; |
296 | |
|
297 | 0 | return restore_reg_p; |
298 | 0 | } |
299 | | |
300 | | /* Append the SCFI operation OP to the list of SCFI operations in the |
301 | | given GINSN. */ |
302 | | |
303 | | static int |
304 | | ginsn_append_scfi_op (ginsnS *ginsn, scfi_opS *op) |
305 | 0 | { |
306 | 0 | scfi_opS *sop; |
307 | |
|
308 | 0 | if (!ginsn || !op) |
309 | 0 | return 1; |
310 | | |
311 | 0 | if (!ginsn->scfi_ops) |
312 | 0 | { |
313 | 0 | ginsn->scfi_ops = XCNEW (scfi_opS *); |
314 | 0 | *ginsn->scfi_ops = op; |
315 | 0 | } |
316 | 0 | else |
317 | 0 | { |
318 | | /* Add to tail. Most ginsns have a single SCFI operation, |
319 | | so this traversal for every insertion is acceptable for now. */ |
320 | 0 | sop = *ginsn->scfi_ops; |
321 | 0 | while (sop->next) |
322 | 0 | sop = sop->next; |
323 | |
|
324 | 0 | sop->next = op; |
325 | 0 | } |
326 | 0 | ginsn->num_scfi_ops++; |
327 | |
|
328 | 0 | return 0; |
329 | 0 | } |
330 | | |
331 | | static void |
332 | | scfi_op_add_def_cfa_reg (scfi_stateS *state, ginsnS *ginsn, unsigned int reg) |
333 | 0 | { |
334 | 0 | scfi_opS *op = NULL; |
335 | |
|
336 | 0 | state->regs[REG_CFA].base = reg; |
337 | |
|
338 | 0 | op = init_scfi_op (); |
339 | |
|
340 | 0 | op->dw2cfi_op = DW_CFA_def_cfa_register; |
341 | 0 | op->reg = REG_CFA; |
342 | 0 | op->loc = state->regs[REG_CFA]; |
343 | |
|
344 | 0 | ginsn_append_scfi_op (ginsn, op); |
345 | 0 | } |
346 | | |
347 | | static void |
348 | | scfi_op_add_cfa_offset_inc (scfi_stateS *state, ginsnS *ginsn, offsetT num) |
349 | 0 | { |
350 | 0 | scfi_opS *op = NULL; |
351 | |
|
352 | 0 | state->regs[REG_CFA].offset -= num; |
353 | |
|
354 | 0 | op = init_scfi_op (); |
355 | |
|
356 | 0 | op->dw2cfi_op = DW_CFA_def_cfa_offset; |
357 | 0 | op->reg = REG_CFA; |
358 | 0 | op->loc = state->regs[REG_CFA]; |
359 | |
|
360 | 0 | ginsn_append_scfi_op (ginsn, op); |
361 | 0 | } |
362 | | |
363 | | static void |
364 | | scfi_op_add_cfa_offset_dec (scfi_stateS *state, ginsnS *ginsn, offsetT num) |
365 | 0 | { |
366 | 0 | scfi_opS *op = NULL; |
367 | |
|
368 | 0 | state->regs[REG_CFA].offset += num; |
369 | |
|
370 | 0 | op = init_scfi_op (); |
371 | |
|
372 | 0 | op->dw2cfi_op = DW_CFA_def_cfa_offset; |
373 | 0 | op->reg = REG_CFA; |
374 | 0 | op->loc = state->regs[REG_CFA]; |
375 | |
|
376 | 0 | ginsn_append_scfi_op (ginsn, op); |
377 | 0 | } |
378 | | |
379 | | static void |
380 | | scfi_op_add_def_cfa (scfi_stateS *state, ginsnS *ginsn, unsigned int reg, |
381 | | offsetT num) |
382 | 0 | { |
383 | 0 | scfi_opS *op = NULL; |
384 | |
|
385 | 0 | state->regs[REG_CFA].base = reg; |
386 | 0 | state->regs[REG_CFA].offset = num; |
387 | |
|
388 | 0 | op = init_scfi_op (); |
389 | |
|
390 | 0 | op->dw2cfi_op = DW_CFA_def_cfa; |
391 | 0 | op->reg = REG_CFA; |
392 | 0 | op->loc = state->regs[REG_CFA]; |
393 | |
|
394 | 0 | ginsn_append_scfi_op (ginsn, op); |
395 | 0 | } |
396 | | |
397 | | static void |
398 | | scfi_op_add_cfi_offset (scfi_stateS *state, ginsnS *ginsn, unsigned int reg) |
399 | 0 | { |
400 | 0 | scfi_opS *op = NULL; |
401 | |
|
402 | 0 | op = init_scfi_op (); |
403 | |
|
404 | 0 | op->dw2cfi_op = DW_CFA_offset; |
405 | 0 | op->reg = reg; |
406 | 0 | op->loc = state->regs[reg]; |
407 | |
|
408 | 0 | ginsn_append_scfi_op (ginsn, op); |
409 | 0 | } |
410 | | |
411 | | static void |
412 | | scfi_op_add_cfa_restore (ginsnS *ginsn, unsigned int reg) |
413 | 0 | { |
414 | 0 | scfi_opS *op = NULL; |
415 | |
|
416 | 0 | op = init_scfi_op (); |
417 | |
|
418 | 0 | op->dw2cfi_op = DW_CFA_restore; |
419 | 0 | op->reg = reg; |
420 | 0 | op->loc.base = REG_INVALID; |
421 | 0 | op->loc.offset = 0; |
422 | |
|
423 | 0 | ginsn_append_scfi_op (ginsn, op); |
424 | 0 | } |
425 | | |
426 | | static void |
427 | | scfi_op_add_cfi_remember_state (ginsnS *ginsn) |
428 | 0 | { |
429 | 0 | scfi_opS *op = NULL; |
430 | |
|
431 | 0 | op = init_scfi_op (); |
432 | |
|
433 | 0 | op->dw2cfi_op = DW_CFA_remember_state; |
434 | |
|
435 | 0 | ginsn_append_scfi_op (ginsn, op); |
436 | 0 | } |
437 | | |
438 | | static void |
439 | | scfi_op_add_cfi_restore_state (ginsnS *ginsn) |
440 | 0 | { |
441 | 0 | scfi_opS *op = NULL; |
442 | |
|
443 | 0 | op = init_scfi_op (); |
444 | |
|
445 | 0 | op->dw2cfi_op = DW_CFA_restore_state; |
446 | | |
447 | | /* FIXME - add to the beginning of the scfi_ops. */ |
448 | 0 | ginsn_append_scfi_op (ginsn, op); |
449 | 0 | } |
450 | | |
451 | | void |
452 | | scfi_op_add_cfi_label (ginsnS *ginsn, const char *name) |
453 | 0 | { |
454 | 0 | scfi_opS *op = NULL; |
455 | |
|
456 | 0 | op = init_scfi_op (); |
457 | 0 | op->dw2cfi_op = CFI_label; |
458 | 0 | op->op_data = XCNEW (scfi_op_dataS); |
459 | 0 | op->op_data->name = name; |
460 | |
|
461 | 0 | ginsn_append_scfi_op (ginsn, op); |
462 | 0 | } |
463 | | |
464 | | void |
465 | | scfi_op_add_signal_frame (ginsnS *ginsn) |
466 | 0 | { |
467 | 0 | scfi_opS *op = NULL; |
468 | |
|
469 | 0 | op = init_scfi_op (); |
470 | 0 | op->dw2cfi_op = CFI_signal_frame; |
471 | |
|
472 | 0 | ginsn_append_scfi_op (ginsn, op); |
473 | 0 | } |
474 | | |
475 | | static int |
476 | | verify_heuristic_traceable_reg_fp (ginsnS *ginsn, scfi_stateS *state) |
477 | 0 | { |
478 | | /* The function uses this variable to issue error to user right away. */ |
479 | 0 | int fp_traceable_p = 0; |
480 | 0 | struct ginsn_dst *dst; |
481 | 0 | struct ginsn_src *src1; |
482 | 0 | struct ginsn_src *src2; |
483 | |
|
484 | 0 | src1 = ginsn_get_src1 (ginsn); |
485 | 0 | src2 = ginsn_get_src2 (ginsn); |
486 | 0 | dst = ginsn_get_dst (ginsn); |
487 | | |
488 | | /* Stack manipulation can be done in a variety of ways. A program may |
489 | | allocate stack statically or may perform dynamic stack allocation in |
490 | | the prologue. |
491 | | |
492 | | The SCFI machinery in GAS is based on some heuristics: |
493 | | |
494 | | - Rule 3 If the base register for CFA tracking is REG_FP, the program |
495 | | must not clobber REG_FP, unless it is for switch to REG_SP based CFA |
496 | | tracking (via say, a pop %rbp in X86). */ |
497 | | |
498 | | /* Check all applicable instructions with dest REG_FP, when the CFA base |
499 | | register is REG_FP. */ |
500 | 0 | if (state->regs[REG_CFA].base == REG_FP && ginsn_get_dst_reg (dst) == REG_FP) |
501 | 0 | { |
502 | | /* Excuse the add/sub with imm usage: They are OK. */ |
503 | 0 | if ((ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB) |
504 | 0 | && ginsn_get_src_reg (src1) == REG_FP |
505 | 0 | && ginsn_get_src_type (src2) == GINSN_SRC_IMM) |
506 | 0 | fp_traceable_p = 0; |
507 | | /* REG_FP restore is OK too. */ |
508 | 0 | else if (ginsn->type == GINSN_TYPE_LOAD) |
509 | 0 | fp_traceable_p = 0; |
510 | | /* mov's to memory with REG_FP base do not make REG_FP untraceable. */ |
511 | 0 | else if (ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT |
512 | 0 | && (ginsn->type == GINSN_TYPE_MOV |
513 | 0 | || ginsn->type == GINSN_TYPE_STORE)) |
514 | 0 | fp_traceable_p = 0; |
515 | | /* Manipulations of the values possibly on stack are OK too. */ |
516 | 0 | else if ((ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB |
517 | 0 | || ginsn->type == GINSN_TYPE_AND) |
518 | 0 | && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT) |
519 | 0 | fp_traceable_p = 0; |
520 | | /* All other ginsns with REG_FP as destination make REG_FP not |
521 | | traceable. */ |
522 | 0 | else |
523 | 0 | fp_traceable_p = 1; |
524 | 0 | } |
525 | |
|
526 | 0 | if (fp_traceable_p) |
527 | 0 | as_bad_where (ginsn->file, ginsn->line, |
528 | 0 | _("SCFI: usage of REG_FP as scratch not supported")); |
529 | |
|
530 | 0 | return fp_traceable_p; |
531 | 0 | } |
532 | | |
533 | | static int |
534 | | verify_heuristic_traceable_stack_manipulation (ginsnS *ginsn, |
535 | | scfi_stateS *state) |
536 | 0 | { |
537 | | /* The function uses this variable to issue error to user right away. */ |
538 | 0 | int sp_untraceable_p = 0; |
539 | 0 | bool possibly_untraceable = false; |
540 | 0 | struct ginsn_dst *dst; |
541 | 0 | struct ginsn_src *src1; |
542 | 0 | struct ginsn_src *src2; |
543 | |
|
544 | 0 | src1 = ginsn_get_src1 (ginsn); |
545 | 0 | src2 = ginsn_get_src2 (ginsn); |
546 | 0 | dst = ginsn_get_dst (ginsn); |
547 | | |
548 | | /* Stack manipulation can be done in a variety of ways. A program may |
549 | | allocate stack statically in prologue or may need to do dynamic stack |
550 | | allocation. |
551 | | |
552 | | The SCFI machinery in GAS is based on some heuristics: |
553 | | |
554 | | - Rule 1 The base register for CFA tracking may be either REG_SP or |
555 | | REG_FP. |
556 | | |
557 | | - Rule 2 If the base register for CFA tracking is REG_SP, the precise |
558 | | amount of stack usage (and hence, the value of rsp) must be known at |
559 | | all times. */ |
560 | |
|
561 | 0 | if (ginsn->type == GINSN_TYPE_MOV |
562 | 0 | && ginsn_get_dst_type (dst) == GINSN_DST_REG |
563 | 0 | && ginsn_get_dst_reg (dst) == REG_SP |
564 | 0 | && ginsn_get_src_type (src1) == GINSN_SRC_REG |
565 | | /* Exclude mov %rbp, %rsp from this check. */ |
566 | 0 | && ginsn_get_src_reg (src1) != REG_FP) |
567 | 0 | { |
568 | | /* mov %reg, %rsp. */ |
569 | | /* A previous mov %rsp, %reg must have been seen earlier for this to be |
570 | | an OK for stack manipulation. */ |
571 | 0 | if (state->scratch[ginsn_get_src_reg (src1)].base != REG_CFA |
572 | 0 | || state->scratch[ginsn_get_src_reg (src1)].state != CFI_IN_REG) |
573 | 0 | { |
574 | 0 | possibly_untraceable = true; |
575 | 0 | } |
576 | 0 | } |
577 | | /* Check add/sub/and insn usage when CFA base register is REG_SP. |
578 | | Any stack size manipulation, including stack realignment is not allowed |
579 | | if CFA base register is REG_SP. */ |
580 | 0 | else if (ginsn_get_dst_type (dst) == GINSN_DST_REG |
581 | 0 | && ginsn_get_dst_reg (dst) == REG_SP |
582 | 0 | && (((ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB) |
583 | 0 | && ginsn_get_src_type (src2) != GINSN_SRC_IMM) |
584 | 0 | || ginsn->type == GINSN_TYPE_AND |
585 | 0 | || ginsn->type == GINSN_TYPE_OTHER)) |
586 | 0 | possibly_untraceable = true; |
587 | | /* If a register save operation is seen when REG_SP is untraceable, |
588 | | CFI cannot be synthesized for register saves, hence bail out. */ |
589 | 0 | else if (ginsn_scfi_save_reg_p (ginsn, state) && !state->traceable_p) |
590 | 0 | { |
591 | 0 | sp_untraceable_p = 1; |
592 | | /* If, however, the register save is an REG_FP-based, indirect mov |
593 | | like: mov reg, disp(%rbp) and CFA base register is REG_BP, |
594 | | untraceable REG_SP is not a problem. */ |
595 | 0 | if (ginsn->type == GINSN_TYPE_MOV |
596 | 0 | && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT |
597 | 0 | && (ginsn_get_dst_reg (dst) == REG_FP |
598 | 0 | && state->regs[REG_CFA].base == REG_FP)) |
599 | 0 | sp_untraceable_p = 0; |
600 | 0 | } |
601 | 0 | else if (ginsn_scfi_restore_reg_p (ginsn, state) && !state->traceable_p) |
602 | 0 | { |
603 | 0 | if (ginsn->type == GINSN_TYPE_MOV |
604 | 0 | && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT |
605 | 0 | && (ginsn_get_src_reg (src1) == REG_SP |
606 | 0 | || (ginsn_get_src_reg (src1) == REG_FP |
607 | 0 | && state->regs[REG_CFA].base != REG_FP))) |
608 | 0 | sp_untraceable_p = 1; |
609 | 0 | } |
610 | |
|
611 | 0 | if (possibly_untraceable) |
612 | 0 | { |
613 | | /* See Rule 2. For SP-based CFA, this makes CFA tracking not possible. |
614 | | Propagate now to caller. */ |
615 | 0 | if (state->regs[REG_CFA].base == REG_SP) |
616 | 0 | sp_untraceable_p = 1; |
617 | 0 | else if (state->traceable_p) |
618 | 0 | { |
619 | | /* An extension of Rule 2. |
620 | | For FP-based CFA, this may be a problem *if* certain specific |
621 | | changes to the SCFI state are seen beyond this point, e.g., |
622 | | register save / restore from stack. */ |
623 | 0 | gas_assert (state->regs[REG_CFA].base == REG_FP); |
624 | | /* Simply make a note in the SCFI state object for now and |
625 | | continue. Indicate an error when register save / restore |
626 | | for callee-saved registers is seen. */ |
627 | 0 | sp_untraceable_p = 0; |
628 | 0 | state->traceable_p = false; |
629 | 0 | } |
630 | 0 | } |
631 | | |
632 | 0 | if (sp_untraceable_p) |
633 | 0 | as_bad_where (ginsn->file, ginsn->line, |
634 | 0 | _("SCFI: unsupported stack manipulation pattern")); |
635 | |
|
636 | 0 | return sp_untraceable_p; |
637 | 0 | } |
638 | | |
639 | | static int |
640 | | verify_heuristic_symmetrical_restore_reg (scfi_stateS *state, ginsnS* ginsn) |
641 | 0 | { |
642 | 0 | int sym_restore = true; |
643 | 0 | offsetT expected_offset = 0; |
644 | 0 | struct ginsn_src *src1; |
645 | 0 | struct ginsn_dst *dst; |
646 | 0 | unsigned int reg; |
647 | | |
648 | | /* Rule 4: Save and Restore of callee-saved registers must be symmetrical. |
649 | | It is expected that value of the saved register is restored correctly. |
650 | | E.g., |
651 | | push reg1 |
652 | | push reg2 |
653 | | ... |
654 | | body of func which uses reg1 , reg2 as scratch, |
655 | | and may be even spills them to stack. |
656 | | ... |
657 | | pop reg2 |
658 | | pop reg1 |
659 | | It is difficult to verify the Rule 4 in all cases. For the SCFI machinery, |
660 | | it is difficult to separate prologue-epilogue from the body of the function |
661 | | |
662 | | Hence, the SCFI machinery at this time, should only warn on an asymetrical |
663 | | restore. */ |
664 | 0 | src1 = ginsn_get_src1 (ginsn); |
665 | 0 | dst = ginsn_get_dst (ginsn); |
666 | 0 | reg = ginsn_get_dst_reg (dst); |
667 | | |
668 | | /* For non callee-saved registers, calling the API is meaningless. */ |
669 | 0 | if (!ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI)) |
670 | 0 | return sym_restore; |
671 | | |
672 | | /* The register must have been saved on stack, for sure. */ |
673 | 0 | gas_assert (state->regs[reg].state == CFI_ON_STACK); |
674 | 0 | gas_assert (state->regs[reg].base == REG_CFA); |
675 | | |
676 | 0 | if ((ginsn->type == GINSN_TYPE_MOV |
677 | 0 | || ginsn->type == GINSN_TYPE_LOAD) |
678 | 0 | && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT |
679 | 0 | && (ginsn_get_src_reg (src1) == REG_SP |
680 | 0 | || (ginsn_get_src_reg (src1) == REG_FP |
681 | 0 | && state->regs[REG_CFA].base == REG_FP))) |
682 | 0 | { |
683 | | /* mov disp(%rsp), reg. */ |
684 | | /* mov disp(%rbp), reg. */ |
685 | 0 | expected_offset = (((ginsn_get_src_reg (src1) == REG_SP) |
686 | 0 | ? -state->stack_size |
687 | 0 | : state->regs[REG_FP].offset) |
688 | 0 | + ginsn_get_src_disp (src1)); |
689 | 0 | } |
690 | |
|
691 | 0 | sym_restore = (expected_offset == state->regs[reg].offset); |
692 | |
|
693 | 0 | return sym_restore; |
694 | 0 | } |
695 | | |
696 | | /* Perform symbolic execution of the GINSN and update its list of scfi_ops. |
697 | | scfi_ops are later used to directly generate the DWARF CFI directives. |
698 | | Also update the SCFI state object STATE for the caller. */ |
699 | | |
700 | | static int |
701 | | gen_scfi_ops (ginsnS *ginsn, scfi_stateS *state) |
702 | 0 | { |
703 | 0 | int ret = 0; |
704 | 0 | offsetT offset; |
705 | 0 | struct ginsn_src *src1; |
706 | 0 | struct ginsn_src *src2; |
707 | 0 | struct ginsn_dst *dst; |
708 | |
|
709 | 0 | if (!ginsn || !state) |
710 | 0 | ret = 1; |
711 | | |
712 | | /* For the first ginsn (of type GINSN_TYPE_SYMBOL) in the gbb, generate |
713 | | the SCFI op with DW_CFA_def_cfa. Note that the register and offset are |
714 | | target-specific. */ |
715 | 0 | if (GINSN_F_FUNC_BEGIN_P (ginsn)) |
716 | 0 | { |
717 | 0 | scfi_op_add_def_cfa (state, ginsn, REG_SP, SCFI_INIT_CFA_OFFSET); |
718 | 0 | state->stack_size += SCFI_INIT_CFA_OFFSET; |
719 | 0 | return ret; |
720 | 0 | } |
721 | | |
722 | 0 | src1 = ginsn_get_src1 (ginsn); |
723 | 0 | src2 = ginsn_get_src2 (ginsn); |
724 | 0 | dst = ginsn_get_dst (ginsn); |
725 | |
|
726 | 0 | ret = verify_heuristic_traceable_stack_manipulation (ginsn, state); |
727 | 0 | if (ret) |
728 | 0 | return ret; |
729 | | |
730 | 0 | ret = verify_heuristic_traceable_reg_fp (ginsn, state); |
731 | 0 | if (ret) |
732 | 0 | return ret; |
733 | | |
734 | 0 | switch (ginsn->dst.type) |
735 | 0 | { |
736 | 0 | case GINSN_DST_REG: |
737 | 0 | switch (ginsn->type) |
738 | 0 | { |
739 | 0 | case GINSN_TYPE_MOV: |
740 | 0 | if (ginsn_get_src_type (src1) == GINSN_SRC_REG |
741 | 0 | && ginsn_get_src_reg (src1) == REG_SP |
742 | 0 | && ginsn_get_dst_reg (dst) == REG_FP |
743 | 0 | && state->regs[REG_CFA].base == REG_SP) |
744 | 0 | { |
745 | | /* mov %rsp, %rbp. */ |
746 | 0 | scfi_op_add_def_cfa_reg (state, ginsn, ginsn_get_dst_reg (dst)); |
747 | 0 | } |
748 | 0 | else if (ginsn_get_src_type (src1) == GINSN_SRC_REG |
749 | 0 | && ginsn_get_src_reg (src1) == REG_FP |
750 | 0 | && ginsn_get_dst_reg (dst) == REG_SP |
751 | 0 | && state->regs[REG_CFA].base == REG_FP) |
752 | 0 | { |
753 | | /* mov %rbp, %rsp. */ |
754 | 0 | state->stack_size = -state->regs[REG_FP].offset; |
755 | 0 | scfi_op_add_def_cfa_reg (state, ginsn, ginsn_get_dst_reg (dst)); |
756 | 0 | state->traceable_p = true; |
757 | 0 | } |
758 | 0 | else if (ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT |
759 | 0 | && (ginsn_get_src_reg (src1) == REG_SP |
760 | 0 | || ginsn_get_src_reg (src1) == REG_FP) |
761 | 0 | && ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI)) |
762 | 0 | { |
763 | | /* mov disp(%rsp), reg. */ |
764 | | /* mov disp(%rbp), reg. */ |
765 | 0 | if (verify_heuristic_symmetrical_restore_reg (state, ginsn)) |
766 | 0 | { |
767 | 0 | scfi_state_restore_reg (state, ginsn_get_dst_reg (dst)); |
768 | 0 | scfi_op_add_cfa_restore (ginsn, ginsn_get_dst_reg (dst)); |
769 | 0 | } |
770 | 0 | else |
771 | 0 | as_warn_where (ginsn->file, ginsn->line, |
772 | 0 | _("SCFI: asymetrical register restore")); |
773 | 0 | } |
774 | 0 | else if (ginsn_get_src_type (src1) == GINSN_SRC_REG |
775 | 0 | && ginsn_get_dst_type (dst) == GINSN_DST_REG |
776 | 0 | && ginsn_get_src_reg (src1) == REG_SP) |
777 | 0 | { |
778 | | /* mov %rsp, %reg. */ |
779 | | /* The value of rsp is taken directly from state->stack_size. |
780 | | IMP: The workflow in gen_scfi_ops must keep it updated. |
781 | | PS: Not taking the value from state->scratch[REG_SP] is |
782 | | intentional. */ |
783 | 0 | state->scratch[ginsn_get_dst_reg (dst)].base = REG_CFA; |
784 | 0 | state->scratch[ginsn_get_dst_reg (dst)].offset = -state->stack_size; |
785 | 0 | state->scratch[ginsn_get_dst_reg (dst)].state = CFI_IN_REG; |
786 | 0 | } |
787 | 0 | else if (ginsn_get_src_type (src1) == GINSN_SRC_REG |
788 | 0 | && ginsn_get_dst_type (dst) == GINSN_DST_REG |
789 | 0 | && ginsn_get_dst_reg (dst) == REG_SP) |
790 | 0 | { |
791 | | /* mov %reg, %rsp. */ |
792 | | /* Keep the value of REG_SP updated. */ |
793 | 0 | if (state->scratch[ginsn_get_src_reg (src1)].state == CFI_IN_REG) |
794 | 0 | { |
795 | 0 | state->stack_size = -state->scratch[ginsn_get_src_reg (src1)].offset; |
796 | 0 | state->traceable_p = true; |
797 | 0 | } |
798 | | # if 0 |
799 | | scfi_state_update_reg (state, ginsn_get_dst_reg (dst), |
800 | | state->scratch[ginsn_get_src_reg (src1)].base, |
801 | | state->scratch[ginsn_get_src_reg (src1)].offset); |
802 | | #endif |
803 | |
|
804 | 0 | } |
805 | 0 | break; |
806 | 0 | case GINSN_TYPE_SUB: |
807 | 0 | if (ginsn_get_src_reg (src1) == REG_SP |
808 | 0 | && ginsn_get_dst_reg (dst) == REG_SP) |
809 | 0 | { |
810 | | /* Stack inc/dec offset, when generated due to stack push and pop is |
811 | | target-specific. Use the value encoded in the ginsn. */ |
812 | 0 | state->stack_size += ginsn_get_src_imm (src2); |
813 | 0 | if (state->regs[REG_CFA].base == REG_SP) |
814 | 0 | { |
815 | | /* push reg. */ |
816 | 0 | scfi_op_add_cfa_offset_dec (state, ginsn, ginsn_get_src_imm (src2)); |
817 | 0 | } |
818 | 0 | } |
819 | 0 | break; |
820 | 0 | case GINSN_TYPE_ADD: |
821 | 0 | if (ginsn_get_src_reg (src1) == REG_SP |
822 | 0 | && ginsn_get_dst_reg (dst) == REG_SP) |
823 | 0 | { |
824 | | /* Stack inc/dec offset is target-specific. Use the value |
825 | | encoded in the ginsn. */ |
826 | 0 | state->stack_size -= ginsn_get_src_imm (src2); |
827 | | /* pop %reg affects CFA offset only if CFA is currently |
828 | | stack-pointer based. */ |
829 | 0 | if (state->regs[REG_CFA].base == REG_SP) |
830 | 0 | { |
831 | 0 | scfi_op_add_cfa_offset_inc (state, ginsn, ginsn_get_src_imm (src2)); |
832 | 0 | } |
833 | 0 | } |
834 | 0 | else if (ginsn_get_src_reg (src1) == REG_FP |
835 | 0 | && ginsn_get_dst_reg (dst) == REG_SP |
836 | 0 | && state->regs[REG_CFA].base == REG_FP) |
837 | 0 | { |
838 | | /* FIXME - what is this for ? */ |
839 | 0 | state->stack_size = 0 - (state->regs[REG_FP].offset + ginsn_get_src_imm (src2)); |
840 | 0 | } |
841 | 0 | break; |
842 | 0 | case GINSN_TYPE_LOAD: |
843 | | /* If this is a load from stack. */ |
844 | 0 | if (ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT |
845 | 0 | && (ginsn_get_src_reg (src1) == REG_SP |
846 | 0 | || (ginsn_get_src_reg (src1) == REG_FP |
847 | 0 | && state->regs[REG_CFA].base == REG_FP))) |
848 | 0 | { |
849 | | /* pop %rbp when CFA tracking is REG_FP based. */ |
850 | 0 | if (ginsn_get_dst_reg (dst) == REG_FP |
851 | 0 | && state->regs[REG_CFA].base == REG_FP) |
852 | 0 | { |
853 | 0 | scfi_op_add_def_cfa_reg (state, ginsn, REG_SP); |
854 | 0 | if (state->regs[REG_CFA].offset != state->stack_size) |
855 | 0 | scfi_op_add_cfa_offset_inc (state, ginsn, |
856 | 0 | (state->regs[REG_CFA].offset - state->stack_size)); |
857 | 0 | } |
858 | 0 | if (ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI)) |
859 | 0 | { |
860 | 0 | if (verify_heuristic_symmetrical_restore_reg (state, ginsn)) |
861 | 0 | { |
862 | 0 | scfi_state_restore_reg (state, ginsn_get_dst_reg (dst)); |
863 | 0 | scfi_op_add_cfa_restore (ginsn, ginsn_get_dst_reg (dst)); |
864 | 0 | } |
865 | 0 | else |
866 | 0 | as_warn_where (ginsn->file, ginsn->line, |
867 | 0 | _("SCFI: asymetrical register restore")); |
868 | 0 | } |
869 | 0 | } |
870 | 0 | break; |
871 | 0 | default: |
872 | 0 | break; |
873 | 0 | } |
874 | 0 | break; |
875 | | |
876 | 0 | case GINSN_DST_INDIRECT: |
877 | | /* Some operations with an indirect access to memory (or even to stack) |
878 | | may still be uninteresting for SCFI purpose (e.g, addl %edx, -32(%rsp) |
879 | | in x86). In case of x86_64, these can neither be a register |
880 | | save / unsave, nor can alter the stack size. |
881 | | PS: This condition may need to be revisited for other arches. */ |
882 | 0 | if (ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB |
883 | 0 | || ginsn->type == GINSN_TYPE_AND) |
884 | 0 | break; |
885 | 0 | gas_assert (ginsn->type == GINSN_TYPE_MOV |
886 | 0 | || ginsn->type == GINSN_TYPE_STORE |
887 | 0 | || ginsn->type == GINSN_TYPE_LOAD); |
888 | | /* mov reg, disp(%rbp) */ |
889 | | /* mov reg, disp(%rsp) */ |
890 | 0 | if (ginsn_scfi_save_reg_p (ginsn, state)) |
891 | 0 | { |
892 | 0 | if (ginsn_get_dst_reg (dst) == REG_SP) |
893 | 0 | { |
894 | | /* mov reg, disp(%rsp) */ |
895 | 0 | offset = 0 - state->stack_size + ginsn_get_dst_disp (dst); |
896 | 0 | scfi_state_save_reg (state, ginsn_get_src_reg (src1), REG_CFA, offset); |
897 | 0 | scfi_op_add_cfi_offset (state, ginsn, ginsn_get_src_reg (src1)); |
898 | 0 | } |
899 | 0 | else if (ginsn_get_dst_reg (dst) == REG_FP) |
900 | 0 | { |
901 | 0 | gas_assert (state->regs[REG_CFA].base == REG_FP); |
902 | | /* mov reg, disp(%rbp) */ |
903 | 0 | offset = 0 - state->regs[REG_CFA].offset + ginsn_get_dst_disp (dst); |
904 | 0 | scfi_state_save_reg (state, ginsn_get_src_reg (src1), REG_CFA, offset); |
905 | 0 | scfi_op_add_cfi_offset (state, ginsn, ginsn_get_src_reg (src1)); |
906 | 0 | } |
907 | 0 | } |
908 | 0 | break; |
909 | | |
910 | 0 | default: |
911 | | /* Skip GINSN_DST_UNKNOWN and GINSN_DST_MEM as they are uninteresting |
912 | | currently for SCFI. */ |
913 | 0 | break; |
914 | 0 | } |
915 | | |
916 | 0 | return ret; |
917 | 0 | } |
918 | | |
919 | | /* Recursively perform forward flow of the (unwind information) SCFI STATE |
920 | | starting at basic block GBB. |
921 | | |
922 | | The core of forward flow process takes the SCFI state at the entry of a bb |
923 | | and updates it incrementally as per the semantics of each ginsn in the bb. |
924 | | |
925 | | Returns error code, if any. */ |
926 | | |
927 | | static int |
928 | | forward_flow_scfi_state (gcfgS *gcfg, gbbS *gbb, scfi_stateS *state) |
929 | 0 | { |
930 | 0 | ginsnS *ginsn; |
931 | 0 | gbbS *prev_bb; |
932 | 0 | gedgeS *gedge = NULL; |
933 | 0 | int ret = 0; |
934 | |
|
935 | 0 | if (gbb->visited) |
936 | 0 | { |
937 | | /* Check that the SCFI state is the same as previous. */ |
938 | 0 | ret = cmp_scfi_state (state, gbb->entry_state); |
939 | 0 | if (ret) |
940 | 0 | as_bad (_("SCFI: Bad CFI propagation perhaps")); |
941 | 0 | return ret; |
942 | 0 | } |
943 | | |
944 | 0 | gbb->visited = true; |
945 | |
|
946 | 0 | gbb->entry_state = XCNEW (scfi_stateS); |
947 | 0 | memcpy (gbb->entry_state, state, sizeof (scfi_stateS)); |
948 | | |
949 | | /* Perform symbolic execution of each ginsn in the gbb and update the |
950 | | scfi_ops list of each ginsn (and also update the STATE object). */ |
951 | 0 | bb_for_each_insn(gbb, ginsn) |
952 | 0 | { |
953 | 0 | ret = gen_scfi_ops (ginsn, state); |
954 | 0 | if (ret) |
955 | 0 | goto fail; |
956 | 0 | } |
957 | | |
958 | 0 | gbb->exit_state = XCNEW (scfi_stateS); |
959 | 0 | memcpy (gbb->exit_state, state, sizeof (scfi_stateS)); |
960 | | |
961 | | /* Forward flow the SCFI state. Currently, we process the next basic block |
962 | | in DFS order. But any forward traversal order should be fine. */ |
963 | 0 | prev_bb = gbb; |
964 | 0 | if (gbb->num_out_gedges) |
965 | 0 | { |
966 | 0 | bb_for_each_edge(gbb, gedge) |
967 | 0 | { |
968 | 0 | gbb = gedge->dst_bb; |
969 | | /* Ensure that the state is the one from the exit of the prev bb. */ |
970 | 0 | memcpy (state, prev_bb->exit_state, sizeof (scfi_stateS)); |
971 | 0 | if (gbb->visited) |
972 | 0 | { |
973 | 0 | ret = cmp_scfi_state (gbb->entry_state, state); |
974 | 0 | if (ret) |
975 | 0 | goto fail; |
976 | 0 | } |
977 | | |
978 | 0 | if (!gedge->visited) |
979 | 0 | { |
980 | 0 | gedge->visited = true; |
981 | | |
982 | | /* Entry SCFI state for the destination bb of the edge is the |
983 | | same as the exit SCFI state of the source bb of the edge. */ |
984 | 0 | memcpy (state, prev_bb->exit_state, sizeof (scfi_stateS)); |
985 | 0 | ret = forward_flow_scfi_state (gcfg, gbb, state); |
986 | 0 | if (ret) |
987 | 0 | goto fail; |
988 | 0 | } |
989 | 0 | } |
990 | 0 | } |
991 | | |
992 | 0 | return 0; |
993 | | |
994 | 0 | fail: |
995 | |
|
996 | 0 | if (gedge) |
997 | 0 | gedge->visited = true; |
998 | 0 | return 1; |
999 | 0 | } |
1000 | | |
1001 | | static int |
1002 | | backward_flow_scfi_state (const symbolS *func ATTRIBUTE_UNUSED, gcfgS *gcfg) |
1003 | 0 | { |
1004 | 0 | gbbS **prog_order_bbs; |
1005 | 0 | gbbS **restore_bbs; |
1006 | 0 | gbbS *current_bb; |
1007 | 0 | gbbS *prev_bb; |
1008 | 0 | gbbS *dst_bb; |
1009 | 0 | ginsnS *ginsn; |
1010 | 0 | gedgeS *gedge = NULL; |
1011 | |
|
1012 | 0 | int ret = 0; |
1013 | 0 | uint64_t i, j; |
1014 | | |
1015 | | /* Basic blocks in reverse program order. */ |
1016 | 0 | prog_order_bbs = XCNEWVEC (gbbS *, gcfg->num_gbbs); |
1017 | | /* Basic blocks for which CFI remember op needs to be generated. */ |
1018 | 0 | restore_bbs = XCNEWVEC (gbbS *, gcfg->num_gbbs); |
1019 | |
|
1020 | 0 | gcfg_get_bbs_in_prog_order (gcfg, prog_order_bbs); |
1021 | |
|
1022 | 0 | i = gcfg->num_gbbs - 1; |
1023 | | /* Traverse in reverse program order. */ |
1024 | 0 | while (i > 0) |
1025 | 0 | { |
1026 | 0 | current_bb = prog_order_bbs[i]; |
1027 | 0 | prev_bb = prog_order_bbs[i-1]; |
1028 | 0 | if (cmp_scfi_state (prev_bb->exit_state, current_bb->entry_state)) |
1029 | 0 | { |
1030 | | /* Candidate for .cfi_restore_state found. */ |
1031 | 0 | ginsn = bb_get_first_ginsn (current_bb); |
1032 | 0 | scfi_op_add_cfi_restore_state (ginsn); |
1033 | | /* Memorize current_bb now to find location for its remember state |
1034 | | later. */ |
1035 | 0 | restore_bbs[i] = current_bb; |
1036 | 0 | } |
1037 | 0 | else |
1038 | 0 | { |
1039 | 0 | bb_for_each_edge (current_bb, gedge) |
1040 | 0 | { |
1041 | 0 | dst_bb = gedge->dst_bb; |
1042 | 0 | for (j = 0; j < gcfg->num_gbbs; j++) |
1043 | 0 | if (restore_bbs[j] == dst_bb) |
1044 | 0 | { |
1045 | 0 | ginsn = bb_get_last_ginsn (current_bb); |
1046 | 0 | scfi_op_add_cfi_remember_state (ginsn); |
1047 | | /* Remove the memorised restore_bb from the list. */ |
1048 | 0 | restore_bbs[j] = NULL; |
1049 | 0 | break; |
1050 | 0 | } |
1051 | 0 | } |
1052 | 0 | } |
1053 | 0 | i--; |
1054 | 0 | } |
1055 | | |
1056 | | /* All .cfi_restore_state pseudo-ops must have a corresponding |
1057 | | .cfi_remember_state by now. */ |
1058 | 0 | for (j = 0; j < gcfg->num_gbbs; j++) |
1059 | 0 | if (restore_bbs[j] != NULL) |
1060 | 0 | { |
1061 | 0 | ret = 1; |
1062 | 0 | break; |
1063 | 0 | } |
1064 | |
|
1065 | 0 | free (restore_bbs); |
1066 | 0 | free (prog_order_bbs); |
1067 | |
|
1068 | 0 | return ret; |
1069 | 0 | } |
1070 | | |
1071 | | /* Synthesize DWARF CFI for a function. */ |
1072 | | |
1073 | | int |
1074 | | scfi_synthesize_dw2cfi (const symbolS *func, gcfgS *gcfg, gbbS *root_bb) |
1075 | 0 | { |
1076 | 0 | int ret; |
1077 | 0 | scfi_stateS *init_state; |
1078 | |
|
1079 | 0 | init_state = XCNEW (scfi_stateS); |
1080 | 0 | init_state->traceable_p = true; |
1081 | | |
1082 | | /* Traverse the input GCFG and perform forward flow of information. |
1083 | | Update the scfi_op(s) per ginsn. */ |
1084 | 0 | ret = forward_flow_scfi_state (gcfg, root_bb, init_state); |
1085 | 0 | if (ret) |
1086 | 0 | { |
1087 | 0 | as_bad (_("SCFI: forward pass failed for func '%s'"), S_GET_NAME (func)); |
1088 | 0 | goto end; |
1089 | 0 | } |
1090 | | |
1091 | 0 | ret = backward_flow_scfi_state (func, gcfg); |
1092 | 0 | if (ret) |
1093 | 0 | { |
1094 | 0 | as_bad (_("SCFI: backward pass failed for func '%s'"), S_GET_NAME (func)); |
1095 | 0 | goto end; |
1096 | 0 | } |
1097 | | |
1098 | 0 | end: |
1099 | 0 | free (init_state); |
1100 | 0 | return ret; |
1101 | 0 | } |
1102 | | |
1103 | | static int |
1104 | | handle_scfi_dot_cfi (ginsnS *ginsn) |
1105 | 0 | { |
1106 | 0 | scfi_opS *op; |
1107 | | |
1108 | | /* Nothing to do. */ |
1109 | 0 | if (!ginsn->scfi_ops) |
1110 | 0 | return 0; |
1111 | | |
1112 | 0 | op = *ginsn->scfi_ops; |
1113 | 0 | if (!op) |
1114 | 0 | goto bad; |
1115 | | |
1116 | 0 | while (op) |
1117 | 0 | { |
1118 | 0 | switch (op->dw2cfi_op) |
1119 | 0 | { |
1120 | 0 | case DW_CFA_def_cfa_register: |
1121 | 0 | scfi_dot_cfi (DW_CFA_def_cfa_register, op->loc.base, 0, 0, NULL, |
1122 | 0 | ginsn->sym); |
1123 | 0 | break; |
1124 | 0 | case DW_CFA_def_cfa_offset: |
1125 | 0 | scfi_dot_cfi (DW_CFA_def_cfa_offset, op->loc.base, 0, |
1126 | 0 | op->loc.offset, NULL, ginsn->sym); |
1127 | 0 | break; |
1128 | 0 | case DW_CFA_def_cfa: |
1129 | 0 | scfi_dot_cfi (DW_CFA_def_cfa, op->loc.base, 0, op->loc.offset, |
1130 | 0 | NULL, ginsn->sym); |
1131 | 0 | break; |
1132 | 0 | case DW_CFA_offset: |
1133 | 0 | scfi_dot_cfi (DW_CFA_offset, op->reg, 0, op->loc.offset, NULL, |
1134 | 0 | ginsn->sym); |
1135 | 0 | break; |
1136 | 0 | case DW_CFA_restore: |
1137 | 0 | scfi_dot_cfi (DW_CFA_restore, op->reg, 0, 0, NULL, ginsn->sym); |
1138 | 0 | break; |
1139 | 0 | case DW_CFA_remember_state: |
1140 | 0 | scfi_dot_cfi (DW_CFA_remember_state, 0, 0, 0, NULL, ginsn->sym); |
1141 | 0 | break; |
1142 | 0 | case DW_CFA_restore_state: |
1143 | 0 | scfi_dot_cfi (DW_CFA_restore_state, 0, 0, 0, NULL, ginsn->sym); |
1144 | 0 | break; |
1145 | 0 | case CFI_label: |
1146 | 0 | scfi_dot_cfi (CFI_label, 0, 0, 0, op->op_data->name, ginsn->sym); |
1147 | 0 | free ((char *) op->op_data->name); |
1148 | 0 | break; |
1149 | 0 | case CFI_signal_frame: |
1150 | 0 | scfi_dot_cfi (CFI_signal_frame, 0, 0, 0, NULL, ginsn->sym); |
1151 | 0 | break; |
1152 | 0 | default: |
1153 | 0 | goto bad; |
1154 | 0 | break; |
1155 | 0 | } |
1156 | 0 | op = op->next; |
1157 | 0 | } |
1158 | | |
1159 | 0 | return 0; |
1160 | 0 | bad: |
1161 | 0 | as_bad (_("SCFI: Invalid DWARF CFI opcode data")); |
1162 | 0 | return 1; |
1163 | 0 | } |
1164 | | |
1165 | | /* Emit Synthesized DWARF CFI. */ |
1166 | | |
1167 | | int |
1168 | | scfi_emit_dw2cfi (const symbolS *func) |
1169 | 0 | { |
1170 | 0 | struct frch_ginsn_data *frch_gdata; |
1171 | 0 | ginsnS* ginsn = NULL; |
1172 | |
|
1173 | 0 | frch_gdata = frchain_now->frch_ginsn_data; |
1174 | 0 | ginsn = frch_gdata->gins_rootP; |
1175 | |
|
1176 | 0 | while (ginsn) |
1177 | 0 | { |
1178 | 0 | switch (ginsn->type) |
1179 | 0 | { |
1180 | 0 | case GINSN_TYPE_SYMBOL: |
1181 | | /* .cfi_startproc and .cfi_endproc pseudo-ops. */ |
1182 | 0 | if (GINSN_F_FUNC_BEGIN_P (ginsn)) |
1183 | 0 | { |
1184 | 0 | scfi_dot_cfi_startproc (frch_gdata->start_addr); |
1185 | 0 | break; |
1186 | 0 | } |
1187 | 0 | else if (GINSN_F_FUNC_END_P (ginsn)) |
1188 | 0 | { |
1189 | 0 | scfi_dot_cfi_endproc (ginsn->sym); |
1190 | 0 | break; |
1191 | 0 | } |
1192 | | /* Fall through. */ |
1193 | 0 | case GINSN_TYPE_ADD: |
1194 | 0 | case GINSN_TYPE_AND: |
1195 | 0 | case GINSN_TYPE_CALL: |
1196 | 0 | case GINSN_TYPE_JUMP: |
1197 | 0 | case GINSN_TYPE_JUMP_COND: |
1198 | 0 | case GINSN_TYPE_MOV: |
1199 | 0 | case GINSN_TYPE_LOAD: |
1200 | 0 | case GINSN_TYPE_PHANTOM: |
1201 | 0 | case GINSN_TYPE_STORE: |
1202 | 0 | case GINSN_TYPE_SUB: |
1203 | 0 | case GINSN_TYPE_OTHER: |
1204 | 0 | case GINSN_TYPE_RETURN: |
1205 | | |
1206 | | /* For all other SCFI ops, invoke the handler. */ |
1207 | 0 | if (ginsn->scfi_ops) |
1208 | 0 | handle_scfi_dot_cfi (ginsn); |
1209 | 0 | break; |
1210 | | |
1211 | 0 | default: |
1212 | | /* No other GINSN_TYPE_* expected. */ |
1213 | 0 | as_bad (_("SCFI: bad ginsn for func '%s'"), |
1214 | 0 | S_GET_NAME (func)); |
1215 | 0 | break; |
1216 | 0 | } |
1217 | 0 | ginsn = ginsn->next; |
1218 | 0 | } |
1219 | 0 | return 0; |
1220 | 0 | } |
1221 | | |
1222 | | #else |
1223 | | |
1224 | | int |
1225 | | scfi_emit_dw2cfi (const symbolS *func ATTRIBUTE_UNUSED) |
1226 | | { |
1227 | | as_bad (_("SCFI: unsupported for target")); |
1228 | | return 1; |
1229 | | } |
1230 | | |
1231 | | int |
1232 | | scfi_synthesize_dw2cfi (const symbolS *func ATTRIBUTE_UNUSED, |
1233 | | gcfgS *gcfg ATTRIBUTE_UNUSED, |
1234 | | gbbS *root_bb ATTRIBUTE_UNUSED) |
1235 | | { |
1236 | | as_bad (_("SCFI: unsupported for target")); |
1237 | | return 1; |
1238 | | } |
1239 | | |
1240 | | #endif /* defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN). */ |