xref: /openbsd-src/lib/libc/gen/nlist.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*
2  * Copyright (c) 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #if defined(LIBC_SCCS) && !defined(lint)
31 static char rcsid[] = "$OpenBSD: nlist.c,v 1.45 2003/06/25 21:16:47 deraadt Exp $";
32 #endif /* LIBC_SCCS and not lint */
33 
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/mman.h>
37 #include <sys/stat.h>
38 
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <a.out.h>		/* pulls in nlist.h */
46 
47 #ifdef _NLIST_DO_ELF
48 #include <elf_abi.h>
49 #include <olf_abi.h>
50 #endif
51 
52 #ifdef _NLIST_DO_ECOFF
53 #include <sys/exec_ecoff.h>
54 #endif
55 
56 int	__fdnlist(int, struct nlist *);
57 int	__aout_fdnlist(int, struct nlist *);
58 int	__ecoff_fdnlist(int, struct nlist *);
59 int	__elf_fdnlist(int, struct nlist *);
60 #ifdef _NLIST_DO_ELF
61 int	__elf_is_okay__(register Elf_Ehdr *ehdr);
62 #endif
63 
64 #define	ISLAST(p)	(p->n_un.n_name == 0 || p->n_un.n_name[0] == 0)
65 
66 #ifdef _NLIST_DO_AOUT
67 int
68 __aout_fdnlist(fd, list)
69 	register int fd;
70 	register struct nlist *list;
71 {
72 	register struct nlist *p, *s;
73 	register char *strtab;
74 	register off_t symoff, stroff;
75 	register u_long symsize;
76 	register int nent, cc;
77 	int strsize, usemalloc = 0;
78 	struct nlist nbuf[1024];
79 	struct exec exec;
80 
81 	if (pread(fd, &exec, sizeof(exec), (off_t)0) != sizeof(exec) ||
82 	    N_BADMAG(exec) || exec.a_syms == NULL)
83 		return (-1);
84 
85 	stroff = N_STROFF(exec);
86 	symoff = N_SYMOFF(exec);
87 	symsize = exec.a_syms;
88 
89 	/* Read in the size of the string table. */
90 	if (pread(fd, (void *)&strsize, sizeof(strsize), stroff) !=
91 	    sizeof(strsize))
92 		return (-1);
93 	else
94 		stroff += sizeof(strsize);
95 
96 	/*
97 	 * Read in the string table.  We try mmap, but that will fail
98 	 * for /dev/ksyms so fall back on malloc.  Since OpenBSD's malloc(3)
99 	 * returns memory to the system on free this does not cause bloat.
100 	 */
101 	strsize -= sizeof(strsize);
102 	strtab = mmap(NULL, (size_t)strsize, PROT_READ, MAP_SHARED|MAP_FILE,
103 	    fd, stroff);
104 	if (strtab == MAP_FAILED) {
105 		usemalloc = 1;
106 		if ((strtab = (char *)malloc(strsize)) == NULL)
107 			return (-1);
108 		errno = EIO;
109 		if (pread(fd, strtab, strsize, stroff) != strsize) {
110 			nent = -1;
111 			goto aout_done;
112 		}
113 	}
114 
115 	/*
116 	 * clean out any left-over information for all valid entries.
117 	 * Type and value defined to be 0 if not found; historical
118 	 * versions cleared other and desc as well.  Also figure out
119 	 * the largest string length so don't read any more of the
120 	 * string table than we have to.
121 	 *
122 	 * XXX clearing anything other than n_type and n_value violates
123 	 * the semantics given in the man page.
124 	 */
125 	nent = 0;
126 	for (p = list; !ISLAST(p); ++p) {
127 		p->n_type = 0;
128 		p->n_other = 0;
129 		p->n_desc = 0;
130 		p->n_value = 0;
131 		++nent;
132 	}
133 
134 	while (symsize > 0) {
135 		cc = MIN(symsize, sizeof(nbuf));
136 		if (pread(fd, nbuf, cc, symoff) != cc)
137 			break;
138 		symsize -= cc;
139 		symoff += cc;
140 		for (s = nbuf; cc > 0; ++s, cc -= sizeof(*s)) {
141 			char *sname = strtab + s->n_un.n_strx - sizeof(int);
142 
143 			if (s->n_un.n_strx == 0 || (s->n_type & N_STAB) != 0)
144 				continue;
145 			for (p = list; !ISLAST(p); p++) {
146 				char *pname = p->n_un.n_name;
147 
148 				if (*sname != '_' && *pname == '_')
149 					pname++;
150 				if (!strcmp(sname, pname)) {
151 					p->n_value = s->n_value;
152 					p->n_type = s->n_type;
153 					p->n_desc = s->n_desc;
154 					p->n_other = s->n_other;
155 					if (--nent <= 0)
156 						break;
157 				}
158 			}
159 		}
160 	}
161 aout_done:
162 	if (usemalloc)
163 		free(strtab);
164 	else
165 		munmap(strtab, strsize);
166 	return (nent);
167 }
168 #endif /* _NLIST_DO_AOUT */
169 
170 #ifdef _NLIST_DO_ECOFF
171 #define check(off, size)	((off < 0) || (off + size > mappedsize))
172 #define	BAD			do { rv = -1; goto out; } while (0)
173 #define	BADUNMAP		do { rv = -1; goto unmap; } while (0)
174 
175 int
176 __ecoff_fdnlist(fd, list)
177 	register int fd;
178 	register struct nlist *list;
179 {
180 	struct nlist *p;
181 	struct ecoff_exechdr *exechdrp;
182 	struct ecoff_symhdr *symhdrp;
183 	struct ecoff_extsym *esyms;
184 	struct stat st;
185 	char *mappedfile;
186 	size_t mappedsize;
187 	u_long symhdroff, extstroff;
188 	u_int symhdrsize;
189 	int rv, nent;
190 	long i, nesyms;
191 
192 	rv = -3;
193 
194 	if (fstat(fd, &st) < 0)
195 		BAD;
196 	if (st.st_size > SIZE_T_MAX) {
197 		errno = EFBIG;
198 		BAD;
199 	}
200 	mappedsize = st.st_size;
201 	mappedfile = mmap(NULL, mappedsize, PROT_READ, MAP_SHARED|MAP_FILE,
202 	    fd, 0);
203 	if (mappedfile == MAP_FAILED)
204 		BAD;
205 
206 	if (check(0, sizeof *exechdrp))
207 		BADUNMAP;
208 	exechdrp = (struct ecoff_exechdr *)&mappedfile[0];
209 
210 	if (ECOFF_BADMAG(exechdrp))
211 		BADUNMAP;
212 
213 	symhdroff = exechdrp->f.f_symptr;
214 	symhdrsize = exechdrp->f.f_nsyms;
215 
216 	if (check(symhdroff, sizeof *symhdrp) ||
217 	    sizeof *symhdrp != symhdrsize)
218 		BADUNMAP;
219 	symhdrp = (struct ecoff_symhdr *)&mappedfile[symhdroff];
220 
221 	nesyms = symhdrp->esymMax;
222 	if (check(symhdrp->cbExtOffset, nesyms * sizeof *esyms))
223 		BADUNMAP;
224 	esyms = (struct ecoff_extsym *)&mappedfile[symhdrp->cbExtOffset];
225 	extstroff = symhdrp->cbSsExtOffset;
226 
227 	/*
228 	 * clean out any left-over information for all valid entries.
229 	 * Type and value defined to be 0 if not found; historical
230 	 * versions cleared other and desc as well.
231 	 *
232 	 * XXX clearing anything other than n_type and n_value violates
233 	 * the semantics given in the man page.
234 	 */
235 	nent = 0;
236 	for (p = list; !ISLAST(p); ++p) {
237 		p->n_type = 0;
238 		p->n_other = 0;
239 		p->n_desc = 0;
240 		p->n_value = 0;
241 		++nent;
242 	}
243 
244 	for (i = 0; i < nesyms; i++) {
245 		for (p = list; !ISLAST(p); p++) {
246 			char *nlistname;
247 			char *symtabname;
248 
249 			nlistname = p->n_un.n_name;
250 			if (*nlistname == '_')
251 				nlistname++;
252 			symtabname =
253 			    &mappedfile[extstroff + esyms[i].es_strindex];
254 
255 			if (!strcmp(symtabname, nlistname)) {
256 				p->n_value = esyms[i].es_value;
257 				p->n_type = N_EXT;		/* XXX */
258 				p->n_desc = 0;			/* XXX */
259 				p->n_other = 0;			/* XXX */
260 				if (--nent <= 0)
261 					break;
262 			}
263 		}
264 	}
265 	rv = nent;
266 
267 unmap:
268 	munmap(mappedfile, mappedsize);
269 out:
270 	return (rv);
271 }
272 #endif /* _NLIST_DO_ECOFF */
273 
274 #ifdef _NLIST_DO_ELF
275 /*
276  * __elf_is_okay__ - Determine if ehdr really
277  * is ELF and valid for the target platform.
278  *
279  * WARNING:  This is NOT a ELF ABI function and
280  * as such it's use should be restricted.
281  */
282 int
283 __elf_is_okay__(ehdr)
284 	register Elf_Ehdr *ehdr;
285 {
286 	register int retval = 0;
287 	/*
288 	 * We need to check magic, class size, endianess,
289 	 * and version before we look at the rest of the
290 	 * Elf_Ehdr structure.  These few elements are
291 	 * represented in a machine independent fashion.
292 	 */
293 	if ((IS_ELF(*ehdr) || IS_OLF(*ehdr)) &&
294 	    ehdr->e_ident[EI_CLASS] == ELF_TARG_CLASS &&
295 	    ehdr->e_ident[EI_DATA] == ELF_TARG_DATA &&
296 	    ehdr->e_ident[EI_VERSION] == ELF_TARG_VER) {
297 
298 		/* Now check the machine dependant header */
299 		if (ehdr->e_machine == ELF_TARG_MACH &&
300 		    ehdr->e_version == ELF_TARG_VER)
301 			retval = 1;
302 	}
303 
304 	return retval;
305 }
306 
307 int
308 __elf_fdnlist(fd, list)
309 	register int fd;
310 	register struct nlist *list;
311 {
312 	register struct nlist *p;
313 	register caddr_t strtab;
314 	register Elf_Off symoff = 0, symstroff = 0;
315 	register Elf_Word symsize = 0, symstrsize = 0;
316 	register Elf_Sword nent, cc, i;
317 	Elf_Sym sbuf[1024];
318 	Elf_Sym *s;
319 	Elf_Ehdr ehdr;
320 	Elf_Shdr *shdr = NULL;
321 	Elf_Word shdr_size;
322 	struct stat st;
323 	int usemalloc = 0;
324 
325 	/* Make sure obj is OK */
326 	if (pread(fd, &ehdr, sizeof(Elf_Ehdr), (off_t)0) != sizeof(Elf_Ehdr) ||
327 	    !__elf_is_okay__(&ehdr) || fstat(fd, &st) < 0)
328 		return (-1);
329 
330 	/* calculate section header table size */
331 	shdr_size = ehdr.e_shentsize * ehdr.e_shnum;
332 
333 	/* Make sure it's not too big to mmap */
334 	if (shdr_size > SIZE_T_MAX) {
335 		errno = EFBIG;
336 		return (-1);
337 	}
338 
339 	/* mmap section header table */
340 	shdr = (Elf_Shdr *)mmap(NULL, (size_t)shdr_size, PROT_READ,
341 	    MAP_SHARED|MAP_FILE, fd, (off_t) ehdr.e_shoff);
342 	if (shdr == MAP_FAILED) {
343 		usemalloc = 1;
344 		if ((shdr = malloc(shdr_size)) == NULL)
345 			return (-1);
346 		if (pread(fd, shdr, shdr_size, ehdr.e_shoff) != shdr_size) {
347 			free(shdr);
348 			return (-1);
349 		}
350 	}
351 
352 	/*
353 	 * Find the symbol table entry and it's corresponding
354 	 * string table entry.	Version 1.1 of the ABI states
355 	 * that there is only one symbol table but that this
356 	 * could change in the future.
357 	 */
358 	for (i = 0; i < ehdr.e_shnum; i++) {
359 		if (shdr[i].sh_type == SHT_SYMTAB) {
360 			symoff = shdr[i].sh_offset;
361 			symsize = shdr[i].sh_size;
362 			symstroff = shdr[shdr[i].sh_link].sh_offset;
363 			symstrsize = shdr[shdr[i].sh_link].sh_size;
364 			break;
365 		}
366 	}
367 
368 	/* Flush the section header table */
369 	if (usemalloc)
370 		free(shdr);
371 	else
372 		munmap((caddr_t)shdr, shdr_size);
373 
374 	/* Check for files too large to mmap. */
375 	/* XXX is this really possible? */
376 	if (symstrsize > SIZE_T_MAX) {
377 		errno = EFBIG;
378 		return (-1);
379 	}
380 	/*
381 	 * Map string table into our address space.  This gives us
382 	 * an easy way to randomly access all the strings, without
383 	 * making the memory allocation permanent as with malloc/free
384 	 * (i.e., munmap will return it to the system).
385 	 */
386 	if (usemalloc) {
387 		if ((strtab = malloc(symstrsize)) == NULL)
388 			return (-1);
389 		if (pread(fd, strtab, symstrsize, symstroff) != symstrsize) {
390 			free(strtab);
391 			return (-1);
392 		}
393 	} else {
394 		strtab = mmap(NULL, (size_t)symstrsize, PROT_READ,
395 		    MAP_SHARED|MAP_FILE, fd, (off_t) symstroff);
396 		if (strtab == MAP_FAILED)
397 			return (-1);
398 	}
399 	/*
400 	 * clean out any left-over information for all valid entries.
401 	 * Type and value defined to be 0 if not found; historical
402 	 * versions cleared other and desc as well.  Also figure out
403 	 * the largest string length so don't read any more of the
404 	 * string table than we have to.
405 	 *
406 	 * XXX clearing anything other than n_type and n_value violates
407 	 * the semantics given in the man page.
408 	 */
409 	nent = 0;
410 	for (p = list; !ISLAST(p); ++p) {
411 		p->n_type = 0;
412 		p->n_other = 0;
413 		p->n_desc = 0;
414 		p->n_value = 0;
415 		++nent;
416 	}
417 
418 	/* Don't process any further if object is stripped. */
419 	/* ELFism - dunno if stripped by looking at header */
420 	if (symoff == 0)
421 		goto elf_done;
422 
423 	while (symsize > 0) {
424 		cc = MIN(symsize, sizeof(sbuf));
425 		if (pread(fd, sbuf, cc, symoff) != cc)
426 			break;
427 		symsize -= cc;
428 		symoff += cc;
429 		for (s = sbuf; cc > 0; ++s, cc -= sizeof(*s)) {
430 			int soff = s->st_name;
431 
432 			if (soff == 0)
433 				continue;
434 			for (p = list; !ISLAST(p); p++) {
435 				char *sym;
436 
437 				/*
438 				 * First we check for the symbol as it was
439 				 * provided by the user. If that fails,
440 				 * skip the first char if it's an '_' and
441 				 * try again.
442 				 * XXX - What do we do when the user really
443 				 *       wants '_foo' and the are symbols
444 				 *       for both 'foo' and '_foo' in the
445 				 *	 table and 'foo' is first?
446 				 */
447 				sym = p->n_un.n_name;
448 				if (strcmp(&strtab[soff], sym) != 0 &&
449 				    ((sym[0] == '_') &&
450 				     strcmp(&strtab[soff], sym + 1) != 0))
451 					continue;
452 
453 				p->n_value = s->st_value;
454 
455 				/* XXX - type conversion */
456 				/*	 is pretty rude. */
457 				switch(ELF_ST_TYPE(s->st_info)) {
458 				case STT_NOTYPE:
459 					p->n_type = N_UNDF;
460 					break;
461 				case STT_OBJECT:
462 					p->n_type = N_DATA;
463 					break;
464 				case STT_FUNC:
465 					p->n_type = N_TEXT;
466 					break;
467 				case STT_FILE:
468 					p->n_type = N_FN;
469 					break;
470 				}
471 				if (ELF_ST_BIND(s->st_info) ==
472 				    STB_LOCAL)
473 					p->n_type = N_EXT;
474 				p->n_desc = 0;
475 				p->n_other = 0;
476 				if (--nent <= 0)
477 					break;
478 			}
479 		}
480 	}
481 elf_done:
482 	if (usemalloc)
483 		free(strtab);
484 	else
485 		munmap(strtab, symstrsize);
486 	return (nent);
487 }
488 #endif /* _NLIST_DO_ELF */
489 
490 
491 static struct nlist_handlers {
492 	int	(*fn)(int fd, struct nlist *list);
493 } nlist_fn[] = {
494 #ifdef _NLIST_DO_AOUT
495 	{ __aout_fdnlist },
496 #endif
497 #ifdef _NLIST_DO_ELF
498 	{ __elf_fdnlist },
499 #endif
500 #ifdef _NLIST_DO_ECOFF
501 	{ __ecoff_fdnlist },
502 #endif
503 };
504 
505 int
506 __fdnlist(fd, list)
507 	register int fd;
508 	register struct nlist *list;
509 {
510 	int n = -1, i;
511 
512 	for (i = 0; i < sizeof(nlist_fn)/sizeof(nlist_fn[0]); i++) {
513 		n = (nlist_fn[i].fn)(fd, list);
514 		if (n != -1)
515 			break;
516 	}
517 	return (n);
518 }
519 
520 
521 int
522 nlist(name, list)
523 	const char *name;
524 	struct nlist *list;
525 {
526 	int fd, n;
527 
528 	fd = open(name, O_RDONLY, 0);
529 	if (fd < 0)
530 		return (-1);
531 	n = __fdnlist(fd, list);
532 	(void)close(fd);
533 	return (n);
534 }
535