xref: /openbsd-src/lib/libc/gen/nlist.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: nlist.c,v 1.57 2014/01/19 20:48:57 deraadt Exp $ */
2 /*
3  * Copyright (c) 1989, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the University nor the names of its contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/mman.h>
34 #include <sys/stat.h>
35 
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <a.out.h>		/* pulls in nlist.h */
43 
44 #ifdef _NLIST_DO_ELF
45 #include <elf_abi.h>
46 #endif
47 
48 int	__fdnlist(int, struct nlist *);
49 #ifdef _NLIST_DO_ELF
50 int	__elf_is_okay__(Elf_Ehdr *ehdr);
51 #endif
52 
53 #define	ISLAST(p)	(p->n_un.n_name == 0 || p->n_un.n_name[0] == 0)
54 
55 #ifdef _NLIST_DO_ELF
56 /*
57  * __elf_is_okay__ - Determine if ehdr really
58  * is ELF and valid for the target platform.
59  *
60  * WARNING:  This is NOT a ELF ABI function and
61  * as such its use should be restricted.
62  */
63 int
64 __elf_is_okay__(Elf_Ehdr *ehdr)
65 {
66 	int retval = 0;
67 	/*
68 	 * We need to check magic, class size, endianess,
69 	 * and version before we look at the rest of the
70 	 * Elf_Ehdr structure.  These few elements are
71 	 * represented in a machine independent fashion.
72 	 */
73 	if (IS_ELF(*ehdr) &&
74 	    ehdr->e_ident[EI_CLASS] == ELF_TARG_CLASS &&
75 	    ehdr->e_ident[EI_DATA] == ELF_TARG_DATA &&
76 	    ehdr->e_ident[EI_VERSION] == ELF_TARG_VER) {
77 
78 		/* Now check the machine dependant header */
79 		if (ehdr->e_machine == ELF_TARG_MACH &&
80 		    ehdr->e_version == ELF_TARG_VER)
81 			retval = 1;
82 	}
83 
84 	return retval;
85 }
86 
87 int
88 __fdnlist(int fd, struct nlist *list)
89 {
90 	struct nlist *p;
91 	caddr_t strtab;
92 	Elf_Off symoff = 0, symstroff = 0;
93 	Elf_Word symsize = 0, symstrsize = 0;
94 	Elf_Sword nent, cc, i;
95 	Elf_Sym sbuf[1024];
96 	Elf_Sym *s;
97 	Elf_Ehdr ehdr;
98 	Elf_Shdr *shdr = NULL;
99 	Elf_Word shdr_size;
100 	struct stat st;
101 	int usemalloc = 0;
102 
103 	/* Make sure obj is OK */
104 	if (pread(fd, &ehdr, sizeof(Elf_Ehdr), (off_t)0) != sizeof(Elf_Ehdr) ||
105 	    !__elf_is_okay__(&ehdr) || fstat(fd, &st) < 0)
106 		return (-1);
107 
108 	/* calculate section header table size */
109 	shdr_size = ehdr.e_shentsize * ehdr.e_shnum;
110 
111 	/* Make sure it's not too big to mmap */
112 	if (shdr_size > SIZE_T_MAX) {
113 		errno = EFBIG;
114 		return (-1);
115 	}
116 
117 	/* mmap section header table */
118 	shdr = (Elf_Shdr *)mmap(NULL, (size_t)shdr_size, PROT_READ,
119 	    MAP_SHARED|MAP_FILE, fd, (off_t) ehdr.e_shoff);
120 	if (shdr == MAP_FAILED) {
121 		usemalloc = 1;
122 		if ((shdr = malloc(shdr_size)) == NULL)
123 			return (-1);
124 
125 		if (pread(fd, shdr, shdr_size, (off_t)ehdr.e_shoff) != shdr_size) {
126 			free(shdr);
127 			return (-1);
128 		}
129 	}
130 
131 	/*
132 	 * Find the symbol table entry and its corresponding
133 	 * string table entry.	Version 1.1 of the ABI states
134 	 * that there is only one symbol table but that this
135 	 * could change in the future.
136 	 */
137 	for (i = 0; i < ehdr.e_shnum; i++) {
138 		if (shdr[i].sh_type == SHT_SYMTAB) {
139 			symoff = shdr[i].sh_offset;
140 			symsize = shdr[i].sh_size;
141 			symstroff = shdr[shdr[i].sh_link].sh_offset;
142 			symstrsize = shdr[shdr[i].sh_link].sh_size;
143 			break;
144 		}
145 	}
146 
147 	/* Flush the section header table */
148 	if (usemalloc)
149 		free(shdr);
150 	else
151 		munmap((caddr_t)shdr, shdr_size);
152 
153 	/* Check for files too large to mmap. */
154 	/* XXX is this really possible? */
155 	if (symstrsize > SIZE_T_MAX) {
156 		errno = EFBIG;
157 		return (-1);
158 	}
159 	/*
160 	 * Map string table into our address space.  This gives us
161 	 * an easy way to randomly access all the strings, without
162 	 * making the memory allocation permanent as with malloc/free
163 	 * (i.e., munmap will return it to the system).
164 	 */
165 	if (usemalloc) {
166 		if ((strtab = malloc(symstrsize)) == NULL)
167 			return (-1);
168 		if (pread(fd, strtab, symstrsize, (off_t)symstroff) != symstrsize) {
169 			free(strtab);
170 			return (-1);
171 		}
172 	} else {
173 		strtab = mmap(NULL, (size_t)symstrsize, PROT_READ,
174 		    MAP_SHARED|MAP_FILE, fd, (off_t) symstroff);
175 		if (strtab == MAP_FAILED)
176 			return (-1);
177 	}
178 	/*
179 	 * clean out any left-over information for all valid entries.
180 	 * Type and value defined to be 0 if not found; historical
181 	 * versions cleared other and desc as well.  Also figure out
182 	 * the largest string length so don't read any more of the
183 	 * string table than we have to.
184 	 *
185 	 * XXX clearing anything other than n_type and n_value violates
186 	 * the semantics given in the man page.
187 	 */
188 	nent = 0;
189 	for (p = list; !ISLAST(p); ++p) {
190 		p->n_type = 0;
191 		p->n_other = 0;
192 		p->n_desc = 0;
193 		p->n_value = 0;
194 		++nent;
195 	}
196 
197 	/* Don't process any further if object is stripped. */
198 	/* ELFism - dunno if stripped by looking at header */
199 	if (symoff == 0)
200 		goto elf_done;
201 
202 	while (symsize > 0) {
203 		cc = MIN(symsize, sizeof(sbuf));
204 		if (pread(fd, sbuf, cc, (off_t)symoff) != cc)
205 			break;
206 		symsize -= cc;
207 		symoff += cc;
208 		for (s = sbuf; cc > 0; ++s, cc -= sizeof(*s)) {
209 			int soff = s->st_name;
210 
211 			if (soff == 0)
212 				continue;
213 			for (p = list; !ISLAST(p); p++) {
214 				char *sym;
215 
216 				/*
217 				 * First we check for the symbol as it was
218 				 * provided by the user. If that fails
219 				 * and the first char is an '_', skip over
220 				 * the '_' and try again.
221 				 * XXX - What do we do when the user really
222 				 *       wants '_foo' and the are symbols
223 				 *       for both 'foo' and '_foo' in the
224 				 *	 table and 'foo' is first?
225 				 */
226 				sym = p->n_un.n_name;
227 				if (strcmp(&strtab[soff], sym) != 0 &&
228 				    (sym[0] != '_' ||
229 				     strcmp(&strtab[soff], sym + 1) != 0))
230 					continue;
231 
232 				p->n_value = s->st_value;
233 
234 				/* XXX - type conversion */
235 				/*	 is pretty rude. */
236 				switch(ELF_ST_TYPE(s->st_info)) {
237 				case STT_NOTYPE:
238 					switch (s->st_shndx) {
239 					case SHN_UNDEF:
240 						p->n_type = N_UNDF;
241 						break;
242 					case SHN_ABS:
243 						p->n_type = N_ABS;
244 						break;
245 					case SHN_COMMON:
246 						p->n_type = N_COMM;
247 						break;
248 					default:
249 						p->n_type = N_COMM | N_EXT;
250 						break;
251 					}
252 					break;
253 				case STT_OBJECT:
254 					p->n_type = N_DATA;
255 					break;
256 				case STT_FUNC:
257 					p->n_type = N_TEXT;
258 					break;
259 				case STT_FILE:
260 					p->n_type = N_FN;
261 					break;
262 				}
263 				if (ELF_ST_BIND(s->st_info) ==
264 				    STB_LOCAL)
265 					p->n_type = N_EXT;
266 				p->n_desc = 0;
267 				p->n_other = 0;
268 				if (--nent <= 0)
269 					break;
270 			}
271 		}
272 	}
273 elf_done:
274 	if (usemalloc)
275 		free(strtab);
276 	else
277 		munmap(strtab, symstrsize);
278 	return (nent);
279 }
280 #endif /* _NLIST_DO_ELF */
281 
282 int
283 nlist(const char *name, struct nlist *list)
284 {
285 	int fd, n;
286 
287 	fd = open(name, O_RDONLY, 0);
288 	if (fd < 0)
289 		return (-1);
290 	n = __fdnlist(fd, list);
291 	(void)close(fd);
292 	return (n);
293 }
294