1  /* DWARF2 exception handling CFA execution engine.
       2     Copyright (C) 1997-2023 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, ®);
      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, ®);
     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, ®);
     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, ®);
     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, ®);
     139  	    insn_ptr = read_uleb128 (insn_ptr, ®2);
     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, ®);
     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, ®);
     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, ®);
     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, ®);
     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, ®);
     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, ®);
     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