diff options
-rw-r--r-- | agent/tcf/services/dwarfreloc.c | 109 | ||||
-rw-r--r-- | agent/tcf/services/tcf_elf.c | 1 | ||||
-rw-r--r-- | agent/tcf/services/tcf_elf.h | 4 |
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 { |