blob: 8476ca84834996d7c967ad5b0e68d485ad39178e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 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
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* This module implements reading and caching of ELF files.
*/
#include "config.h"
#if SERVICE_LineNumbers || SERVICE_Symbols
#include <assert.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "elf.h"
#include "myalloc.h"
#if defined(_WRS_KERNEL)
#elif defined(WIN32)
#else
# include <libelf.h>
# define USE_LIBELF
#endif
#define MAX_CACHED_FILES 8
static ELF_File * files = NULL;
static ELFCloseListener * listeners = NULL;
static U4_T listeners_cnt = 0;
static U4_T listeners_max = 0;
static void elf_dispose(ELF_File * file) {
U4_T n;
assert(file->ref_cnt == 0);
for (n = 0; n < listeners_cnt; n++) {
listeners[n](file);
}
#ifdef USE_LIBELF
if (file->libelf_cache != NULL) elf_end(file->libelf_cache);
#endif
if (file->fd >= 0) close(file->fd);
if (file->sections != NULL) {
for (n = 0; n < file->section_cnt; n++) {
loc_free(file->sections[n]);
}
loc_free(file->sections);
}
free(file->name);
loc_free(file);
}
ELF_File * elf_open(char * file_name) {
int cnt = 0;
int error = 0;
struct_stat st;
ELF_File * prev = NULL;
ELF_File * file = files;
file_name = canonicalize_file_name(file_name);
if (file_name == NULL) return NULL;
if (stat(file_name, &st) < 0) {
error = errno;
free(file_name);
errno = error;
return NULL;
}
while (file != NULL) {
if (strcmp(file->name, file_name) == 0 &&
file->dev == st.st_dev &&
file->ino == st.st_ino &&
file->mtime == st.st_mtime) {
if (prev != NULL) {
prev->next = file->next;
file->next = files;
files = file;
}
file->ref_cnt++;
free(file_name);
return file;
}
if (cnt >= MAX_CACHED_FILES && file->ref_cnt == 0) {
prev->next = file->next;
elf_dispose(file);
file = prev->next;
}
else {
prev = file;
file = file->next;
cnt++;
}
}
file = (ELF_File *)loc_alloc_zero(sizeof(ELF_File));
file->name = file_name;
file->dev = st.st_dev;
file->ino = st.st_ino;
file->mtime = st.st_mtime;
#ifdef _WRS_KERNEL
if ((file->fd = open(file->name, O_RDONLY, 0)) < 0) {
#else
if ((file->fd = open(file->name, O_RDONLY)) < 0) {
#endif
error = errno;
}
#ifdef USE_LIBELF
if (error == 0) {
Elf * elf;
Elf32_Ehdr * ehdr;
/* Obtain the ELF descriptor */
(void)elf_version(EV_CURRENT);
if ((elf = elf_begin(file->fd, ELF_C_READ, NULL)) == NULL) {
error = errno;
}
else {
file->libelf_cache = elf;
if ((ehdr = elf32_getehdr(elf)) == NULL) {
error = errno;
}
}
if (error == 0) {
size_t snum = 0;
size_t shstrndx = 0;
if (elf_getshnum(elf, &snum) < 0) error = errno;
if (error == 0) {
file->sections = (ELF_Section **)loc_alloc_zero(sizeof(ELF_Section *) * snum);
file->section_cnt = snum;
if (elf_getshstrndx(elf, &shstrndx) < 0) error = errno;
}
if (error == 0) {
/* Iterate sections */
Elf_Scn * scn = NULL;
while ((scn = elf_nextscn(elf, scn)) != NULL) {
Elf32_Shdr * shdr = NULL;
char * name = NULL;
ELF_Section * sec = NULL;
if ((shdr = elf32_getshdr(scn)) == NULL) {
error = errno;
break;
}
if ((name = elf_strptr(elf, shstrndx, shdr->sh_name)) == NULL) {
error = errno;
break;
}
sec = (ELF_Section *)loc_alloc(sizeof(ELF_Section));
sec->file = file;
sec->index = elf_ndxscn(scn);
sec->name = name;
sec->type = shdr->sh_type;
sec->offset = shdr->sh_offset;
sec->size = shdr->sh_size;
sec->flags = shdr->sh_flags;
sec->addr = shdr->sh_addr;
sec->link = shdr->sh_link;
sec->info = shdr->sh_info;
assert(sec->index < snum);
file->sections[sec->index] = sec;
}
}
}
}
#else
if (error == 0) {
error = EINVAL;
}
#endif
if (error != 0) {
elf_dispose(file);
errno = error;
return NULL;
}
file->ref_cnt = 1;
file->next = files;
return files = file;
}
int elf_load(ELF_Section * section, U1_T ** address) {
#ifdef USE_LIBELF
Elf * elf = (Elf *)section->file->libelf_cache;
Elf_Scn * scn = elf_getscn(elf, section->index);
Elf_Data * data = elf_getdata(scn, NULL);
if (data == NULL) return -1;
assert(data->d_buf != NULL && data->d_size == section->size);
*address = data->d_buf;
return 0;
#else
*address = NULL;
errno = EINVAL;
return -1;
#endif
}
int elf_read(ELF_Section * section, U8_T offset, U1_T * buf, U4_T size, U4_T * rd_len) {
#ifdef USE_LIBELF
U4_T rd = size;
Elf * elf = (Elf *)section->file->libelf_cache;
Elf_Scn * scn = elf_getscn(elf, section->index);
Elf_Data * data = elf_getdata(scn, NULL);
if (data == NULL) return -1;
assert(data->d_buf != NULL && data->d_size == section->size);
assert(offset < section->size);
if (offset + rd > section->size) rd = (U4_T)(section->size - offset);
memcpy(buf, data->d_buf + offset, rd);
*rd_len = rd;
return 0;
#else
*rd_len = 0;
errno = EINVAL;
return -1;
#endif
}
void elf_close(ELF_File * file) {
assert(file != NULL);
assert(file->ref_cnt > 0);
file->ref_cnt--;
}
void elf_add_close_listener(ELFCloseListener listener) {
if (listeners_cnt >= listeners_max) {
listeners_max = listeners_max == 0 ? 16 : listeners_max * 2;
listeners = (ELFCloseListener *)loc_realloc(listeners, sizeof(ELFCloseListener) * listeners_max);
}
listeners[listeners_cnt++] = listener;
}
#endif