blob: c8d33dbf19a83d5f8f0acbd80808d30980954799 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 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.
*
* 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 <config.h>
#if ENABLE_ELF
#include <assert.h>
#include <framework/exceptions.h>
#include <services/dwarfreloc.h>
static ELF_Section * section = NULL;
static ELF_Section * relocs = NULL;
static ELF_Section * symbols = NULL;
static ELF_Section ** destination_section = 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;
#define elf_relocate elf_relocate_i386
#include <machine/i386/elf-mdep.h>
#undef elf_relocate
typedef struct ElfRelocateFunc {
int machine;
void (*func)(void);
} ElfRelocateFunc;
static ElfRelocateFunc elf_relocate_funcs[] = {
{ EM_386, elf_relocate_i386 },
{ EM_NONE, NULL }
};
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;
}
if (sym_index != STN_UNDEF) {
Elf32_Sym bf = ((Elf32_Sym *)symbols->data)[sym_index];
if (symbols->file->byte_swap) {
SWAP(bf.st_name);
SWAP(bf.st_value);
SWAP(bf.st_size);
SWAP(bf.st_info);
SWAP(bf.st_other);
SWAP(bf.st_shndx);
}
if (symbols->file->type != ET_EXEC) {
switch (bf.st_shndx) {
case SHN_ABS:
sym_value = bf.st_value;
break;
case SHN_COMMON:
case SHN_UNDEF:
str_exception(ERR_INV_FORMAT, "Invalid relocation record");
break;
default:
if (bf.st_shndx >= symbols->file->section_cnt) str_exception(ERR_INV_FORMAT, "Invalid relocation record");
sym_value = (symbols->file->sections + bf.st_shndx)->addr + bf.st_value;
*destination_section = symbols->file->sections + bf.st_shndx;
break;
}
}
else {
sym_value = bf.st_value;
}
}
}
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) {
Elf64_Sym bf = ((Elf64_Sym *)symbols->data)[sym_index];
if (symbols->file->byte_swap) {
SWAP(bf.st_name);
SWAP(bf.st_value);
SWAP(bf.st_size);
SWAP(bf.st_info);
SWAP(bf.st_other);
SWAP(bf.st_shndx);
}
if (symbols->file->type != ET_EXEC) {
switch (bf.st_shndx) {
case SHN_ABS:
sym_value = bf.st_value;
break;
case SHN_COMMON:
case SHN_UNDEF:
str_exception(ERR_INV_FORMAT, "Invalid relocation record");
break;
default:
if (bf.st_shndx >= symbols->file->section_cnt) str_exception(ERR_INV_FORMAT, "Invalid relocation record");
sym_value = (symbols->file->sections + bf.st_shndx)->addr + bf.st_value;
*destination_section = symbols->file->sections + bf.st_shndx;
break;
}
}
else {
sym_value = bf.st_value;
}
}
}
/* 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(ELF_Section * s, U8_T offset, void * buf, size_t size, ELF_Section ** dst) {
unsigned i;
ELF_Section * d = NULL;
if (dst == NULL) dst = &d;
else *dst = NULL;
if (!s->relocate) return;
section = s;
destination_section = dst;
reloc_offset = offset;
data_buf = buf;
data_size = size;
for (i = 1; i < s->file->section_cnt; i++) {
ELF_Section * r = s->file->sections + i;
if (r->size == 0) continue;
if (r->type != SHT_REL && r->type != SHT_RELA) continue;
if (r->info == s->index) {
uint8_t * p;
uint8_t * q;
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 (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;
}
}
}
}
#endif /* ENABLE_ELF */