2796N/A/*
2796N/A * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
2796N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
2796N/A *
2796N/A * This code is free software; you can redistribute it and/or modify it
2796N/A * under the terms of the GNU General Public License version 2 only, as
2796N/A * published by the Free Software Foundation.
2796N/A *
2796N/A * This code is distributed in the hope that it will be useful, but WITHOUT
2796N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
2796N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
2796N/A * version 2 for more details (a copy is included in the LICENSE file that
2796N/A * accompanied this code).
2796N/A *
2796N/A * You should have received a copy of the GNU General Public License version
2796N/A * 2 along with this work; if not, write to the Free Software Foundation,
2796N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2796N/A *
2796N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2796N/A * or visit www.oracle.com if you need additional information or have any
2796N/A * questions.
2796N/A *
2796N/A */
2796N/A
2796N/A#include <unistd.h>
2796N/A#include <search.h>
2796N/A#include <stdlib.h>
2796N/A#include <string.h>
2796N/A#include <db.h>
2796N/A#include <fcntl.h>
2796N/A#include "symtab.h"
2796N/A#include "salibelf.h"
2796N/A
2796N/A
2796N/A// ----------------------------------------------------
2796N/A// functions for symbol lookups
2796N/A// ----------------------------------------------------
2796N/A
2796N/Astruct elf_section {
2796N/A ELF_SHDR *c_shdr;
2796N/A void *c_data;
2796N/A};
2796N/A
2796N/Astruct elf_symbol {
2796N/A char *name;
2796N/A uintptr_t offset;
2796N/A uintptr_t size;
2796N/A};
2796N/A
2796N/Atypedef struct symtab {
2796N/A char *strs;
2796N/A size_t num_symbols;
2796N/A struct elf_symbol *symbols;
2796N/A DB* hash_table;
2796N/A} symtab_t;
2796N/A
2796N/A// read symbol table from given fd.
2796N/Astruct symtab* build_symtab(int fd) {
2796N/A ELF_EHDR ehdr;
2796N/A struct symtab* symtab = NULL;
2796N/A
2796N/A // Reading of elf header
2796N/A struct elf_section *scn_cache = NULL;
2796N/A int cnt = 0;
2796N/A ELF_SHDR* shbuf = NULL;
2796N/A ELF_SHDR* cursct = NULL;
2796N/A ELF_PHDR* phbuf = NULL;
2796N/A int symtab_found = 0;
2796N/A int dynsym_found = 0;
2796N/A uint32_t symsection = SHT_SYMTAB;
2796N/A
2796N/A uintptr_t baseaddr = (uintptr_t)-1;
2796N/A
2796N/A lseek(fd, (off_t)0L, SEEK_SET);
2796N/A if (! read_elf_header(fd, &ehdr)) {
2796N/A // not an elf
2796N/A return NULL;
2796N/A }
2796N/A
2796N/A // read ELF header
2796N/A if ((shbuf = read_section_header_table(fd, &ehdr)) == NULL) {
2796N/A goto quit;
2796N/A }
2796N/A
2796N/A baseaddr = find_base_address(fd, &ehdr);
2796N/A
2796N/A scn_cache = calloc(ehdr.e_shnum, sizeof(*scn_cache));
2796N/A if (scn_cache == NULL) {
2796N/A goto quit;
2796N/A }
2796N/A
2796N/A for (cursct = shbuf, cnt = 0; cnt < ehdr.e_shnum; cnt++) {
2796N/A scn_cache[cnt].c_shdr = cursct;
2796N/A if (cursct->sh_type == SHT_SYMTAB ||
2796N/A cursct->sh_type == SHT_STRTAB ||
2796N/A cursct->sh_type == SHT_DYNSYM) {
2796N/A if ( (scn_cache[cnt].c_data = read_section_data(fd, &ehdr, cursct)) == NULL) {
2796N/A goto quit;
2796N/A }
2796N/A }
2796N/A
2796N/A if (cursct->sh_type == SHT_SYMTAB)
2796N/A symtab_found++;
2796N/A
2796N/A if (cursct->sh_type == SHT_DYNSYM)
2796N/A dynsym_found++;
2796N/A
2796N/A cursct++;
2796N/A }
2796N/A
2796N/A if (!symtab_found && dynsym_found)
2796N/A symsection = SHT_DYNSYM;
2796N/A
2796N/A for (cnt = 1; cnt < ehdr.e_shnum; cnt++) {
2796N/A ELF_SHDR *shdr = scn_cache[cnt].c_shdr;
2796N/A
2796N/A if (shdr->sh_type == symsection) {
2796N/A ELF_SYM *syms;
2842N/A int j, n;
2796N/A size_t size;
2796N/A
2796N/A // FIXME: there could be multiple data buffers associated with the
2796N/A // same ELF section. Here we can handle only one buffer. See man page
2796N/A // for elf_getdata on Solaris.
2796N/A
2796N/A // guarantee(symtab == NULL, "multiple symtab");
2796N/A symtab = calloc(1, sizeof(*symtab));
2796N/A if (symtab == NULL) {
2796N/A goto quit;
2796N/A }
2796N/A // the symbol table
2796N/A syms = (ELF_SYM *)scn_cache[cnt].c_data;
2796N/A
2796N/A // number of symbols
2796N/A n = shdr->sh_size / shdr->sh_entsize;
2796N/A
2796N/A // create hash table, we use berkeley db to
2796N/A // manipulate the hash table.
2796N/A symtab->hash_table = dbopen(NULL, O_CREAT | O_RDWR, 0600, DB_HASH, NULL);
2796N/A // guarantee(symtab->hash_table, "unexpected failure: dbopen");
2842N/A if (symtab->hash_table == NULL)
2842N/A goto bad;
2796N/A
2796N/A // shdr->sh_link points to the section that contains the actual strings
2796N/A // for symbol names. the st_name field in ELF_SYM is just the
2796N/A // string table index. we make a copy of the string table so the
2796N/A // strings will not be destroyed by elf_end.
2796N/A size = scn_cache[shdr->sh_link].c_shdr->sh_size;
2796N/A symtab->strs = malloc(size);
2842N/A if (symtab->strs == NULL)
2842N/A goto bad;
2796N/A memcpy(symtab->strs, scn_cache[shdr->sh_link].c_data, size);
2796N/A
2796N/A // allocate memory for storing symbol offset and size;
2796N/A symtab->num_symbols = n;
2796N/A symtab->symbols = calloc(n , sizeof(*symtab->symbols));
2842N/A if (symtab->symbols == NULL)
2842N/A goto bad;
2796N/A
2796N/A // copy symbols info our symtab and enter them info the hash table
2796N/A for (j = 0; j < n; j++, syms++) {
2796N/A DBT key, value;
2796N/A char *sym_name = symtab->strs + syms->st_name;
2796N/A
2796N/A // skip non-object and non-function symbols
2796N/A int st_type = ELF_ST_TYPE(syms->st_info);
2796N/A if ( st_type != STT_FUNC && st_type != STT_OBJECT)
2796N/A continue;
2796N/A // skip empty strings and undefined symbols
2796N/A if (*sym_name == '\0' || syms->st_shndx == SHN_UNDEF) continue;
2796N/A
2796N/A symtab->symbols[j].name = sym_name;
2796N/A symtab->symbols[j].offset = syms->st_value - baseaddr;
2796N/A symtab->symbols[j].size = syms->st_size;
2796N/A
2796N/A key.data = sym_name;
2796N/A key.size = strlen(sym_name) + 1;
2796N/A value.data = &(symtab->symbols[j]);
2796N/A value.size = sizeof(void *);
2796N/A (*symtab->hash_table->put)(symtab->hash_table, &key, &value, 0);
2796N/A }
2796N/A }
2796N/A }
2842N/A goto quit;
2842N/A
2842N/Abad:
2842N/A destroy_symtab(symtab);
2842N/A symtab = NULL;
2796N/A
2796N/Aquit:
2796N/A if (shbuf) free(shbuf);
2796N/A if (phbuf) free(phbuf);
2796N/A if (scn_cache) {
2796N/A for (cnt = 0; cnt < ehdr.e_shnum; cnt++) {
2796N/A if (scn_cache[cnt].c_data != NULL) {
2796N/A free(scn_cache[cnt].c_data);
2796N/A }
2796N/A }
2796N/A free(scn_cache);
2796N/A }
2796N/A return symtab;
2796N/A}
2796N/A
2796N/Avoid destroy_symtab(struct symtab* symtab) {
2796N/A if (!symtab) return;
2796N/A if (symtab->strs) free(symtab->strs);
2796N/A if (symtab->symbols) free(symtab->symbols);
2796N/A if (symtab->hash_table) {
2842N/A (*symtab->hash_table->close)(symtab->hash_table);
2796N/A }
2796N/A free(symtab);
2796N/A}
2796N/A
2796N/Auintptr_t search_symbol(struct symtab* symtab, uintptr_t base,
2796N/A const char *sym_name, int *sym_size) {
2796N/A DBT key, value;
2796N/A int ret;
2796N/A
2796N/A // library does not have symbol table
2796N/A if (!symtab || !symtab->hash_table)
2796N/A return 0;
2796N/A
2796N/A key.data = (char*)(uintptr_t)sym_name;
2796N/A key.size = strlen(sym_name) + 1;
2796N/A ret = (*symtab->hash_table->get)(symtab->hash_table, &key, &value, 0);
2796N/A if (ret == 0) {
2796N/A struct elf_symbol *sym = value.data;
2796N/A uintptr_t rslt = (uintptr_t) ((char*)base + sym->offset);
2796N/A if (sym_size) *sym_size = sym->size;
2796N/A return rslt;
2796N/A }
2796N/A
2796N/A return 0;
2796N/A}
2796N/A
2796N/Aconst char* nearest_symbol(struct symtab* symtab, uintptr_t offset,
2796N/A uintptr_t* poffset) {
2796N/A int n = 0;
2796N/A if (!symtab) return NULL;
2796N/A for (; n < symtab->num_symbols; n++) {
2842N/A struct elf_symbol* sym = &(symtab->symbols[n]);
2842N/A if (sym->name != NULL &&
2842N/A offset >= sym->offset && offset < sym->offset + sym->size) {
2842N/A if (poffset) *poffset = (offset - sym->offset);
2842N/A return sym->name;
2842N/A }
2796N/A }
2796N/A return NULL;
2796N/A}