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 */