__fex_sym.c revision ddc0e0b53c661f6e439e3b7072b3ef353eadb4af
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <elf.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <procfs.h>
#include <string.h>
#include <sys/stat.h>
#if defined(__sparcv9) || defined(__amd64)
#define Elf_Ehdr Elf64_Ehdr
#define Elf_Phdr Elf64_Phdr
#define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym
#define ELF_ST_BIND ELF64_ST_BIND
#define ELF_ST_TYPE ELF64_ST_TYPE
#else
#define Elf_Ehdr Elf32_Ehdr
#define Elf_Phdr Elf32_Phdr
#define Elf_Shdr Elf32_Shdr
#define Elf_Sym Elf32_Sym
#define ELF_ST_BIND ELF32_ST_BIND
#define ELF_ST_TYPE ELF32_ST_TYPE
#endif /* __sparcv9 */
/* semi-permanent data established by __fex_sym_init */
static prmap_t *pm = NULL; /* prmap_t array */
static int npm = 0; /* number of entries in pm */
/* transient data modified by __fex_sym */
static prmap_t *lpm = NULL; /* prmap_t found in last call */
static Elf_Phdr *ph = NULL; /* program header array */
static int phsize = 0; /* size of ph */
static int nph; /* number of entries in ph */
static char *stbuf = NULL; /* symbol and string table buffer */
static int stbufsize = 0; /* size of stbuf */
static int stoffset; /* offset of string table in stbuf */
static int nsyms; /* number of symbols in stbuf */
/* get a current prmap_t list (must call this before each stack trace) */
void
__fex_sym_init()
{
struct stat statbuf;
long n;
int i;
/* clear out the previous prmap_t list */
if (pm != NULL)
free(pm);
pm = lpm = NULL;
npm = 0;
/* get the current prmap_t list */
if (stat("/proc/self/map", &statbuf) < 0 || statbuf.st_size <= 0 ||
(pm = (prmap_t*)malloc(statbuf.st_size)) == NULL)
return;
if ((i = open("/proc/self/map", O_RDONLY)) < 0)
{
free(pm);
pm = NULL;
return;
}
n = read(i, pm, statbuf.st_size);
close(i);
if (n != statbuf.st_size)
{
free(pm);
pm = NULL;
}
else
npm = (int) (n / sizeof(prmap_t));
}
/* read ELF program headers and symbols; return -1 on error, 0 otherwise */
static int
__fex_read_syms(int fd)
{
Elf_Ehdr h;
Elf_Shdr *sh;
int i, size;
/* read the ELF header */
if (read(fd, &h, sizeof(h)) != sizeof(h))
return -1;
if (h.e_ident[EI_MAG0] != ELFMAG0 ||
h.e_ident[EI_MAG1] != ELFMAG1 ||
h.e_ident[EI_MAG2] != ELFMAG2 ||
h.e_ident[EI_MAG3] != ELFMAG3 ||
h.e_phentsize != sizeof(Elf_Phdr) ||
h.e_shentsize != sizeof(Elf_Shdr))
return -1;
/* get space for the program headers */
size = h.e_phnum * h.e_phentsize;
if (size > phsize)
{
if (ph)
free(ph);
phsize = nph = 0;
if ((ph = (Elf_Phdr*)malloc(size)) == NULL)
return -1;
phsize = size;
}
/* read the program headers */
if (lseek(fd, h.e_phoff, SEEK_SET) != h.e_phoff ||
read(fd, ph, size) != (ssize_t)size)
{
nph = 0;
return -1;
}
nph = h.e_phnum;
/* read the section headers */
size = h.e_shnum * h.e_shentsize;
if ((sh = (Elf_Shdr*)malloc(size)) == NULL)
return -1;
if (lseek(fd, h.e_shoff, SEEK_SET) != h.e_shoff ||
read(fd, sh, size) != (ssize_t)size)
{
free(sh);
return -1;
}
/* find the symtab section header */
for (i = 0; i < h.e_shnum; i++)
{
if (sh[i].sh_type == SHT_SYMTAB)
break; /* assume there is only one */
}
if (i == h.e_shnum || sh[i].sh_size == 0 ||
sh[i].sh_entsize != sizeof(Elf_Sym) ||
sh[i].sh_link < 1 || sh[i].sh_link >= h.e_shnum ||
sh[sh[i].sh_link].sh_type != SHT_STRTAB ||
sh[sh[i].sh_link].sh_size == 0)
{
free(sh);
return -1;
}
/* get space for the symbol and string tables */
size = (int) (sh[i].sh_size + sh[sh[i].sh_link].sh_size);
if (size > stbufsize)
{
if (stbuf)
free(stbuf);
stbufsize = nsyms = 0;
if ((stbuf = (char*)malloc(size)) == NULL)
{
free(sh);
return -1;
}
stbufsize = size;
}
/* read the symbol and string tables */
if (lseek(fd, sh[i].sh_offset, SEEK_SET) != sh[i].sh_offset ||
read(fd, stbuf, sh[i].sh_size) != sh[i].sh_size ||
lseek(fd, sh[sh[i].sh_link].sh_offset, SEEK_SET) !=
sh[sh[i].sh_link].sh_offset ||
read(fd, stbuf + sh[i].sh_size, sh[sh[i].sh_link].sh_size) !=
sh[sh[i].sh_link].sh_size)
{
free(sh);
return (-1);
}
nsyms = (int) (sh[i].sh_size / sh[i].sh_entsize);
stoffset = (int) sh[i].sh_size;
free(sh);
return (0);
}
/* find the symbol corresponding to the given text address;
return NULL on error, symbol address otherwise */
char *
__fex_sym(char *a, char **name)
{
Elf_Sym *s;
unsigned long fo, va, value;
int fd, i, j, nm;
char fname[PRMAPSZ+20];
/* see if the last prmap_t found contains the indicated address */
if (lpm)
{
if (a >= (char*)lpm->pr_vaddr && a < (char*)lpm->pr_vaddr +
lpm->pr_size)
goto cont;
}
/* look for a prmap_t that contains the indicated address */
for (i = 0; i < npm; i++)
{
if (a >= (char*)pm[i].pr_vaddr && a < (char*)pm[i].pr_vaddr +
pm[i].pr_size)
break;
}
if (i == npm)
return NULL;
/* get an open file descriptor for the mapped object */
if (pm[i].pr_mapname[0] == '\0')
return NULL;
strcpy(fname, "/proc/self/object/");
strncat(fname, pm[i].pr_mapname, PRMAPSZ);
fd = open(fname, O_RDONLY);
if (fd < 0)
return NULL;
/* read the program headers and symbols */
lpm = NULL;
j = __fex_read_syms(fd);
close(fd);
if (j < 0)
return NULL;
lpm = &pm[i];
cont:
/* compute the file offset corresponding to the mapped address */
fo = (a - (char*)lpm->pr_vaddr) + lpm->pr_offset;
/* find the program header containing the file offset */
for (i = 0; i < nph; i++)
{
if (ph[i].p_type == PT_LOAD && fo >= ph[i].p_offset &&
fo < ph[i].p_offset + ph[i].p_filesz)
break;
}
if (i == nph)
return NULL;
/* compute the virtual address corresponding to the file offset */
va = (fo - ph[i].p_offset) + ph[i].p_vaddr;
/* find the symbol in this segment with the highest value
less than or equal to the virtual address */
s = (Elf_Sym*)stbuf;
value = nm = 0;
for (j = 0; j < nsyms; j++)
{
if (s[j].st_name == 0 || s[j].st_shndx == SHN_UNDEF ||
(ELF_ST_BIND(s[j].st_info) != STB_LOCAL &&
ELF_ST_BIND(s[j].st_info) != STB_GLOBAL &&
ELF_ST_BIND(s[j].st_info) != STB_WEAK) ||
(ELF_ST_TYPE(s[j].st_info) != STT_NOTYPE &&
ELF_ST_TYPE(s[j].st_info) != STT_OBJECT &&
ELF_ST_TYPE(s[j].st_info) != STT_FUNC))
{
continue;
}
if (s[j].st_value < ph[i].p_vaddr || s[j].st_value >= ph[i].p_vaddr
+ ph[i].p_memsz)
{
continue;
}
if (s[j].st_value < value || s[j].st_value > va)
continue;
value = s[j].st_value;
nm = s[j].st_name;
}
if (nm == 0)
return NULL;
/* pass back the name and return the mapped address of the symbol */
*name = stbuf + stoffset + nm;
fo = (value - ph[i].p_vaddr) + ph[i].p_offset;
return (char*)lpm->pr_vaddr + (fo - lpm->pr_offset);
}