Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--agent/tcf/services/dwarfreloc.c109
-rw-r--r--agent/tcf/services/tcf_elf.c1
-rw-r--r--agent/tcf/services/tcf_elf.h4
3 files changed, 86 insertions, 28 deletions
diff --git a/agent/tcf/services/dwarfreloc.c b/agent/tcf/services/dwarfreloc.c
index bf1e0735..5f67b2af 100644
--- a/agent/tcf/services/dwarfreloc.c
+++ b/agent/tcf/services/dwarfreloc.c
@@ -25,6 +25,8 @@
#include <assert.h>
#include <tcf/framework/exceptions.h>
+#include <tcf/framework/myalloc.h>
+#include <tcf/framework/trace.h>
#include <tcf/services/dwarfreloc.h>
static ELF_Section * section = NULL;
@@ -193,45 +195,96 @@ void drl_relocate(ELF_Section * s, U8_T offset, void * buf, size_t size, ELF_Sec
if (r->info == s->index) {
uint8_t * p;
uint8_t * q;
+ int ix;
relocs = r;
symbols = s->file->sections + r->link;
if (elf_load(relocs) < 0) exception(errno);
if (elf_load(symbols) < 0) exception(errno);
if (r->entsize == 0 || r->size % r->entsize != 0) str_exception(ERR_INV_FORMAT, "Invalid sh_entsize");
- p = (uint8_t *)r->data;
- q = p + r->size;
- while (p < q) {
- unsigned n = (q - p) / r->entsize / 2;
- uint8_t * x = p + n * r->entsize;
- assert(x < q);
- if (r->file->elf64) {
- U8_T offs = *(U8_T *)x;
- if (r->file->byte_swap) SWAP(offs);
- if (s->file->type != ET_REL) offs -= s->addr;
- if (offset > offs) {
- p = x + r->entsize;
- continue;
+
+ if (r->reloc_num_zones == 0) {
+ unsigned max_bondaries = 2; /* default is two bondaries ... */
+ r->reloc_num_zones = 1; /* ... for one zone */
+ r->reloc_zones_bondaries = loc_alloc_zero(sizeof (unsigned) * max_bondaries);
+ r->reloc_zones_bondaries[0] = 0; /* first zone starting index */
+ U8_T prev_offs = 0;
+ for (ix = 0; ix < r->size / r->entsize; ix++) {
+ U8_T offs;
+ uint8_t * x = (uint8_t *)r->data + ix * r->entsize;
+ if (r->file->elf64) {
+ offs = *(U8_T *)x;
+ if (r->file->byte_swap) SWAP(offs);
+ }
+ else {
+ U4_T offs4 = *(U4_T *)x;
+ if (r->file->byte_swap) SWAP(offs4);
+ offs = offs4;
}
- if (offset < offs) {
- q = x;
- continue;
+ if (offs < prev_offs) {
+ /*
+ * Relocation offsets are not ordered. Store the start
+ * index of the new zone.
+ */
+ if ((r->reloc_num_zones + 1) == max_bondaries) {
+ max_bondaries += 5;
+ r->reloc_zones_bondaries = loc_realloc(r->reloc_zones_bondaries, sizeof (unsigned) * max_bondaries);
+ }
+ r->reloc_zones_bondaries[r->reloc_num_zones++] = ix;
}
+ prev_offs = offs;
}
- else {
- U4_T offs = *(U4_T *)x;
- if (r->file->byte_swap) SWAP(offs);
- if (s->file->type != ET_REL) offs -= (U4_T)s->addr;
- if (offset > offs) {
- p = x + r->entsize;
- continue;
+ /* Store the last zone boundary index */
+ r->reloc_zones_bondaries[r->reloc_num_zones] = ix;
+ if (r->reloc_num_zones > 1) {
+ trace(LOG_ELF, "ELF relocations are not ordered; the performances "\
+ "may be degraded.");
+ }
+ /*
+ * As we parsed the relocation section, it would be possible to
+ * localize the searched offset at the same time, to optimize the
+ * first lookup. But I don't know if it worth it, compare to some
+ * code duplication with the rest of the routine below.
+ */
+ }
+
+ /* Perform a dichotomic look up for each ordered area */
+
+ for (ix = 0; ix < r->reloc_num_zones; ix++) {
+ p = (uint8_t *)r->data + r->reloc_zones_bondaries[ix] * r->entsize;
+ q = (uint8_t *)r->data + r->reloc_zones_bondaries[ix + 1] * r->entsize;
+ while (p < q) {
+ unsigned n = (q - p) / r->entsize / 2;
+ uint8_t * x = p + n * r->entsize;
+ assert(x < q);
+ if (r->file->elf64) {
+ U8_T offs = *(U8_T *)x;
+ if (r->file->byte_swap) SWAP(offs);
+ if (s->file->type != ET_REL) offs -= s->addr;
+ if (offset > offs) {
+ p = x + r->entsize;
+ continue;
+ }
+ if (offset < offs) {
+ q = x;
+ continue;
+ }
}
- if (offset < offs) {
- q = x;
- continue;
+ else {
+ U4_T offs = *(U4_T *)x;
+ if (r->file->byte_swap) SWAP(offs);
+ if (s->file->type != ET_REL) offs -= (U4_T)s->addr;
+ if (offset > offs) {
+ p = x + r->entsize;
+ continue;
+ }
+ if (offset < offs) {
+ q = x;
+ continue;
+ }
}
+ relocate(x);
+ return;
}
- relocate(x);
- return;
}
}
}
diff --git a/agent/tcf/services/tcf_elf.c b/agent/tcf/services/tcf_elf.c
index bdee2478..836ce1f9 100644
--- a/agent/tcf/services/tcf_elf.c
+++ b/agent/tcf/services/tcf_elf.c
@@ -115,6 +115,7 @@ static void elf_dispose(ELF_File * file) {
loc_free(s->sym_addr_table);
loc_free(s->sym_names_hash);
loc_free(s->sym_names_next);
+ loc_free(s->reloc_zones_bondaries);
}
loc_free(file->sections);
}
diff --git a/agent/tcf/services/tcf_elf.h b/agent/tcf/services/tcf_elf.h
index 8f30dfa7..b9908b56 100644
--- a/agent/tcf/services/tcf_elf.h
+++ b/agent/tcf/services/tcf_elf.h
@@ -490,6 +490,10 @@ struct ELF_Section {
unsigned sym_names_hash_size;
unsigned * sym_names_hash;
unsigned * sym_names_next;
+
+ /* Relocations blocks */
+ unsigned reloc_num_zones;
+ unsigned * reloc_zones_bondaries;
};
struct ELF_PHeader {

Back to the top