• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* DW_EH_PE_* support for libdw unwinder.
2    Copyright (C) 2009-2010, 2014, 2015 Red Hat, Inc.
3    This file is part of elfutils.
4 
5    This file is free software; you can redistribute it and/or modify
6    it under the terms of either
7 
8      * the GNU Lesser General Public License as published by the Free
9        Software Foundation; either version 3 of the License, or (at
10        your option) any later version
11 
12    or
13 
14      * the GNU General Public License as published by the Free
15        Software Foundation; either version 2 of the License, or (at
16        your option) any later version
17 
18    or both in parallel, as here.
19 
20    elfutils is distributed in the hope that it will be useful, but
21    WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23    General Public License for more details.
24 
25    You should have received copies of the GNU General Public License and
26    the GNU Lesser General Public License along with this program.  If
27    not, see <http://www.gnu.org/licenses/>.  */
28 
29 #ifndef _ENCODED_VALUE_H
30 #define _ENCODED_VALUE_H 1
31 
32 #include <dwarf.h>
33 #include <stdlib.h>
34 #include "libdwP.h"
35 #include "../libelf/common.h"
36 
37 
38 /* Returns zero if the value is omitted, the encoding is unknown or
39    the (leb128) size cannot be determined.  */
40 static size_t __attribute__ ((unused))
encoded_value_size(const Elf_Data * data,const unsigned char e_ident[],uint8_t encoding,const uint8_t * p)41 encoded_value_size (const Elf_Data *data, const unsigned char e_ident[],
42 		    uint8_t encoding, const uint8_t *p)
43 {
44   if (encoding == DW_EH_PE_omit)
45     return 0;
46 
47   switch (encoding & 0x07)
48     {
49     case DW_EH_PE_udata2:
50       return 2;
51     case DW_EH_PE_udata4:
52       return 4;
53     case DW_EH_PE_udata8:
54       return 8;
55 
56     case DW_EH_PE_absptr:
57       return e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
58 
59     case DW_EH_PE_uleb128:
60       if (p != NULL)
61 	{
62 	  const uint8_t *end = p;
63 	  while (end < (uint8_t *) data->d_buf + data->d_size)
64 	    if (*end++ & 0x80u)
65 	      return end - p;
66 	}
67       return 0;
68 
69     default:
70       return 0;
71     }
72 }
73 
74 /* Returns zero when value was read successfully, minus one otherwise.  */
75 static inline int __attribute__ ((unused))
__libdw_cfi_read_address_inc(const Dwarf_CFI * cache,const unsigned char ** addrp,int width,Dwarf_Addr * ret)76 __libdw_cfi_read_address_inc (const Dwarf_CFI *cache,
77 			      const unsigned char **addrp,
78 			      int width, Dwarf_Addr *ret)
79 {
80   width = width ?: cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
81 
82   if (cache->dbg != NULL)
83     return __libdw_read_address_inc (cache->dbg, IDX_debug_frame,
84 				     addrp, width, ret);
85 
86   /* Only .debug_frame might have relocation to consider.
87      Read plain values from .eh_frame data.  */
88 
89   const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
90   Dwarf eh_dbg = { .other_byte_order = MY_ELFDATA != cache->e_ident[EI_DATA] };
91 
92   if (width == 4)
93     {
94       if (unlikely (*addrp + 4 > endp))
95 	{
96 	invalid_data:
97 	  __libdw_seterrno (DWARF_E_INVALID_CFI);
98 	  return -1;
99 	}
100       *ret = read_4ubyte_unaligned_inc (&eh_dbg, *addrp);
101     }
102   else
103     {
104       if (unlikely (*addrp + 8 > endp))
105 	goto invalid_data;
106       *ret = read_8ubyte_unaligned_inc (&eh_dbg, *addrp);
107     }
108   return 0;
109 }
110 
111 /* Returns true on error, false otherwise. */
112 static bool __attribute__ ((unused))
read_encoded_value(const Dwarf_CFI * cache,uint8_t encoding,const uint8_t ** p,Dwarf_Addr * result)113 read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding,
114 		    const uint8_t **p, Dwarf_Addr *result)
115 {
116   *result = 0;
117   switch (encoding & 0x70)
118     {
119     case DW_EH_PE_absptr:
120       break;
121     case DW_EH_PE_pcrel:
122       *result = (cache->frame_vaddr
123 		 + (*p - (const uint8_t *) cache->data->d.d_buf));
124       break;
125     case DW_EH_PE_textrel:
126       // ia64: segrel
127       *result = cache->textrel;
128       break;
129     case DW_EH_PE_datarel:
130       // i386: GOTOFF
131       // ia64: gprel
132       *result = cache->datarel;
133       break;
134     case DW_EH_PE_funcrel:	/* XXX */
135       break;
136     case DW_EH_PE_aligned:
137       {
138 	const size_t size = encoded_value_size (&cache->data->d,
139 						cache->e_ident,
140 						encoding, *p);
141 	if (unlikely (size == 0))
142 	  return true;
143 	size_t align = ((cache->frame_vaddr
144 			 + (*p - (const uint8_t *) cache->data->d.d_buf))
145 			& (size - 1));
146 	if (align != 0)
147 	  *p += size - align;
148 	break;
149       }
150 
151     default:
152       __libdw_seterrno (DWARF_E_INVALID_CFI);
153       return true;
154     }
155 
156   Dwarf_Addr value = 0;
157   const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
158   switch (encoding & 0x0f)
159     {
160     case DW_EH_PE_udata2:
161       if (unlikely (*p + 2 > endp))
162 	{
163 	invalid_data:
164 	  __libdw_seterrno (DWARF_E_INVALID_CFI);
165 	  return true;
166 	}
167       value = read_2ubyte_unaligned_inc (cache, *p);
168       break;
169 
170     case DW_EH_PE_sdata2:
171       if (unlikely (*p + 2 > endp))
172 	goto invalid_data;
173       value = read_2sbyte_unaligned_inc (cache, *p);
174       break;
175 
176     case DW_EH_PE_udata4:
177       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
178 	return true;
179       break;
180 
181     case DW_EH_PE_sdata4:
182       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
183 	return true;
184       value = (Dwarf_Sword) (Elf32_Sword) value; /* Sign-extend.  */
185       break;
186 
187     case DW_EH_PE_udata8:
188     case DW_EH_PE_sdata8:
189       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 8, &value) != 0))
190 	return true;
191       break;
192 
193     case DW_EH_PE_absptr:
194       if (unlikely (__libdw_cfi_read_address_inc (cache, p, 0, &value) != 0))
195 	return true;
196       break;
197 
198     case DW_EH_PE_uleb128:
199       get_uleb128 (value, *p, endp);
200       break;
201 
202     case DW_EH_PE_sleb128:
203       get_sleb128 (value, *p, endp);
204       break;
205 
206     default:
207       __libdw_seterrno (DWARF_E_INVALID_CFI);
208       return true;
209     }
210 
211   *result += value;
212 
213   if (encoding & DW_EH_PE_indirect)
214     {
215       if (unlikely (*result < cache->frame_vaddr))
216 	return true;
217       *result -= cache->frame_vaddr;
218       size_t ptrsize = encoded_value_size (NULL, cache->e_ident,
219 					   DW_EH_PE_absptr, NULL);
220       if (unlikely (cache->data->d.d_size < ptrsize
221 		    || *result > (cache->data->d.d_size - ptrsize)))
222 	return true;
223       const uint8_t *ptr = cache->data->d.d_buf + *result;
224       if (unlikely (__libdw_cfi_read_address_inc (cache, &ptr, 0, result)
225 		    != 0))
226 	return true;
227     }
228 
229   return false;
230 }
231 
232 #endif	/* encoded-value.h */
233