1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  24  */
  25 /*
  26  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  27  * Use is subject to license terms.
  28  */
  29 
  30 #include "fenv_synonyms.h"
  31 #include <elf.h>
  32 #include <stdio.h>
  33 #include <stdlib.h>
  34 #include <unistd.h>
  35 #include <fcntl.h>
  36 #include <procfs.h>
  37 #include <string.h>
  38 #include <sys/stat.h>
  39 
  40 #if defined(__sparcv9) || defined(__amd64)
  41 
  42 #define Elf_Ehdr        Elf64_Ehdr
  43 #define Elf_Phdr        Elf64_Phdr
  44 #define Elf_Shdr        Elf64_Shdr
  45 #define Elf_Sym         Elf64_Sym
  46 #define ELF_ST_BIND     ELF64_ST_BIND
  47 #define ELF_ST_TYPE     ELF64_ST_TYPE
  48 
  49 #else
  50 
  51 #define Elf_Ehdr        Elf32_Ehdr
  52 #define Elf_Phdr        Elf32_Phdr
  53 #define Elf_Shdr        Elf32_Shdr
  54 #define Elf_Sym         Elf32_Sym
  55 #define ELF_ST_BIND     ELF32_ST_BIND
  56 #define ELF_ST_TYPE     ELF32_ST_TYPE
  57 
  58 #endif  /* __sparcv9 */
  59 
  60 /* semi-permanent data established by __fex_sym_init */
  61 static  prmap_t         *pm = NULL;             /* prmap_t array */
  62 static  int                     npm = 0;                /* number of entries in pm */
  63 
  64 /* transient data modified by __fex_sym */
  65 static  prmap_t         *lpm = NULL;    /* prmap_t found in last call */
  66 static  Elf_Phdr        *ph = NULL;             /* program header array */
  67 static  int                     phsize = 0;             /* size of ph */
  68 static  int                     nph;                    /* number of entries in ph */
  69 static  char            *stbuf = NULL;  /* symbol and string table buffer */
  70 static  int                     stbufsize = 0;  /* size of stbuf */
  71 static  int                     stoffset;               /* offset of string table in stbuf */
  72 static  int                     nsyms;                  /* number of symbols in stbuf */
  73 
  74 /* get a current prmap_t list (must call this before each stack trace) */
  75 void
  76 __fex_sym_init()
  77 {
  78         struct stat     statbuf;
  79         long            n;
  80         int                     i;
  81 
  82         /* clear out the previous prmap_t list */
  83         if (pm != NULL)
  84                 free(pm);
  85         pm = lpm = NULL;
  86         npm = 0;
  87 
  88         /* get the current prmap_t list */
  89         if (stat("/proc/self/map", &statbuf) < 0 || statbuf.st_size <= 0 ||
  90                 (pm = (prmap_t*)malloc(statbuf.st_size)) == NULL)
  91                 return;
  92         if ((i = open("/proc/self/map", O_RDONLY)) < 0)
  93         {
  94                 free(pm);
  95                 pm = NULL;
  96                 return;
  97         }
  98         n = read(i, pm, statbuf.st_size);
  99         close(i);
 100         if (n != statbuf.st_size)
 101         {
 102                 free(pm);
 103                 pm = NULL;
 104         }
 105         else
 106                 npm = (int) (n / sizeof(prmap_t));
 107 }
 108 
 109 /* read ELF program headers and symbols; return -1 on error, 0 otherwise */
 110 static int
 111 __fex_read_syms(int fd)
 112 {
 113         Elf_Ehdr        h;
 114         Elf_Shdr        *sh;
 115         int                     i, size;
 116 
 117         /* read the ELF header */
 118         if (read(fd, &h, sizeof(h)) != sizeof(h))
 119                 return -1;
 120         if (h.e_ident[EI_MAG0] != ELFMAG0 ||
 121                 h.e_ident[EI_MAG1] != ELFMAG1 ||
 122                 h.e_ident[EI_MAG2] != ELFMAG2 ||
 123                 h.e_ident[EI_MAG3] != ELFMAG3 ||
 124                 h.e_phentsize != sizeof(Elf_Phdr) ||
 125                 h.e_shentsize != sizeof(Elf_Shdr))
 126                 return -1;
 127 
 128         /* get space for the program headers */
 129         size = h.e_phnum * h.e_phentsize;
 130         if (size > phsize)
 131         {
 132                 if (ph)
 133                         free(ph);
 134                 phsize = nph = 0;
 135                 if ((ph = (Elf_Phdr*)malloc(size)) == NULL)
 136                         return -1;
 137                 phsize = size;
 138         }
 139 
 140         /* read the program headers */
 141         if (lseek(fd, h.e_phoff, SEEK_SET) != h.e_phoff ||
 142                 read(fd, ph, size) != (ssize_t)size)
 143         {
 144                 nph = 0;
 145                 return -1;
 146         }
 147         nph = h.e_phnum;
 148 
 149         /* read the section headers */
 150         size = h.e_shnum * h.e_shentsize;
 151         if ((sh = (Elf_Shdr*)malloc(size)) == NULL)
 152                 return -1;
 153         if (lseek(fd, h.e_shoff, SEEK_SET) != h.e_shoff ||
 154                 read(fd, sh, size) != (ssize_t)size)
 155         {
 156                 free(sh);
 157                 return -1;
 158         }
 159 
 160         /* find the symtab section header */
 161         for (i = 0; i < h.e_shnum; i++)
 162         {
 163                 if (sh[i].sh_type == SHT_SYMTAB)
 164                         break; /* assume there is only one */
 165         }
 166         if (i == h.e_shnum || sh[i].sh_size == 0 ||
 167                 sh[i].sh_entsize != sizeof(Elf_Sym) ||
 168                 sh[i].sh_link < 1 || sh[i].sh_link >= h.e_shnum ||
 169                 sh[sh[i].sh_link].sh_type != SHT_STRTAB ||
 170                 sh[sh[i].sh_link].sh_size == 0)
 171         {
 172                 free(sh);
 173                 return -1;
 174         }
 175 
 176         /* get space for the symbol and string tables */
 177         size = (int) (sh[i].sh_size + sh[sh[i].sh_link].sh_size);
 178         if (size > stbufsize)
 179         {
 180                 if (stbuf)
 181                         free(stbuf);
 182                 stbufsize = nsyms = 0;
 183                 if ((stbuf = (char*)malloc(size)) == NULL)
 184                 {
 185                         free(sh);
 186                         return -1;
 187                 }
 188                 stbufsize = size;
 189         }
 190 
 191         /* read the symbol and string tables */
 192         if (lseek(fd, sh[i].sh_offset, SEEK_SET) != sh[i].sh_offset ||
 193                 read(fd, stbuf, sh[i].sh_size) != sh[i].sh_size ||
 194                 lseek(fd, sh[sh[i].sh_link].sh_offset, SEEK_SET) !=
 195                         sh[sh[i].sh_link].sh_offset ||
 196                 read(fd, stbuf + sh[i].sh_size, sh[sh[i].sh_link].sh_size) !=
 197                         sh[sh[i].sh_link].sh_size)
 198         {
 199                 free(sh);
 200                 return -1;
 201         }
 202         nsyms = (int) (sh[i].sh_size / sh[i].sh_entsize);
 203         stoffset = (int) sh[i].sh_size;
 204 
 205         free(sh);
 206         return 0;
 207 }
 208 
 209 /* find the symbol corresponding to the given text address;
 210    return NULL on error, symbol address otherwise */
 211 char *
 212 __fex_sym(char *a, char **name)
 213 {
 214         Elf_Sym                 *s;
 215         unsigned long   fo, va, value;
 216         int                             fd, i, j, nm;
 217         char                    fname[PRMAPSZ+20];
 218 
 219         /* see if the last prmap_t found contains the indicated address */
 220         if (lpm)
 221         {
 222                 if (a >= (char*)lpm->pr_vaddr && a < (char*)lpm->pr_vaddr +
 223                         lpm->pr_size)
 224                         goto cont;
 225         }
 226 
 227         /* look for a prmap_t that contains the indicated address */
 228         for (i = 0; i < npm; i++)
 229         {
 230                 if (a >= (char*)pm[i].pr_vaddr && a < (char*)pm[i].pr_vaddr +
 231                         pm[i].pr_size)
 232                         break;
 233         }
 234         if (i == npm)
 235                 return NULL;
 236 
 237         /* get an open file descriptor for the mapped object */
 238         if (pm[i].pr_mapname[0] == '\0')
 239                 return NULL;
 240         strcpy(fname, "/proc/self/object/");
 241         strncat(fname, pm[i].pr_mapname, PRMAPSZ);
 242         fd = open(fname, O_RDONLY);
 243         if (fd < 0)
 244                 return NULL;
 245 
 246         /* read the program headers and symbols */
 247         lpm = NULL;
 248         j = __fex_read_syms(fd);
 249         close(fd);
 250         if (j < 0)
 251                 return NULL;
 252         lpm = &pm[i];
 253 
 254 cont:
 255         /* compute the file offset corresponding to the mapped address */
 256         fo = (a - (char*)lpm->pr_vaddr) + lpm->pr_offset;
 257 
 258         /* find the program header containing the file offset */
 259         for (i = 0; i < nph; i++)
 260         {
 261                 if (ph[i].p_type == PT_LOAD && fo >= ph[i].p_offset &&
 262                         fo < ph[i].p_offset + ph[i].p_filesz)
 263                         break;
 264         }
 265         if (i == nph)
 266                 return NULL;
 267 
 268         /* compute the virtual address corresponding to the file offset */
 269         va = (fo - ph[i].p_offset) + ph[i].p_vaddr;
 270 
 271         /* find the symbol in this segment with the highest value
 272            less than or equal to the virtual address */
 273         s = (Elf_Sym*)stbuf;
 274         value = nm = 0;
 275         for (j = 0; j < nsyms; j++)
 276         {
 277                 if (s[j].st_name == 0 || s[j].st_shndx == SHN_UNDEF ||
 278                         (ELF_ST_BIND(s[j].st_info) != STB_LOCAL &&
 279                         ELF_ST_BIND(s[j].st_info) != STB_GLOBAL &&
 280                         ELF_ST_BIND(s[j].st_info) != STB_WEAK) ||
 281                         (ELF_ST_TYPE(s[j].st_info) != STT_NOTYPE &&
 282                         ELF_ST_TYPE(s[j].st_info) != STT_OBJECT &&
 283                         ELF_ST_TYPE(s[j].st_info) != STT_FUNC))
 284                 {
 285                         continue;
 286                 }
 287 
 288                 if (s[j].st_value < ph[i].p_vaddr || s[j].st_value >= ph[i].p_vaddr
 289                         + ph[i].p_memsz)
 290                 {
 291                         continue;
 292                 }
 293 
 294                 if (s[j].st_value < value || s[j].st_value > va)
 295                         continue;
 296 
 297                 value = s[j].st_value;
 298                 nm = s[j].st_name;
 299         }
 300         if (nm == 0)
 301                 return NULL;
 302 
 303         /* pass back the name and return the mapped address of the symbol */
 304         *name = stbuf + stoffset + nm;
 305         fo = (value - ph[i].p_vaddr) + ph[i].p_offset;
 306         return (char*)lpm->pr_vaddr + (fo - lpm->pr_offset);
 307 }