| /******************************************************************************* |
| * 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 |
| |