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.  */
40static size_t __attribute__ ((unused))
41encoded_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
68    default:
69      return 0;
70    }
71}
72
73/* Returns zero when value was read successfully, minus one otherwise.  */
74static inline int __attribute__ ((unused))
75__libdw_cfi_read_address_inc (const Dwarf_CFI *cache,
76			      const unsigned char **addrp,
77			      int width, Dwarf_Addr *ret)
78{
79  width = width ?: cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
80
81  if (cache->dbg != NULL)
82    return __libdw_read_address_inc (cache->dbg, IDX_debug_frame,
83				     addrp, width, ret);
84
85  /* Only .debug_frame might have relocation to consider.
86     Read plain values from .eh_frame data.  */
87
88  const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
89  Dwarf eh_dbg = { .other_byte_order = MY_ELFDATA != cache->e_ident[EI_DATA] };
90
91  if (width == 4)
92    {
93      if (unlikely (*addrp + 4 > endp))
94	{
95	invalid_data:
96	  __libdw_seterrno (DWARF_E_INVALID_CFI);
97	  return -1;
98	}
99      *ret = read_4ubyte_unaligned_inc (&eh_dbg, *addrp);
100    }
101  else
102    {
103      if (unlikely (*addrp + 8 > endp))
104	goto invalid_data;
105      *ret = read_8ubyte_unaligned_inc (&eh_dbg, *addrp);
106    }
107  return 0;
108}
109
110/* Returns true on error, false otherwise. */
111static bool __attribute__ ((unused))
112read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding,
113		    const uint8_t **p, Dwarf_Addr *result)
114{
115  *result = 0;
116  switch (encoding & 0x70)
117    {
118    case DW_EH_PE_absptr:
119      break;
120    case DW_EH_PE_pcrel:
121      *result = (cache->frame_vaddr
122		 + (*p - (const uint8_t *) cache->data->d.d_buf));
123      break;
124    case DW_EH_PE_textrel:
125      // ia64: segrel
126      *result = cache->textrel;
127      break;
128    case DW_EH_PE_datarel:
129      // i386: GOTOFF
130      // ia64: gprel
131      *result = cache->datarel;
132      break;
133    case DW_EH_PE_funcrel:	/* XXX */
134      break;
135    case DW_EH_PE_aligned:
136      {
137	const size_t size = encoded_value_size (&cache->data->d,
138						cache->e_ident,
139						encoding, *p);
140	if (unlikely (size == 0))
141	  return true;
142	size_t align = ((cache->frame_vaddr
143			 + (*p - (const uint8_t *) cache->data->d.d_buf))
144			& (size - 1));
145	if (align != 0)
146	  *p += size - align;
147	break;
148      }
149
150    default:
151      __libdw_seterrno (DWARF_E_INVALID_CFI);
152      return true;
153    }
154
155  Dwarf_Addr value = 0;
156  const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
157  switch (encoding & 0x0f)
158    {
159    case DW_EH_PE_udata2:
160      if (unlikely (*p + 2 > endp))
161	{
162	invalid_data:
163	  __libdw_seterrno (DWARF_E_INVALID_CFI);
164	  return true;
165	}
166      value = read_2ubyte_unaligned_inc (cache, *p);
167      break;
168
169    case DW_EH_PE_sdata2:
170      if (unlikely (*p + 2 > endp))
171	goto invalid_data;
172      value = read_2sbyte_unaligned_inc (cache, *p);
173      break;
174
175    case DW_EH_PE_udata4:
176      if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
177	return true;
178      break;
179
180    case DW_EH_PE_sdata4:
181      if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
182	return true;
183      value = (Dwarf_Sword) (Elf32_Sword) value; /* Sign-extend.  */
184      break;
185
186    case DW_EH_PE_udata8:
187    case DW_EH_PE_sdata8:
188      if (unlikely (__libdw_cfi_read_address_inc (cache, p, 8, &value) != 0))
189	return true;
190      break;
191
192    case DW_EH_PE_absptr:
193      if (unlikely (__libdw_cfi_read_address_inc (cache, p, 0, &value) != 0))
194	return true;
195      break;
196
197    case DW_EH_PE_uleb128:
198      get_uleb128 (value, *p, endp);
199      break;
200
201    case DW_EH_PE_sleb128:
202      get_sleb128 (value, *p, endp);
203      break;
204
205    default:
206      __libdw_seterrno (DWARF_E_INVALID_CFI);
207      return true;
208    }
209
210  *result += value;
211
212  if (encoding & DW_EH_PE_indirect)
213    {
214      if (unlikely (*result < cache->frame_vaddr))
215	return true;
216      *result -= cache->frame_vaddr;
217      size_t ptrsize = encoded_value_size (NULL, cache->e_ident,
218					   DW_EH_PE_absptr, NULL);
219      if (unlikely (cache->data->d.d_size < ptrsize
220		    || *result > (cache->data->d.d_size - ptrsize)))
221	return true;
222      const uint8_t *ptr = cache->data->d.d_buf + *result;
223      if (unlikely (__libdw_cfi_read_address_inc (cache, &ptr, 0, result)
224		    != 0))
225	return true;
226    }
227
228  return false;
229}
230
231#endif	/* encoded-value.h */
232