summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenoit Perrin2012-12-04 06:26:11 (EST)
committerEugene Tarassov2012-12-12 14:33:41 (EST)
commit7d606b58a7035e988b55323ba27a2a39d03ed9db (patch)
tree395f0faaee846e05a0d1bf7ea279b8cecbe0d5f9
parent4111ef8542d0a46780c0cfa12490b0b65706f7e1 (diff)
downloadorg.eclipse.tcf.agent-7d606b58a7035e988b55323ba27a2a39d03ed9db.zip
org.eclipse.tcf.agent-7d606b58a7035e988b55323ba27a2a39d03ed9db.tar.gz
org.eclipse.tcf.agent-7d606b58a7035e988b55323ba27a2a39d03ed9db.tar.bz2
Bug 394565: handle partially ordered relocation sections.
Most of the time, the relocation sections are ordered by relocation offset in ascending order. But unfortunately, it exists also cases where the relocation entries are only partially ordered. This commit handles such cases without ordering all the section contents; only the ordered zones bondaries are managed.
-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 bf1e073..5f67b2a 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 bdee247..836ce1f 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 8f30dfa..b9908b5 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 {