#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
static prmap_t *pm = NULL;
static int npm = 0;
static prmap_t *lpm = NULL;
static Elf_Phdr *ph = NULL;
static int phsize = 0;
static int nph;
static char *stbuf = NULL;
static int stbufsize = 0;
static int stoffset;
static int nsyms;
void
__fex_sym_init()
{
struct stat statbuf;
long n;
int i;
if (pm != NULL)
free(pm);
pm = lpm = NULL;
npm = 0;
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));
}
static int
__fex_read_syms(int fd)
{
Elf_Ehdr h;
Elf_Shdr *sh;
int i, size;
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;
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;
}
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;
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;
}
for (i = 0; i < h.e_shnum; i++)
{
if (sh[i].sh_type == SHT_SYMTAB)
break;
}
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;
}
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;
}
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);
}
char *
__fex_sym(char *a, char **name)
{
Elf_Sym *s;
unsigned long fo, va, value;
int fd, i, j, nm;
char fname[PRMAPSZ+20];
if (lpm)
{
if (a >= (char*)lpm->pr_vaddr && a < (char*)lpm->pr_vaddr +
lpm->pr_size)
goto cont;
}
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;
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;
lpm = NULL;
j = __fex_read_syms(fd);
close(fd);
if (j < 0)
return NULL;
lpm = &pm[i];
cont:
fo = (a - (char*)lpm->pr_vaddr) + lpm->pr_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;
va = (fo - ph[i].p_offset) + ph[i].p_vaddr;
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;
*name = stbuf + stoffset + nm;
fo = (value - ph[i].p_vaddr) + ph[i].p_offset;
return (char*)lpm->pr_vaddr + (fo - lpm->pr_offset);
}