Home | History | Annotate | Line # | Download | only in libgcc
      1 /* DWARF2 exception handling CFA execution engine.
      2    Copyright (C) 1997-2024 Free Software Foundation, Inc.
      3 
      4    This file is part of GCC.
      5 
      6    GCC is free software; you can redistribute it and/or modify it
      7    under the terms of the GNU General Public License as published by
      8    the Free Software Foundation; either version 3, or (at your option)
      9    any later version.
     10 
     11    GCC is distributed in the hope that it will be useful, but WITHOUT
     12    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     13    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
     14    License for more details.
     15 
     16    Under Section 7 of GPL version 3, you are granted additional
     17    permissions described in the GCC Runtime Library Exception, version
     18    3.1, as published by the Free Software Foundation.
     19 
     20    You should have received a copy of the GNU General Public License and
     21    a copy of the GCC Runtime Library Exception along with this program;
     22    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
     23    <http://www.gnu.org/licenses/>.  */
     24 
     25 /* This file is included from unwind-dw2.c to specialize the code for certain
     26    values of DATA_ALIGN and CODE_ALIGN.  These macros must be defined prior to
     27    including this file.  */
     28 
     29 {
     30   struct frame_state_reg_info *unused_rs = NULL;
     31 
     32   /* Don't allow remember/restore between CIE and FDE programs.  */
     33   fs->regs.prev = NULL;
     34 
     35   /* The comparison with the return address uses < rather than <= because
     36      we are only interested in the effects of code before the call; for a
     37      noreturn function, the return address may point to unrelated code with
     38      a different stack configuration that we are not interested in.  We
     39      assume that the call itself is unwind info-neutral; if not, or if
     40      there are delay instructions that adjust the stack, these must be
     41      reflected at the point immediately before the call insn.
     42      In signal frames, return address is after last completed instruction,
     43      so we add 1 to return address to make the comparison <=.  */
     44   while (insn_ptr < insn_end
     45 	 && fs->pc < context->ra + _Unwind_IsSignalFrame (context))
     46     {
     47       unsigned char insn = *insn_ptr++;
     48       _uleb128_t reg, utmp;
     49       _sleb128_t offset, stmp;
     50 
     51       if ((insn & 0xc0) == DW_CFA_advance_loc)
     52 	fs->pc += (insn & 0x3f) * CODE_ALIGN;
     53       else if ((insn & 0xc0) == DW_CFA_offset)
     54 	{
     55 	  reg = insn & 0x3f;
     56 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
     57 	  offset = (_Unwind_Sword) utmp * DATA_ALIGN;
     58 	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
     59 	  if (UNWIND_COLUMN_IN_RANGE (reg))
     60 	    {
     61 	      fs->regs.how[reg] = REG_SAVED_OFFSET;
     62 	      fs->regs.reg[reg].loc.offset = offset;
     63 	    }
     64 	}
     65       else if ((insn & 0xc0) == DW_CFA_restore)
     66 	{
     67 	  reg = insn & 0x3f;
     68 	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
     69 	  if (UNWIND_COLUMN_IN_RANGE (reg))
     70 	    fs->regs.how[reg] = REG_UNSAVED;
     71 	}
     72       else switch (insn)
     73 	{
     74 	case DW_CFA_set_loc:
     75 	  {
     76 	    _Unwind_Ptr pc;
     77 
     78 	    insn_ptr = read_encoded_value (context, fs->fde_encoding,
     79 					   insn_ptr, &pc);
     80 	    fs->pc = (void *) pc;
     81 	  }
     82 	  break;
     83 
     84 	case DW_CFA_advance_loc1:
     85 	  fs->pc += read_1u (insn_ptr) * CODE_ALIGN;
     86 	  insn_ptr += 1;
     87 	  break;
     88 	case DW_CFA_advance_loc2:
     89 	  fs->pc += read_2u (insn_ptr) * CODE_ALIGN;
     90 	  insn_ptr += 2;
     91 	  break;
     92 	case DW_CFA_advance_loc4:
     93 	  fs->pc += read_4u (insn_ptr) * CODE_ALIGN;
     94 	  insn_ptr += 4;
     95 	  break;
     96 
     97 	case DW_CFA_offset_extended:
     98 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
     99 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
    100 	  offset = (_Unwind_Sword) utmp * DATA_ALIGN;
    101 	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
    102 	  if (UNWIND_COLUMN_IN_RANGE (reg))
    103 	    {
    104 	      fs->regs.how[reg] = REG_SAVED_OFFSET;
    105 	      fs->regs.reg[reg].loc.offset = offset;
    106 	    }
    107 	  break;
    108 
    109 	case DW_CFA_restore_extended:
    110 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
    111 	  /* FIXME, this is wrong; the CIE might have said that the
    112 	     register was saved somewhere.  */
    113 	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
    114 	  if (UNWIND_COLUMN_IN_RANGE (reg))
    115 	    fs->regs.how[reg] = REG_UNSAVED;
    116 	  break;
    117 
    118 	case DW_CFA_same_value:
    119 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
    120 	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
    121 	  if (UNWIND_COLUMN_IN_RANGE (reg))
    122 	    fs->regs.how[reg] = REG_UNSAVED;
    123 	  break;
    124 
    125 	case DW_CFA_undefined:
    126 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
    127 	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
    128 	  if (UNWIND_COLUMN_IN_RANGE (reg))
    129 	    fs->regs.how[reg] = REG_UNDEFINED;
    130 	  break;
    131 
    132 	case DW_CFA_nop:
    133 	  break;
    134 
    135 	case DW_CFA_register:
    136 	  {
    137 	    _uleb128_t reg2;
    138 	    insn_ptr = read_uleb128 (insn_ptr, &reg);
    139 	    insn_ptr = read_uleb128 (insn_ptr, &reg2);
    140 	    reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
    141 	    if (UNWIND_COLUMN_IN_RANGE (reg))
    142 	      {
    143 		fs->regs.how[reg] = REG_SAVED_REG;
    144 	        fs->regs.reg[reg].loc.reg = (_Unwind_Word)reg2;
    145 	      }
    146 	  }
    147 	  break;
    148 
    149 	case DW_CFA_remember_state:
    150 	  {
    151 	    struct frame_state_reg_info *new_rs;
    152 	    if (unused_rs)
    153 	      {
    154 		new_rs = unused_rs;
    155 		unused_rs = unused_rs->prev;
    156 	      }
    157 	    else
    158 	      new_rs = alloca (sizeof (struct frame_state_reg_info));
    159 
    160 	    *new_rs = fs->regs;
    161 	    fs->regs.prev = new_rs;
    162 	  }
    163 	  break;
    164 
    165 	case DW_CFA_restore_state:
    166 	  {
    167 	    struct frame_state_reg_info *old_rs = fs->regs.prev;
    168 	    fs->regs = *old_rs;
    169 	    old_rs->prev = unused_rs;
    170 	    unused_rs = old_rs;
    171 	  }
    172 	  break;
    173 
    174 	case DW_CFA_def_cfa:
    175 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
    176 	  fs->regs.cfa_reg = (_Unwind_Word)utmp;
    177 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
    178 	  fs->regs.cfa_offset = (_Unwind_Word)utmp;
    179 	  fs->regs.cfa_how = CFA_REG_OFFSET;
    180 	  break;
    181 
    182 	case DW_CFA_def_cfa_register:
    183 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
    184 	  fs->regs.cfa_reg = (_Unwind_Word)utmp;
    185 	  fs->regs.cfa_how = CFA_REG_OFFSET;
    186 	  break;
    187 
    188 	case DW_CFA_def_cfa_offset:
    189 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
    190 	  fs->regs.cfa_offset = utmp;
    191 	  /* cfa_how deliberately not set.  */
    192 	  break;
    193 
    194 	case DW_CFA_def_cfa_expression:
    195 	  fs->regs.cfa_exp = insn_ptr;
    196 	  fs->regs.cfa_how = CFA_EXP;
    197 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
    198 	  insn_ptr += utmp;
    199 	  break;
    200 
    201 	case DW_CFA_expression:
    202 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
    203 	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
    204 	  if (UNWIND_COLUMN_IN_RANGE (reg))
    205 	    {
    206 	      fs->regs.how[reg] = REG_SAVED_EXP;
    207 	      fs->regs.reg[reg].loc.exp = insn_ptr;
    208 	    }
    209 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
    210 	  insn_ptr += utmp;
    211 	  break;
    212 
    213 	  /* Dwarf3.  */
    214 	case DW_CFA_offset_extended_sf:
    215 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
    216 	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
    217 	  offset = stmp * DATA_ALIGN;
    218 	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
    219 	  if (UNWIND_COLUMN_IN_RANGE (reg))
    220 	    {
    221 	      fs->regs.how[reg] = REG_SAVED_OFFSET;
    222 	      fs->regs.reg[reg].loc.offset = offset;
    223 	    }
    224 	  break;
    225 
    226 	case DW_CFA_def_cfa_sf:
    227 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
    228 	  fs->regs.cfa_reg = (_Unwind_Word)utmp;
    229 	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
    230 	  fs->regs.cfa_offset = (_Unwind_Sword)stmp;
    231 	  fs->regs.cfa_how = CFA_REG_OFFSET;
    232 	  fs->regs.cfa_offset *= DATA_ALIGN;
    233 	  break;
    234 
    235 	case DW_CFA_def_cfa_offset_sf:
    236 	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
    237 	  fs->regs.cfa_offset = (_Unwind_Sword)stmp;
    238 	  fs->regs.cfa_offset *= DATA_ALIGN;
    239 	  /* cfa_how deliberately not set.  */
    240 	  break;
    241 
    242 	case DW_CFA_val_offset:
    243 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
    244 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
    245 	  offset = (_Unwind_Sword) utmp * DATA_ALIGN;
    246 	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
    247 	  if (UNWIND_COLUMN_IN_RANGE (reg))
    248 	    {
    249 	      fs->regs.how[reg] = REG_SAVED_VAL_OFFSET;
    250 	      fs->regs.reg[reg].loc.offset = offset;
    251 	    }
    252 	  break;
    253 
    254 	case DW_CFA_val_offset_sf:
    255 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
    256 	  insn_ptr = read_sleb128 (insn_ptr, &stmp);
    257 	  offset = stmp * DATA_ALIGN;
    258 	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
    259 	  if (UNWIND_COLUMN_IN_RANGE (reg))
    260 	    {
    261 	      fs->regs.how[reg] = REG_SAVED_VAL_OFFSET;
    262 	      fs->regs.reg[reg].loc.offset = offset;
    263 	    }
    264 	  break;
    265 
    266 	case DW_CFA_val_expression:
    267 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
    268 	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
    269 	  if (UNWIND_COLUMN_IN_RANGE (reg))
    270 	    {
    271 	      fs->regs.how[reg] = REG_SAVED_VAL_EXP;
    272 	      fs->regs.reg[reg].loc.exp = insn_ptr;
    273 	    }
    274 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
    275 	  insn_ptr += utmp;
    276 	  break;
    277 
    278 	case DW_CFA_GNU_window_save:
    279 #if defined (__aarch64__) && !defined (__ILP32__)
    280 	  /* This CFA is multiplexed with Sparc.  On AArch64 it's used to toggle
    281 	     return address signing status.  REG_UNSAVED/REG_UNSAVED_ARCHEXT
    282 	     mean RA signing is disabled/enabled.  */
    283 	  reg = DWARF_REGNUM_AARCH64_RA_STATE;
    284 	  gcc_assert (fs->regs.how[reg] == REG_UNSAVED
    285 		      || fs->regs.how[reg] == REG_UNSAVED_ARCHEXT);
    286 	  if (fs->regs.how[reg] == REG_UNSAVED)
    287 	    fs->regs.how[reg] = REG_UNSAVED_ARCHEXT;
    288 	  else
    289 	    fs->regs.how[reg] = REG_UNSAVED;
    290 #else
    291 	  /* ??? Hardcoded for SPARC register window configuration.  */
    292 	  if (__LIBGCC_DWARF_FRAME_REGISTERS__ >= 32)
    293 	    for (reg = 16; reg < 32; ++reg)
    294 	      {
    295 		fs->regs.how[reg] = REG_SAVED_OFFSET;
    296 		fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *);
    297 	      }
    298 #endif
    299 	  break;
    300 
    301 	case DW_CFA_GNU_args_size:
    302 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
    303 	  context->args_size = (_Unwind_Word)utmp;
    304 	  break;
    305 
    306 	case DW_CFA_GNU_negative_offset_extended:
    307 	  /* Obsoleted by DW_CFA_offset_extended_sf, but used by
    308 	     older PowerPC code.  */
    309 	  insn_ptr = read_uleb128 (insn_ptr, &reg);
    310 	  insn_ptr = read_uleb128 (insn_ptr, &utmp);
    311 	  offset = (_Unwind_Word) utmp * DATA_ALIGN;
    312 	  reg = DWARF_REG_TO_UNWIND_COLUMN (reg);
    313 	  if (UNWIND_COLUMN_IN_RANGE (reg))
    314 	    {
    315 	      fs->regs.how[reg] = REG_SAVED_OFFSET;
    316 	      fs->regs.reg[reg].loc.offset = -offset;
    317 	    }
    318 	  break;
    319 
    320 	default:
    321 	  gcc_unreachable ();
    322 	}
    323     }
    324 }
    325 
    326 #undef DATA_ALIGN
    327 #undef CODE_ALIGN
    328