xref: /openbsd-src/lib/libc/gen/nlist.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: nlist.c,v 1.66 2015/12/29 22:31:21 mmcc 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/mman.h>
33 #include <sys/stat.h>
34 
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <stdint.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 #include <elf_abi.h>
44 
45 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
46 
47 int	__fdnlist(int, struct nlist *);
48 PROTO_NORMAL(__fdnlist);
49 
50 #define	ISLAST(p)	(p->n_un.n_name == 0 || p->n_un.n_name[0] == 0)
51 
52 /*
53  * __elf_is_okay__ - Determine if ehdr really
54  * is ELF and valid for the target platform.
55  *
56  * WARNING:  This is NOT a ELF ABI function and
57  * as such its use should be restricted.
58  */
59 static int
60 __elf_is_okay__(Elf_Ehdr *ehdr)
61 {
62 	int retval = 0;
63 	/*
64 	 * We need to check magic, class size, endianess,
65 	 * and version before we look at the rest of the
66 	 * Elf_Ehdr structure.  These few elements are
67 	 * represented in a machine independent fashion.
68 	 */
69 	if (IS_ELF(*ehdr) &&
70 	    ehdr->e_ident[EI_CLASS] == ELF_TARG_CLASS &&
71 	    ehdr->e_ident[EI_DATA] == ELF_TARG_DATA &&
72 	    ehdr->e_ident[EI_VERSION] == ELF_TARG_VER) {
73 
74 		/* Now check the machine dependent header */
75 		if (ehdr->e_machine == ELF_TARG_MACH &&
76 		    ehdr->e_version == ELF_TARG_VER)
77 			retval = 1;
78 	}
79 
80 	if (ehdr->e_shentsize != sizeof(Elf_Shdr))
81 		return 0;
82 
83 	return retval;
84 }
85 
86 int
87 __fdnlist(int fd, struct nlist *list)
88 {
89 	struct nlist *p;
90 	caddr_t strtab;
91 	Elf_Off symoff = 0, symstroff = 0;
92 	Elf_Word symsize = 0, symstrsize = 0;
93 	Elf_Sword nent, cc, i;
94 	Elf_Sym sbuf[1024];
95 	Elf_Sym *s;
96 	Elf_Ehdr ehdr;
97 	Elf_Shdr *shdr = NULL;
98 	Elf_Word shdr_size;
99 	struct stat st;
100 	int usemalloc = 0;
101 	size_t left, len;
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 (SIZE_MAX - ehdr.e_shoff < shdr_size ||
113 	    (S_ISREG(st.st_mode) && ehdr.e_shoff + shdr_size > st.st_size)) {
114 		errno = EFBIG;
115 		return (-1);
116 	}
117 
118 	/* mmap section header table */
119 	shdr = (Elf_Shdr *)mmap(NULL, (size_t)shdr_size, PROT_READ,
120 	    MAP_SHARED|MAP_FILE, fd, (off_t) ehdr.e_shoff);
121 	if (shdr == MAP_FAILED) {
122 		usemalloc = 1;
123 		if ((shdr = malloc(shdr_size)) == NULL)
124 			return (-1);
125 
126 		if (pread(fd, shdr, shdr_size, (off_t)ehdr.e_shoff) !=
127 		    shdr_size) {
128 			free(shdr);
129 			return (-1);
130 		}
131 	}
132 
133 	/*
134 	 * Find the symbol table entry and its corresponding
135 	 * string table entry.	Version 1.1 of the ABI states
136 	 * that there is only one symbol table but that this
137 	 * could change in the future.
138 	 */
139 	for (i = 0; i < ehdr.e_shnum; i++) {
140 		if (shdr[i].sh_type == SHT_SYMTAB) {
141 			if (shdr[i].sh_link >= ehdr.e_shnum)
142 				continue;
143 			symoff = shdr[i].sh_offset;
144 			symsize = shdr[i].sh_size;
145 			symstroff = shdr[shdr[i].sh_link].sh_offset;
146 			symstrsize = shdr[shdr[i].sh_link].sh_size;
147 			break;
148 		}
149 	}
150 
151 	/* Flush the section header table */
152 	if (usemalloc)
153 		free(shdr);
154 	else
155 		munmap((caddr_t)shdr, shdr_size);
156 
157 	/*
158 	 * clean out any left-over information for all valid entries.
159 	 * Type and value defined to be 0 if not found; historical
160 	 * versions cleared other and desc as well.  Also figure out
161 	 * the largest string length so don't read any more of the
162 	 * string table than we have to.
163 	 *
164 	 * XXX clearing anything other than n_type and n_value violates
165 	 * the semantics given in the man page.
166 	 */
167 	nent = 0;
168 	for (p = list; !ISLAST(p); ++p) {
169 		p->n_type = 0;
170 		p->n_other = 0;
171 		p->n_desc = 0;
172 		p->n_value = 0;
173 		++nent;
174 	}
175 
176 	/* Don't process any further if object is stripped. */
177 	/* ELFism - dunno if stripped by looking at header */
178 	if (symoff == 0)
179 		return nent;
180 
181 	/* Check for files too large to mmap. */
182 	if (SIZE_MAX - symstrsize < symstroff ||
183 	    (S_ISREG(st.st_mode) && symstrsize + symstroff > st.st_size)) {
184 		errno = EFBIG;
185 		return (-1);
186 	}
187 
188 	/*
189 	 * Map string table into our address space.  This gives us
190 	 * an easy way to randomly access all the strings, without
191 	 * making the memory allocation permanent as with malloc/free
192 	 * (i.e., munmap will return it to the system).
193 	 */
194 	if (usemalloc) {
195 		if ((strtab = malloc(symstrsize)) == NULL)
196 			return (-1);
197 		if (pread(fd, strtab, symstrsize, (off_t)symstroff) !=
198 		    symstrsize) {
199 			free(strtab);
200 			return (-1);
201 		}
202 	} else {
203 		strtab = mmap(NULL, (size_t)symstrsize, PROT_READ,
204 		    MAP_SHARED|MAP_FILE, fd, (off_t) symstroff);
205 		if (strtab == MAP_FAILED)
206 			return (-1);
207 	}
208 
209 	while (symsize >= sizeof(Elf_Sym)) {
210 		cc = MINIMUM(symsize, sizeof(sbuf));
211 		if (pread(fd, sbuf, cc, (off_t)symoff) != cc)
212 			break;
213 		symsize -= cc;
214 		symoff += cc;
215 		for (s = sbuf; cc > 0; ++s, cc -= sizeof(*s)) {
216 			Elf_Word soff = s->st_name;
217 
218 			if (soff == 0 || soff >= symstrsize)
219 				continue;
220 			left = symstrsize - soff;
221 
222 			for (p = list; !ISLAST(p); p++) {
223 				char *sym;
224 
225 				/*
226 				 * First we check for the symbol as it was
227 				 * provided by the user. If that fails
228 				 * and the first char is an '_', skip over
229 				 * the '_' and try again.
230 				 * XXX - What do we do when the user really
231 				 *       wants '_foo' and there are symbols
232 				 *       for both 'foo' and '_foo' in the
233 				 *	 table and 'foo' is first?
234 				 */
235 				sym = p->n_un.n_name;
236 				len = strlen(sym);
237 
238 				if ((len >= left ||
239 				    strcmp(&strtab[soff], sym) != 0) &&
240 				    (sym[0] != '_' || len - 1 >= left ||
241 				     strcmp(&strtab[soff], sym + 1) != 0))
242 					continue;
243 
244 				p->n_value = s->st_value;
245 
246 				/* XXX - type conversion */
247 				/*	 is pretty rude. */
248 				switch(ELF_ST_TYPE(s->st_info)) {
249 				case STT_NOTYPE:
250 					switch (s->st_shndx) {
251 					case SHN_UNDEF:
252 						p->n_type = N_UNDF;
253 						break;
254 					case SHN_ABS:
255 						p->n_type = N_ABS;
256 						break;
257 					case SHN_COMMON:
258 						p->n_type = N_COMM;
259 						break;
260 					default:
261 						p->n_type = N_COMM | N_EXT;
262 						break;
263 					}
264 					break;
265 				case STT_OBJECT:
266 					p->n_type = N_DATA;
267 					break;
268 				case STT_FUNC:
269 					p->n_type = N_TEXT;
270 					break;
271 				case STT_FILE:
272 					p->n_type = N_FN;
273 					break;
274 				}
275 				if (ELF_ST_BIND(s->st_info) == STB_LOCAL)
276 					p->n_type = N_EXT;
277 				p->n_desc = 0;
278 				p->n_other = 0;
279 				if (--nent <= 0)
280 					break;
281 			}
282 		}
283 	}
284 elf_done:
285 	if (usemalloc)
286 		free(strtab);
287 	else
288 		munmap(strtab, symstrsize);
289 	return (nent);
290 }
291 DEF_STRONG(__fdnlist);
292 
293 int
294 nlist(const char *name, struct nlist *list)
295 {
296 	int fd, n;
297 
298 	fd = open(name, O_RDONLY, 0);
299 	if (fd < 0)
300 		return (-1);
301 	n = __fdnlist(fd, list);
302 	(void)close(fd);
303 	return (n);
304 }
305