mirror of
https://github.com/autc04/Retro68.git
synced 2024-11-27 14:50:23 +00:00
d21be3b4e1
(from https://sourceware.org/elfutils/, GPL/LGPL licensed)
233 lines
6.0 KiB
C
233 lines
6.0 KiB
C
/* 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 */
|