Home | History | Annotate | Line # | Download | only in libgcc
      1 /* Exception handling and frame unwind runtime interface routines.
      2    Copyright (C) 2001-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 /* @@@ Really this should be out of line, but this also causes link
     26    compatibility problems with the base ABI.  This is slightly better
     27    than duplicating code, however.  */
     28 
     29 #ifndef GCC_UNWIND_PE_H
     30 #define GCC_UNWIND_PE_H
     31 
     32 /* If using C++, references to abort have to be qualified with std::.  */
     33 #if __cplusplus
     34 #define __gxx_abort std::abort
     35 #else
     36 #define __gxx_abort abort
     37 #endif
     38 
     39 /* Pointer encodings, from dwarf2.h.  */
     40 #define DW_EH_PE_absptr         0x00
     41 #define DW_EH_PE_omit           0xff
     42 
     43 #define DW_EH_PE_uleb128        0x01
     44 #define DW_EH_PE_udata2         0x02
     45 #define DW_EH_PE_udata4         0x03
     46 #define DW_EH_PE_udata8         0x04
     47 #define DW_EH_PE_sleb128        0x09
     48 #define DW_EH_PE_sdata2         0x0A
     49 #define DW_EH_PE_sdata4         0x0B
     50 #define DW_EH_PE_sdata8         0x0C
     51 #define DW_EH_PE_signed         0x08
     52 
     53 #define DW_EH_PE_pcrel          0x10
     54 #define DW_EH_PE_textrel        0x20
     55 #define DW_EH_PE_datarel        0x30
     56 #define DW_EH_PE_funcrel        0x40
     57 #define DW_EH_PE_aligned        0x50
     58 
     59 #define DW_EH_PE_indirect	0x80
     60 
     61 
     63 #ifndef NO_SIZE_OF_ENCODED_VALUE
     64 
     65 /* Given an encoding, return the number of bytes the format occupies.
     66    This is only defined for fixed-size encodings, and so does not
     67    include leb128.  */
     68 
     69 static unsigned int
     70 size_of_encoded_value (unsigned char encoding) __attribute__ ((unused));
     71 
     72 static unsigned int
     73 size_of_encoded_value (unsigned char encoding)
     74 {
     75   if (encoding == DW_EH_PE_omit)
     76     return 0;
     77 
     78   switch (encoding & 0x07)
     79     {
     80     case DW_EH_PE_absptr:
     81       return sizeof (void *);
     82     case DW_EH_PE_udata2:
     83       return 2;
     84     case DW_EH_PE_udata4:
     85       return 4;
     86     case DW_EH_PE_udata8:
     87       return 8;
     88     }
     89   __gxx_abort ();
     90 }
     91 
     92 #endif
     93 
     94 #ifndef NO_BASE_OF_ENCODED_VALUE
     95 
     96 /* Given an encoding and an _Unwind_Context, return the base to which
     97    the encoding is relative.  This base may then be passed to
     98    read_encoded_value_with_base for use when the _Unwind_Context is
     99    not available.  */
    100 
    101 static _Unwind_Ptr
    102 base_of_encoded_value (unsigned char encoding, struct _Unwind_Context *context)
    103 {
    104   if (encoding == DW_EH_PE_omit)
    105     return 0;
    106 
    107   switch (encoding & 0x70)
    108     {
    109     case DW_EH_PE_absptr:
    110     case DW_EH_PE_pcrel:
    111     case DW_EH_PE_aligned:
    112       return 0;
    113 
    114     case DW_EH_PE_textrel:
    115       return _Unwind_GetTextRelBase (context);
    116     case DW_EH_PE_datarel:
    117       return _Unwind_GetDataRelBase (context);
    118     case DW_EH_PE_funcrel:
    119       return _Unwind_GetRegionStart (context);
    120     }
    121   __gxx_abort ();
    122 }
    123 
    124 #endif
    125 
    126 /* Read an unsigned leb128 value from P, store the value in VAL, return
    127    P incremented past the value.  We assume that a word is large enough to
    128    hold any value so encoded; if it is smaller than a pointer on some target,
    129    pointers should not be leb128 encoded on that target.  */
    130 
    131 static const unsigned char *
    132 read_uleb128 (const unsigned char *p, _uleb128_t *val)
    133 {
    134   unsigned int shift = 0;
    135   unsigned char byte;
    136   _uleb128_t result;
    137 
    138   result = 0;
    139   do
    140     {
    141       byte = *p++;
    142       result |= ((_uleb128_t)byte & 0x7f) << shift;
    143       shift += 7;
    144     }
    145   while (byte & 0x80);
    146 
    147   *val = result;
    148   return p;
    149 }
    150 
    151 /* Similar, but read a signed leb128 value.  */
    152 
    153 static const unsigned char *
    154 read_sleb128 (const unsigned char *p, _sleb128_t *val)
    155 {
    156   unsigned int shift = 0;
    157   unsigned char byte;
    158   _uleb128_t result;
    159 
    160   result = 0;
    161   do
    162     {
    163       byte = *p++;
    164       result |= ((_uleb128_t)byte & 0x7f) << shift;
    165       shift += 7;
    166     }
    167   while (byte & 0x80);
    168 
    169   /* Sign-extend a negative value.  */
    170   if (shift < 8 * sizeof(result) && (byte & 0x40) != 0)
    171     result |= -(((_uleb128_t)1L) << shift);
    172 
    173   *val = (_sleb128_t) result;
    174   return p;
    175 }
    176 
    177 extern _Unwind_Ptr _Unwind_gnu_Find_got (_Unwind_Ptr);
    178 
    179 /* Load an encoded value from memory at P.  The value is returned in VAL;
    180    The function returns P incremented past the value.  BASE is as given
    181    by base_of_encoded_value for this encoding in the appropriate context.  */
    182 
    183 #pragma GCC diagnostic push
    184 #pragma GCC diagnostic ignored "-Waddress-of-packed-member"
    185 
    186 static const unsigned char *
    187 read_encoded_value_with_base (unsigned char encoding, _Unwind_Ptr base,
    188 			      const unsigned char *p, _Unwind_Ptr *val)
    189 {
    190   union unaligned
    191     {
    192       void *ptr;
    193       unsigned u2 __attribute__ ((mode (HI)));
    194       unsigned u4 __attribute__ ((mode (SI)));
    195       unsigned u8 __attribute__ ((mode (DI)));
    196       signed s2 __attribute__ ((mode (HI)));
    197       signed s4 __attribute__ ((mode (SI)));
    198       signed s8 __attribute__ ((mode (DI)));
    199     } __attribute__((__packed__));
    200 
    201   const union unaligned *u = (const union unaligned *) p;
    202   _Unwind_Internal_Ptr result;
    203 
    204   if (encoding == DW_EH_PE_aligned)
    205     {
    206       _Unwind_Internal_Ptr a = (_Unwind_Internal_Ptr) p;
    207       a = (a + sizeof (void *) - 1) & - sizeof(void *);
    208       result = *(_Unwind_Internal_Ptr *) a;
    209       p = (const unsigned char *) (_Unwind_Internal_Ptr) (a + sizeof (void *));
    210     }
    211   else
    212     {
    213       switch (encoding & 0x0f)
    214 	{
    215 	case DW_EH_PE_absptr:
    216 	  result = (_Unwind_Internal_Ptr) u->ptr;
    217 	  p += sizeof (void *);
    218 	  break;
    219 
    220 	case DW_EH_PE_uleb128:
    221 	  {
    222 	    _uleb128_t tmp;
    223 	    p = read_uleb128 (p, &tmp);
    224 	    result = (_Unwind_Internal_Ptr) tmp;
    225 	  }
    226 	  break;
    227 
    228 	case DW_EH_PE_sleb128:
    229 	  {
    230 	    _sleb128_t tmp;
    231 	    p = read_sleb128 (p, &tmp);
    232 	    result = (_Unwind_Internal_Ptr) tmp;
    233 	  }
    234 	  break;
    235 
    236 	case DW_EH_PE_udata2:
    237 	  result = u->u2;
    238 	  p += 2;
    239 	  break;
    240 	case DW_EH_PE_udata4:
    241 	  result = u->u4;
    242 	  p += 4;
    243 	  break;
    244 	case DW_EH_PE_udata8:
    245 	  result = u->u8;
    246 	  p += 8;
    247 	  break;
    248 
    249 	case DW_EH_PE_sdata2:
    250 	  result = u->s2;
    251 	  p += 2;
    252 	  break;
    253 	case DW_EH_PE_sdata4:
    254 	  result = u->s4;
    255 	  p += 4;
    256 	  break;
    257 	case DW_EH_PE_sdata8:
    258 	  result = u->s8;
    259 	  p += 8;
    260 	  break;
    261 
    262 	default:
    263 	  __gxx_abort ();
    264 	}
    265 
    266       if (result != 0)
    267 	{
    268 #if __FDPIC__
    269 	  /* FDPIC relative addresses imply taking the GOT address
    270 	     into account.  */
    271 	  if ((encoding & DW_EH_PE_pcrel) && (encoding & DW_EH_PE_indirect))
    272 	    {
    273 	      result += _Unwind_gnu_Find_got ((_Unwind_Ptr) u);
    274 	      result = *(_Unwind_Internal_Ptr *) result;
    275 	    }
    276 	  else
    277 	    {
    278 	      result += ((encoding & 0x70) == DW_EH_PE_pcrel
    279 			 ? (_Unwind_Internal_Ptr) u : base);
    280 	      if (encoding & DW_EH_PE_indirect)
    281 		result = *(_Unwind_Internal_Ptr *) result;
    282 	    }
    283 #else
    284 	  result += ((encoding & 0x70) == DW_EH_PE_pcrel
    285 		     ? (_Unwind_Internal_Ptr) u : base);
    286 	  if (encoding & DW_EH_PE_indirect)
    287 	    result = *(_Unwind_Internal_Ptr *) result;
    288 #endif
    289 	}
    290     }
    291 
    292   *val = result;
    293   return p;
    294 }
    295 
    296 #pragma GCC diagnostic pop
    297 
    298 #ifndef NO_BASE_OF_ENCODED_VALUE
    299 
    300 /* Like read_encoded_value_with_base, but get the base from the context
    301    rather than providing it directly.  */
    302 
    303 static inline const unsigned char *
    304 read_encoded_value (struct _Unwind_Context *context, unsigned char encoding,
    305 		    const unsigned char *p, _Unwind_Ptr *val)
    306 {
    307   return read_encoded_value_with_base (encoding,
    308 		base_of_encoded_value (encoding, context),
    309 		p, val);
    310 }
    311 
    312 #endif
    313 
    314 #endif /* unwind-pe.h */
    315