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 } 308