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 }