xref: /freebsd/lib/libc/gen/nlist.c (revision ee41f1b1cf5e3d4f586cb85b46123b416275862c)
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. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  *	$FreeBSD$
34  */
35 
36 #if defined(LIBC_SCCS) && !defined(lint)
37 static char sccsid[] = "@(#)nlist.c	8.1 (Berkeley) 6/4/93";
38 #endif /* LIBC_SCCS and not lint */
39 
40 #include "namespace.h"
41 #include <sys/param.h>
42 #include <sys/mman.h>
43 #include <sys/stat.h>
44 #include <sys/file.h>
45 
46 #include <errno.h>
47 #include <a.out.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include "un-namespace.h"
52 
53 #define _NLIST_DO_AOUT
54 #define _NLIST_DO_ELF
55 
56 #ifdef _NLIST_DO_ELF
57 #include <elf.h>
58 #endif
59 
60 int __fdnlist		__P((int, struct nlist *));
61 int __aout_fdnlist	__P((int, struct nlist *));
62 int __elf_fdnlist	__P((int, struct nlist *));
63 
64 int
65 nlist(name, list)
66 	const char *name;
67 	struct nlist *list;
68 {
69 	int fd, n;
70 
71 	fd = _open(name, O_RDONLY, 0);
72 	if (fd < 0)
73 		return (-1);
74 	n = __fdnlist(fd, list);
75 	(void)_close(fd);
76 	return (n);
77 }
78 
79 static struct nlist_handlers {
80 	int	(*fn) __P((int fd, struct nlist *list));
81 } nlist_fn[] = {
82 #ifdef _NLIST_DO_AOUT
83 	{ __aout_fdnlist },
84 #endif
85 #ifdef _NLIST_DO_ELF
86 	{ __elf_fdnlist },
87 #endif
88 };
89 
90 int
91 __fdnlist(fd, list)
92 	register int fd;
93 	register struct nlist *list;
94 {
95 	int n = -1, i;
96 
97 	for (i = 0; i < sizeof(nlist_fn) / sizeof(nlist_fn[0]); i++) {
98 		n = (nlist_fn[i].fn)(fd, list);
99 		if (n != -1)
100 			break;
101 	}
102 	return (n);
103 }
104 
105 #define	ISLAST(p)	(p->n_un.n_name == 0 || p->n_un.n_name[0] == 0)
106 
107 #ifdef _NLIST_DO_AOUT
108 int
109 __aout_fdnlist(fd, list)
110 	register int fd;
111 	register struct nlist *list;
112 {
113 	register struct nlist *p, *symtab;
114 	register caddr_t strtab, a_out_mmap;
115 	register off_t stroff, symoff;
116 	register u_long symsize;
117 	register int nent;
118 	struct exec * exec;
119 	struct stat st;
120 
121 	/* check that file is at least as large as struct exec! */
122 	if ((_fstat(fd, &st) < 0) || (st.st_size < sizeof(struct exec)))
123 		return (-1);
124 
125 	/* Check for files too large to mmap. */
126 	if (st.st_size > SIZE_T_MAX) {
127 		errno = EFBIG;
128 		return (-1);
129 	}
130 
131 	/*
132 	 * Map the whole a.out file into our address space.
133 	 * We then find the string table withing this area.
134 	 * We do not just mmap the string table, as it probably
135 	 * does not start at a page boundary - we save ourselves a
136 	 * lot of nastiness by mmapping the whole file.
137 	 *
138 	 * This gives us an easy way to randomly access all the strings,
139 	 * without making the memory allocation permanent as with
140 	 * malloc/free (i.e., munmap will return it to the system).
141 	 */
142 	a_out_mmap = mmap(NULL, (size_t)st.st_size, PROT_READ, MAP_PRIVATE, fd, (off_t)0);
143 	if (a_out_mmap == MAP_FAILED)
144 		return (-1);
145 
146 	exec = (struct exec *)a_out_mmap;
147 	if (N_BADMAG(*exec)) {
148 		munmap(a_out_mmap, (size_t)st.st_size);
149 		return (-1);
150 	}
151 
152 	symoff = N_SYMOFF(*exec);
153 	symsize = exec->a_syms;
154 	stroff = symoff + symsize;
155 
156 	/* find the string table in our mmapped area */
157 	strtab = a_out_mmap + stroff;
158 	symtab = (struct nlist *)(a_out_mmap + symoff);
159 
160 	/*
161 	 * clean out any left-over information for all valid entries.
162 	 * Type and value defined to be 0 if not found; historical
163 	 * versions cleared other and desc as well.  Also figure out
164 	 * the largest string length so don't read any more of the
165 	 * string table than we have to.
166 	 *
167 	 * XXX clearing anything other than n_type and n_value violates
168 	 * the semantics given in the man page.
169 	 */
170 	nent = 0;
171 	for (p = list; !ISLAST(p); ++p) {
172 		p->n_type = 0;
173 		p->n_other = 0;
174 		p->n_desc = 0;
175 		p->n_value = 0;
176 		++nent;
177 	}
178 
179 	while (symsize > 0) {
180 		register int soff;
181 
182 		symsize-= sizeof(struct nlist);
183 		soff = symtab->n_un.n_strx;
184 
185 
186 		if (soff != 0 && (symtab->n_type & N_STAB) == 0)
187 			for (p = list; !ISLAST(p); p++)
188 				if (!strcmp(&strtab[soff], p->n_un.n_name)) {
189 					p->n_value = symtab->n_value;
190 					p->n_type = symtab->n_type;
191 					p->n_desc = symtab->n_desc;
192 					p->n_other = symtab->n_other;
193 					if (--nent <= 0)
194 						break;
195 				}
196 		symtab++;
197 	}
198 	munmap(a_out_mmap, (size_t)st.st_size);
199 	return (nent);
200 }
201 #endif
202 
203 #ifdef _NLIST_DO_ELF
204 static void elf_sym_to_nlist __P((struct nlist *, Elf_Sym *, Elf_Shdr *, int));
205 
206 /*
207  * __elf_is_okay__ - Determine if ehdr really
208  * is ELF and valid for the target platform.
209  *
210  * WARNING:  This is NOT a ELF ABI function and
211  * as such it's use should be restricted.
212  */
213 int
214 __elf_is_okay__(ehdr)
215 	register Elf_Ehdr *ehdr;
216 {
217 	register int retval = 0;
218 	/*
219 	 * We need to check magic, class size, endianess,
220 	 * and version before we look at the rest of the
221 	 * Elf_Ehdr structure.  These few elements are
222 	 * represented in a machine independant fashion.
223 	 */
224 	if (IS_ELF(*ehdr) &&
225 	    ehdr->e_ident[EI_CLASS] == ELF_TARG_CLASS &&
226 	    ehdr->e_ident[EI_DATA] == ELF_TARG_DATA &&
227 	    ehdr->e_ident[EI_VERSION] == ELF_TARG_VER) {
228 
229 		/* Now check the machine dependant header */
230 		if (ehdr->e_machine == ELF_TARG_MACH &&
231 		    ehdr->e_version == ELF_TARG_VER)
232 			retval = 1;
233 	}
234 	return retval;
235 }
236 
237 int
238 __elf_fdnlist(fd, list)
239 	register int fd;
240 	register struct nlist *list;
241 {
242 	register struct nlist *p;
243 	register Elf_Off symoff = 0, symstroff = 0;
244 	register Elf_Word symsize = 0, symstrsize = 0;
245 	register Elf_Sword cc, i;
246 	int nent = -1;
247 	int errsave;
248 	Elf_Sym sbuf[1024];
249 	Elf_Sym *s;
250 	Elf_Ehdr ehdr;
251 	char *strtab = NULL;
252 	Elf_Shdr *shdr = NULL;
253 	Elf_Shdr *sh;
254 	Elf_Word shdr_size;
255 	void *base;
256 	struct stat st;
257 
258 	/* Make sure obj is OK */
259 	if (lseek(fd, (off_t)0, SEEK_SET) == -1 ||
260 	    _read(fd, &ehdr, sizeof(Elf_Ehdr)) != sizeof(Elf_Ehdr) ||
261 	    !__elf_is_okay__(&ehdr) ||
262 	    _fstat(fd, &st) < 0)
263 		return (-1);
264 
265 	/* calculate section header table size */
266 	shdr_size = ehdr.e_shentsize * ehdr.e_shnum;
267 
268 	/* Make sure it's not too big to mmap */
269 	if (shdr_size > SIZE_T_MAX) {
270 		errno = EFBIG;
271 		return (-1);
272 	}
273 
274 	/* mmap section header table */
275 	base = mmap(NULL, (size_t)shdr_size, PROT_READ, 0, fd,
276 	    (off_t)ehdr.e_shoff);
277 	if (base == MAP_FAILED)
278 		return (-1);
279 	shdr = (Elf_Shdr *)base;
280 
281 	/*
282 	 * Find the symbol table entry and it's corresponding
283 	 * string table entry.	Version 1.1 of the ABI states
284 	 * that there is only one symbol table but that this
285 	 * could change in the future.
286 	 */
287 	for (i = 0; i < ehdr.e_shnum; i++) {
288 		if (shdr[i].sh_type == SHT_SYMTAB) {
289 			symoff = shdr[i].sh_offset;
290 			symsize = shdr[i].sh_size;
291 			symstroff = shdr[shdr[i].sh_link].sh_offset;
292 			symstrsize = shdr[shdr[i].sh_link].sh_size;
293 			break;
294 		}
295 	}
296 
297 	/* Check for files too large to mmap. */
298 	if (symstrsize > SIZE_T_MAX) {
299 		errno = EFBIG;
300 		goto done;
301 	}
302 	/*
303 	 * Map string table into our address space.  This gives us
304 	 * an easy way to randomly access all the strings, without
305 	 * making the memory allocation permanent as with malloc/free
306 	 * (i.e., munmap will return it to the system).
307 	 */
308 	base = mmap(NULL, (size_t)symstrsize, PROT_READ, 0, fd,
309 	    (off_t)symstroff);
310 	if (base == MAP_FAILED)
311 		goto done;
312 	strtab = (char *)base;
313 
314 	/*
315 	 * clean out any left-over information for all valid entries.
316 	 * Type and value defined to be 0 if not found; historical
317 	 * versions cleared other and desc as well.  Also figure out
318 	 * the largest string length so don't read any more of the
319 	 * string table than we have to.
320 	 *
321 	 * XXX clearing anything other than n_type and n_value violates
322 	 * the semantics given in the man page.
323 	 */
324 	nent = 0;
325 	for (p = list; !ISLAST(p); ++p) {
326 		p->n_type = 0;
327 		p->n_other = 0;
328 		p->n_desc = 0;
329 		p->n_value = 0;
330 		++nent;
331 	}
332 
333 	/* Don't process any further if object is stripped. */
334 	if (symoff == 0)
335 		goto done;
336 
337 	if (lseek(fd, (off_t) symoff, SEEK_SET) == -1) {
338 		nent = -1;
339 		goto done;
340 	}
341 
342 	while (symsize > 0 && nent > 0) {
343 		cc = MIN(symsize, sizeof(sbuf));
344 		if (_read(fd, sbuf, cc) != cc)
345 			break;
346 		symsize -= cc;
347 		for (s = sbuf; cc > 0 && nent > 0; ++s, cc -= sizeof(*s)) {
348 			char *name;
349 			struct nlist *p;
350 
351 			name = strtab + s->st_name;
352 			if (name[0] == '\0')
353 				continue;
354 			for (p = list; !ISLAST(p); p++) {
355 				if ((p->n_un.n_name[0] == '_' &&
356 				    strcmp(name, p->n_un.n_name+1) == 0)
357 				    || strcmp(name, p->n_un.n_name) == 0) {
358 					elf_sym_to_nlist(p, s, shdr,
359 					    ehdr.e_shnum);
360 					if (--nent <= 0)
361 						break;
362 				}
363 			}
364 		}
365 	}
366   done:
367 	errsave = errno;
368 	if (strtab != NULL)
369 		munmap(strtab, symstrsize);
370 	if (shdr != NULL)
371 		munmap(shdr, shdr_size);
372 	errno = errsave;
373 	return (nent);
374 }
375 
376 /*
377  * Convert an Elf_Sym into an nlist structure.  This fills in only the
378  * n_value and n_type members.
379  */
380 static void
381 elf_sym_to_nlist(nl, s, shdr, shnum)
382 	struct nlist *nl;
383 	Elf_Sym *s;
384 	Elf_Shdr *shdr;
385 	int shnum;
386 {
387 	nl->n_value = s->st_value;
388 
389 	switch (s->st_shndx) {
390 	case SHN_UNDEF:
391 	case SHN_COMMON:
392 		nl->n_type = N_UNDF;
393 		break;
394 	case SHN_ABS:
395 		nl->n_type = ELF_ST_TYPE(s->st_info) == STT_FILE ?
396 		    N_FN : N_ABS;
397 		break;
398 	default:
399 		if (s->st_shndx >= shnum)
400 			nl->n_type = N_UNDF;
401 		else {
402 			Elf_Shdr *sh = shdr + s->st_shndx;
403 
404 			nl->n_type = sh->sh_type == SHT_PROGBITS ?
405 			    (sh->sh_flags & SHF_WRITE ? N_DATA : N_TEXT) :
406 			    (sh->sh_type == SHT_NOBITS ? N_BSS : N_UNDF);
407 		}
408 		break;
409 	}
410 
411 	if (ELF_ST_BIND(s->st_info) == STB_GLOBAL ||
412 	    ELF_ST_BIND(s->st_info) == STB_WEAK)
413 		nl->n_type |= N_EXT;
414 }
415 #endif /* _NLIST_DO_ELF */
416