blob: 132892ae915498b06d026477bfdd736928a8e7e2 [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
*******************************************************************************/
/*
* Symbols service.
*/
#include "config.h"
#if SERVICE_Symbols
#if defined(_WRS_KERNEL)
# include <vxWorks.h>
# include <symLib.h>
# include <sysSymTbl.h>
#elif defined(WIN32)
#else
# include <elf.h>
# include <libelf.h>
# include <fcntl.h>
#endif
#include <errno.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include "errors.h"
#include "elf.h"
#include "myalloc.h"
#include "symbols.h"
#define SYM_HASH_SIZE 1023
typedef struct SymbolTable SymbolTable;
struct SymbolTable {
SymbolTable * next;
char * str;
int str_size;
int sym_cnt;
void * syms; /* pointer to ELF section data: Elf32_Sym* or Elf64_Sym* */
int hash[SYM_HASH_SIZE];
int * hash_next;
};
static int calc_hash(char * s) {
unsigned h = 0;
while (*s) {
unsigned g;
h = (h << 4) + *s++;
if (g = h & 0xf0000000) h ^= g >> 24;
h &= ~g;
}
return h % SYM_HASH_SIZE;
}
static void free_sym_cache(ELF_File * file) {
SymbolTable * tables = (SymbolTable *)file->sym_cache;
while (tables != NULL) {
SymbolTable * tbl = tables;
tables = tbl->next;
loc_free(tbl->hash_next);
loc_free(tbl);
}
}
static int load_tables(ELF_File * file) {
int error = 0;
#ifdef ELFMAG
unsigned idx;
for (idx = 0; idx < file->section_cnt; idx++) {
ELF_Section * sym_sec = file->sections[idx];
if (sym_sec == NULL) continue;
if (sym_sec->type == SHT_SYMTAB && sym_sec->size > 0) {
int i;
ELF_Section * str_sec;
U1_T * str_data = NULL;
U1_T * sym_data = NULL;
SymbolTable * tbl = (SymbolTable *)loc_alloc_zero(sizeof(SymbolTable));
tbl->next = (SymbolTable *)file->sym_cache;
file->sym_cache = tbl;
if (sym_sec->link >= file->section_cnt || (str_sec = file->sections[sym_sec->link]) == NULL) {
error = EINVAL;
break;
}
if (elf_load(sym_sec, &sym_data) < 0) {
error = errno;
assert(error != 0);
break;
}
if (elf_load(str_sec, &str_data) < 0) {
error = errno;
assert(error != 0);
break;
}
tbl->str = (char *)str_data;
tbl->str_size = str_sec->size;
tbl->syms = sym_data;
tbl->sym_cnt = sym_sec->size / sizeof(Elf32_Sym);
tbl->hash_next = (int *)loc_alloc(tbl->sym_cnt * sizeof(int));
for (i = 0; i < tbl->sym_cnt; i++) {
Elf32_Sym * s = (Elf32_Sym *)tbl->syms + i;
assert(s->st_name < tbl->str_size);
if (s->st_name == 0) {
tbl->hash_next[i] = 0;
}
else {
int h = calc_hash(tbl->str + s->st_name);
tbl->hash_next[i] = tbl->hash[h];
tbl->hash[h] = i;
}
}
}
}
#else
error = EINVAL;
#endif
if (error != 0) {
free_sym_cache(file);
errno = error;
return -1;
}
return 0;
}
int find_symbol(Context * ctx, char * name, Symbol * sym) {
int error = 0;
#if defined(WIN32)
memset(sym, 0, sizeof(Symbol));
error = EINVAL;
#elif defined(_WRS_KERNEL)
char * ptr;
SYM_TYPE type;
memset(sym, 0, sizeof(Symbol));
if (symFindByName(sysSymTbl, name, &ptr, &type) != OK) {
error = errno;
if (error == S_symLib_SYMBOL_NOT_FOUND) error = ERR_SYM_NOT_FOUND;
assert(error != 0);
}
else {
sym->abs = 1;
sym->value = (unsigned long)ptr;
if (SYM_IS_UNDF(type)) sym->storage = "UNDEF";
else if (SYM_IS_COMMON(type)) sym->storage = "COMMON";
else if (SYM_IS_GLOBAL(type)) sym->storage = "GLOBAL";
else if (SYM_IS_LOCAL(type)) sym->storage = "LOCAL";
if (SYM_IS_TEXT(type)) sym->section = ".text";
else if (SYM_IS_DATA(type)) sym->section = ".data";
else if (SYM_IS_BSS(type)) sym->section = ".bss";
assert(!SYM_IS_ABS(type) || sym->section == NULL);
}
#else
char fnm[FILE_PATH_SIZE];
int found = 0;
ELF_File * file;
memset(sym, 0, sizeof(Symbol));
snprintf(fnm, sizeof(fnm), "/proc/%d/exe", ctx->mem);
file = elf_open(fnm);
if (file == NULL) error = errno;
if (error == 0 && file->sym_cache == NULL) {
if (load_tables(file) < 0) error = errno;
}
if (error == 0) {
int h = calc_hash(name);
SymbolTable * tbl = (SymbolTable *)file->sym_cache;
while (tbl != NULL && !found) {
int n = tbl->hash[h];
while (n && !found) {
Elf32_Sym * s = (Elf32_Sym *)tbl->syms + n;
if (strcmp(name, tbl->str + s->st_name) == 0) {
found = 1;
sym->abs = 1;
sym->value = s->st_value;
switch (ELF32_ST_BIND(s->st_info)) {
case STB_LOCAL: sym->storage = "LOCAL"; break;
case STB_GLOBAL: sym->storage = "GLOBAL"; break;
case STB_WEAK: sym->storage = "WEAK"; break;
}
if (s->st_shndx > 0 && s->st_shndx < file->section_cnt) {
static char sec_name[128];
ELF_Section * sec = file->sections[s->st_shndx];
if (sec != NULL && sec->name != NULL) {
sym->section = strncpy(sec_name, sec->name, sizeof(sec_name));
}
}
}
n = tbl->hash_next[n];
}
tbl = tbl->next;
}
}
if (error == 0 && !found) error = ERR_SYM_NOT_FOUND;
#endif
if (error) {
errno = error;
return -1;
}
return 0;
}
void ini_symbols_service(void) {
elf_add_close_listener(free_sym_cache);
}
#endif