diff options
author | moberhuber | 2008-01-10 19:58:38 +0000 |
---|---|---|
committer | moberhuber | 2008-01-10 19:58:38 +0000 |
commit | 3a6bbc7188e18b9439a8b25e4b59b2450294c4a1 (patch) | |
tree | 10a1daeb17915a6b78688159803cca098043262a /elf.c | |
download | org.eclipse.tcf.agent-3a6bbc7188e18b9439a8b25e4b59b2450294c4a1.tar.gz org.eclipse.tcf.agent-3a6bbc7188e18b9439a8b25e4b59b2450294c4a1.tar.xz org.eclipse.tcf.agent-3a6bbc7188e18b9439a8b25e4b59b2450294c4a1.zip |
tcf-0.1.0 initial contributioninitial
Diffstat (limited to 'elf.c')
-rw-r--r-- | elf.c | 235 |
1 files changed, 235 insertions, 0 deletions
@@ -0,0 +1,235 @@ +/******************************************************************************* + * 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 |