Home | History | Annotate | Line # | Download | only in config
      1  1.1  christos /* tc-aarch64-ginsn.c -- Ginsn generation for the AArch64 ISA
      2  1.1  christos 
      3  1.1  christos    Copyright (C) 2024-2025 Free Software Foundation, Inc.
      4  1.1  christos 
      5  1.1  christos    This file is part of GAS.
      6  1.1  christos 
      7  1.1  christos    GAS is free software; you can redistribute it and/or modify
      8  1.1  christos    it under the terms of the GNU General Public License as published by
      9  1.1  christos    the Free Software Foundation; either version 3 of the license, or
     10  1.1  christos    (at your option) any later version.
     11  1.1  christos 
     12  1.1  christos    GAS is distributed in the hope that it will be useful,
     13  1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15  1.1  christos    GNU General Public License for more details.
     16  1.1  christos 
     17  1.1  christos    You should have received a copy of the GNU General Public License
     18  1.1  christos    along with this program; see the file COPYING3. If not,
     19  1.1  christos    see <http://www.gnu.org/licenses/>.  */
     20  1.1  christos 
     21  1.1  christos /* This file contains the implementation of the ginsn creation for aarch64
     22  1.1  christos    instructions.  Most functions will read the aarch64_instruction inst
     23  1.1  christos    object, but none should need to modify it.  */
     24  1.1  christos 
     25  1.1  christos #ifdef OBJ_ELF
     26  1.1  christos 
     27  1.1  christos /* Invalid DWARF register number.  Used when WZR / XZR is seen.  */
     28  1.1  christos #define GINSN_DW2_REGNUM_INVALID  (~0U)
     29  1.1  christos 
     30  1.1  christos /* Return whether the given register number is a callee-saved register for
     31  1.1  christos    SCFI purposes.
     32  1.1  christos 
     33  1.1  christos    Apart from the callee-saved GPRs, SCFI always tracks SP, FP and LR
     34  1.1  christos    additionally.  As for the FP/Advanced SIMD registers, v8-v15 are
     35  1.1  christos    callee-saved.  */
     36  1.1  christos 
     37  1.1  christos bool
     38  1.1  christos aarch64_scfi_callee_saved_p (unsigned int dw2reg_num)
     39  1.1  christos {
     40  1.1  christos   /* PS: Ensure SCFI_MAX_REG_ID is the max DWARF register number to cover
     41  1.1  christos      all the registers here.  */
     42  1.1  christos   if (dw2reg_num == REG_SP /* x31.  */
     43  1.1  christos       || dw2reg_num == REG_FP /* x29.  */
     44  1.1  christos       || dw2reg_num == REG_LR /* x30.  */
     45  1.1  christos       || (dw2reg_num >= 19 && dw2reg_num <= 28) /* x19 - x28.  */
     46  1.1  christos       || (dw2reg_num >= 72 && dw2reg_num <= 79) /* v8 - v15.  */)
     47  1.1  christos     return true;
     48  1.1  christos 
     49  1.1  christos   return false;
     50  1.1  christos }
     51  1.1  christos 
     52  1.1  christos /* Get the DWARF register number for the given OPND.  If OPND is an address,
     53  1.1  christos    the returned register is the base register.  If OPND spans multiple
     54  1.1  christos    registers, the returned register is the first of those registers.  */
     55  1.1  christos 
     56  1.1  christos static unsigned int
     57  1.1  christos ginsn_dw2_regnum (aarch64_opnd_info *opnd)
     58  1.1  christos {
     59  1.1  christos   enum aarch64_operand_class opnd_class;
     60  1.1  christos   unsigned int dw2reg_num = 0;
     61  1.1  christos 
     62  1.1  christos   opnd_class = aarch64_get_operand_class (opnd->type);
     63  1.1  christos 
     64  1.1  christos   switch (opnd_class)
     65  1.1  christos     {
     66  1.1  christos     case AARCH64_OPND_CLASS_FP_REG:
     67  1.1  christos       dw2reg_num = opnd->reg.regno + 64;
     68  1.1  christos       break;
     69  1.1  christos     case AARCH64_OPND_CLASS_SVE_REGLIST:
     70  1.1  christos       dw2reg_num = opnd->reglist.first_regno + 64;
     71  1.1  christos       break;
     72  1.1  christos     case AARCH64_OPND_CLASS_ADDRESS:
     73  1.1  christos       dw2reg_num = opnd->addr.base_regno;
     74  1.1  christos       break;
     75  1.1  christos     case AARCH64_OPND_CLASS_INT_REG:
     76  1.1  christos     case AARCH64_OPND_CLASS_MODIFIED_REG:
     77  1.1  christos       /* Use an invalid DWARF register value in case of WZR, else this will be an
     78  1.1  christos 	 incorrect dependency on REG_SP.  */
     79  1.1  christos       if (aarch64_zero_register_p (opnd))
     80  1.1  christos 	dw2reg_num = GINSN_DW2_REGNUM_INVALID;
     81  1.1  christos       else
     82  1.1  christos 	/* For GPRs of our interest (callee-saved regs, SP, FP, LR),
     83  1.1  christos 	   DWARF register number is the same as AArch64 register number.  */
     84  1.1  christos 	dw2reg_num = opnd->reg.regno;
     85  1.1  christos       break;
     86  1.1  christos     default:
     87  1.1  christos       as_bad ("Unexpected operand class in ginsn_dw2_regnum");
     88  1.1  christos       break;
     89  1.1  christos     }
     90  1.1  christos 
     91  1.1  christos   return dw2reg_num;
     92  1.1  christos }
     93  1.1  christos 
     94  1.1  christos static bool
     95  1.1  christos ginsn_dw2_regnum_invalid_p (unsigned int opnd_reg)
     96  1.1  christos {
     97  1.1  christos   return (opnd_reg == GINSN_DW2_REGNUM_INVALID);
     98  1.1  christos }
     99  1.1  christos 
    100  1.1  christos /* Generate ginsn for addsub instructions with immediate opnd.  */
    101  1.1  christos 
    102  1.1  christos static ginsnS *
    103  1.1  christos aarch64_ginsn_addsub_imm (const symbolS *insn_end_sym)
    104  1.1  christos {
    105  1.1  christos   ginsnS *ginsn = NULL;
    106  1.1  christos   bool add_p, sub_p;
    107  1.1  christos   offsetT src_imm = 0;
    108  1.1  christos   unsigned int dst_reg, opnd_reg;
    109  1.1  christos   aarch64_opnd_info *dst, *opnd;
    110  1.1  christos   ginsnS *(*ginsn_func) (const symbolS *, bool,
    111  1.1  christos 			 enum ginsn_src_type, unsigned int, offsetT,
    112  1.1  christos 			 enum ginsn_src_type, unsigned int, offsetT,
    113  1.1  christos 			 enum ginsn_dst_type, unsigned int, offsetT);
    114  1.1  christos 
    115  1.1  christos   aarch64_inst *base = &inst.base;
    116  1.1  christos   const aarch64_opcode *opcode = base->opcode;
    117  1.1  christos 
    118  1.1  christos   add_p = aarch64_opcode_subclass_p (opcode, F_ARITH_ADD);
    119  1.1  christos   sub_p = aarch64_opcode_subclass_p (opcode, F_ARITH_SUB);
    120  1.1  christos   gas_assert (add_p || sub_p);
    121  1.1  christos   ginsn_func = add_p ? ginsn_new_add : ginsn_new_sub;
    122  1.1  christos 
    123  1.1  christos   gas_assert (aarch64_num_of_operands (opcode) == 3);
    124  1.1  christos   dst = &base->operands[0];
    125  1.1  christos   opnd = &base->operands[1];
    126  1.1  christos 
    127  1.1  christos   if (aarch64_gas_internal_fixup_p () && inst.reloc.exp.X_op == O_constant)
    128  1.1  christos     src_imm = inst.reloc.exp.X_add_number;
    129  1.1  christos   /* For any other relocation type, e.g., in add reg, reg, symbol, skip now
    130  1.1  christos      and handle via aarch64_ginsn_unhandled () code path.  */
    131  1.1  christos   else if (inst.reloc.type != BFD_RELOC_UNUSED)
    132  1.1  christos     return ginsn;
    133  1.1  christos   /* FIXME - verify the understanding and remove assert.  */
    134  1.1  christos   else
    135  1.1  christos     gas_assert (0);
    136  1.1  christos 
    137  1.1  christos   dst_reg = ginsn_dw2_regnum (dst);
    138  1.1  christos   opnd_reg = ginsn_dw2_regnum (opnd);
    139  1.1  christos 
    140  1.1  christos   if (ginsn_dw2_regnum_invalid_p (dst_reg)
    141  1.1  christos       || ginsn_dw2_regnum_invalid_p (opnd_reg))
    142  1.1  christos     return ginsn;
    143  1.1  christos 
    144  1.1  christos   ginsn = ginsn_func (insn_end_sym, true,
    145  1.1  christos 		      GINSN_SRC_REG, opnd_reg, 0,
    146  1.1  christos 		      GINSN_SRC_IMM, 0, src_imm,
    147  1.1  christos 		      GINSN_DST_REG, dst_reg, 0);
    148  1.1  christos   ginsn_set_where (ginsn);
    149  1.1  christos 
    150  1.1  christos   return ginsn;
    151  1.1  christos }
    152  1.1  christos 
    153  1.1  christos /* Generate ginsn for addsub instructions with reg opnd.  */
    154  1.1  christos 
    155  1.1  christos static ginsnS *
    156  1.1  christos aarch64_ginsn_addsub_reg (const symbolS *insn_end_sym)
    157  1.1  christos {
    158  1.1  christos   ginsnS *ginsn = NULL;
    159  1.1  christos   bool add_p, sub_p;
    160  1.1  christos   unsigned int dst_reg, src1_reg, src2_reg;
    161  1.1  christos   aarch64_opnd_info *dst, *src1, *src2;
    162  1.1  christos   ginsnS *(*ginsn_func) (const symbolS *, bool,
    163  1.1  christos 			 enum ginsn_src_type, unsigned int, offsetT,
    164  1.1  christos 			 enum ginsn_src_type, unsigned int, offsetT,
    165  1.1  christos 			 enum ginsn_dst_type, unsigned int, offsetT);
    166  1.1  christos 
    167  1.1  christos   aarch64_inst *base = &inst.base;
    168  1.1  christos   const aarch64_opcode *opcode = base->opcode;
    169  1.1  christos 
    170  1.1  christos   add_p = aarch64_opcode_subclass_p (opcode, F_ARITH_ADD);
    171  1.1  christos   sub_p = aarch64_opcode_subclass_p (opcode, F_ARITH_SUB);
    172  1.1  christos   gas_assert (add_p || sub_p);
    173  1.1  christos   ginsn_func = add_p ? ginsn_new_add : ginsn_new_sub;
    174  1.1  christos 
    175  1.1  christos   gas_assert (aarch64_num_of_operands (opcode) == 3);
    176  1.1  christos   dst = &base->operands[0];
    177  1.1  christos   src1 = &base->operands[1];
    178  1.1  christos   src2 = &base->operands[2];
    179  1.1  christos 
    180  1.1  christos   dst_reg = ginsn_dw2_regnum (dst);
    181  1.1  christos   src1_reg = ginsn_dw2_regnum (src1);
    182  1.1  christos   src2_reg = ginsn_dw2_regnum (src2);
    183  1.1  christos 
    184  1.1  christos   if (ginsn_dw2_regnum_invalid_p (dst_reg)
    185  1.1  christos       || ginsn_dw2_regnum_invalid_p (src1_reg)
    186  1.1  christos       || ginsn_dw2_regnum_invalid_p (src2_reg))
    187  1.1  christos     return ginsn;
    188  1.1  christos 
    189  1.1  christos   /* ATM, shift amount, if any, cannot be represented in the GINSN_TYPE_ADD or
    190  1.1  christos      GINSN_TYPE_SUB.  As the extra information does not impact SCFI
    191  1.1  christos      correctness, skip generating ginsn for these cases.  Note
    192  1.1  christos      TBD_GINSN_INFO_LOSS.  */
    193  1.1  christos   if (src2->shifter.kind != AARCH64_MOD_NONE
    194  1.1  christos       && (src2->shifter.kind != AARCH64_MOD_LSL || src2->shifter.amount != 0))
    195  1.1  christos     return ginsn;
    196  1.1  christos 
    197  1.1  christos   ginsn = ginsn_func (insn_end_sym, true,
    198  1.1  christos 		      GINSN_SRC_REG, src1_reg, 0,
    199  1.1  christos 		      GINSN_SRC_REG, src2_reg, 0,
    200  1.1  christos 		      GINSN_DST_REG, dst_reg, 0);
    201  1.1  christos   ginsn_set_where (ginsn);
    202  1.1  christos 
    203  1.1  christos   return ginsn;
    204  1.1  christos }
    205  1.1  christos 
    206  1.1  christos /* Generate ginsn for the load pair and store pair instructions.  */
    207  1.1  christos 
    208  1.1  christos static ginsnS *
    209  1.1  christos aarch64_ginsn_ldstp (const symbolS *insn_end_sym)
    210  1.1  christos {
    211  1.1  christos   ginsnS *ginsn = NULL;
    212  1.1  christos   ginsnS *ginsn_ind = NULL;
    213  1.1  christos   ginsnS *ginsn_mem1 = NULL;
    214  1.1  christos   ginsnS *ginsn_mem2 = NULL;
    215  1.1  christos   ginsnS *ginsn_mem = NULL;
    216  1.1  christos   unsigned int opnd1_reg, opnd2_reg, addr_reg;
    217  1.1  christos   offsetT offset, mem_offset;
    218  1.1  christos   unsigned int width = 8;
    219  1.1  christos   bool load_p = false;
    220  1.1  christos   bool store_p = false;
    221  1.1  christos   bool other_p = false;
    222  1.1  christos 
    223  1.1  christos   aarch64_opnd_info *opnd1, *opnd2, *addr;
    224  1.1  christos   aarch64_inst *base = &inst.base;
    225  1.1  christos   const aarch64_opcode *opcode = base->opcode;
    226  1.1  christos 
    227  1.1  christos   /* This function is for handling ldp / stp ops only.  */
    228  1.1  christos   gas_assert (opcode->iclass == ldstpair_indexed
    229  1.1  christos 	      || opcode->iclass == ldstnapair_offs
    230  1.1  christos 	      || opcode->iclass == ldstpair_off);
    231  1.1  christos   gas_assert (aarch64_num_of_operands (opcode) == 3);
    232  1.1  christos 
    233  1.1  christos   opnd1 = &base->operands[0];
    234  1.1  christos   opnd2 = &base->operands[1];
    235  1.1  christos   addr = &base->operands[2];
    236  1.1  christos 
    237  1.1  christos   load_p = aarch64_opcode_subclass_p (opcode, F_LDST_LOAD);
    238  1.1  christos   store_p = aarch64_opcode_subclass_p (opcode, F_LDST_STORE);
    239  1.1  christos   other_p = aarch64_opcode_subclass_p (opcode, F_SUBCLASS_OTHER);
    240  1.1  christos   gas_assert (load_p || store_p || other_p);
    241  1.1  christos 
    242  1.1  christos   addr_reg = ginsn_dw2_regnum (addr);
    243  1.1  christos   gas_assert (!addr->addr.offset.is_reg);
    244  1.1  christos   mem_offset = addr->addr.offset.imm;
    245  1.1  christos 
    246  1.1  christos   offset = mem_offset;
    247  1.1  christos   /* Handle address calculation.  */
    248  1.1  christos   if ((addr->addr.preind || addr->addr.postind) && addr->addr.writeback)
    249  1.1  christos     {
    250  1.1  christos       /* Pre-indexed store, e.g., stp x29, x30, [sp, -128]!
    251  1.1  christos 	 Pre-indexed addressing is like offset addressing, except that
    252  1.1  christos 	 the base pointer is updated as a result of the instruction.
    253  1.1  christos 
    254  1.1  christos 	 Post-indexed store, e.g., stp     x29, x30, [sp],128
    255  1.1  christos 	 Post-index addressing is useful for popping off the stack.  The
    256  1.1  christos 	 instruction loads the value from the location pointed at by the stack
    257  1.1  christos 	 pointer, and then moves the stack pointer on to the next full location
    258  1.1  christos 	 in the stack.  */
    259  1.1  christos       ginsn_ind = ginsn_new_add (insn_end_sym, false,
    260  1.1  christos 				 GINSN_SRC_REG, addr_reg, 0,
    261  1.1  christos 				 GINSN_SRC_IMM, 0, mem_offset,
    262  1.1  christos 				 GINSN_DST_REG, addr_reg, 0);
    263  1.1  christos       ginsn_set_where (ginsn_ind);
    264  1.1  christos 
    265  1.1  christos       /* With post-index addressing, the value is loaded from the address in
    266  1.1  christos 	 the base pointer, and then the pointer is updated.  With pre-index
    267  1.1  christos 	 addressing, the addr computation has already been explicitly done.  */
    268  1.1  christos       offset = 0;
    269  1.1  christos     }
    270  1.1  christos 
    271  1.1  christos   /* Insns like ldpsw (marked with subclass F_SUBCLASS_OTHER) do not need to
    272  1.1  christos      generate any load or store for SCFI purposes.  Next, enforce that for CFI
    273  1.1  christos      purposes, the width of save / restore operation has to be 8 bytes or more.
    274  1.1  christos      However, the address processing component may have updated the stack
    275  1.1  christos      pointer.  At least, emit that ginsn and return.  Also note,
    276  1.1  christos      TBD_GINSN_GEN_NOT_SCFI.  */
    277  1.1  christos   if (other_p || aarch64_get_qualifier_esize (opnd1->qualifier) < 8)
    278  1.1  christos     return ginsn_ind;
    279  1.1  christos 
    280  1.1  christos   opnd1_reg = ginsn_dw2_regnum (opnd1);
    281  1.1  christos   opnd2_reg = ginsn_dw2_regnum (opnd2);
    282  1.1  christos   /* Save / restore of WZR is not of interest for SCFI.  Exit now if both
    283  1.1  christos      registers are not of interest.  */
    284  1.1  christos   if (ginsn_dw2_regnum_invalid_p (opnd1_reg)
    285  1.1  christos       && ginsn_dw2_regnum_invalid_p (opnd2_reg))
    286  1.1  christos     return ginsn_ind;
    287  1.1  christos 
    288  1.1  christos   if (opnd1->qualifier == AARCH64_OPND_QLF_S_Q)
    289  1.1  christos     {
    290  1.1  christos       width = 16;
    291  1.1  christos       if (target_big_endian)
    292  1.1  christos 	offset += 8;
    293  1.1  christos     }
    294  1.1  christos 
    295  1.1  christos   /* Load store pair where only one of the opnd registers is a zero register
    296  1.1  christos      is possible.  E.g., stp	xzr, x19, [sp, #16].  */
    297  1.1  christos   if (!ginsn_dw2_regnum_invalid_p (opnd1_reg))
    298  1.1  christos     {
    299  1.1  christos       if (store_p)
    300  1.1  christos 	{
    301  1.1  christos 	  ginsn_mem1 = ginsn_new_store (insn_end_sym, false,
    302  1.1  christos 					GINSN_SRC_REG, opnd1_reg,
    303  1.1  christos 					GINSN_DST_INDIRECT, addr_reg, offset);
    304  1.1  christos 	  ginsn_set_where (ginsn_mem1);
    305  1.1  christos 	}
    306  1.1  christos       else
    307  1.1  christos 	{
    308  1.1  christos 	  ginsn_mem1 = ginsn_new_load (insn_end_sym, false,
    309  1.1  christos 				       GINSN_SRC_INDIRECT, addr_reg, offset,
    310  1.1  christos 				       GINSN_DST_REG, opnd1_reg);
    311  1.1  christos 	  ginsn_set_where (ginsn_mem1);
    312  1.1  christos 	}
    313  1.1  christos       /* Keep track of the last memory ginsn created so far.  */
    314  1.1  christos       ginsn_mem = ginsn_mem1;
    315  1.1  christos     }
    316  1.1  christos   if (!ginsn_dw2_regnum_invalid_p (opnd2_reg))
    317  1.1  christos     {
    318  1.1  christos       if (store_p)
    319  1.1  christos 	{
    320  1.1  christos 	  ginsn_mem2 = ginsn_new_store (insn_end_sym, false,
    321  1.1  christos 					GINSN_SRC_REG, opnd2_reg,
    322  1.1  christos 					GINSN_DST_INDIRECT, addr_reg,
    323  1.1  christos 					offset + width);
    324  1.1  christos 	  ginsn_set_where (ginsn_mem2);
    325  1.1  christos 	}
    326  1.1  christos       else
    327  1.1  christos 	{
    328  1.1  christos 	  ginsn_mem2 = ginsn_new_load (insn_end_sym, false,
    329  1.1  christos 				       GINSN_SRC_INDIRECT, addr_reg, offset + width,
    330  1.1  christos 				       GINSN_DST_REG, opnd2_reg);
    331  1.1  christos 	  ginsn_set_where (ginsn_mem2);
    332  1.1  christos 	}
    333  1.1  christos       /* Keep track of the last memory ginsn created so far.  */
    334  1.1  christos       ginsn_mem = ginsn_mem2;
    335  1.1  christos     }
    336  1.1  christos 
    337  1.1  christos   if (!ginsn_dw2_regnum_invalid_p (opnd1_reg)
    338  1.1  christos       && !ginsn_dw2_regnum_invalid_p (opnd2_reg))
    339  1.1  christos     goto link_two_ginsn_mem;
    340  1.1  christos   else
    341  1.1  christos     goto link_one_ginsn_mem;
    342  1.1  christos 
    343  1.1  christos link_one_ginsn_mem:
    344  1.1  christos   /* Link the list of ginsns created.  */
    345  1.1  christos   if (addr->addr.preind && addr->addr.writeback)
    346  1.1  christos     gas_assert (!ginsn_link_next (ginsn_ind, ginsn_mem));
    347  1.1  christos 
    348  1.1  christos   if (addr->addr.postind && addr->addr.writeback)
    349  1.1  christos     gas_assert (!ginsn_link_next (ginsn_mem, ginsn_ind));
    350  1.1  christos 
    351  1.1  christos   /* Make note of the first instruction in the list.  */
    352  1.1  christos   ginsn = (addr->addr.preind && addr->addr.writeback) ? ginsn_ind : ginsn_mem;
    353  1.1  christos   return ginsn;
    354  1.1  christos 
    355  1.1  christos link_two_ginsn_mem:
    356  1.1  christos   /* Link the list of ginsns created.  */
    357  1.1  christos   if (addr->addr.preind && addr->addr.writeback)
    358  1.1  christos     gas_assert (!ginsn_link_next (ginsn_ind, ginsn_mem1));
    359  1.1  christos 
    360  1.1  christos   gas_assert (ginsn_mem1 && ginsn_mem2 && ginsn_mem1 != ginsn_mem2);
    361  1.1  christos   gas_assert (!ginsn_link_next (ginsn_mem1, ginsn_mem2));
    362  1.1  christos 
    363  1.1  christos   if (addr->addr.postind && addr->addr.writeback)
    364  1.1  christos     gas_assert (!ginsn_link_next (ginsn_mem2, ginsn_ind));
    365  1.1  christos 
    366  1.1  christos   /* Make note of the first instruction in the list.  */
    367  1.1  christos   ginsn = (addr->addr.preind && addr->addr.writeback) ? ginsn_ind : ginsn_mem1;
    368  1.1  christos   return ginsn;
    369  1.1  christos }
    370  1.1  christos 
    371  1.1  christos /* Generate ginsn for load and store instructions.  */
    372  1.1  christos 
    373  1.1  christos static ginsnS *
    374  1.1  christos aarch64_ginsn_ldstr (const symbolS *insn_end_sym)
    375  1.1  christos {
    376  1.1  christos   ginsnS *ginsn = NULL;
    377  1.1  christos   ginsnS *ginsn_ind = NULL;
    378  1.1  christos   ginsnS *ginsn_mem = NULL;
    379  1.1  christos   unsigned int opnd_reg, addr_reg;
    380  1.1  christos   offsetT offset, mem_offset;
    381  1.1  christos   bool load_p = false;
    382  1.1  christos   bool store_p = false;
    383  1.1  christos   bool other_p = false;
    384  1.1  christos 
    385  1.1  christos   aarch64_opnd_info *opnd1, *addr;
    386  1.1  christos   aarch64_inst *base = &inst.base;
    387  1.1  christos   const aarch64_opcode *opcode = base->opcode;
    388  1.1  christos 
    389  1.1  christos   /* This function is for handling ldr, str ops only.  */
    390  1.1  christos   gas_assert (opcode->iclass == ldst_imm9 || opcode->iclass == ldst_pos);
    391  1.1  christos   gas_assert (aarch64_num_of_operands (opcode) == 2);
    392  1.1  christos 
    393  1.1  christos   opnd1 = &base->operands[0];
    394  1.1  christos   addr = &base->operands[1];
    395  1.1  christos 
    396  1.1  christos   load_p = aarch64_opcode_subclass_p (opcode, F_LDST_LOAD);
    397  1.1  christos   store_p = aarch64_opcode_subclass_p (opcode, F_LDST_STORE);
    398  1.1  christos   other_p = aarch64_opcode_subclass_p (opcode, F_SUBCLASS_OTHER);
    399  1.1  christos   gas_assert (load_p || store_p || other_p);
    400  1.1  christos 
    401  1.1  christos   addr_reg = ginsn_dw2_regnum (addr);
    402  1.1  christos 
    403  1.1  christos   if (aarch64_gas_internal_fixup_p () && inst.reloc.exp.X_op == O_constant)
    404  1.1  christos     mem_offset = inst.reloc.exp.X_add_number;
    405  1.1  christos   else
    406  1.1  christos     {
    407  1.1  christos       gas_assert (!addr->addr.offset.is_reg);
    408  1.1  christos       mem_offset = addr->addr.offset.imm;
    409  1.1  christos     }
    410  1.1  christos 
    411  1.1  christos   offset = mem_offset;
    412  1.1  christos   /* Handle address calculation.  */
    413  1.1  christos   if ((addr->addr.preind || addr->addr.postind) && addr->addr.writeback)
    414  1.1  christos     {
    415  1.1  christos       ginsn_ind = ginsn_new_add (insn_end_sym, false,
    416  1.1  christos 				 GINSN_SRC_REG, addr_reg, 0,
    417  1.1  christos 				 GINSN_SRC_IMM, 0, mem_offset,
    418  1.1  christos 				 GINSN_DST_REG, addr_reg, 0);
    419  1.1  christos       ginsn_set_where (ginsn_ind);
    420  1.1  christos 
    421  1.1  christos       /* With post-index addressing, the value is loaded from the address in
    422  1.1  christos 	 the base pointer, and then the pointer is updated.  With pre-index
    423  1.1  christos 	 addressing, the addr computation has already been explicitly done.  */
    424  1.1  christos       offset = 0;
    425  1.1  christos     }
    426  1.1  christos 
    427  1.1  christos   /* Insns like stg, prfm, ldrsw etc. (marked with subclass F_SUBCLASS_OTHER)
    428  1.1  christos      do not need to generate any load / store ginsns for SCFI purposes.  Next,
    429  1.1  christos      enforce that for CFI purposes, the width of save / restore operation has
    430  1.1  christos      to be 8 bytes or more.  That said, the address processing component may
    431  1.1  christos      have updated the stack pointer.  At least, emit that ginsn and return.
    432  1.1  christos      Also note, TBD_GINSN_GEN_NOT_SCFI.  */
    433  1.1  christos   if (other_p || aarch64_get_qualifier_esize (opnd1->qualifier) < 8)
    434  1.1  christos     return ginsn_ind;
    435  1.1  christos 
    436  1.1  christos   opnd_reg = ginsn_dw2_regnum (opnd1);
    437  1.1  christos   /* Save / restore of WZR is not of interest for SCFI.  */
    438  1.1  christos   if (ginsn_dw2_regnum_invalid_p (opnd_reg))
    439  1.1  christos     return ginsn_ind;
    440  1.1  christos 
    441  1.1  christos   if (target_big_endian && opnd1->qualifier == AARCH64_OPND_QLF_S_Q)
    442  1.1  christos     offset += 8;
    443  1.1  christos 
    444  1.1  christos   if (store_p)
    445  1.1  christos     ginsn_mem = ginsn_new_store (insn_end_sym, false,
    446  1.1  christos 				 GINSN_SRC_REG, opnd_reg,
    447  1.1  christos 				 GINSN_DST_INDIRECT, addr_reg, offset);
    448  1.1  christos   else
    449  1.1  christos     ginsn_mem = ginsn_new_load (insn_end_sym, false,
    450  1.1  christos 				GINSN_SRC_INDIRECT, addr_reg, offset,
    451  1.1  christos 				GINSN_DST_REG, opnd_reg);
    452  1.1  christos   ginsn_set_where (ginsn_mem);
    453  1.1  christos 
    454  1.1  christos   if (addr->addr.preind && addr->addr.writeback)
    455  1.1  christos     gas_assert (!ginsn_link_next (ginsn_ind, ginsn_mem));
    456  1.1  christos   else if (addr->addr.postind && addr->addr.writeback)
    457  1.1  christos     gas_assert (!ginsn_link_next (ginsn_mem, ginsn_ind));
    458  1.1  christos 
    459  1.1  christos   /* Make note of the first instruction in the list.  */
    460  1.1  christos   ginsn = (addr->addr.preind && addr->addr.writeback) ? ginsn_ind : ginsn_mem;
    461  1.1  christos 
    462  1.1  christos   return ginsn;
    463  1.1  christos }
    464  1.1  christos 
    465  1.1  christos /* Generate ginsn for unconditional branch instructions.  */
    466  1.1  christos 
    467  1.1  christos static ginsnS *
    468  1.1  christos aarch64_ginsn_branch_uncond (const symbolS *insn_end_sym)
    469  1.1  christos {
    470  1.1  christos   ginsnS *ginsn = NULL;
    471  1.1  christos   const symbolS *src_symbol = NULL;
    472  1.1  christos   enum ginsn_src_type src_type = GINSN_SRC_UNKNOWN;
    473  1.1  christos   unsigned int src_reg = 0;
    474  1.1  christos 
    475  1.1  christos   aarch64_inst *base = &inst.base;
    476  1.1  christos   const aarch64_opcode *opcode = base->opcode;
    477  1.1  christos 
    478  1.1  christos   if (opcode->iclass == branch_imm
    479  1.1  christos       && (inst.reloc.type == BFD_RELOC_AARCH64_CALL26
    480  1.1  christos 	  || inst.reloc.type == BFD_RELOC_AARCH64_JUMP26))
    481  1.1  christos     {
    482  1.1  christos       if (inst.reloc.exp.X_add_number)
    483  1.1  christos 	{
    484  1.1  christos 	  /* A non-zero addend in b/bl target makes control-flow tracking
    485  1.1  christos 	     difficult.  Skip SCFI for now.  */
    486  1.1  christos 	  as_bad (_("SCFI: %#x op with non-zero addend to sym not supported"),
    487  1.1  christos 		  opcode->opcode);
    488  1.1  christos 	  return ginsn;
    489  1.1  christos 	}
    490  1.1  christos       /* b or bl.  */
    491  1.1  christos       src_symbol = inst.reloc.exp.X_add_symbol;
    492  1.1  christos       src_type = GINSN_SRC_SYMBOL;
    493  1.1  christos     }
    494  1.1  christos   else if (opcode->iclass == branch_reg
    495  1.1  christos 	   && aarch64_num_of_operands (opcode) >= 1)
    496  1.1  christos     {
    497  1.1  christos       /* Some insns (e.g., braa, blraa etc.) may have > 1 operands.  For
    498  1.1  christos 	 current SCFI implementation, it suffices however to simply pass
    499  1.1  christos 	 the information about the first source.  Although, strictly speaking,
    500  1.1  christos 	 (if reg) the source info is currently of no material use either.  */
    501  1.1  christos       src_type = GINSN_SRC_REG;
    502  1.1  christos       src_reg = ginsn_dw2_regnum (&base->operands[0]);
    503  1.1  christos     }
    504  1.1  christos   else
    505  1.1  christos     /* Skip insns like branch imm.  */
    506  1.1  christos     return ginsn;
    507  1.1  christos 
    508  1.1  christos   if (aarch64_opcode_subclass_p (opcode, F_BRANCH_CALL))
    509  1.1  christos     {
    510  1.1  christos       gas_assert (src_type != GINSN_SRC_UNKNOWN);
    511  1.1  christos       ginsn = ginsn_new_call (insn_end_sym, true,
    512  1.1  christos 			      src_type, src_reg, src_symbol);
    513  1.1  christos     }
    514  1.1  christos   else if (aarch64_opcode_subclass_p (opcode, F_BRANCH_RET))
    515  1.1  christos     /* TBD_GINSN_REPRESENTATION_LIMIT.  The following function to create a
    516  1.1  christos        GINSN_TYPE_RETURN does not allow src info ATM.  */
    517  1.1  christos     ginsn = ginsn_new_return (insn_end_sym, true);
    518  1.1  christos   else
    519  1.1  christos     ginsn = ginsn_new_jump (insn_end_sym, true,
    520  1.1  christos 			    src_type, src_reg, src_symbol);
    521  1.1  christos 
    522  1.1  christos   ginsn_set_where (ginsn);
    523  1.1  christos 
    524  1.1  christos   return ginsn;
    525  1.1  christos }
    526  1.1  christos 
    527  1.1  christos /* Generate ginsn for conditional branch instructions.  */
    528  1.1  christos 
    529  1.1  christos static ginsnS *
    530  1.1  christos aarch64_ginsn_branch_cond (const symbolS *insn_end_sym)
    531  1.1  christos {
    532  1.1  christos   ginsnS *ginsn = NULL;
    533  1.1  christos   const symbolS *src_symbol;
    534  1.1  christos   enum ginsn_src_type src_type;
    535  1.1  christos 
    536  1.1  christos   aarch64_inst *base = &inst.base;
    537  1.1  christos   const aarch64_opcode *opcode = base->opcode;
    538  1.1  christos 
    539  1.1  christos   if (inst.reloc.type == BFD_RELOC_AARCH64_BRANCH19
    540  1.1  christos       || inst.reloc.type == BFD_RELOC_AARCH64_TSTBR14)
    541  1.1  christos     {
    542  1.1  christos       if (inst.reloc.exp.X_add_number)
    543  1.1  christos 	{
    544  1.1  christos 	  /* A non-zero addend in target makes control-flow tracking
    545  1.1  christos 	     difficult.  Skip SCFI for now.  */
    546  1.1  christos 	  as_bad (_("SCFI: %#x op with non-zero addend to sym not supported"),
    547  1.1  christos 		  opcode->opcode);
    548  1.1  christos 	  return ginsn;
    549  1.1  christos 	}
    550  1.1  christos 
    551  1.1  christos       src_symbol = inst.reloc.exp.X_add_symbol;
    552  1.1  christos       src_type = GINSN_SRC_SYMBOL;
    553  1.1  christos 
    554  1.1  christos       ginsn = ginsn_new_jump_cond (insn_end_sym, true, src_type, 0, src_symbol);
    555  1.1  christos       ginsn_set_where (ginsn);
    556  1.1  christos     }
    557  1.1  christos 
    558  1.1  christos   return ginsn;
    559  1.1  christos }
    560  1.1  christos 
    561  1.1  christos /* Generate ginsn for mov instructions with reg opnd.  */
    562  1.1  christos 
    563  1.1  christos static ginsnS *
    564  1.1  christos aarch64_ginsn_mov_reg (const symbolS *insn_end_sym)
    565  1.1  christos {
    566  1.1  christos   ginsnS *ginsn = NULL;
    567  1.1  christos   unsigned int src_reg = 0, dst_reg;
    568  1.1  christos   aarch64_opnd_info *src, *dst;
    569  1.1  christos   offsetT src_imm = 0;
    570  1.1  christos   enum ginsn_src_type src_type;
    571  1.1  christos 
    572  1.1  christos   aarch64_inst *base = &inst.base;
    573  1.1  christos   const aarch64_opcode *opcode = base->opcode;
    574  1.1  christos 
    575  1.1  christos   gas_assert (aarch64_num_of_operands (opcode) == 2);
    576  1.1  christos 
    577  1.1  christos   dst = &base->operands[0];
    578  1.1  christos   src = &base->operands[1];
    579  1.1  christos 
    580  1.1  christos   dst_reg = ginsn_dw2_regnum (dst);
    581  1.1  christos   src_reg = ginsn_dw2_regnum (src);
    582  1.1  christos   src_type = GINSN_SRC_REG;
    583  1.1  christos 
    584  1.1  christos   /* FIXME Explicitly bar GINSN_TYPE_MOV with a GINSN_DW2_REGNUM_INVALID in src
    585  1.1  christos      or dest at this time.  This can be removed later when SCFI machinery is
    586  1.1  christos      more robust to deal with GINSN_DW2_REGNUM_INVALID.  */
    587  1.1  christos   if (ginsn_dw2_regnum_invalid_p (dst_reg)
    588  1.1  christos       || ginsn_dw2_regnum_invalid_p (src_reg))
    589  1.1  christos     return ginsn;
    590  1.1  christos 
    591  1.1  christos   ginsn = ginsn_new_mov (insn_end_sym, false,
    592  1.1  christos 			 src_type, src_reg, src_imm,
    593  1.1  christos 			 GINSN_DST_REG, dst_reg, 0);
    594  1.1  christos   ginsn_set_where (ginsn);
    595  1.1  christos 
    596  1.1  christos   return ginsn;
    597  1.1  christos }
    598  1.1  christos 
    599  1.1  christos /* Generate ginsn for mov instructions with imm opnd.  */
    600  1.1  christos 
    601  1.1  christos static ginsnS *
    602  1.1  christos aarch64_ginsn_mov_imm (const symbolS *insn_end_sym)
    603  1.1  christos {
    604  1.1  christos   ginsnS *ginsn = NULL;
    605  1.1  christos   unsigned int src_reg = 0, dst_reg;
    606  1.1  christos   aarch64_opnd_info *src, *dst;
    607  1.1  christos   offsetT src_imm = 0;
    608  1.1  christos   enum ginsn_src_type src_type;
    609  1.1  christos 
    610  1.1  christos   aarch64_inst *base = &inst.base;
    611  1.1  christos   const aarch64_opcode *opcode = base->opcode;
    612  1.1  christos 
    613  1.1  christos   gas_assert (aarch64_num_of_operands (opcode) == 2);
    614  1.1  christos 
    615  1.1  christos   dst = &base->operands[0];
    616  1.1  christos   src = &base->operands[1];
    617  1.1  christos 
    618  1.1  christos   /* For some mov ops, e.g., movn, movk, or movz, there may optionally be more
    619  1.1  christos      work than just a simple mov.  Skip handling these mov altogether and let
    620  1.1  christos      the aarch64_ginsn_unhandled () alert if these insns affect SCFI
    621  1.1  christos      correctness.  TBD_GINSN_GEN_NOT_SCFI.  */
    622  1.1  christos   if (src->type == AARCH64_OPND_HALF)
    623  1.1  christos     return ginsn;
    624  1.1  christos 
    625  1.1  christos   dst_reg = ginsn_dw2_regnum (dst);
    626  1.1  christos   /* FIXME Explicitly bar GINSN_TYPE_MOV which write to WZR / XZR at this time.
    627  1.1  christos      This can be removed later when SCFI machinery is more robust to deal with
    628  1.1  christos      GINSN_DW2_REGNUM_INVALID.  */
    629  1.1  christos   if (ginsn_dw2_regnum_invalid_p (dst_reg))
    630  1.1  christos     return ginsn;
    631  1.1  christos 
    632  1.1  christos   if (src->type == AARCH64_OPND_IMM_MOV
    633  1.1  christos       && aarch64_gas_internal_fixup_p () && inst.reloc.exp.X_op == O_constant)
    634  1.1  christos     {
    635  1.1  christos       src_imm = inst.reloc.exp.X_add_number;
    636  1.1  christos       src_type = GINSN_SRC_IMM;
    637  1.1  christos     }
    638  1.1  christos   else
    639  1.1  christos     /* Skip now and handle via aarch64_ginsn_unhandled () code path.  */
    640  1.1  christos     return ginsn;
    641  1.1  christos 
    642  1.1  christos   ginsn = ginsn_new_mov (insn_end_sym, false,
    643  1.1  christos 			 src_type, src_reg, src_imm,
    644  1.1  christos 			 GINSN_DST_REG, dst_reg, 0);
    645  1.1  christos   ginsn_set_where (ginsn);
    646  1.1  christos 
    647  1.1  christos   return ginsn;
    648  1.1  christos }
    649  1.1  christos 
    650  1.1  christos /* Check if an instruction is whitelisted.
    651  1.1  christos 
    652  1.1  christos    An instruction is a candidate for whitelisting if not generating ginsn for
    653  1.1  christos    it, does not affect SCFI correctness.
    654  1.1  christos 
    655  1.1  christos    TBD_GINSN_GEN_NOT_SCFI.  This function assumes GINSN_GEN_SCFI is in effect.
    656  1.1  christos    When other ginsn_gen_mode are added, this will need fixing.  */
    657  1.1  christos 
    658  1.1  christos static bool
    659  1.1  christos aarch64_ginsn_safe_to_skip_p (void)
    660  1.1  christos {
    661  1.1  christos   bool skip_p = false;
    662  1.1  christos   aarch64_opnd_info *opnd = NULL;
    663  1.1  christos   unsigned int opnd_reg;
    664  1.1  christos   int num_opnds = 0;
    665  1.1  christos   bool dp_tag_only_p = false;
    666  1.1  christos 
    667  1.1  christos   aarch64_inst *base = &inst.base;
    668  1.1  christos   const aarch64_opcode *opcode = base->opcode;
    669  1.1  christos 
    670  1.1  christos   /* ATM, whitelisting operations with no operands does not seem to be
    671  1.1  christos      necessary.  In fact, whitelisting insns like ERET will be dangerous for
    672  1.1  christos      SCFI.  So, return false now and bar any such insns from being whitelisted
    673  1.1  christos      altogether.  */
    674  1.1  christos   num_opnds = aarch64_num_of_operands (opcode);
    675  1.1  christos   if (!num_opnds)
    676  1.1  christos     return false;
    677  1.1  christos 
    678  1.1  christos   opnd = &base->operands[0];
    679  1.1  christos 
    680  1.1  christos   switch (opcode->iclass)
    681  1.1  christos     {
    682  1.1  christos     case ldst_regoff:
    683  1.1  christos       /* It is not expected to have reg offset based ld/st ops to be used
    684  1.1  christos 	 for reg save and restore operations.  Warn the user though.  */
    685  1.1  christos       opnd_reg = ginsn_dw2_regnum (opnd);
    686  1.1  christos       if (aarch64_scfi_callee_saved_p (opnd_reg))
    687  1.1  christos 	{
    688  1.1  christos 	  skip_p = true;
    689  1.1  christos 	  as_warn ("SCFI: ignored probable save/restore op with reg offset");
    690  1.1  christos 	}
    691  1.1  christos       break;
    692  1.1  christos 
    693  1.1  christos     case dp_2src:
    694  1.1  christos       /* irg insn needs to be explicitly whitelisted.  This is because the
    695  1.1  christos 	 dest is Rd_SP, but irg insn affects the tag only.  To detect irg
    696  1.1  christos 	 insn, avoid an opcode-based check, however.  */
    697  1.1  christos       dp_tag_only_p = aarch64_opcode_subclass_p (opcode, F_DP_TAG_ONLY);
    698  1.1  christos       if (dp_tag_only_p)
    699  1.1  christos 	skip_p = true;
    700  1.1  christos       break;
    701  1.1  christos 
    702  1.1  christos     default:
    703  1.1  christos       break;
    704  1.1  christos     }
    705  1.1  christos 
    706  1.1  christos   return skip_p;
    707  1.1  christos }
    708  1.1  christos 
    709  1.1  christos enum aarch64_ginsn_unhandled_code
    710  1.1  christos {
    711  1.1  christos   AARCH64_GINSN_UNHANDLED_NONE,
    712  1.1  christos   AARCH64_GINSN_UNHANDLED_DEST_REG,
    713  1.1  christos   AARCH64_GINSN_UNHANDLED_CFG,
    714  1.1  christos   AARCH64_GINSN_UNHANDLED_STACKOP,
    715  1.1  christos   AARCH64_GINSN_UNHANDLED_UNEXPECTED,
    716  1.1  christos };
    717  1.1  christos 
    718  1.1  christos /* Check the input insn for its impact on the correctness of the synthesized
    719  1.1  christos    CFI.  Returns an error code to the caller.  */
    720  1.1  christos 
    721  1.1  christos static enum aarch64_ginsn_unhandled_code
    722  1.1  christos aarch64_ginsn_unhandled (void)
    723  1.1  christos {
    724  1.1  christos   enum aarch64_ginsn_unhandled_code err = AARCH64_GINSN_UNHANDLED_NONE;
    725  1.1  christos   aarch64_inst *base = &inst.base;
    726  1.1  christos   const aarch64_opcode *opcode = base->opcode;
    727  1.1  christos   aarch64_opnd_info *dest = &base->operands[0];
    728  1.1  christos   int num_opnds = aarch64_num_of_operands (opcode);
    729  1.1  christos   aarch64_opnd_info *addr;
    730  1.1  christos   unsigned int dw2_regnum;
    731  1.1  christos   unsigned int addr_reg;
    732  1.1  christos   aarch64_opnd_info *opnd;
    733  1.1  christos   unsigned int opnd_reg;
    734  1.1  christos 
    735  1.1  christos   /* All change of flow instructions (COFI) are important for SCFI.
    736  1.1  christos      N.B. New iclasses for COFI when defined must be added here too.  */
    737  1.1  christos   if (opcode->iclass == condbranch
    738  1.1  christos       || opcode->iclass == compbranch
    739  1.1  christos       || opcode->iclass == testbranch
    740  1.1  christos       || opcode->iclass == branch_imm
    741  1.1  christos       || opcode->iclass == branch_reg)
    742  1.1  christos     err = AARCH64_GINSN_UNHANDLED_CFG;
    743  1.1  christos   /* Also, any memory instructions that may involve an update to the stack
    744  1.1  christos      pointer or save/restore of callee-saved registers must not be skipped.
    745  1.1  christos      Note that, some iclasses cannot be used to push or pop stack because of
    746  1.1  christos      disallowed writeback: ldst_unscaled, ldst_regoff, ldst_unpriv, ldstexcl,
    747  1.1  christos      loadlit, ldstnapair_offs.  Except ldstnapair_offs from the afore-mentioned
    748  1.1  christos      list, these iclasses do not seem to be amenable to being used for
    749  1.1  christos      save/restore ops either.  */
    750  1.1  christos   else if (opcode->iclass == ldstpair_off
    751  1.1  christos 	   || opcode->iclass == ldstnapair_offs
    752  1.1  christos 	   || opcode->iclass == ldstpair_indexed
    753  1.1  christos 	   || opcode->iclass == ldst_imm9
    754  1.1  christos 	   || opcode->iclass == ldst_imm10
    755  1.1  christos 	   || opcode->iclass == ldst_pos)
    756  1.1  christos     {
    757  1.1  christos       addr = &base->operands[num_opnds - 1];
    758  1.1  christos       addr_reg = ginsn_dw2_regnum (addr);
    759  1.1  christos       if (addr_reg == REG_SP || addr_reg == REG_FP)
    760  1.1  christos 	{
    761  1.1  christos 	  /* For all skipped memory operations, check if an update to REG_SP or
    762  1.1  christos 	     REG_FP is involved.  */
    763  1.1  christos 	  if ((addr->addr.postind || addr->addr.preind) && addr->addr.writeback)
    764  1.1  christos 	    err = AARCH64_GINSN_UNHANDLED_STACKOP;
    765  1.1  christos 	  /* Also check if a save / restore of a callee-saved register has been
    766  1.1  christos 	     missed.  */
    767  1.1  christos 	  else if (!aarch64_opcode_subclass_p (opcode, F_SUBCLASS_OTHER))
    768  1.1  christos 	    {
    769  1.1  christos 	      for (int i = 0; i < num_opnds - 1; i++)
    770  1.1  christos 		{
    771  1.1  christos 		  opnd = &base->operands[i];
    772  1.1  christos 		  opnd_reg = ginsn_dw2_regnum (opnd);
    773  1.1  christos 		  if (aarch64_scfi_callee_saved_p (opnd_reg)
    774  1.1  christos 		      && aarch64_get_qualifier_esize (opnd->qualifier) >= 8)
    775  1.1  christos 		    {
    776  1.1  christos 		      err = AARCH64_GINSN_UNHANDLED_STACKOP;
    777  1.1  christos 		      break;
    778  1.1  christos 		    }
    779  1.1  christos 		}
    780  1.1  christos 	    }
    781  1.1  christos 	}
    782  1.1  christos     }
    783  1.1  christos   /* STR Zn are especially complicated as they do not store in the same byte
    784  1.1  christos      order for big-endian: STR Qn stores as a 128-bit integer (MSB first),
    785  1.1  christos      whereas STR Zn stores as a stream of bytes (LSB first).  FIXME Simply punt
    786  1.1  christos      on the big-endian and little-endian SVE PCS case for now.  */
    787  1.1  christos   else if (opcode->iclass == sve_misc)
    788  1.1  christos     {
    789  1.1  christos       opnd = &base->operands[0];
    790  1.1  christos       addr = &base->operands[num_opnds - 1];
    791  1.1  christos       addr_reg = ginsn_dw2_regnum (addr);
    792  1.1  christos       opnd_reg = ginsn_dw2_regnum (opnd);
    793  1.1  christos       /* For all skipped memory operations, check if an update to REG_SP or
    794  1.1  christos 	 REG_FP is involved.  */
    795  1.1  christos       if (aarch64_get_operand_class (addr->type) == AARCH64_OPND_CLASS_ADDRESS
    796  1.1  christos 	  && (addr_reg == REG_SP || addr_reg == REG_FP)
    797  1.1  christos 	  && (((addr->addr.postind || addr->addr.preind) && addr->addr.writeback)
    798  1.1  christos 	      || aarch64_scfi_callee_saved_p (opnd_reg)))
    799  1.1  christos 	err = AARCH64_GINSN_UNHANDLED_STACKOP;
    800  1.1  christos     }
    801  1.1  christos 
    802  1.1  christos   /* Finally, irrespective of the iclass, check if the missed instructions are
    803  1.1  christos      affecting REG_SP or REG_FP.  */
    804  1.1  christos   else if (dest && (dest->type == AARCH64_OPND_Rd
    805  1.1  christos 		    || dest->type == AARCH64_OPND_Rd_SP))
    806  1.1  christos     {
    807  1.1  christos       dw2_regnum = ginsn_dw2_regnum (dest);
    808  1.1  christos 
    809  1.1  christos       if (dw2_regnum == REG_SP || dw2_regnum == REG_FP)
    810  1.1  christos 	err = AARCH64_GINSN_UNHANDLED_DEST_REG;
    811  1.1  christos     }
    812  1.1  christos 
    813  1.1  christos   return err;
    814  1.1  christos }
    815  1.1  christos 
    816  1.1  christos /* Generate one or more generic GAS instructions, a.k.a, ginsns for the
    817  1.1  christos    current machine instruction.
    818  1.1  christos 
    819  1.1  christos    Returns the head of linked list of ginsn(s) added, if success; Returns NULL
    820  1.1  christos    if failure.
    821  1.1  christos 
    822  1.1  christos    The input ginsn_gen_mode GMODE determines the set of minimal necessary
    823  1.1  christos    ginsns necessary for correctness of any passes applicable for that mode.
    824  1.1  christos    For supporting the GINSN_GEN_SCFI generation mode, following is the list of
    825  1.1  christos    machine instructions that must be translated into the corresponding ginsns
    826  1.1  christos    to ensure correctness of SCFI:
    827  1.1  christos      - All instructions affecting the two registers that could potentially
    828  1.1  christos        be used as the base register for CFA tracking.  For SCFI, the base
    829  1.1  christos        register for CFA tracking is limited to REG_SP and REG_FP only.
    830  1.1  christos      - All change of flow instructions: conditional and unconditional
    831  1.1  christos        branches, call and return from functions.
    832  1.1  christos      - All instructions that can potentially be a register save / restore
    833  1.1  christos        operations.
    834  1.1  christos      - All instructions that may update the stack pointer: pre-indexed and
    835  1.1  christos        post-indexed stack operations with writeback.
    836  1.1  christos 
    837  1.1  christos    The function currently supports GINSN_GEN_SCFI ginsn generation mode only.
    838  1.1  christos    To support other generation modes will require work on this target-specific
    839  1.1  christos    process of creation of ginsns:
    840  1.1  christos      - Some of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
    841  1.1  christos        possible starting points.
    842  1.1  christos      - Also note that ginsn representation may need enhancements.  Specifically,
    843  1.1  christos        note some TBD_GINSN_INFO_LOSS and TBD_GINSN_REPRESENTATION_LIMIT markers.
    844  1.1  christos    */
    845  1.1  christos 
    846  1.1  christos static ginsnS *
    847  1.1  christos aarch64_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
    848  1.1  christos {
    849  1.1  christos   enum aarch64_ginsn_unhandled_code err = 0;
    850  1.1  christos   ginsnS *ginsn = NULL;
    851  1.1  christos   unsigned int dw2_regnum;
    852  1.1  christos   aarch64_opnd_info *dest = NULL;
    853  1.1  christos   aarch64_inst *base = &inst.base;
    854  1.1  christos   const aarch64_opcode *opcode = base->opcode;
    855  1.1  christos 
    856  1.1  christos   /* Currently supports generation of selected ginsns, sufficient for
    857  1.1  christos      the use-case of SCFI only.  To remove this condition will require
    858  1.1  christos      work on this target-specific process of creation of ginsns.  Some
    859  1.1  christos      of such places are tagged with TBD_GINSN_GEN_NOT_SCFI to serve as
    860  1.1  christos      examples.  */
    861  1.1  christos   if (gmode != GINSN_GEN_SCFI)
    862  1.1  christos     return ginsn;
    863  1.1  christos 
    864  1.1  christos   switch (opcode->iclass)
    865  1.1  christos     {
    866  1.1  christos     case addsub_ext:
    867  1.1  christos       /* TBD_GINSN_GEN_NOT_SCFI: other insns are not of interest for SCFI.  */
    868  1.1  christos       if (aarch64_opcode_subclass_p (opcode, F_ARITH_ADD)
    869  1.1  christos 	   || aarch64_opcode_subclass_p (opcode, F_ARITH_SUB))
    870  1.1  christos 	ginsn = aarch64_ginsn_addsub_reg (insn_end_sym);
    871  1.1  christos       break;
    872  1.1  christos 
    873  1.1  christos     case addsub_imm:
    874  1.1  christos       if (aarch64_opcode_subclass_p (opcode, F_ARITH_MOV))
    875  1.1  christos 	ginsn = aarch64_ginsn_mov_reg (insn_end_sym);
    876  1.1  christos       else if (aarch64_opcode_subclass_p (opcode, F_ARITH_ADD)
    877  1.1  christos 	       || aarch64_opcode_subclass_p (opcode, F_ARITH_SUB))
    878  1.1  christos 	ginsn = aarch64_ginsn_addsub_imm (insn_end_sym);
    879  1.1  christos       /* Note how addg, subg involving tags have F_SUBCLASS_OTHER flag.  These
    880  1.1  christos 	 insns will see a GINSN_TYPE_OTHER created for them if the destination
    881  1.1  christos 	 register is of interest via the aarch64_ginsn_unhandled ()
    882  1.1  christos 	 codepath.  */
    883  1.1  christos       break;
    884  1.1  christos 
    885  1.1  christos     case movewide:
    886  1.1  christos       ginsn = aarch64_ginsn_mov_imm (insn_end_sym);
    887  1.1  christos       break;
    888  1.1  christos 
    889  1.1  christos     case ldst_imm9:
    890  1.1  christos     case ldst_pos:
    891  1.1  christos       ginsn = aarch64_ginsn_ldstr (insn_end_sym);
    892  1.1  christos       break;
    893  1.1  christos 
    894  1.1  christos     case ldstpair_indexed:
    895  1.1  christos     case ldstpair_off:
    896  1.1  christos     case ldstnapair_offs:
    897  1.1  christos       ginsn = aarch64_ginsn_ldstp (insn_end_sym);
    898  1.1  christos       break;
    899  1.1  christos 
    900  1.1  christos     case branch_imm:
    901  1.1  christos     case branch_reg:
    902  1.1  christos       ginsn = aarch64_ginsn_branch_uncond (insn_end_sym);
    903  1.1  christos       break;
    904  1.1  christos 
    905  1.1  christos     case compbranch:
    906  1.1  christos       /* Although cbz/cbnz has an additional operand and are functionally
    907  1.1  christos 	 distinct from conditional branches, it is fine to use the same ginsn
    908  1.1  christos 	 type for both from the perspective of SCFI.  */
    909  1.1  christos     case testbranch:
    910  1.1  christos     case condbranch:
    911  1.1  christos       ginsn = aarch64_ginsn_branch_cond (insn_end_sym);
    912  1.1  christos       break;
    913  1.1  christos 
    914  1.1  christos     default:
    915  1.1  christos       /* TBD_GINSN_GEN_NOT_SCFI: Skip all other opcodes uninteresting for
    916  1.1  christos 	 GINSN_GEN_SCFI mode.  */
    917  1.1  christos       break;
    918  1.1  christos     }
    919  1.1  christos 
    920  1.1  christos   if (!ginsn && !aarch64_ginsn_safe_to_skip_p ())
    921  1.1  christos     {
    922  1.1  christos       /* For all unhandled insns, check that they no not impact SCFI
    923  1.1  christos 	 correctness.  */
    924  1.1  christos       err = aarch64_ginsn_unhandled ();
    925  1.1  christos       switch (err)
    926  1.1  christos 	{
    927  1.1  christos 	case AARCH64_GINSN_UNHANDLED_NONE:
    928  1.1  christos 	  break;
    929  1.1  christos 	case AARCH64_GINSN_UNHANDLED_DEST_REG:
    930  1.1  christos 	  /* Not all writes to REG_FP are harmful in context of SCFI.  Simply
    931  1.1  christos 	     generate a GINSN_TYPE_OTHER with destination set to the
    932  1.1  christos 	     appropriate register.  The SCFI machinery will bail out if this
    933  1.1  christos 	     ginsn affects SCFI correctness.  */
    934  1.1  christos 	  dest = &base->operands[0];
    935  1.1  christos 	  dw2_regnum = ginsn_dw2_regnum (dest);
    936  1.1  christos 	  /* Sanity check.  */
    937  1.1  christos 	  gas_assert (!ginsn_dw2_regnum_invalid_p (dw2_regnum));
    938  1.1  christos 	  ginsn = ginsn_new_other (insn_end_sym, true,
    939  1.1  christos 				   GINSN_SRC_IMM, 0,
    940  1.1  christos 				   GINSN_SRC_IMM, 0,
    941  1.1  christos 				   GINSN_DST_REG, dw2_regnum);
    942  1.1  christos 	  ginsn_set_where (ginsn);
    943  1.1  christos 	  break;
    944  1.1  christos 	case AARCH64_GINSN_UNHANDLED_CFG:
    945  1.1  christos 	case AARCH64_GINSN_UNHANDLED_STACKOP:
    946  1.1  christos 	  as_bad (_("SCFI: unhandled op %#x may cause incorrect CFI"),
    947  1.1  christos 		  opcode->opcode);
    948  1.1  christos 	  break;
    949  1.1  christos 	case AARCH64_GINSN_UNHANDLED_UNEXPECTED:
    950  1.1  christos 	  as_bad (_("SCFI: unexpected op %#x may cause incorrect CFI"),
    951  1.1  christos 		  opcode->opcode);
    952  1.1  christos 	  break;
    953  1.1  christos 	default:
    954  1.1  christos 	  abort ();
    955  1.1  christos 	  break;
    956  1.1  christos 	}
    957  1.1  christos     }
    958  1.1  christos 
    959  1.1  christos   return ginsn;
    960  1.1  christos }
    961  1.1  christos 
    962  1.1  christos #endif /* OBJ_ELF.  */
    963  1.1  christos 
    964