xref: /illumos-gate/usr/src/lib/libm/common/m9x/__fex_sym.c (revision 25c28e83beb90e7c80452a7c818c5e6f73a07dc8)
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