1 1.1 christos /* tc-aarch64-ginsn.c -- Ginsn generation for the AArch64 ISA 2 1.1 christos 3 1.1.1.2 christos Copyright (C) 2024-2026 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