Squashed 'third_party/elfutils/' content from commit 555e15e

Change-Id: I61cde98949e47e5c8c09c33260de17f30921be79
git-subtree-dir: third_party/elfutils
git-subtree-split: 555e15ebe8bf1eb33d00747173cfc80cc65648a4
diff --git a/libdw/encoded-value.h b/libdw/encoded-value.h
new file mode 100644
index 0000000..f0df4ce
--- /dev/null
+++ b/libdw/encoded-value.h
@@ -0,0 +1,232 @@
+/* DW_EH_PE_* support for libdw unwinder.
+   Copyright (C) 2009-2010, 2014, 2015 Red Hat, Inc.
+   This file is part of elfutils.
+
+   This file is free software; you can redistribute it and/or modify
+   it under the terms of either
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at
+       your option) any later version
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at
+       your option) any later version
+
+   or both in parallel, as here.
+
+   elfutils is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _ENCODED_VALUE_H
+#define _ENCODED_VALUE_H 1
+
+#include <dwarf.h>
+#include <stdlib.h>
+#include "libdwP.h"
+#include "../libelf/common.h"
+
+
+/* Returns zero if the value is omitted, the encoding is unknown or
+   the (leb128) size cannot be determined.  */
+static size_t __attribute__ ((unused))
+encoded_value_size (const Elf_Data *data, const unsigned char e_ident[],
+		    uint8_t encoding, const uint8_t *p)
+{
+  if (encoding == DW_EH_PE_omit)
+    return 0;
+
+  switch (encoding & 0x07)
+    {
+    case DW_EH_PE_udata2:
+      return 2;
+    case DW_EH_PE_udata4:
+      return 4;
+    case DW_EH_PE_udata8:
+      return 8;
+
+    case DW_EH_PE_absptr:
+      return e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
+
+    case DW_EH_PE_uleb128:
+      if (p != NULL)
+	{
+	  const uint8_t *end = p;
+	  while (end < (uint8_t *) data->d_buf + data->d_size)
+	    if (*end++ & 0x80u)
+	      return end - p;
+	}
+      return 0;
+
+    default:
+      return 0;
+    }
+}
+
+/* Returns zero when value was read successfully, minus one otherwise.  */
+static inline int __attribute__ ((unused))
+__libdw_cfi_read_address_inc (const Dwarf_CFI *cache,
+			      const unsigned char **addrp,
+			      int width, Dwarf_Addr *ret)
+{
+  width = width ?: cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
+
+  if (cache->dbg != NULL)
+    return __libdw_read_address_inc (cache->dbg, IDX_debug_frame,
+				     addrp, width, ret);
+
+  /* Only .debug_frame might have relocation to consider.
+     Read plain values from .eh_frame data.  */
+
+  const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
+  Dwarf eh_dbg = { .other_byte_order = MY_ELFDATA != cache->e_ident[EI_DATA] };
+
+  if (width == 4)
+    {
+      if (unlikely (*addrp + 4 > endp))
+	{
+	invalid_data:
+	  __libdw_seterrno (DWARF_E_INVALID_CFI);
+	  return -1;
+	}
+      *ret = read_4ubyte_unaligned_inc (&eh_dbg, *addrp);
+    }
+  else
+    {
+      if (unlikely (*addrp + 8 > endp))
+	goto invalid_data;
+      *ret = read_8ubyte_unaligned_inc (&eh_dbg, *addrp);
+    }
+  return 0;
+}
+
+/* Returns true on error, false otherwise. */
+static bool __attribute__ ((unused))
+read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding,
+		    const uint8_t **p, Dwarf_Addr *result)
+{
+  *result = 0;
+  switch (encoding & 0x70)
+    {
+    case DW_EH_PE_absptr:
+      break;
+    case DW_EH_PE_pcrel:
+      *result = (cache->frame_vaddr
+		 + (*p - (const uint8_t *) cache->data->d.d_buf));
+      break;
+    case DW_EH_PE_textrel:
+      // ia64: segrel
+      *result = cache->textrel;
+      break;
+    case DW_EH_PE_datarel:
+      // i386: GOTOFF
+      // ia64: gprel
+      *result = cache->datarel;
+      break;
+    case DW_EH_PE_funcrel:	/* XXX */
+      break;
+    case DW_EH_PE_aligned:
+      {
+	const size_t size = encoded_value_size (&cache->data->d,
+						cache->e_ident,
+						encoding, *p);
+	if (unlikely (size == 0))
+	  return true;
+	size_t align = ((cache->frame_vaddr
+			 + (*p - (const uint8_t *) cache->data->d.d_buf))
+			& (size - 1));
+	if (align != 0)
+	  *p += size - align;
+	break;
+      }
+
+    default:
+      __libdw_seterrno (DWARF_E_INVALID_CFI);
+      return true;
+    }
+
+  Dwarf_Addr value = 0;
+  const unsigned char *endp = cache->data->d.d_buf + cache->data->d.d_size;
+  switch (encoding & 0x0f)
+    {
+    case DW_EH_PE_udata2:
+      if (unlikely (*p + 2 > endp))
+	{
+	invalid_data:
+	  __libdw_seterrno (DWARF_E_INVALID_CFI);
+	  return true;
+	}
+      value = read_2ubyte_unaligned_inc (cache, *p);
+      break;
+
+    case DW_EH_PE_sdata2:
+      if (unlikely (*p + 2 > endp))
+	goto invalid_data;
+      value = read_2sbyte_unaligned_inc (cache, *p);
+      break;
+
+    case DW_EH_PE_udata4:
+      if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
+	return true;
+      break;
+
+    case DW_EH_PE_sdata4:
+      if (unlikely (__libdw_cfi_read_address_inc (cache, p, 4, &value) != 0))
+	return true;
+      value = (Dwarf_Sword) (Elf32_Sword) value; /* Sign-extend.  */
+      break;
+
+    case DW_EH_PE_udata8:
+    case DW_EH_PE_sdata8:
+      if (unlikely (__libdw_cfi_read_address_inc (cache, p, 8, &value) != 0))
+	return true;
+      break;
+
+    case DW_EH_PE_absptr:
+      if (unlikely (__libdw_cfi_read_address_inc (cache, p, 0, &value) != 0))
+	return true;
+      break;
+
+    case DW_EH_PE_uleb128:
+      get_uleb128 (value, *p, endp);
+      break;
+
+    case DW_EH_PE_sleb128:
+      get_sleb128 (value, *p, endp);
+      break;
+
+    default:
+      __libdw_seterrno (DWARF_E_INVALID_CFI);
+      return true;
+    }
+
+  *result += value;
+
+  if (encoding & DW_EH_PE_indirect)
+    {
+      if (unlikely (*result < cache->frame_vaddr))
+	return true;
+      *result -= cache->frame_vaddr;
+      size_t ptrsize = encoded_value_size (NULL, cache->e_ident,
+					   DW_EH_PE_absptr, NULL);
+      if (unlikely (cache->data->d.d_size < ptrsize
+		    || *result > (cache->data->d.d_size - ptrsize)))
+	return true;
+      const uint8_t *ptr = cache->data->d.d_buf + *result;
+      if (unlikely (__libdw_cfi_read_address_inc (cache, &ptr, 0, result)
+		    != 0))
+	return true;
+    }
+
+  return false;
+}
+
+#endif	/* encoded-value.h */