nlist.c revision 298120
1194140Simp/*
2194140Simp * Copyright (c) 1989, 1993
3194140Simp *	The Regents of the University of California.  All rights reserved.
4194140Simp *
5194140Simp * Redistribution and use in source and binary forms, with or without
6194140Simp * modification, are permitted provided that the following conditions
7194140Simp * are met:
8194140Simp * 1. Redistributions of source code must retain the above copyright
9194140Simp *    notice, this list of conditions and the following disclaimer.
10194140Simp * 2. Redistributions in binary form must reproduce the above copyright
11194140Simp *    notice, this list of conditions and the following disclaimer in the
12194140Simp *    documentation and/or other materials provided with the distribution.
13194140Simp * 4. Neither the name of the University nor the names of its contributors
14194140Simp *    may be used to endorse or promote products derived from this software
15194140Simp *    without specific prior written permission.
16194140Simp *
17194140Simp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18194140Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19194140Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20194140Simp * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21194140Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22194140Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23194140Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24194140Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25194140Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26194140Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27194140Simp * SUCH DAMAGE.
28194140Simp */
29194140Simp
30194140Simp#if defined(LIBC_SCCS) && !defined(lint)
31194140Simpstatic char sccsid[] = "@(#)nlist.c	8.1 (Berkeley) 6/4/93";
32194140Simp#endif /* LIBC_SCCS and not lint */
33194140Simp#include <sys/cdefs.h>
34194140Simp__FBSDID("$FreeBSD: head/lib/libc/gen/nlist.c 298120 2016-04-16 17:52:00Z pfg $");
35194140Simp
36194140Simp#include "namespace.h"
37194140Simp#include <sys/param.h>
38194140Simp#include <sys/mman.h>
39202985Simp#include <sys/stat.h>
40202985Simp#include <sys/file.h>
41194140Simp#include <arpa/inet.h>
42194140Simp
43194140Simp#include <errno.h>
44194140Simp#include <a.out.h>
45194140Simp#include <stdio.h>
46194140Simp#include <string.h>
47194140Simp#include <unistd.h>
48194140Simp#include "un-namespace.h"
49194140Simp
50194140Simp/* i386 is the only current FreeBSD architecture that used a.out format. */
51194140Simp#ifdef __i386__
52194140Simp#define _NLIST_DO_AOUT
53194140Simp#endif
54194140Simp#define _NLIST_DO_ELF
55194140Simp
56202867Simp#ifdef _NLIST_DO_ELF
57202063Simp#include <machine/elf.h>
58194140Simp#include <elf-hints.h>
59210311Sjmallett#endif
60232812Sjmallett
61210311Sjmallettint __fdnlist(int, struct nlist *);
62202985Simpint __aout_fdnlist(int, struct nlist *);
63202985Simpint __elf_fdnlist(int, struct nlist *);
64210311Sjmallettint __elf_is_okay__(Elf_Ehdr *);
65210311Sjmallett
66210311Sjmallettint
67194140Simpnlist(const char *name, struct nlist *list)
68210311Sjmallett{
69210311Sjmallett	int fd, n;
70210311Sjmallett
71210311Sjmallett	fd = _open(name, O_RDONLY | O_CLOEXEC, 0);
72210311Sjmallett	if (fd < 0)
73194140Simp		return (-1);
74210311Sjmallett	n = __fdnlist(fd, list);
75194140Simp	(void)_close(fd);
76194140Simp	return (n);
77210311Sjmallett}
78210311Sjmallett
79210311Sjmallettstatic struct nlist_handlers {
80194140Simp	int	(*fn)(int fd, struct nlist *list);
81194140Simp} nlist_fn[] = {
82210311Sjmallett#ifdef _NLIST_DO_AOUT
83194140Simp	{ __aout_fdnlist },
84194140Simp#endif
85194140Simp#ifdef _NLIST_DO_ELF
86194140Simp	{ __elf_fdnlist },
87196236Simp#endif
88210311Sjmallett};
89210311Sjmallett
90210311Sjmallettint
91210311Sjmallett__fdnlist(int fd, struct nlist *list)
92210311Sjmallett{
93210311Sjmallett	int n = -1;
94210311Sjmallett	unsigned int i;
95194140Simp
96194140Simp	for (i = 0; i < nitems(nlist_fn); i++) {
97194140Simp		n = (nlist_fn[i].fn)(fd, list);
98194140Simp		if (n != -1)
99194140Simp			break;
100194140Simp	}
101194140Simp	return (n);
102194140Simp}
103194140Simp
104194140Simp#define	ISLAST(p)	(p->n_un.n_name == 0 || p->n_un.n_name[0] == 0)
105194140Simp
106194140Simp#ifdef _NLIST_DO_AOUT
107210311Sjmallettint
108194140Simp__aout_fdnlist(int fd, struct nlist *list)
109194140Simp{
110232812Sjmallett	struct nlist *p, *symtab;
111194140Simp	caddr_t strtab, a_out_mmap;
112194140Simp	off_t stroff, symoff;
113194140Simp	u_long symsize;
114194140Simp	int nent;
115194140Simp	struct exec * exec;
116194140Simp	struct stat st;
117194140Simp
118194140Simp	/* check that file is at least as large as struct exec! */
119194140Simp	if ((_fstat(fd, &st) < 0) || (st.st_size < sizeof(struct exec)))
120194140Simp		return (-1);
121194140Simp
122194140Simp	/* Check for files too large to mmap. */
123194140Simp	if (st.st_size > SIZE_T_MAX) {
124194140Simp		errno = EFBIG;
125194140Simp		return (-1);
126194140Simp	}
127194140Simp
128194140Simp	/*
129194140Simp	 * Map the whole a.out file into our address space.
130194140Simp	 * We then find the string table withing this area.
131194140Simp	 * We do not just mmap the string table, as it probably
132210311Sjmallett	 * does not start at a page boundary - we save ourselves a
133210311Sjmallett	 * lot of nastiness by mmapping the whole file.
134232812Sjmallett	 *
135210311Sjmallett	 * This gives us an easy way to randomly access all the strings,
136210311Sjmallett	 * without making the memory allocation permanent as with
137232812Sjmallett	 * malloc/free (i.e., munmap will return it to the system).
138210311Sjmallett	 */
139210311Sjmallett	a_out_mmap = mmap(NULL, (size_t)st.st_size, PROT_READ, MAP_PRIVATE, fd, (off_t)0);
140210311Sjmallett	if (a_out_mmap == MAP_FAILED)
141210311Sjmallett		return (-1);
142194140Simp
143194140Simp	exec = (struct exec *)a_out_mmap;
144194140Simp	if (N_BADMAG(*exec)) {
145194140Simp		munmap(a_out_mmap, (size_t)st.st_size);
146194140Simp		return (-1);
147194140Simp	}
148202985Simp
149210311Sjmallett	symoff = N_SYMOFF(*exec);
150194140Simp	symsize = exec->a_syms;
151194140Simp	stroff = symoff + symsize;
152194140Simp
153194140Simp	/* find the string table in our mmapped area */
154194140Simp	strtab = a_out_mmap + stroff;
155194140Simp	symtab = (struct nlist *)(a_out_mmap + symoff);
156194140Simp
157194140Simp	/*
158194140Simp	 * clean out any left-over information for all valid entries.
159194140Simp	 * Type and value defined to be 0 if not found; historical
160194140Simp	 * versions cleared other and desc as well.  Also figure out
161194140Simp	 * the largest string length so don't read any more of the
162194140Simp	 * string table than we have to.
163194140Simp	 *
164194140Simp	 * XXX clearing anything other than n_type and n_value violates
165194140Simp	 * the semantics given in the man page.
166194140Simp	 */
167194140Simp	nent = 0;
168194140Simp	for (p = list; !ISLAST(p); ++p) {
169194140Simp		p->n_type = 0;
170194140Simp		p->n_other = 0;
171194140Simp		p->n_desc = 0;
172194140Simp		p->n_value = 0;
173194140Simp		++nent;
174194140Simp	}
175194140Simp
176194140Simp	while (symsize > 0) {
177194140Simp		int soff;
178194140Simp
179194140Simp		symsize-= sizeof(struct nlist);
180194140Simp		soff = symtab->n_un.n_strx;
181194140Simp
182194140Simp
183194140Simp		if (soff != 0 && (symtab->n_type & N_STAB) == 0)
184210311Sjmallett			for (p = list; !ISLAST(p); p++)
185210311Sjmallett				if (!strcmp(&strtab[soff], p->n_un.n_name)) {
186210311Sjmallett					p->n_value = symtab->n_value;
187210311Sjmallett					p->n_type = symtab->n_type;
188194140Simp					p->n_desc = symtab->n_desc;
189210311Sjmallett					p->n_other = symtab->n_other;
190210311Sjmallett					if (--nent <= 0)
191210311Sjmallett						break;
192194140Simp				}
193194140Simp		symtab++;
194194140Simp	}
195212843Sjmallett	munmap(a_out_mmap, (size_t)st.st_size);
196212843Sjmallett	return (nent);
197194140Simp}
198194140Simp#endif
199194140Simp
200194140Simp#ifdef _NLIST_DO_ELF
201194140Simpstatic void elf_sym_to_nlist(struct nlist *, Elf_Sym *, Elf_Shdr *, int);
202194140Simp
203194140Simp/*
204194140Simp * __elf_is_okay__ - Determine if ehdr really
205194140Simp * is ELF and valid for the target platform.
206194140Simp *
207210311Sjmallett * WARNING:  This is NOT an ELF ABI function and
208 * as such its use should be restricted.
209 */
210int
211__elf_is_okay__(Elf_Ehdr *ehdr)
212{
213	int retval = 0;
214	/*
215	 * We need to check magic, class size, endianess,
216	 * and version before we look at the rest of the
217	 * Elf_Ehdr structure.  These few elements are
218	 * represented in a machine independant fashion.
219	 */
220	if (IS_ELF(*ehdr) &&
221	    ehdr->e_ident[EI_CLASS] == ELF_TARG_CLASS &&
222	    ehdr->e_ident[EI_DATA] == ELF_TARG_DATA &&
223	    ehdr->e_ident[EI_VERSION] == ELF_TARG_VER) {
224
225		/* Now check the machine dependant header */
226		if (ehdr->e_machine == ELF_TARG_MACH &&
227		    ehdr->e_version == ELF_TARG_VER)
228			retval = 1;
229	}
230	return retval;
231}
232
233int
234__elf_fdnlist(int fd, struct nlist *list)
235{
236	struct nlist *p;
237	Elf_Off symoff = 0, symstroff = 0;
238	Elf_Size symsize = 0, symstrsize = 0;
239	Elf_Ssize cc, i;
240	int nent = -1;
241	int errsave;
242	Elf_Sym sbuf[1024];
243	Elf_Sym *s;
244	Elf_Ehdr ehdr;
245	char *strtab = NULL;
246	Elf_Shdr *shdr = NULL;
247	Elf_Size shdr_size;
248	void *base;
249	struct stat st;
250
251	/* Make sure obj is OK */
252	if (lseek(fd, (off_t)0, SEEK_SET) == -1 ||
253	    _read(fd, &ehdr, sizeof(Elf_Ehdr)) != sizeof(Elf_Ehdr) ||
254	    !__elf_is_okay__(&ehdr) ||
255	    _fstat(fd, &st) < 0)
256		return (-1);
257
258	/* calculate section header table size */
259	shdr_size = ehdr.e_shentsize * ehdr.e_shnum;
260
261	/* Make sure it's not too big to mmap */
262	if (shdr_size > SIZE_T_MAX) {
263		errno = EFBIG;
264		return (-1);
265	}
266
267	/* mmap section header table */
268	base = mmap(NULL, (size_t)shdr_size, PROT_READ, MAP_PRIVATE, fd,
269	    (off_t)ehdr.e_shoff);
270	if (base == MAP_FAILED)
271		return (-1);
272	shdr = (Elf_Shdr *)base;
273
274	/*
275	 * Find the symbol table entry and it's corresponding
276	 * string table entry.	Version 1.1 of the ABI states
277	 * that there is only one symbol table but that this
278	 * could change in the future.
279	 */
280	for (i = 0; i < ehdr.e_shnum; i++) {
281		if (shdr[i].sh_type == SHT_SYMTAB) {
282			symoff = shdr[i].sh_offset;
283			symsize = shdr[i].sh_size;
284			symstroff = shdr[shdr[i].sh_link].sh_offset;
285			symstrsize = shdr[shdr[i].sh_link].sh_size;
286			break;
287		}
288	}
289
290	/* Check for files too large to mmap. */
291	if (symstrsize > SIZE_T_MAX) {
292		errno = EFBIG;
293		goto done;
294	}
295	/*
296	 * Map string table into our address space.  This gives us
297	 * an easy way to randomly access all the strings, without
298	 * making the memory allocation permanent as with malloc/free
299	 * (i.e., munmap will return it to the system).
300	 */
301	base = mmap(NULL, (size_t)symstrsize, PROT_READ, MAP_PRIVATE, fd,
302	    (off_t)symstroff);
303	if (base == MAP_FAILED)
304		goto done;
305	strtab = (char *)base;
306
307	/*
308	 * clean out any left-over information for all valid entries.
309	 * Type and value defined to be 0 if not found; historical
310	 * versions cleared other and desc as well.  Also figure out
311	 * the largest string length so don't read any more of the
312	 * string table than we have to.
313	 *
314	 * XXX clearing anything other than n_type and n_value violates
315	 * the semantics given in the man page.
316	 */
317	nent = 0;
318	for (p = list; !ISLAST(p); ++p) {
319		p->n_type = 0;
320		p->n_other = 0;
321		p->n_desc = 0;
322		p->n_value = 0;
323		++nent;
324	}
325
326	/* Don't process any further if object is stripped. */
327	if (symoff == 0)
328		goto done;
329
330	if (lseek(fd, (off_t) symoff, SEEK_SET) == -1) {
331		nent = -1;
332		goto done;
333	}
334
335	while (symsize > 0 && nent > 0) {
336		cc = MIN(symsize, sizeof(sbuf));
337		if (_read(fd, sbuf, cc) != cc)
338			break;
339		symsize -= cc;
340		for (s = sbuf; cc > 0 && nent > 0; ++s, cc -= sizeof(*s)) {
341			char *name;
342			struct nlist *p;
343
344			name = strtab + s->st_name;
345			if (name[0] == '\0')
346				continue;
347			for (p = list; !ISLAST(p); p++) {
348				if ((p->n_un.n_name[0] == '_' &&
349				    strcmp(name, p->n_un.n_name+1) == 0)
350				    || strcmp(name, p->n_un.n_name) == 0) {
351					elf_sym_to_nlist(p, s, shdr,
352					    ehdr.e_shnum);
353					if (--nent <= 0)
354						break;
355				}
356			}
357		}
358	}
359  done:
360	errsave = errno;
361	if (strtab != NULL)
362		munmap(strtab, symstrsize);
363	if (shdr != NULL)
364		munmap(shdr, shdr_size);
365	errno = errsave;
366	return (nent);
367}
368
369/*
370 * Convert an Elf_Sym into an nlist structure.  This fills in only the
371 * n_value and n_type members.
372 */
373static void
374elf_sym_to_nlist(struct nlist *nl, Elf_Sym *s, Elf_Shdr *shdr, int shnum)
375{
376	nl->n_value = s->st_value;
377
378	switch (s->st_shndx) {
379	case SHN_UNDEF:
380	case SHN_COMMON:
381		nl->n_type = N_UNDF;
382		break;
383	case SHN_ABS:
384		nl->n_type = ELF_ST_TYPE(s->st_info) == STT_FILE ?
385		    N_FN : N_ABS;
386		break;
387	default:
388		if (s->st_shndx >= shnum)
389			nl->n_type = N_UNDF;
390		else {
391			Elf_Shdr *sh = shdr + s->st_shndx;
392
393			nl->n_type = sh->sh_type == SHT_PROGBITS ?
394			    (sh->sh_flags & SHF_WRITE ? N_DATA : N_TEXT) :
395			    (sh->sh_type == SHT_NOBITS ? N_BSS : N_UNDF);
396		}
397		break;
398	}
399
400	if (ELF_ST_BIND(s->st_info) == STB_GLOBAL ||
401	    ELF_ST_BIND(s->st_info) == STB_WEAK)
402		nl->n_type |= N_EXT;
403}
404#endif /* _NLIST_DO_ELF */
405