summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2010-06-29 13:19:26 -0700
committerRoland McGrath <roland@redhat.com>2011-01-05 10:54:36 -0800
commit264e372b3a27747823b0ef5aea388750f45e4baa (patch)
tree574fca3fc597002dad9e7dc01cb5ede03d7e57d9
parent2167f54e91fec095b5dd443432687e1d253033dd (diff)
downloadelfutils-roland/relocate-cfi-tmp.tar.gz
unfinished cfi relocatableroland/relocate-cfi-tmp
-rw-r--r--libdw/cfi.c26
-rw-r--r--libdw/cfi.h17
-rw-r--r--libdw/dwarf_getcfi.c2
-rw-r--r--libdw/dwarf_getcfi_elf.c45
-rw-r--r--libdw/encoded-value.h2
-rw-r--r--libdw/fde.c115
-rw-r--r--libdw/frame-cache.c7
7 files changed, 154 insertions, 60 deletions
diff --git a/libdw/cfi.c b/libdw/cfi.c
index 7dacfc8b..9a67c378 100644
--- a/libdw/cfi.c
+++ b/libdw/cfi.c
@@ -170,11 +170,11 @@ execute_cfi (Dwarf_CFI *cache,
goto advance_loc;
case DW_CFA_set_loc:
- if (likely (!read_encoded_value (cache, cie->fde_encoding,
- &program, &loc)))
- break;
- result = INTUSE(dwarf_errno) ();
- goto out;
+ loc = 0;
+ fs->base_address = program;
+ program += encoded_value_size (&cache->data->d, cie->address_size,
+ cie->fde_encoding, program);
+ break;
/* Now all following cases affect this row, but do not touch LOC.
These cases end with 'continue'. We only get out of the
@@ -438,9 +438,14 @@ cie_cache_initial_state (Dwarf_CFI *cache, struct dwarf_cie *cie)
/* Make sure we have a backend handle cached. */
if (unlikely (cache->ebl == NULL))
{
- cache->ebl = ebl_openbackend (cache->data->s->elf);
- if (unlikely (cache->ebl == NULL))
- cache->ebl = (void *) -1l;
+ if (cache->dbg != NULL && cache->dbg->relocate != NULL)
+ cache->ebl = cache->dbg->relocate->ebl;
+ if (cache->ebl == NULL)
+ {
+ cache->ebl = ebl_openbackend (cache->data->s->elf);
+ if (unlikely (cache->ebl == NULL))
+ cache->ebl = (void *) -1l;
+ }
}
/* Fetch the ABI's default CFI program. */
@@ -501,8 +506,9 @@ __libdw_frame_at_address (Dwarf_CFI *cache, struct dwarf_fde *fde,
return DWARF_E_NOMEM;
fs->fde = fde;
- fs->start = fde->start;
- fs->end = fde->end;
+ fs->base_address = fde->initial_location;
+ fs->start = 0;
+ fs->end = fde->address_range;
result = execute_cfi (cache, fde->cie, &fs, true,
fde->instructions, fde->instructions_end, false,
diff --git a/libdw/cfi.h b/libdw/cfi.h
index 357a8e6f..b1c07d36 100644
--- a/libdw/cfi.h
+++ b/libdw/cfi.h
@@ -88,9 +88,10 @@ struct dwarf_fde
struct dwarf_cie *cie;
struct dwarf_fde *next; /* Chain from cie->first_fde. */
- /* This FDE describes PC values in [start, end). */
- Dwarf_Addr start;
- Dwarf_Addr end;
+ /* This is a pointer into the CFI data, which might be relocatable.
+ The length of the range is not allowed to be relocatable. */
+ const uint8_t *initial_location;
+ Dwarf_Word address_range;
const uint8_t *instructions;
const uint8_t *instructions_end;
@@ -108,6 +109,9 @@ struct Dwarf_CFI_s
Elf_Data_Scn *data;
const unsigned char *e_ident; /* For EI_DATA and EI_CLASS. */
+ /* Relocation hook for the data. */
+ struct dwarf_section_reloc *relocate;
+
Dwarf_Addr frame_vaddr; /* DW_EH_PE_pcrel, address of frame section. */
Dwarf_Addr textrel; /* DW_EH_PE_textrel base address. */
Dwarf_Addr datarel; /* DW_EH_PE_datarel base address. */
@@ -187,7 +191,12 @@ struct dwarf_frame_register
at a particular PC location described by an FDE. */
struct Dwarf_Frame_s
{
- /* This frame description covers PC values in [start, end). */
+ /* This frame description covers PC values in [start, end).
+ We track these as offsets relative to the a position
+ indicated by pointer into the CFI data where there might be
+ a relocation, either at fde->initial_location, or at the
+ position of a DW_CFA_set_loc operand. */
+ const uint8_t *base_address;
Dwarf_Addr start;
Dwarf_Addr end;
diff --git a/libdw/dwarf_getcfi.c b/libdw/dwarf_getcfi.c
index 91e179f3..ed983ed2 100644
--- a/libdw/dwarf_getcfi.c
+++ b/libdw/dwarf_getcfi.c
@@ -68,6 +68,8 @@ dwarf_getcfi (dbg)
cfi->dbg = dbg;
cfi->data = (Elf_Data_Scn *) dbg->sectiondata[IDX_debug_frame];
+ if (dbg->relocate != NULL)
+ cfi->relocate = dbg->relocate->sectionrel[IDX_debug_frame];
cfi->search_table = NULL;
cfi->search_table_vaddr = 0;
diff --git a/libdw/dwarf_getcfi_elf.c b/libdw/dwarf_getcfi_elf.c
index 50015202..2c4ed5fa 100644
--- a/libdw/dwarf_getcfi_elf.c
+++ b/libdw/dwarf_getcfi_elf.c
@@ -55,7 +55,7 @@
#include <string.h>
#include <assert.h>
-#include "libdwP.h"
+#include "relocate.h"
#include "cfi.h"
#include "encoded-value.h"
#include <dwarf.h>
@@ -231,7 +231,8 @@ getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
static Dwarf_CFI *
getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
Elf_Scn *scn, GElf_Shdr *shdr,
- Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr)
+ Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr,
+ Elf_Scn *relscn)
{
Elf_Data *data = elf_rawdata (scn, NULL);
if (data == NULL)
@@ -243,7 +244,18 @@ getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
if (cfi != NULL)
{
cfi->data = (Elf_Data_Scn *) data;
- if (hdr_scn != NULL)
+ if (relscn != NULL)
+ {
+ cfi->relocate = malloc (sizeof *cfi->relocate);
+ if (unlikely (cfi->relocate == NULL))
+ {
+ free (cfi);
+ __libdw_seterrno (DWARF_E_NOMEM);
+ return NULL;
+ }
+ cfi->relocate->scn = relscn;
+ }
+ else if (hdr_scn != NULL)
{
Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
if (hdr_data != NULL)
@@ -303,8 +315,31 @@ getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
hdr_vaddr = shdr->sh_addr;
}
else if (!strcmp (name, ".eh_frame"))
- return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
- hdr_scn, hdr_vaddr);
+ break;
+ }
+
+ if (scn != NULL)
+ {
+ Elf_Scn *relscn = NULL;
+
+ if (ehdr->e_type == ET_REL)
+ {
+ relscn = scn;
+ for (int i = 0; i < 2; ++i)
+ while ((relscn = elf_nextscn (elf, relscn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (relscn, &shdr_mem);
+ if (shdr != NULL
+ && (shdr->sh_type == SHT_REL
+ || shdr->sh_type == SHT_RELA)
+ && shdr->sh_link == elf_ndxscn (scn))
+ break;
+ }
+ }
+
+ return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
+ hdr_scn, hdr_vaddr, relscn);
}
}
diff --git a/libdw/encoded-value.h b/libdw/encoded-value.h
index ee8a6f31..5c87a68a 100644
--- a/libdw/encoded-value.h
+++ b/libdw/encoded-value.h
@@ -50,9 +50,9 @@
#ifndef _ENCODED_VALUE_H
#define _ENCODED_VALUE_H 1
+#include "cfi.h"
#include <dwarf.h>
#include <stdlib.h>
-#include "libdwP.h"
static size_t __attribute__ ((unused))
diff --git a/libdw/fde.c b/libdw/fde.c
index 44a39ccd..bdb55fe5 100644
--- a/libdw/fde.c
+++ b/libdw/fde.c
@@ -57,34 +57,52 @@
#include "encoded-value.h"
+struct fde_search
+{
+ struct dwarf_cie *cie; /* Always NULL, a marker. */
+
+
+}
+
+static int
+compare_fde_1 (const struct dwarf_fde *fde, const struct fde_search *search)
+{
+ if (unlikely (search->initial_location == NULL))
+ ;
+
+ const uint8_t **p = fde->initial_location;
+ Dwarf_Addr start;
+
+ if (unlikely (read_encoded_value (cache, cie->fde_encoding & 0x0f,
+ p, &start)))
+ ;
+
+ if (search->start < fde->start)
+ return 1;
+ if (search->start >= fde1->end)
+ return -1;
+
+ return 0;
+}
+
static int
compare_fde (const void *a, const void *b)
{
const struct dwarf_fde *fde1 = a;
const struct dwarf_fde *fde2 = b;
- /* Find out which of the two arguments is the search value.
- It has end offset 0. */
- if (fde1->end == 0)
- {
- if (fde1->start < fde2->start)
- return -1;
- if (fde1->start >= fde2->end)
- return 1;
- }
- else
- {
- if (fde2->start < fde1->start)
- return 1;
- if (fde2->start >= fde1->end)
- return -1;
- }
+ /* Find out which of the two arguments is the search value. */
+
+ if (fde1->cie == NULL)
+ return compare_fde_1 (fde2, a);
+ else if (fde2->cie == NULL)
+ return compare_fde_1 (fde1, b);
+
- return 0;
}
static struct dwarf_fde *
-intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
+intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry, Dwarf_address *start)
{
/* Look up the new entry's CIE. */
struct dwarf_cie *cie = __libdw_find_cie (cache, entry->CIE_pointer);
@@ -102,12 +120,18 @@ intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
fde->instructions = entry->start;
fde->instructions_end = entry->end;
- if (unlikely (read_encoded_value (cache, cie->fde_encoding,
- &fde->instructions, &fde->start))
- || unlikely (read_encoded_value (cache, cie->fde_encoding & 0x0f,
- &fde->instructions, &fde->end)))
- return NULL;
- fde->end += fde->start;
+ fde->initial_location = fde->instructions;
+ fde->instructions += encoded_value_size (&cache->data->d, cie->address_size,
+ cie->fde_encoding,
+ fde->instructions);
+ if (unlikely (read_encoded_value (cache, cie->fde_encoding & 0x0f,
+ &fde->instructions, &fde->address_range))
+ || unlikely (fde->address_range == 0))
+ {
+ free (fde);
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return NULL;
+ }
fde->cie = cie;
@@ -130,11 +154,24 @@ intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
We've recorded the number of data bytes in FDEs. */
fde->instructions += cie->fde_augmentation_data_size;
+ int result = __libdw_relocatable (cu->dbg, sec_idx, valp, width,
+ &symndx, addend);
+ if (unlikely (result < 0))
+ {
+ free (fde);
+ return NULL;
+ }
+ if (result == 0)
+ {
+ /* No relocation here. */
+
+ }
+
/* Add the new entry to the search tree. */
if (tsearch (fde, &cache->fde_tree, &compare_fde) == NULL)
{
- free (fde);
__libdw_seterrno (DWARF_E_NOMEM);
+ free (fde);
return NULL;
}
@@ -168,17 +205,22 @@ __libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset, ptrdiff_t *nextoff)
if (cache->next_offset == offset)
cache->next_offset = next_offset;
- offset = next_offset;
-
if (!dwarf_cfi_cie_p (&entry))
{
+ offset = next_offset;
if (nextoff != NULL)
- *nextoff = next_offset;
+ *nextoff = offset;
break;
}
if (nextoff == NULL)
goto invalid;
+
+ /* This is a CIE, not an FDE. We eagerly intern these
+ because the next FDE will usually refer to this CIE. */
+ __libdw_intern_cie (cache, offset, &entry.cie);
+
+ offset = next_offset;
}
/* We have a new FDE to consider. */
@@ -276,15 +318,7 @@ __libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address)
Dwarf_Off offset = binary_search_fde (cache, address);
if (offset == (Dwarf_Off) -1l)
goto no_match;
- struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset, NULL);
- if (unlikely (fde != NULL)
- /* Sanity check the address range. */
- && unlikely (address < fde->start || address >= fde->end))
- {
- __libdw_seterrno (DWARF_E_INVALID_DWARF);
- return NULL;
- }
- return fde;
+ return __libdw_fde_by_offset (cache, offset, NULL);
}
/* It's not there. Read more CFI entries until we find it. */
@@ -316,7 +350,8 @@ __libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address)
}
/* We have a new FDE to consider. */
- struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
+ Dwarf_Addr start;
+ struct dwarf_fde *fde = intern_fde (cache, &entry.fde, &start);
if (fde == (void *) -1l) /* Bad FDE, but we can keep looking. */
continue;
@@ -324,8 +359,10 @@ __libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address)
if (fde == NULL) /* Bad data. */
return NULL;
+ if (read_encoded_value (cache, fde->cie->fde_encoding,
+
/* Is this the one we're looking for? */
- if (fde->start <= address && fde->end > address)
+ if (start <= address && address - start < fde->address_range)
return fde;
}
diff --git a/libdw/frame-cache.c b/libdw/frame-cache.c
index f4876638..37d49b32 100644
--- a/libdw/frame-cache.c
+++ b/libdw/frame-cache.c
@@ -1,5 +1,5 @@
/* Frame cache handling.
- Copyright (C) 2009 Red Hat, Inc.
+ Copyright (C) 2009-2010 Red Hat, Inc.
This file is part of Red Hat elfutils.
Red Hat elfutils is free software; you can redistribute it and/or modify
@@ -54,6 +54,7 @@
#include "cfi.h"
#include <search.h>
#include <stdlib.h>
+#include <libebl.h>
static void
@@ -84,4 +85,8 @@ __libdw_destroy_frame_cache (Dwarf_CFI *cache)
tdestroy (cache->fde_tree, free_fde);
tdestroy (cache->cie_tree, free_cie);
tdestroy (cache->expr_tree, free_expr);
+
+ if (cache->dbg == NULL || cache->dbg->relocate == NULL
+ || cache->dbg->relocate->ebl != cache->ebl)
+ ebl_closebackend (cache->ebl);
}