blob: 47af0673d389c0a5c3cdc04b7490b06b17c66ca8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2014 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* This module implements ELF relocation records handling for reading DWARF debug information.
*
* Functions in this module use exceptions to report errors, see exceptions.h
*/
#include <tcf/config.h>
#if ENABLE_ELF
#include <assert.h>
#include <tcf/framework/exceptions.h>
#include <tcf/framework/myalloc.h>
#include <tcf/framework/trace.h>
#include <tcf/services/elf-symbols.h>
#include <tcf/services/dwarfreloc.h>
static Context * ctx = NULL;
static ELF_Section * section = NULL;
static ELF_Section * relocs = NULL;
static ELF_Section * symbols = NULL;
static ELF_Section ** destination_section = NULL;
static int * run_time_addr = NULL;
static U8_T reloc_offset = 0;
static U4_T reloc_type = 0;
static U8_T reloc_addend = 0;
static U4_T sym_index = 0;
static U8_T sym_value = 0;
static void * data_buf = NULL;
static size_t data_size = 0;
typedef struct ElfRelocateFunc {
int machine;
void (*func)(void);
} ElfRelocateFunc;
#include <tcf/services/dwarfreloc-ext.h>
static void relocate(void * r) {
ElfRelocateFunc * func;
if (!relocs->file->elf64) {
if (relocs->type == SHT_REL) {
Elf32_Rel bf = *(Elf32_Rel *)r;
if (relocs->file->byte_swap) {
SWAP(bf.r_offset);
SWAP(bf.r_info);
}
sym_index = ELF32_R_SYM(bf.r_info);
reloc_type = ELF32_R_TYPE(bf.r_info);
reloc_addend = 0;
}
else {
Elf32_Rela bf = *(Elf32_Rela *)r;
if (relocs->file->byte_swap) {
SWAP(bf.r_offset);
SWAP(bf.r_info);
SWAP(bf.r_addend);
}
sym_index = ELF32_R_SYM(bf.r_info);
reloc_type = ELF32_R_TYPE(bf.r_info);
reloc_addend = bf.r_addend;
}
}
else {
if (relocs->type == SHT_REL) {
Elf64_Rel bf = *(Elf64_Rel *)r;
if (relocs->file->byte_swap) {
SWAP(bf.r_offset);
SWAP(bf.r_info);
}
sym_index = ELF64_R_SYM(bf.r_info);
reloc_type = ELF64_R_TYPE(bf.r_info);
reloc_addend = 0;
}
else {
Elf64_Rela bf = *(Elf64_Rela *)r;
if (relocs->file->byte_swap) {
SWAP(bf.r_offset);
SWAP(bf.r_info);
SWAP(bf.r_addend);
}
sym_index = ELF64_R_SYM(bf.r_info);
reloc_type = ELF64_R_TYPE(bf.r_info);
reloc_addend = bf.r_addend;
}
}
if (sym_index != STN_UNDEF) {
ELF_SymbolInfo info;
unpack_elf_symbol_info(symbols, sym_index, &info);
switch (info.section_index) {
case SHN_ABS:
sym_value = info.value;
break;
case SHN_COMMON:
#if SERVICE_Symbols && (!ENABLE_SymbolsProxy || ENABLE_SymbolsMux)
if (ctx != NULL) {
ContextAddress addr = 0;
if (elf_symbol_address(ctx, &info, &addr) < 0) exception(errno);
sym_value = addr;
if (run_time_addr) *run_time_addr = 1;
break;
}
#endif
str_exception(ERR_INV_FORMAT, "Common relocation record unsupported");
break;
case SHN_UNDEF:
str_exception(ERR_INV_FORMAT, "Invalid relocation record");
break;
default:
if (info.section == NULL) str_exception(ERR_INV_FORMAT, "Invalid relocation record");
if (symbols->file->type != ET_EXEC) {
sym_value = info.section->addr + info.value;
}
else {
sym_value = info.value;
}
*destination_section = info.section;
break;
}
}
/* For executable file we don't need to apply the relocation,
* all we need is destination_section */
if (section->file->type != ET_REL) return;
func = elf_relocate_funcs;
while (func->machine != section->file->machine) {
if (func->func == NULL) str_exception(ERR_INV_FORMAT, "Unsupported ELF machine code");
func++;
}
func->func();
}
void drl_relocate_in_context(Context * c, ELF_Section * s, U8_T offset,
void * buf, size_t size, ELF_Section ** dst, int * rt_addr) {
ELF_Section * r = s->relocate;
ELF_Section * d = NULL;
uint8_t * p;
uint8_t * q;
unsigned ix;
if (rt_addr) *rt_addr = 0;
if (dst == NULL) dst = &d;
else *dst = NULL;
if (r == NULL) return;
ctx = c;
section = s;
relocs = r;
destination_section = dst;
reloc_offset = offset;
data_buf = buf;
data_size = size;
run_time_addr = rt_addr;
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");
if (r->reloc_num_zones == 0) {
U8_T prev_offs = 0;
unsigned max_bondaries = 2; /* default is two bondaries ... */
r->reloc_num_zones = 1; /* ... for one zone */
r->reloc_zones_bondaries = (unsigned *)loc_alloc_zero(sizeof (unsigned) * max_bondaries);
r->reloc_zones_bondaries[0] = 0; /* first zone starting index */
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 (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 =
(unsigned *)loc_realloc(r->reloc_zones_bondaries,
sizeof (unsigned) * max_bondaries);
}
r->reloc_zones_bondaries[r->reloc_num_zones++] = ix;
}
prev_offs = offs;
}
/* 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;
}
}
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;
}
}
}
void drl_relocate(ELF_Section * s, U8_T offset, void * buf, size_t size, ELF_Section ** dst) {
drl_relocate_in_context(NULL, s, offset, buf, size, dst, NULL);
}
#endif /* ENABLE_ELF */