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 }