#ifndef _ENCODED_VALUE_H
#define _ENCODED_VALUE_H 1
#include <dwarf.h>
#include <stdlib.h>
#include "libdwP.h"
#include "common.h"
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;
}
}
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);
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;
}
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:
*result = cache->textrel;
break;
case DW_EH_PE_datarel:
*result = cache->datarel;
break;
case DW_EH_PE_funcrel:
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;
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:
if (*p >= endp)
goto invalid_data;
get_uleb128 (value, *p, endp);
break;
case DW_EH_PE_sleb128:
if (*p >= endp)
goto invalid_data;
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