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