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