|  | /* scfi.c - Support for synthesizing DWARF CFI for hand-written asm. | 
|  | Copyright (C) 2023-2025 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GAS, the GNU Assembler. | 
|  |  | 
|  | GAS is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3, or (at your option) | 
|  | any later version. | 
|  |  | 
|  | GAS is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with GAS; see the file COPYING.  If not, write to the Free | 
|  | Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA | 
|  | 02110-1301, USA.  */ | 
|  |  | 
|  | #include "as.h" | 
|  | #include "scfi.h" | 
|  | #include "subsegs.h" | 
|  | #include "scfidw2gen.h" | 
|  | #include "dw2gencfi.h" | 
|  |  | 
|  | #if defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN) | 
|  |  | 
|  | /* Beyond the target defined number of registers to be tracked | 
|  | (SCFI_MAX_REG_ID), keep the next register ID, in sequence, for REG_CFA.  */ | 
|  | #define REG_CFA	      (SCFI_MAX_REG_ID+1) | 
|  | /* Define the total number of registers being tracked. | 
|  | Used as index into an array of cfi_reglocS.  Note that a ginsn may carry a | 
|  | register number greater than MAX_NUM_SCFI_REGS, e.g., for the ginsns | 
|  | corresponding to push fs/gs in AMD64.  */ | 
|  | #define MAX_NUM_SCFI_REGS   (REG_CFA+1) | 
|  |  | 
|  | #define REG_INVALID	    ((unsigned int)-1) | 
|  |  | 
|  | enum cfi_reglocstate | 
|  | { | 
|  | CFI_UNDEFINED, | 
|  | CFI_IN_REG, | 
|  | CFI_ON_STACK | 
|  | }; | 
|  |  | 
|  | /* Location at which CFI register is saved. | 
|  |  | 
|  | A CFI register (callee-saved registers, RA/LR) are always an offset from | 
|  | the CFA.  REG_CFA itself, however, may have REG_SP or REG_FP as base | 
|  | register.  Hence, keep the base reg ID and offset per tracked register.  */ | 
|  |  | 
|  | struct cfi_regloc | 
|  | { | 
|  | /* Base reg ID (DWARF register number).  */ | 
|  | unsigned int base; | 
|  | /* Location as offset from the CFA.  */ | 
|  | offsetT offset; | 
|  | /* Current state of the CFI register.  */ | 
|  | enum cfi_reglocstate state; | 
|  | }; | 
|  |  | 
|  | typedef struct cfi_regloc cfi_reglocS; | 
|  |  | 
|  | struct scfi_op_data | 
|  | { | 
|  | const char *name; | 
|  | }; | 
|  |  | 
|  | typedef struct scfi_op_data scfi_op_dataS; | 
|  |  | 
|  | /* SCFI operation. | 
|  |  | 
|  | An SCFI operation represents a single atomic change to the SCFI state. | 
|  | This can also be understood as an abstraction for what eventually gets | 
|  | emitted as a DWARF CFI operation.  */ | 
|  |  | 
|  | struct scfi_op | 
|  | { | 
|  | /* An SCFI op updates the state of either the CFA or other tracked | 
|  | (callee-saved, REG_SP etc) registers.  'reg' is in the DWARF register | 
|  | number space and must be strictly less than MAX_NUM_SCFI_REGS.  */ | 
|  | unsigned int reg; | 
|  | /* Location of the reg.  */ | 
|  | cfi_reglocS loc; | 
|  | /* DWARF CFI opcode.  */ | 
|  | uint32_t dw2cfi_op; | 
|  | /* Some SCFI ops, e.g., for CFI_label, may need to carry additional data.  */ | 
|  | scfi_op_dataS *op_data; | 
|  | /* A linked list.  */ | 
|  | struct scfi_op *next; | 
|  | }; | 
|  |  | 
|  | /* SCFI State - accumulated unwind information at a PC. | 
|  |  | 
|  | SCFI state is the accumulated unwind information encompassing: | 
|  | - REG_SP, REG_FP, | 
|  | - RA, and | 
|  | - all callee-saved registers. | 
|  |  | 
|  | Note that SCFI_MAX_REG_ID is target/ABI dependent and is provided by the | 
|  | backends.  The backend must also identify the DWARF register numbers for | 
|  | the REG_SP, and REG_FP registers.  */ | 
|  |  | 
|  | struct scfi_state | 
|  | { | 
|  | cfi_reglocS regs[MAX_NUM_SCFI_REGS]; | 
|  | cfi_reglocS scratch[MAX_NUM_SCFI_REGS]; | 
|  | /* Current stack size.  */ | 
|  | offsetT stack_size; | 
|  | /* Whether the stack size is known. | 
|  | Stack size may become untraceable depending on the specific stack | 
|  | manipulation machine instruction, e.g., rsp = rsp op reg instruction | 
|  | makes the stack size untraceable.  */ | 
|  | bool traceable_p; | 
|  | }; | 
|  |  | 
|  | /* Initialize a new SCFI op.  */ | 
|  |  | 
|  | static scfi_opS * | 
|  | init_scfi_op (void) | 
|  | { | 
|  | scfi_opS *op = XCNEW (scfi_opS); | 
|  |  | 
|  | return op; | 
|  | } | 
|  |  | 
|  | /* Free the SCFI ops, given the HEAD of the list.  */ | 
|  |  | 
|  | void | 
|  | scfi_ops_cleanup (scfi_opS **head) | 
|  | { | 
|  | scfi_opS *op; | 
|  | scfi_opS *next; | 
|  |  | 
|  | if (!head || !*head) | 
|  | return; | 
|  |  | 
|  | op = *head; | 
|  | next = op->next; | 
|  |  | 
|  | while (op) | 
|  | { | 
|  | free (op->op_data); | 
|  | free (op); | 
|  | op = next; | 
|  | next = op ? op->next : NULL; | 
|  | } | 
|  |  | 
|  | free (head); | 
|  | } | 
|  |  | 
|  | /* Compare two SCFI states.  */ | 
|  |  | 
|  | static int | 
|  | cmp_scfi_state (scfi_stateS *state1, scfi_stateS *state2) | 
|  | { | 
|  | int ret; | 
|  |  | 
|  | if (!state1 || !state2) | 
|  | return 1; | 
|  |  | 
|  | /* Skip comparing the scratch[] value of registers.  The user visible | 
|  | unwind information is derived from the regs[] from the SCFI state.  */ | 
|  | ret = memcmp (state1->regs, state2->regs, | 
|  | sizeof (cfi_reglocS) * MAX_NUM_SCFI_REGS); | 
|  |  | 
|  | /* For user functions which perform dynamic stack allocation, after switching | 
|  | t REG_FP based CFA tracking, it is perfectly possible to have stack usage | 
|  | in some control flows.  Further, the different control flows may even not | 
|  | have the same idea of CFA tracking (likely later necessitating generation | 
|  | of .cfi_remember_state / .cfi_restore_state pair).  */ | 
|  | ret |= state1->regs[REG_CFA].base != state2->regs[REG_CFA].base; | 
|  |  | 
|  | if (!ret && state1->regs[REG_CFA].base == REG_SP) | 
|  | ret |= state1->stack_size != state2->stack_size; | 
|  |  | 
|  | ret |= state1->traceable_p != state2->traceable_p; | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | #if 0 | 
|  | static void | 
|  | scfi_state_update_reg (scfi_stateS *state, uint32_t dst, uint32_t base, | 
|  | int32_t offset) | 
|  | { | 
|  | if (dst >= MAX_NUM_SCFI_REGS) | 
|  | return; | 
|  |  | 
|  | state->regs[dst].base = base; | 
|  | state->regs[dst].offset = offset; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* Update the SCFI state of REG as available on execution stack at OFFSET | 
|  | from REG_CFA (BASE). | 
|  |  | 
|  | Note that BASE must be REG_CFA, because any other base (REG_SP, REG_FP) | 
|  | is by definition transitory in the function.  */ | 
|  |  | 
|  | static void | 
|  | scfi_state_save_reg (scfi_stateS *state, unsigned int reg, unsigned int base, | 
|  | offsetT offset) | 
|  | { | 
|  | if (reg >= MAX_NUM_SCFI_REGS) | 
|  | return; | 
|  |  | 
|  | gas_assert (base == REG_CFA); | 
|  |  | 
|  | state->regs[reg].base = base; | 
|  | state->regs[reg].offset = offset; | 
|  | state->regs[reg].state = CFI_ON_STACK; | 
|  | } | 
|  |  | 
|  | static void | 
|  | scfi_state_restore_reg (scfi_stateS *state, unsigned int reg) | 
|  | { | 
|  | if (reg >= MAX_NUM_SCFI_REGS) | 
|  | return; | 
|  |  | 
|  | /* Sanity check.  See Rule 4.  */ | 
|  | gas_assert (state->regs[reg].state == CFI_ON_STACK); | 
|  | gas_assert (state->regs[reg].base == REG_CFA); | 
|  |  | 
|  | /* PS: the register may still be on stack much after the restore.  Reset the | 
|  | SCFI state to CFI_UNDEFINED, however, to indicate that the most updated | 
|  | source of value is register itself from here onwards.  */ | 
|  | state->regs[reg].base = 0; | 
|  | state->regs[reg].offset = 0; | 
|  | state->regs[reg].state = CFI_UNDEFINED; | 
|  | } | 
|  |  | 
|  | /* Identify if the given GAS instruction GINSN saves a register | 
|  | (of interest) on stack.  */ | 
|  |  | 
|  | static bool | 
|  | ginsn_scfi_save_reg_p (ginsnS *ginsn, scfi_stateS *state) | 
|  | { | 
|  | bool save_reg_p = false; | 
|  | struct ginsn_src *src; | 
|  | struct ginsn_dst *dst; | 
|  |  | 
|  | src = ginsn_get_src1 (ginsn); | 
|  | dst = ginsn_get_dst (ginsn); | 
|  |  | 
|  | /* The first save to stack of callee-saved register is deemed as | 
|  | register save.  */ | 
|  | if (!ginsn_track_reg_p (ginsn_get_src_reg (src), GINSN_GEN_SCFI) | 
|  | || state->regs[ginsn_get_src_reg (src)].state == CFI_ON_STACK) | 
|  | return save_reg_p; | 
|  |  | 
|  | /* A register save insn may be an indirect mov.  */ | 
|  | if (ginsn->type == GINSN_TYPE_MOV | 
|  | && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT | 
|  | && (ginsn_get_dst_reg (dst) == REG_SP | 
|  | || (ginsn_get_dst_reg (dst) == REG_FP | 
|  | && state->regs[REG_CFA].base == REG_FP))) | 
|  | save_reg_p = true; | 
|  | /* or an explicit store to stack.  */ | 
|  | else if (ginsn->type == GINSN_TYPE_STORE | 
|  | && ginsn_get_dst_type (dst) == GINSN_DST_INDIRECT | 
|  | && ginsn_get_dst_reg (dst) == REG_SP) | 
|  | save_reg_p = true; | 
|  |  | 
|  | return save_reg_p; | 
|  | } | 
|  |  | 
|  | /* Identify if the given GAS instruction GINSN restores a register | 
|  | (of interest) on stack.  */ | 
|  |  | 
|  | static bool | 
|  | ginsn_scfi_restore_reg_p (ginsnS *ginsn, scfi_stateS *state) | 
|  | { | 
|  | bool restore_reg_p = false; | 
|  | struct ginsn_dst *dst; | 
|  | struct ginsn_src *src1; | 
|  |  | 
|  | dst = ginsn_get_dst (ginsn); | 
|  | src1 = ginsn_get_src1 (ginsn); | 
|  |  | 
|  | if (!ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI)) | 
|  | return restore_reg_p; | 
|  |  | 
|  | /* A register restore insn may be an indirect mov...  */ | 
|  | if (ginsn->type == GINSN_TYPE_MOV | 
|  | && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT | 
|  | && (ginsn_get_src_reg (src1) == REG_SP | 
|  | || (ginsn_get_src_reg (src1) == REG_FP | 
|  | && state->regs[REG_CFA].base == REG_FP))) | 
|  | restore_reg_p = true; | 
|  | /* ...or an explicit load from stack.  */ | 
|  | else if (ginsn->type == GINSN_TYPE_LOAD | 
|  | && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT | 
|  | && ginsn_get_src_reg (src1) == REG_SP) | 
|  | restore_reg_p = true; | 
|  |  | 
|  | return restore_reg_p; | 
|  | } | 
|  |  | 
|  | /* Append the SCFI operation OP to the list of SCFI operations in the | 
|  | given GINSN.  */ | 
|  |  | 
|  | static int | 
|  | ginsn_append_scfi_op (ginsnS *ginsn, scfi_opS *op) | 
|  | { | 
|  | scfi_opS *sop; | 
|  |  | 
|  | if (!ginsn || !op) | 
|  | return 1; | 
|  |  | 
|  | if (!ginsn->scfi_ops) | 
|  | { | 
|  | ginsn->scfi_ops = XCNEW (scfi_opS *); | 
|  | *ginsn->scfi_ops = op; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Add to tail.  Most ginsns have a single SCFI operation, | 
|  | so this traversal for every insertion is acceptable for now.  */ | 
|  | sop = *ginsn->scfi_ops; | 
|  | while (sop->next) | 
|  | sop = sop->next; | 
|  |  | 
|  | sop->next = op; | 
|  | } | 
|  | ginsn->num_scfi_ops++; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | scfi_op_add_def_cfa_reg (scfi_stateS *state, ginsnS *ginsn, unsigned int reg) | 
|  | { | 
|  | scfi_opS *op = NULL; | 
|  |  | 
|  | state->regs[REG_CFA].base = reg; | 
|  |  | 
|  | op = init_scfi_op (); | 
|  |  | 
|  | op->dw2cfi_op = DW_CFA_def_cfa_register; | 
|  | op->reg = REG_CFA; | 
|  | op->loc = state->regs[REG_CFA]; | 
|  |  | 
|  | ginsn_append_scfi_op (ginsn, op); | 
|  | } | 
|  |  | 
|  | static void | 
|  | scfi_op_add_cfa_offset_inc (scfi_stateS *state, ginsnS *ginsn, offsetT num) | 
|  | { | 
|  | scfi_opS *op = NULL; | 
|  |  | 
|  | state->regs[REG_CFA].offset -= num; | 
|  |  | 
|  | op = init_scfi_op (); | 
|  |  | 
|  | op->dw2cfi_op = DW_CFA_def_cfa_offset; | 
|  | op->reg = REG_CFA; | 
|  | op->loc = state->regs[REG_CFA]; | 
|  |  | 
|  | ginsn_append_scfi_op (ginsn, op); | 
|  | } | 
|  |  | 
|  | static void | 
|  | scfi_op_add_cfa_offset_dec (scfi_stateS *state, ginsnS *ginsn, offsetT num) | 
|  | { | 
|  | scfi_opS *op = NULL; | 
|  |  | 
|  | state->regs[REG_CFA].offset += num; | 
|  |  | 
|  | op = init_scfi_op (); | 
|  |  | 
|  | op->dw2cfi_op = DW_CFA_def_cfa_offset; | 
|  | op->reg = REG_CFA; | 
|  | op->loc = state->regs[REG_CFA]; | 
|  |  | 
|  | ginsn_append_scfi_op (ginsn, op); | 
|  | } | 
|  |  | 
|  | static void | 
|  | scfi_op_add_def_cfa (scfi_stateS *state, ginsnS *ginsn, unsigned int reg, | 
|  | offsetT num) | 
|  | { | 
|  | scfi_opS *op = NULL; | 
|  |  | 
|  | state->regs[REG_CFA].base = reg; | 
|  | state->regs[REG_CFA].offset = num; | 
|  |  | 
|  | op = init_scfi_op (); | 
|  |  | 
|  | op->dw2cfi_op = DW_CFA_def_cfa; | 
|  | op->reg = REG_CFA; | 
|  | op->loc = state->regs[REG_CFA]; | 
|  |  | 
|  | ginsn_append_scfi_op (ginsn, op); | 
|  | } | 
|  |  | 
|  | static void | 
|  | scfi_op_add_cfi_offset (scfi_stateS *state, ginsnS *ginsn, unsigned int reg) | 
|  | { | 
|  | scfi_opS *op = NULL; | 
|  |  | 
|  | op = init_scfi_op (); | 
|  |  | 
|  | op->dw2cfi_op = DW_CFA_offset; | 
|  | op->reg = reg; | 
|  | op->loc = state->regs[reg]; | 
|  |  | 
|  | ginsn_append_scfi_op (ginsn, op); | 
|  | } | 
|  |  | 
|  | static void | 
|  | scfi_op_add_cfa_restore (ginsnS *ginsn, unsigned int reg) | 
|  | { | 
|  | scfi_opS *op = NULL; | 
|  |  | 
|  | op = init_scfi_op (); | 
|  |  | 
|  | op->dw2cfi_op = DW_CFA_restore; | 
|  | op->reg = reg; | 
|  | op->loc.base = REG_INVALID; | 
|  | op->loc.offset = 0; | 
|  |  | 
|  | ginsn_append_scfi_op (ginsn, op); | 
|  | } | 
|  |  | 
|  | static void | 
|  | scfi_op_add_cfi_remember_state (ginsnS *ginsn) | 
|  | { | 
|  | scfi_opS *op = NULL; | 
|  |  | 
|  | op = init_scfi_op (); | 
|  |  | 
|  | op->dw2cfi_op = DW_CFA_remember_state; | 
|  |  | 
|  | ginsn_append_scfi_op (ginsn, op); | 
|  | } | 
|  |  | 
|  | static void | 
|  | scfi_op_add_cfi_restore_state (ginsnS *ginsn) | 
|  | { | 
|  | scfi_opS *op = NULL; | 
|  |  | 
|  | op = init_scfi_op (); | 
|  |  | 
|  | op->dw2cfi_op = DW_CFA_restore_state; | 
|  |  | 
|  | /* FIXME - add to the beginning of the scfi_ops.  */ | 
|  | ginsn_append_scfi_op (ginsn, op); | 
|  | } | 
|  |  | 
|  | void | 
|  | scfi_op_add_cfi_label (ginsnS *ginsn, const char *name) | 
|  | { | 
|  | scfi_opS *op = NULL; | 
|  |  | 
|  | op = init_scfi_op (); | 
|  | op->dw2cfi_op = CFI_label; | 
|  | op->op_data = XCNEW (scfi_op_dataS); | 
|  | op->op_data->name = name; | 
|  |  | 
|  | ginsn_append_scfi_op (ginsn, op); | 
|  | } | 
|  |  | 
|  | void | 
|  | scfi_op_add_signal_frame (ginsnS *ginsn) | 
|  | { | 
|  | scfi_opS *op = NULL; | 
|  |  | 
|  | op = init_scfi_op (); | 
|  | op->dw2cfi_op = CFI_signal_frame; | 
|  |  | 
|  | ginsn_append_scfi_op (ginsn, op); | 
|  | } | 
|  |  | 
|  | static int | 
|  | verify_heuristic_traceable_reg_fp (ginsnS *ginsn, scfi_stateS *state) | 
|  | { | 
|  | /* The function uses this variable to issue error to user right away.  */ | 
|  | int fp_traceable_p = 0; | 
|  | enum ginsn_type gtype; | 
|  | struct ginsn_src *src1; | 
|  | struct ginsn_src *src2; | 
|  | struct ginsn_dst *dst; | 
|  | unsigned int src1_reg; | 
|  | unsigned int dst_reg; | 
|  | enum ginsn_src_type src1_type; | 
|  | enum ginsn_src_type src2_type; | 
|  | enum ginsn_dst_type dst_type; | 
|  |  | 
|  | gtype = ginsn->type; | 
|  |  | 
|  | src1 = ginsn_get_src1 (ginsn); | 
|  | src2 = ginsn_get_src2 (ginsn); | 
|  | dst = ginsn_get_dst (ginsn); | 
|  |  | 
|  | src1_reg = ginsn_get_src_reg (src1); | 
|  | dst_reg = ginsn_get_dst_reg (dst); | 
|  |  | 
|  | src1_type = ginsn_get_src_type (src1); | 
|  | src2_type = ginsn_get_src_type (src2); | 
|  | dst_type = ginsn_get_dst_type (dst); | 
|  |  | 
|  | /* Stack manipulation can be done in a variety of ways.  A program may | 
|  | allocate stack statically or may perform dynamic stack allocation in | 
|  | the prologue. | 
|  |  | 
|  | The SCFI machinery in GAS is based on some heuristics: | 
|  |  | 
|  | - Rule 3 If the base register for CFA tracking is REG_FP, the program | 
|  | must not clobber REG_FP, unless it is for switch to REG_SP based CFA | 
|  | tracking (via say, a pop %rbp in X86).  */ | 
|  |  | 
|  | /* Check all applicable instructions with dest REG_FP, when the CFA base | 
|  | register is REG_FP.  */ | 
|  | if (state->regs[REG_CFA].base == REG_FP | 
|  | && (dst_type == GINSN_DST_REG || dst_type == GINSN_DST_INDIRECT) | 
|  | && dst_reg == REG_FP) | 
|  | { | 
|  | /* Excuse the add/sub with imm usage: They are OK.  */ | 
|  | if ((gtype == GINSN_TYPE_ADD || gtype == GINSN_TYPE_SUB) | 
|  | && src1_type == GINSN_SRC_REG && src1_reg == REG_FP | 
|  | && src2_type == GINSN_SRC_IMM) | 
|  | fp_traceable_p = 0; | 
|  | /* REG_FP restore is OK too.  */ | 
|  | else if (ginsn->type == GINSN_TYPE_LOAD) | 
|  | fp_traceable_p = 0; | 
|  | /* mov's to memory with REG_FP base do not make REG_FP untraceable.  */ | 
|  | else if (dst_type == GINSN_DST_INDIRECT | 
|  | && (gtype == GINSN_TYPE_MOV || gtype == GINSN_TYPE_STORE)) | 
|  | fp_traceable_p = 0; | 
|  | /* Manipulations of the values possibly on stack are OK too.  */ | 
|  | else if ((gtype == GINSN_TYPE_ADD || gtype == GINSN_TYPE_SUB | 
|  | || gtype == GINSN_TYPE_AND) | 
|  | && dst_type == GINSN_DST_INDIRECT) | 
|  | fp_traceable_p = 0; | 
|  | /* All other ginsns with REG_FP as destination make REG_FP not | 
|  | traceable.  */ | 
|  | else | 
|  | fp_traceable_p = 1; | 
|  | } | 
|  |  | 
|  | if (fp_traceable_p) | 
|  | as_bad_where (ginsn->file, ginsn->line, | 
|  | _("SCFI: usage of REG_FP as scratch not supported")); | 
|  |  | 
|  | return fp_traceable_p; | 
|  | } | 
|  |  | 
|  | static int | 
|  | verify_heuristic_traceable_stack_manipulation (ginsnS *ginsn, | 
|  | scfi_stateS *state) | 
|  | { | 
|  | /* The function uses this variable to issue error to user right away.  */ | 
|  | int sp_untraceable_p = 0; | 
|  | bool possibly_untraceable = false; | 
|  | enum ginsn_type gtype; | 
|  | struct ginsn_dst *dst; | 
|  | struct ginsn_src *src1; | 
|  | struct ginsn_src *src2; | 
|  | unsigned int src1_reg; | 
|  | unsigned int dst_reg; | 
|  | enum ginsn_src_type src1_type; | 
|  | enum ginsn_src_type src2_type; | 
|  | enum ginsn_dst_type dst_type; | 
|  |  | 
|  | gtype = ginsn->type; | 
|  |  | 
|  | src1 = ginsn_get_src1 (ginsn); | 
|  | src2 = ginsn_get_src2 (ginsn); | 
|  | dst = ginsn_get_dst (ginsn); | 
|  |  | 
|  | src1_reg = ginsn_get_src_reg (src1); | 
|  | dst_reg = ginsn_get_dst_reg (dst); | 
|  |  | 
|  | src1_type = ginsn_get_src_type (src1); | 
|  | src2_type = ginsn_get_src_type (src2); | 
|  | dst_type = ginsn_get_dst_type (dst); | 
|  |  | 
|  | /* Stack manipulation can be done in a variety of ways.  A program may | 
|  | allocate stack statically in prologue or may need to do dynamic stack | 
|  | allocation. | 
|  |  | 
|  | The SCFI machinery in GAS is based on some heuristics: | 
|  |  | 
|  | - Rule 1 The base register for CFA tracking may be either REG_SP or | 
|  | REG_FP. | 
|  |  | 
|  | - Rule 2 If the base register for CFA tracking is REG_SP, the precise | 
|  | amount of stack usage (and hence, the value of rsp) must be known at | 
|  | all times.  */ | 
|  |  | 
|  | if (gtype == GINSN_TYPE_MOV | 
|  | && dst_type == GINSN_DST_REG && dst_reg == REG_SP | 
|  | /* Exclude mov %rbp, %rsp from this check.  */ | 
|  | && src1_type == GINSN_SRC_REG && src1_reg != REG_FP) | 
|  | { | 
|  | /* A previous mov %rsp, %reg must have been seen earlier for this to be | 
|  | an OK for stack manipulation.  */ | 
|  | if (state->scratch[src1_reg].base != REG_CFA | 
|  | || state->scratch[src1_reg].state != CFI_IN_REG) | 
|  | possibly_untraceable = true; | 
|  | } | 
|  | /* Check add/sub/and insn usage when CFA base register is REG_SP. | 
|  | Any stack size manipulation, including stack realignment is not allowed | 
|  | if CFA base register is REG_SP.  */ | 
|  | else if (dst_type == GINSN_DST_REG && dst_reg == REG_SP | 
|  | && (((gtype == GINSN_TYPE_ADD || gtype == GINSN_TYPE_SUB) | 
|  | && src2_type != GINSN_SRC_IMM) | 
|  | || gtype == GINSN_TYPE_AND || gtype == GINSN_TYPE_OTHER)) | 
|  | possibly_untraceable = true; | 
|  | /* If a register save operation is seen when REG_SP is untraceable, | 
|  | CFI cannot be synthesized for register saves, hence bail out.  */ | 
|  | else if (ginsn_scfi_save_reg_p (ginsn, state) && !state->traceable_p) | 
|  | { | 
|  | sp_untraceable_p = 1; | 
|  | /* If, however, the register save is an REG_FP-based, indirect mov | 
|  | like: mov reg, disp(%rbp) and CFA base register is REG_BP, | 
|  | untraceable REG_SP is not a problem.  */ | 
|  | if (gtype == GINSN_TYPE_MOV && state->regs[REG_CFA].base == REG_FP | 
|  | && dst_type == GINSN_DST_INDIRECT && dst_reg == REG_FP) | 
|  | sp_untraceable_p = 0; | 
|  | } | 
|  | else if (ginsn_scfi_restore_reg_p (ginsn, state) && !state->traceable_p) | 
|  | { | 
|  | if (gtype == GINSN_TYPE_MOV && dst_type == GINSN_DST_INDIRECT | 
|  | && (src1_reg == REG_SP | 
|  | || (src1_reg == REG_FP && state->regs[REG_CFA].base != REG_FP))) | 
|  | sp_untraceable_p = 1; | 
|  | } | 
|  |  | 
|  | if (possibly_untraceable) | 
|  | { | 
|  | /* See Rule 2.  For SP-based CFA, this makes CFA tracking not possible. | 
|  | Propagate now to caller.  */ | 
|  | if (state->regs[REG_CFA].base == REG_SP) | 
|  | sp_untraceable_p = 1; | 
|  | else if (state->traceable_p) | 
|  | { | 
|  | /* An extension of Rule 2. | 
|  | For FP-based CFA, this may be a problem *if* certain specific | 
|  | changes to the SCFI state are seen beyond this point, e.g., | 
|  | register save / restore from stack.  */ | 
|  | gas_assert (state->regs[REG_CFA].base == REG_FP); | 
|  | /* Simply make a note in the SCFI state object for now and | 
|  | continue.  Indicate an error when register save / restore | 
|  | for callee-saved registers is seen.  */ | 
|  | sp_untraceable_p = 0; | 
|  | state->traceable_p = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (sp_untraceable_p) | 
|  | as_bad_where (ginsn->file, ginsn->line, | 
|  | _("SCFI: unsupported stack manipulation pattern")); | 
|  |  | 
|  | return sp_untraceable_p; | 
|  | } | 
|  |  | 
|  | static int | 
|  | verify_heuristic_symmetrical_restore_reg (scfi_stateS *state, ginsnS* ginsn) | 
|  | { | 
|  | int sym_restore = true; | 
|  | offsetT expected_offset = 0; | 
|  | struct ginsn_src *src1; | 
|  | struct ginsn_dst *dst; | 
|  | unsigned int reg; | 
|  |  | 
|  | /* Rule 4: Save and Restore of callee-saved registers must be symmetrical. | 
|  | It is expected that value of the saved register is restored correctly. | 
|  | E.g., | 
|  | push  reg1 | 
|  | push  reg2 | 
|  | ... | 
|  | body of func which uses reg1 , reg2 as scratch, | 
|  | and may be even spills them to stack. | 
|  | ... | 
|  | pop   reg2 | 
|  | pop   reg1 | 
|  | It is difficult to verify the Rule 4 in all cases.  For the SCFI machinery, | 
|  | it is difficult to separate prologue-epilogue from the body of the function | 
|  |  | 
|  | Hence, the SCFI machinery at this time, should only warn on an asymetrical | 
|  | restore.  */ | 
|  | src1 = ginsn_get_src1 (ginsn); | 
|  | dst = ginsn_get_dst (ginsn); | 
|  | reg = ginsn_get_dst_reg (dst); | 
|  |  | 
|  | /* For non callee-saved registers, calling the API is meaningless.  */ | 
|  | if (!ginsn_track_reg_p (ginsn_get_dst_reg (dst), GINSN_GEN_SCFI)) | 
|  | return sym_restore; | 
|  |  | 
|  | /* The register must have been saved on stack, for sure.  */ | 
|  | gas_assert (state->regs[reg].state == CFI_ON_STACK); | 
|  | gas_assert (state->regs[reg].base == REG_CFA); | 
|  |  | 
|  | if ((ginsn->type == GINSN_TYPE_MOV | 
|  | || ginsn->type == GINSN_TYPE_LOAD) | 
|  | && ginsn_get_src_type (src1) == GINSN_SRC_INDIRECT | 
|  | && (ginsn_get_src_reg (src1) == REG_SP | 
|  | || (ginsn_get_src_reg (src1) == REG_FP | 
|  | && state->regs[REG_CFA].base == REG_FP))) | 
|  | { | 
|  | /* mov disp(%rsp), reg.  */ | 
|  | /* mov disp(%rbp), reg.  */ | 
|  | expected_offset = (((ginsn_get_src_reg (src1) == REG_SP) | 
|  | ? -state->stack_size | 
|  | : state->regs[REG_FP].offset) | 
|  | + ginsn_get_src_disp (src1)); | 
|  | } | 
|  |  | 
|  | sym_restore = (expected_offset == state->regs[reg].offset); | 
|  |  | 
|  | return sym_restore; | 
|  | } | 
|  |  | 
|  | /* Perform symbolic execution of the GINSN and update its list of scfi_ops. | 
|  | scfi_ops are later used to directly generate the DWARF CFI directives. | 
|  | Also update the SCFI state object STATE for the caller.  */ | 
|  |  | 
|  | static int | 
|  | gen_scfi_ops (ginsnS *ginsn, scfi_stateS *state) | 
|  | { | 
|  | int ret = 0; | 
|  | offsetT offset; | 
|  | struct ginsn_src *src1; | 
|  | struct ginsn_src *src2; | 
|  | struct ginsn_dst *dst; | 
|  | unsigned int src1_reg; | 
|  | unsigned int dst_reg; | 
|  | enum ginsn_src_type src1_type; | 
|  | enum ginsn_src_type src2_type; | 
|  | enum ginsn_dst_type dst_type; | 
|  |  | 
|  | if (!ginsn || !state) | 
|  | ret = 1; | 
|  |  | 
|  | /* For the first ginsn (of type GINSN_TYPE_SYMBOL) in the gbb, generate | 
|  | the SCFI op with DW_CFA_def_cfa.  Note that the register and offset are | 
|  | target-specific.  */ | 
|  | if (GINSN_F_FUNC_BEGIN_P (ginsn)) | 
|  | { | 
|  | scfi_op_add_def_cfa (state, ginsn, REG_SP, SCFI_INIT_CFA_OFFSET); | 
|  | state->stack_size += SCFI_INIT_CFA_OFFSET; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | src1 = ginsn_get_src1 (ginsn); | 
|  | src2 = ginsn_get_src2 (ginsn); | 
|  | dst = ginsn_get_dst (ginsn); | 
|  |  | 
|  | src1_reg = ginsn_get_src_reg (src1); | 
|  | dst_reg = ginsn_get_dst_reg (dst); | 
|  |  | 
|  | src1_type = ginsn_get_src_type (src1); | 
|  | src2_type = ginsn_get_src_type (src2); | 
|  | dst_type = ginsn_get_dst_type (dst); | 
|  |  | 
|  | ret = verify_heuristic_traceable_stack_manipulation (ginsn, state); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | ret = verify_heuristic_traceable_reg_fp (ginsn, state); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | switch (dst_type) | 
|  | { | 
|  | case GINSN_DST_REG: | 
|  | switch (ginsn->type) | 
|  | { | 
|  | case GINSN_TYPE_MOV: | 
|  | if (src1_type == GINSN_SRC_REG && src1_reg == REG_SP | 
|  | && dst_type == GINSN_DST_REG && dst_reg == REG_FP | 
|  | && state->regs[REG_CFA].base == REG_SP) | 
|  | { | 
|  | /* mov %rsp, %rbp.  */ | 
|  | scfi_op_add_def_cfa_reg (state, ginsn, dst_reg); | 
|  | } | 
|  | else if (src1_type == GINSN_SRC_REG && src1_reg == REG_FP | 
|  | && dst_type == GINSN_DST_REG && dst_reg == REG_SP | 
|  | && state->regs[REG_CFA].base == REG_FP) | 
|  | { | 
|  | /* mov %rbp, %rsp.  */ | 
|  | state->stack_size = -state->regs[REG_FP].offset; | 
|  | scfi_op_add_def_cfa_reg (state, ginsn, dst_reg); | 
|  | state->traceable_p = true; | 
|  | } | 
|  | else if (src1_type == GINSN_SRC_INDIRECT | 
|  | && (src1_reg == REG_SP || src1_reg == REG_FP) | 
|  | && ginsn_track_reg_p (dst_reg, GINSN_GEN_SCFI)) | 
|  | { | 
|  | /* mov disp(%rsp), reg.  */ | 
|  | /* mov disp(%rbp), reg.  */ | 
|  | if (verify_heuristic_symmetrical_restore_reg (state, ginsn)) | 
|  | { | 
|  | scfi_state_restore_reg (state, dst_reg); | 
|  | scfi_op_add_cfa_restore (ginsn, dst_reg); | 
|  | } | 
|  | else | 
|  | as_warn_where (ginsn->file, ginsn->line, | 
|  | _("SCFI: asymetrical register restore")); | 
|  | } | 
|  | else if (src1_type == GINSN_SRC_REG && src1_reg == REG_SP | 
|  | && dst_type == GINSN_DST_REG) | 
|  | { | 
|  | /* mov %rsp, %reg.  */ | 
|  | /* The value of rsp is taken directly from state->stack_size. | 
|  | IMP: The workflow in gen_scfi_ops must keep it updated. | 
|  | PS: Not taking the value from state->scratch[REG_SP] is | 
|  | intentional.  */ | 
|  | state->scratch[dst_reg].base = REG_CFA; | 
|  | state->scratch[dst_reg].offset = -state->stack_size; | 
|  | state->scratch[dst_reg].state = CFI_IN_REG; | 
|  | } | 
|  | else if (src1_type == GINSN_SRC_REG | 
|  | && dst_type == GINSN_DST_REG && dst_reg == REG_SP) | 
|  | { | 
|  | /* mov %reg, %rsp.  */ | 
|  | /* Keep the value of REG_SP updated.  */ | 
|  | if (state->scratch[src1_reg].state == CFI_IN_REG) | 
|  | { | 
|  | state->stack_size = -state->scratch[src1_reg].offset; | 
|  | state->traceable_p = true; | 
|  | } | 
|  | # if 0 | 
|  | scfi_state_update_reg (state, ginsn_get_dst_reg (dst), | 
|  | state->scratch[ginsn_get_src_reg (src1)].base, | 
|  | state->scratch[ginsn_get_src_reg (src1)].offset); | 
|  | #endif | 
|  |  | 
|  | } | 
|  | break; | 
|  | case GINSN_TYPE_SUB: | 
|  | if (src1_type == GINSN_SRC_REG && src1_reg == REG_SP | 
|  | && dst_type == GINSN_DST_REG && dst_reg == REG_SP | 
|  | && src2_type == GINSN_SRC_IMM) | 
|  | { | 
|  | /* Stack inc/dec offset, when generated due to stack push and pop is | 
|  | target-specific.  Use the value encoded in the ginsn.  */ | 
|  | state->stack_size += ginsn_get_src_imm (src2); | 
|  | if (state->regs[REG_CFA].base == REG_SP) | 
|  | { | 
|  | /* push reg.  */ | 
|  | scfi_op_add_cfa_offset_dec (state, ginsn, ginsn_get_src_imm (src2)); | 
|  | } | 
|  | } | 
|  | break; | 
|  | case GINSN_TYPE_ADD: | 
|  | if (src1_type == GINSN_SRC_REG && src1_reg == REG_SP | 
|  | && dst_type == GINSN_DST_REG && dst_reg == REG_SP | 
|  | && src2_type == GINSN_SRC_IMM) | 
|  | { | 
|  | /* Stack inc/dec offset is target-specific.  Use the value | 
|  | encoded in the ginsn.  */ | 
|  | state->stack_size -= ginsn_get_src_imm (src2); | 
|  | /* pop %reg affects CFA offset only if CFA is currently | 
|  | stack-pointer based.  */ | 
|  | if (state->regs[REG_CFA].base == REG_SP) | 
|  | { | 
|  | scfi_op_add_cfa_offset_inc (state, ginsn, ginsn_get_src_imm (src2)); | 
|  | } | 
|  | } | 
|  | else if (src1_type == GINSN_SRC_REG && src1_reg == REG_FP | 
|  | && dst_type == GINSN_DST_REG && dst_reg == REG_SP | 
|  | && state->regs[REG_CFA].base == REG_FP) | 
|  | { | 
|  | /* FIXME - what is this for ? */ | 
|  | state->stack_size =  0 - (state->regs[REG_FP].offset + ginsn_get_src_imm (src2)); | 
|  | } | 
|  | break; | 
|  | case GINSN_TYPE_LOAD: | 
|  | /* If this is a load from stack.  */ | 
|  | if (src1_type == GINSN_SRC_INDIRECT | 
|  | && ((src1_reg == REG_FP && state->regs[REG_CFA].base == REG_FP) | 
|  | || src1_reg == REG_SP)) | 
|  |  | 
|  | { | 
|  | /* pop %rbp when CFA tracking is REG_FP based.  */ | 
|  | if (dst_reg == REG_FP && state->regs[REG_CFA].base == REG_FP) | 
|  | { | 
|  | scfi_op_add_def_cfa_reg (state, ginsn, REG_SP); | 
|  | if (state->regs[REG_CFA].offset != state->stack_size) | 
|  | scfi_op_add_cfa_offset_inc (state, ginsn, | 
|  | (state->regs[REG_CFA].offset - state->stack_size)); | 
|  | } | 
|  | if (ginsn_track_reg_p (dst_reg, GINSN_GEN_SCFI)) | 
|  | { | 
|  | if (verify_heuristic_symmetrical_restore_reg (state, ginsn)) | 
|  | { | 
|  | scfi_state_restore_reg (state, dst_reg); | 
|  | scfi_op_add_cfa_restore (ginsn, dst_reg); | 
|  | } | 
|  | else | 
|  | as_warn_where (ginsn->file, ginsn->line, | 
|  | _("SCFI: asymetrical register restore")); | 
|  | } | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case GINSN_DST_INDIRECT: | 
|  | /* Some operations with an indirect access to memory (or even to stack) | 
|  | may still be uninteresting for SCFI purpose (e.g, addl %edx, -32(%rsp) | 
|  | in x86).  In case of x86_64, these can neither be a register | 
|  | save / unsave, nor can alter the stack size. | 
|  | PS: This condition may need to be revisited for other arches.  */ | 
|  | if (ginsn->type == GINSN_TYPE_ADD || ginsn->type == GINSN_TYPE_SUB | 
|  | || ginsn->type == GINSN_TYPE_AND) | 
|  | break; | 
|  | gas_assert (ginsn->type == GINSN_TYPE_MOV | 
|  | || ginsn->type == GINSN_TYPE_STORE | 
|  | || ginsn->type == GINSN_TYPE_LOAD); | 
|  | /* mov reg, disp(%rbp) */ | 
|  | /* mov reg, disp(%rsp) */ | 
|  | if (ginsn_scfi_save_reg_p (ginsn, state)) | 
|  | { | 
|  | if (dst_reg == REG_SP) | 
|  | { | 
|  | /* mov reg, disp(%rsp) */ | 
|  | offset = 0 - state->stack_size + ginsn_get_dst_disp (dst); | 
|  | scfi_state_save_reg (state, src1_reg, REG_CFA, offset); | 
|  | scfi_op_add_cfi_offset (state, ginsn, src1_reg); | 
|  | } | 
|  | else if (dst_reg == REG_FP) | 
|  | { | 
|  | gas_assert (state->regs[REG_CFA].base == REG_FP); | 
|  | /* mov reg, disp(%rbp) */ | 
|  | offset = 0 - state->regs[REG_CFA].offset + ginsn_get_dst_disp (dst); | 
|  | scfi_state_save_reg (state, src1_reg, REG_CFA, offset); | 
|  | scfi_op_add_cfi_offset (state, ginsn, src1_reg); | 
|  | } | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | /* Skip GINSN_DST_UNKNOWN and GINSN_DST_MEM as they are uninteresting | 
|  | currently for SCFI.  */ | 
|  | break; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Recursively perform forward flow of the (unwind information) SCFI STATE | 
|  | starting at basic block GBB. | 
|  |  | 
|  | The core of forward flow process takes the SCFI state at the entry of a bb | 
|  | and updates it incrementally as per the semantics of each ginsn in the bb. | 
|  |  | 
|  | Returns error code, if any.  */ | 
|  |  | 
|  | static int | 
|  | forward_flow_scfi_state (gcfgS *gcfg, gbbS *gbb, scfi_stateS *state) | 
|  | { | 
|  | ginsnS *ginsn; | 
|  | gbbS *prev_bb; | 
|  | gedgeS *gedge = NULL; | 
|  | int ret = 0; | 
|  |  | 
|  | if (gbb->visited) | 
|  | { | 
|  | /* Check that the SCFI state is the same as previous.  */ | 
|  | ret = cmp_scfi_state (state, gbb->entry_state); | 
|  | if (ret) | 
|  | as_bad (_("SCFI: Bad CFI propagation perhaps")); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | gbb->visited = true; | 
|  |  | 
|  | gbb->entry_state = XCNEW (scfi_stateS); | 
|  | memcpy (gbb->entry_state, state, sizeof (scfi_stateS)); | 
|  |  | 
|  | /* Perform symbolic execution of each ginsn in the gbb and update the | 
|  | scfi_ops list of each ginsn (and also update the STATE object).   */ | 
|  | bb_for_each_insn(gbb, ginsn) | 
|  | { | 
|  | ret = gen_scfi_ops (ginsn, state); | 
|  | if (ret) | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | gbb->exit_state = XCNEW (scfi_stateS); | 
|  | memcpy (gbb->exit_state, state, sizeof (scfi_stateS)); | 
|  |  | 
|  | /* Forward flow the SCFI state.  Currently, we process the next basic block | 
|  | in DFS order.  But any forward traversal order should be fine.  */ | 
|  | prev_bb = gbb; | 
|  | if (gbb->num_out_gedges) | 
|  | { | 
|  | bb_for_each_edge(gbb, gedge) | 
|  | { | 
|  | gbb = gedge->dst_bb; | 
|  | /* Ensure that the state is the one from the exit of the prev bb.  */ | 
|  | memcpy (state, prev_bb->exit_state, sizeof (scfi_stateS)); | 
|  | if (gbb->visited) | 
|  | { | 
|  | ret = cmp_scfi_state (gbb->entry_state, state); | 
|  | if (ret) | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | if (!gedge->visited) | 
|  | { | 
|  | gedge->visited = true; | 
|  |  | 
|  | /* Entry SCFI state for the destination bb of the edge is the | 
|  | same as the exit SCFI state of the source bb of the edge.  */ | 
|  | memcpy (state, prev_bb->exit_state, sizeof (scfi_stateS)); | 
|  | ret = forward_flow_scfi_state (gcfg, gbb, state); | 
|  | if (ret) | 
|  | goto fail; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | fail: | 
|  |  | 
|  | if (gedge) | 
|  | gedge->visited = true; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | backward_flow_scfi_state (const symbolS *func ATTRIBUTE_UNUSED, gcfgS *gcfg) | 
|  | { | 
|  | gbbS **prog_order_bbs; | 
|  | gbbS **restore_bbs; | 
|  | gbbS *current_bb; | 
|  | gbbS *prev_bb; | 
|  | gbbS *dst_bb; | 
|  | ginsnS *ginsn; | 
|  | gedgeS *gedge = NULL; | 
|  |  | 
|  | int ret = 0; | 
|  | uint64_t i, j; | 
|  |  | 
|  | /* Basic blocks in reverse program order.  */ | 
|  | prog_order_bbs = XCNEWVEC (gbbS *, gcfg->num_gbbs); | 
|  | /* Basic blocks for which CFI remember op needs to be generated.  */ | 
|  | restore_bbs = XCNEWVEC (gbbS *, gcfg->num_gbbs); | 
|  |  | 
|  | gcfg_get_bbs_in_prog_order (gcfg, prog_order_bbs); | 
|  |  | 
|  | i = gcfg->num_gbbs - 1; | 
|  | /* Traverse in reverse program order.  */ | 
|  | while (i > 0) | 
|  | { | 
|  | current_bb = prog_order_bbs[i]; | 
|  | prev_bb = prog_order_bbs[i-1]; | 
|  | if (cmp_scfi_state (prev_bb->exit_state, current_bb->entry_state)) | 
|  | { | 
|  | /* Candidate for .cfi_restore_state found.  */ | 
|  | ginsn = bb_get_first_ginsn (current_bb); | 
|  | scfi_op_add_cfi_restore_state (ginsn); | 
|  | /* Memorize current_bb now to find location for its remember state | 
|  | later.  */ | 
|  | restore_bbs[i] = current_bb; | 
|  | } | 
|  | else | 
|  | { | 
|  | bb_for_each_edge (current_bb, gedge) | 
|  | { | 
|  | dst_bb = gedge->dst_bb; | 
|  | for (j = 0; j < gcfg->num_gbbs; j++) | 
|  | if (restore_bbs[j] == dst_bb) | 
|  | { | 
|  | ginsn = bb_get_last_ginsn (current_bb); | 
|  | scfi_op_add_cfi_remember_state (ginsn); | 
|  | /* Remove the memorised restore_bb from the list.  */ | 
|  | restore_bbs[j] = NULL; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | i--; | 
|  | } | 
|  |  | 
|  | /* All .cfi_restore_state pseudo-ops must have a corresponding | 
|  | .cfi_remember_state by now.  */ | 
|  | for (j = 0; j < gcfg->num_gbbs; j++) | 
|  | if (restore_bbs[j] != NULL) | 
|  | { | 
|  | ret = 1; | 
|  | break; | 
|  | } | 
|  |  | 
|  | free (restore_bbs); | 
|  | free (prog_order_bbs); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Synthesize DWARF CFI for a function.  */ | 
|  |  | 
|  | int | 
|  | scfi_synthesize_dw2cfi (const symbolS *func, gcfgS *gcfg, gbbS *root_bb) | 
|  | { | 
|  | int ret; | 
|  | scfi_stateS *init_state; | 
|  |  | 
|  | init_state = XCNEW (scfi_stateS); | 
|  | init_state->traceable_p = true; | 
|  |  | 
|  | /* Traverse the input GCFG and perform forward flow of information. | 
|  | Update the scfi_op(s) per ginsn.  */ | 
|  | ret = forward_flow_scfi_state (gcfg, root_bb, init_state); | 
|  | if (ret) | 
|  | { | 
|  | as_bad (_("SCFI: forward pass failed for func '%s'"), S_GET_NAME (func)); | 
|  | goto end; | 
|  | } | 
|  |  | 
|  | ret = backward_flow_scfi_state (func, gcfg); | 
|  | if (ret) | 
|  | { | 
|  | as_bad (_("SCFI: backward pass failed for func '%s'"), S_GET_NAME (func)); | 
|  | goto end; | 
|  | } | 
|  |  | 
|  | end: | 
|  | free (init_state); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static int | 
|  | handle_scfi_dot_cfi (ginsnS *ginsn) | 
|  | { | 
|  | scfi_opS *op; | 
|  |  | 
|  | /* Nothing to do.  */ | 
|  | if (!ginsn->scfi_ops) | 
|  | return 0; | 
|  |  | 
|  | op = *ginsn->scfi_ops; | 
|  | if (!op) | 
|  | goto bad; | 
|  |  | 
|  | while (op) | 
|  | { | 
|  | switch (op->dw2cfi_op) | 
|  | { | 
|  | case DW_CFA_def_cfa_register: | 
|  | scfi_dot_cfi (DW_CFA_def_cfa_register, op->loc.base, 0, 0, NULL, | 
|  | ginsn->sym); | 
|  | break; | 
|  | case DW_CFA_def_cfa_offset: | 
|  | scfi_dot_cfi (DW_CFA_def_cfa_offset, op->loc.base, 0, | 
|  | op->loc.offset, NULL, ginsn->sym); | 
|  | break; | 
|  | case DW_CFA_def_cfa: | 
|  | scfi_dot_cfi (DW_CFA_def_cfa, op->loc.base, 0, op->loc.offset, | 
|  | NULL, ginsn->sym); | 
|  | break; | 
|  | case DW_CFA_offset: | 
|  | scfi_dot_cfi (DW_CFA_offset, op->reg, 0, op->loc.offset, NULL, | 
|  | ginsn->sym); | 
|  | break; | 
|  | case DW_CFA_restore: | 
|  | scfi_dot_cfi (DW_CFA_restore, op->reg, 0, 0, NULL, ginsn->sym); | 
|  | break; | 
|  | case DW_CFA_remember_state: | 
|  | scfi_dot_cfi (DW_CFA_remember_state, 0, 0, 0, NULL, ginsn->sym); | 
|  | break; | 
|  | case DW_CFA_restore_state: | 
|  | scfi_dot_cfi (DW_CFA_restore_state, 0, 0, 0, NULL, ginsn->sym); | 
|  | break; | 
|  | case CFI_label: | 
|  | scfi_dot_cfi (CFI_label, 0, 0, 0, op->op_data->name, ginsn->sym); | 
|  | free ((char *) op->op_data->name); | 
|  | break; | 
|  | case CFI_signal_frame: | 
|  | scfi_dot_cfi (CFI_signal_frame, 0, 0, 0, NULL, ginsn->sym); | 
|  | break; | 
|  | default: | 
|  | goto bad; | 
|  | break; | 
|  | } | 
|  | op = op->next; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | bad: | 
|  | as_bad (_("SCFI: Invalid DWARF CFI opcode data")); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* Emit Synthesized DWARF CFI.  */ | 
|  |  | 
|  | int | 
|  | scfi_emit_dw2cfi (const symbolS *func) | 
|  | { | 
|  | struct frch_ginsn_data *frch_gdata; | 
|  | ginsnS* ginsn = NULL; | 
|  |  | 
|  | frch_gdata = frchain_now->frch_ginsn_data; | 
|  | ginsn = frch_gdata->gins_rootP; | 
|  |  | 
|  | while (ginsn) | 
|  | { | 
|  | switch (ginsn->type) | 
|  | { | 
|  | case GINSN_TYPE_SYMBOL: | 
|  | /* .cfi_startproc and .cfi_endproc pseudo-ops.  */ | 
|  | if (GINSN_F_FUNC_BEGIN_P (ginsn)) | 
|  | { | 
|  | scfi_dot_cfi_startproc (frch_gdata->start_addr); | 
|  | break; | 
|  | } | 
|  | else if (GINSN_F_FUNC_END_P (ginsn)) | 
|  | { | 
|  | scfi_dot_cfi_endproc (ginsn->sym); | 
|  | break; | 
|  | } | 
|  | /* Fall through.  */ | 
|  | case GINSN_TYPE_ADD: | 
|  | case GINSN_TYPE_AND: | 
|  | case GINSN_TYPE_CALL: | 
|  | case GINSN_TYPE_JUMP: | 
|  | case GINSN_TYPE_JUMP_COND: | 
|  | case GINSN_TYPE_MOV: | 
|  | case GINSN_TYPE_LOAD: | 
|  | case GINSN_TYPE_PHANTOM: | 
|  | case GINSN_TYPE_STORE: | 
|  | case GINSN_TYPE_SUB: | 
|  | case GINSN_TYPE_OTHER: | 
|  | case GINSN_TYPE_RETURN: | 
|  |  | 
|  | /* For all other SCFI ops, invoke the handler.  */ | 
|  | if (ginsn->scfi_ops) | 
|  | handle_scfi_dot_cfi (ginsn); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | /* No other GINSN_TYPE_* expected.  */ | 
|  | as_bad (_("SCFI: bad ginsn for func '%s'"), | 
|  | S_GET_NAME (func)); | 
|  | break; | 
|  | } | 
|  | ginsn = ginsn->next; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | int | 
|  | scfi_emit_dw2cfi (const symbolS *func ATTRIBUTE_UNUSED) | 
|  | { | 
|  | as_bad (_("SCFI: unsupported for target")); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int | 
|  | scfi_synthesize_dw2cfi (const symbolS *func ATTRIBUTE_UNUSED, | 
|  | gcfgS *gcfg ATTRIBUTE_UNUSED, | 
|  | gbbS *root_bb ATTRIBUTE_UNUSED) | 
|  | { | 
|  | as_bad (_("SCFI: unsupported for target")); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | #endif  /* defined (TARGET_USE_SCFI) && defined (TARGET_USE_GINSN).  */ |