|
Packit Service |
97d2fb |
/* FDE reading.
|
|
Packit Service |
97d2fb |
Copyright (C) 2009-2010, 2014, 2015 Red Hat, Inc.
|
|
Packit Service |
97d2fb |
This file is part of elfutils.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
This file is free software; you can redistribute it and/or modify
|
|
Packit Service |
97d2fb |
it under the terms of either
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
* the GNU Lesser General Public License as published by the Free
|
|
Packit Service |
97d2fb |
Software Foundation; either version 3 of the License, or (at
|
|
Packit Service |
97d2fb |
your option) any later version
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
or
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
* the GNU General Public License as published by the Free
|
|
Packit Service |
97d2fb |
Software Foundation; either version 2 of the License, or (at
|
|
Packit Service |
97d2fb |
your option) any later version
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
or both in parallel, as here.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
elfutils is distributed in the hope that it will be useful, but
|
|
Packit Service |
97d2fb |
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
97d2fb |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit Service |
97d2fb |
General Public License for more details.
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
You should have received copies of the GNU General Public License and
|
|
Packit Service |
97d2fb |
the GNU Lesser General Public License along with this program. If
|
|
Packit Service |
97d2fb |
not, see <http://www.gnu.org/licenses/>. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#ifdef HAVE_CONFIG_H
|
|
Packit Service |
97d2fb |
# include <config.h>
|
|
Packit Service |
97d2fb |
#endif
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#include "cfi.h"
|
|
Packit Service |
97d2fb |
#include <search.h>
|
|
Packit Service |
97d2fb |
#include <stdlib.h>
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
#include "encoded-value.h"
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
static int
|
|
Packit Service |
97d2fb |
compare_fde (const void *a, const void *b)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
const struct dwarf_fde *fde1 = a;
|
|
Packit Service |
97d2fb |
const struct dwarf_fde *fde2 = b;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Find out which of the two arguments is the search value.
|
|
Packit Service |
97d2fb |
It has end offset 0. */
|
|
Packit Service |
97d2fb |
if (fde1->end == 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (fde1->start < fde2->start)
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
if (fde1->start >= fde2->end)
|
|
Packit Service |
97d2fb |
return 1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (fde2->start < fde1->start)
|
|
Packit Service |
97d2fb |
return 1;
|
|
Packit Service |
97d2fb |
if (fde2->start >= fde1->end)
|
|
Packit Service |
97d2fb |
return -1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return 0;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
static struct dwarf_fde *
|
|
Packit Service |
97d2fb |
intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Look up the new entry's CIE. */
|
|
Packit Service |
97d2fb |
struct dwarf_cie *cie = __libdw_find_cie (cache, entry->CIE_pointer);
|
|
Packit Service |
97d2fb |
if (cie == NULL)
|
|
Packit Service |
97d2fb |
return (void *) -1l;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
struct dwarf_fde *fde = malloc (sizeof (struct dwarf_fde));
|
|
Packit Service |
97d2fb |
if (fde == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libdw_seterrno (DWARF_E_NOMEM);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
fde->instructions = entry->start;
|
|
Packit Service |
97d2fb |
fde->instructions_end = entry->end;
|
|
Packit Service |
97d2fb |
if (unlikely (read_encoded_value (cache, cie->fde_encoding,
|
|
Packit Service |
97d2fb |
&fde->instructions, &fde->start))
|
|
Packit Service |
97d2fb |
|| unlikely (read_encoded_value (cache, cie->fde_encoding & 0x0f,
|
|
Packit Service |
97d2fb |
&fde->instructions, &fde->end)))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
free (fde);
|
|
Packit Service |
97d2fb |
__libdw_seterrno (DWARF_E_INVALID_DWARF);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
fde->end += fde->start;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Make sure the fde actually covers a real code range. */
|
|
Packit Service |
97d2fb |
if (fde->start >= fde->end)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
free (fde);
|
|
Packit Service |
97d2fb |
return (void *) -1;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
fde->cie = cie;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (cie->sized_augmentation_data)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* The CIE augmentation says the FDE has a DW_FORM_block
|
|
Packit Service |
97d2fb |
before its actual instruction stream. */
|
|
Packit Service |
97d2fb |
Dwarf_Word len;
|
|
Packit Service |
97d2fb |
get_uleb128 (len, fde->instructions, fde->instructions_end);
|
|
Packit Service |
97d2fb |
if ((Dwarf_Word) (fde->instructions_end - fde->instructions) < len)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
free (fde);
|
|
Packit Service |
97d2fb |
__libdw_seterrno (DWARF_E_INVALID_DWARF);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
fde->instructions += len;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
/* We had to understand all of the CIE augmentation string.
|
|
Packit Service |
97d2fb |
We've recorded the number of data bytes in FDEs. */
|
|
Packit Service |
97d2fb |
fde->instructions += cie->fde_augmentation_data_size;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Add the new entry to the search tree. */
|
|
Packit Service |
97d2fb |
struct dwarf_fde **tres = tsearch (fde, &cache->fde_tree, &compare_fde);
|
|
Packit Service |
97d2fb |
if (tres == NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
free (fde);
|
|
Packit Service |
97d2fb |
__libdw_seterrno (DWARF_E_NOMEM);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
else if (*tres != fde)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* There is already an FDE in the cache that covers the same
|
|
Packit Service |
97d2fb |
address range. That is odd. Ignore this FDE. And just use
|
|
Packit Service |
97d2fb |
the one in the cache for consistency. */
|
|
Packit Service |
97d2fb |
free (fde);
|
|
Packit Service |
97d2fb |
return *tres;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return fde;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
struct dwarf_fde *
|
|
Packit Service |
97d2fb |
internal_function
|
|
Packit Service |
97d2fb |
__libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Dwarf_CFI_Entry entry;
|
|
Packit Service |
97d2fb |
Dwarf_Off next_offset;
|
|
Packit Service |
97d2fb |
int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
|
|
Packit Service |
97d2fb |
&cache->data->d, CFI_IS_EH (cache),
|
|
Packit Service |
97d2fb |
offset, &next_offset, &entry);
|
|
Packit Service |
97d2fb |
if (result != 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (result > 0)
|
|
Packit Service |
97d2fb |
invalid:
|
|
Packit Service |
97d2fb |
__libdw_seterrno (DWARF_E_INVALID_DWARF);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (unlikely (dwarf_cfi_cie_p (&entry)))
|
|
Packit Service |
97d2fb |
goto invalid;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* We have a new FDE to consider. */
|
|
Packit Service |
97d2fb |
struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
|
|
Packit Service |
97d2fb |
if (fde == (void *) -1l || fde == NULL)
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* If this happened to be what we would have read next, notice it. */
|
|
Packit Service |
97d2fb |
if (cache->next_offset == offset)
|
|
Packit Service |
97d2fb |
cache->next_offset = next_offset;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return fde;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Use a binary search table in .eh_frame_hdr format, yield an FDE offset. */
|
|
Packit Service |
97d2fb |
static Dwarf_Off
|
|
Packit Service |
97d2fb |
binary_search_fde (Dwarf_CFI *cache, Dwarf_Addr address)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
const size_t size = 2 * encoded_value_size (&cache->data->d, cache->e_ident,
|
|
Packit Service |
97d2fb |
cache->search_table_encoding,
|
|
Packit Service |
97d2fb |
NULL);
|
|
Packit Service |
97d2fb |
if (unlikely (size == 0))
|
|
Packit Service |
97d2fb |
return (Dwarf_Off) -1l;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Dummy used by read_encoded_value. */
|
|
Packit Service |
97d2fb |
Elf_Data_Scn dummy_cfi_hdr_data =
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
.d = { .d_buf = (void *) cache->search_table,
|
|
Packit Service |
97d2fb |
.d_size = cache->search_table_len }
|
|
Packit Service |
97d2fb |
};
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Dwarf_CFI dummy_cfi =
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
.e_ident = cache->e_ident,
|
|
Packit Service |
97d2fb |
.datarel = cache->search_table_vaddr,
|
|
Packit Service |
97d2fb |
.frame_vaddr = cache->search_table_vaddr,
|
|
Packit Service |
97d2fb |
.data = &dummy_cfi_hdr_data
|
|
Packit Service |
97d2fb |
};
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
size_t l = 0, u = cache->search_table_entries;
|
|
Packit Service |
97d2fb |
while (l < u)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
size_t idx = (l + u) / 2;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Max idx * size is checked against search_table len when
|
|
Packit Service |
97d2fb |
loading eh_frame_hdr. */
|
|
Packit Service |
97d2fb |
const uint8_t *p = &cache->search_table[idx * size];
|
|
Packit Service |
97d2fb |
Dwarf_Addr start;
|
|
Packit Service |
97d2fb |
if (unlikely (read_encoded_value (&dummy_cfi,
|
|
Packit Service |
97d2fb |
cache->search_table_encoding, &p,
|
|
Packit Service |
97d2fb |
&start)))
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
if (address < start)
|
|
Packit Service |
97d2fb |
u = idx;
|
|
Packit Service |
97d2fb |
else
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
l = idx + 1;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
Dwarf_Addr fde;
|
|
Packit Service |
97d2fb |
if (unlikely (read_encoded_value (&dummy_cfi,
|
|
Packit Service |
97d2fb |
cache->search_table_encoding, &p,
|
|
Packit Service |
97d2fb |
&fde)))
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* If this is the last entry, its upper bound is assumed to be
|
|
Packit Service |
97d2fb |
the end of the module.
|
|
Packit Service |
97d2fb |
XXX really should be end of containing PT_LOAD segment */
|
|
Packit Service |
97d2fb |
if (l < cache->search_table_entries)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Look at the start address in the following entry. */
|
|
Packit Service |
97d2fb |
Dwarf_Addr end;
|
|
Packit Service |
97d2fb |
if (unlikely (read_encoded_value
|
|
Packit Service |
97d2fb |
(&dummy_cfi, cache->search_table_encoding, &p,
|
|
Packit Service |
97d2fb |
&end)))
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
if (address >= end)
|
|
Packit Service |
97d2fb |
continue;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return fde - cache->frame_vaddr;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
return (Dwarf_Off) -1l;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
struct dwarf_fde *
|
|
Packit Service |
97d2fb |
internal_function
|
|
Packit Service |
97d2fb |
__libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Look for a cached FDE covering this address. */
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
const struct dwarf_fde fde_key = { .start = address, .end = 0 };
|
|
Packit Service |
97d2fb |
struct dwarf_fde **found = tfind (&fde_key, &cache->fde_tree, &compare_fde);
|
|
Packit Service |
97d2fb |
if (found != NULL)
|
|
Packit Service |
97d2fb |
return *found;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Use .eh_frame_hdr binary search table if possible. */
|
|
Packit Service |
97d2fb |
if (cache->search_table != NULL)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Dwarf_Off offset = binary_search_fde (cache, address);
|
|
Packit Service |
97d2fb |
if (offset == (Dwarf_Off) -1l)
|
|
Packit Service |
97d2fb |
goto no_match;
|
|
Packit Service |
97d2fb |
struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset);
|
|
Packit Service |
97d2fb |
if (likely (fde != NULL))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* Sanity check the address range. */
|
|
Packit Service |
97d2fb |
if (unlikely (address < fde->start))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
__libdw_seterrno (DWARF_E_INVALID_DWARF);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
/* .eh_frame_hdr does not indicate length covered by FDE. */
|
|
Packit Service |
97d2fb |
if (unlikely (address >= fde->end))
|
|
Packit Service |
97d2fb |
goto no_match;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
return fde;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* It's not there. Read more CFI entries until we find it. */
|
|
Packit Service |
97d2fb |
while (1)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
Dwarf_Off last_offset = cache->next_offset;
|
|
Packit Service |
97d2fb |
Dwarf_CFI_Entry entry;
|
|
Packit Service |
97d2fb |
int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
|
|
Packit Service |
97d2fb |
&cache->data->d, CFI_IS_EH (cache),
|
|
Packit Service |
97d2fb |
last_offset, &cache->next_offset,
|
|
Packit Service |
97d2fb |
&entry);
|
|
Packit Service |
97d2fb |
if (result > 0)
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
if (result < 0)
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
if (cache->next_offset == last_offset)
|
|
Packit Service |
97d2fb |
/* We couldn't progress past the bogus FDE. */
|
|
Packit Service |
97d2fb |
break;
|
|
Packit Service |
97d2fb |
/* Skip the loser and look at the next entry. */
|
|
Packit Service |
97d2fb |
continue;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (dwarf_cfi_cie_p (&entry))
|
|
Packit Service |
97d2fb |
{
|
|
Packit Service |
97d2fb |
/* This is a CIE, not an FDE. We eagerly intern these
|
|
Packit Service |
97d2fb |
because the next FDE will usually refer to this CIE. */
|
|
Packit Service |
97d2fb |
__libdw_intern_cie (cache, last_offset, &entry.cie);
|
|
Packit Service |
97d2fb |
continue;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* We have a new FDE to consider. */
|
|
Packit Service |
97d2fb |
struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (fde == (void *) -1l) /* Bad FDE, but we can keep looking. */
|
|
Packit Service |
97d2fb |
continue;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
if (fde == NULL) /* Bad data. */
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
/* Is this the one we're looking for? */
|
|
Packit Service |
97d2fb |
if (fde->start <= address && fde->end > address)
|
|
Packit Service |
97d2fb |
return fde;
|
|
Packit Service |
97d2fb |
}
|
|
Packit Service |
97d2fb |
|
|
Packit Service |
97d2fb |
no_match:
|
|
Packit Service |
97d2fb |
/* We found no FDE covering this address. */
|
|
Packit Service |
97d2fb |
__libdw_seterrno (DWARF_E_NO_MATCH);
|
|
Packit Service |
97d2fb |
return NULL;
|
|
Packit Service |
97d2fb |
}
|