1 /*	$OpenBSD: nlist.c,v 1.71 2019/06/28 13:32:41 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/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.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
__elf_is_okay__(Elf_Ehdr * ehdr)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
__fdnlist(int fd,struct nlist * list)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), 0) != sizeof(Elf_Ehdr) ||
105 	    !__elf_is_okay__(&ehdr) || fstat(fd, &st) == -1)
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 = mmap(NULL, shdr_size, PROT_READ, MAP_SHARED|MAP_FILE, fd,
120 	    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, ehdr.e_shoff) != shdr_size) {
127 			free(shdr);
128 			return (-1);
129 		}
130 	}
131 
132 	/*
133 	 * Find the symbol table entry and its corresponding
134 	 * string table entry.	Version 1.1 of the ABI states
135 	 * that there is only one symbol table but that this
136 	 * could change in the future.
137 	 */
138 	for (i = 0; i < ehdr.e_shnum; i++) {
139 		if (shdr[i].sh_type == SHT_SYMTAB) {
140 			if (shdr[i].sh_link >= ehdr.e_shnum)
141 				continue;
142 			symoff = shdr[i].sh_offset;
143 			symsize = shdr[i].sh_size;
144 			symstroff = shdr[shdr[i].sh_link].sh_offset;
145 			symstrsize = shdr[shdr[i].sh_link].sh_size;
146 			break;
147 		}
148 	}
149 
150 	/* Flush the section header table */
151 	if (usemalloc)
152 		free(shdr);
153 	else
154 		munmap((caddr_t)shdr, shdr_size);
155 
156 	/*
157 	 * clean out any left-over information for all valid entries.
158 	 * Type and value defined to be 0 if not found; historical
159 	 * versions cleared other and desc as well.  Also figure out
160 	 * the largest string length so don't read any more of the
161 	 * string table than we have to.
162 	 *
163 	 * XXX clearing anything other than n_type and n_value violates
164 	 * the semantics given in the man page.
165 	 */
166 	nent = 0;
167 	for (p = list; !ISLAST(p); ++p) {
168 		p->n_type = 0;
169 		p->n_other = 0;
170 		p->n_desc = 0;
171 		p->n_value = 0;
172 		++nent;
173 	}
174 
175 	/* Don't process any further if object is stripped. */
176 	/* ELFism - dunno if stripped by looking at header */
177 	if (symoff == 0)
178 		return nent;
179 
180 	/* Check for files too large to mmap. */
181 	if (SIZE_MAX - symstrsize < symstroff ||
182 	    (S_ISREG(st.st_mode) && symstrsize + symstroff > st.st_size)) {
183 		errno = EFBIG;
184 		return (-1);
185 	}
186 
187 	/*
188 	 * Map string table into our address space.  This gives us
189 	 * an easy way to randomly access all the strings, without
190 	 * making the memory allocation permanent as with malloc/free
191 	 * (i.e., munmap will return it to the system).
192 	 */
193 	if (usemalloc) {
194 		if ((strtab = malloc(symstrsize)) == NULL)
195 			return (-1);
196 		if (pread(fd, strtab, symstrsize, symstroff) != symstrsize) {
197 			free(strtab);
198 			return (-1);
199 		}
200 	} else {
201 		strtab = mmap(NULL, symstrsize, PROT_READ, MAP_SHARED|MAP_FILE,
202 		    fd, symstroff);
203 		if (strtab == MAP_FAILED)
204 			return (-1);
205 	}
206 
207 	while (symsize >= sizeof(Elf_Sym)) {
208 		cc = MINIMUM(symsize, sizeof(sbuf));
209 		if (pread(fd, sbuf, cc, symoff) != cc)
210 			break;
211 		symsize -= cc;
212 		symoff += cc;
213 		for (s = sbuf; cc > 0; ++s, cc -= sizeof(*s)) {
214 			Elf_Word soff = s->st_name;
215 
216 			if (soff == 0 || soff >= symstrsize)
217 				continue;
218 			left = symstrsize - soff;
219 
220 			for (p = list; !ISLAST(p); p++) {
221 				char *sym;
222 
223 				/*
224 				 * First we check for the symbol as it was
225 				 * provided by the user. If that fails
226 				 * and the first char is an '_', skip over
227 				 * the '_' and try again.
228 				 * XXX - What do we do when the user really
229 				 *       wants '_foo' and there are symbols
230 				 *       for both 'foo' and '_foo' in the
231 				 *	 table and 'foo' is first?
232 				 */
233 				sym = p->n_un.n_name;
234 				len = strlen(sym);
235 
236 				if ((len >= left ||
237 				    strcmp(&strtab[soff], sym) != 0) &&
238 				    (sym[0] != '_' || len - 1 >= left ||
239 				     strcmp(&strtab[soff], sym + 1) != 0))
240 					continue;
241 
242 				p->n_value = s->st_value;
243 
244 				/* XXX - type conversion */
245 				/*	 is pretty rude. */
246 				switch(ELF_ST_TYPE(s->st_info)) {
247 				case STT_NOTYPE:
248 					switch (s->st_shndx) {
249 					case SHN_UNDEF:
250 						p->n_type = N_UNDF;
251 						break;
252 					case SHN_ABS:
253 						p->n_type = N_ABS;
254 						break;
255 					case SHN_COMMON:
256 						p->n_type = N_COMM;
257 						break;
258 					default:
259 						p->n_type = N_COMM | N_EXT;
260 						break;
261 					}
262 					break;
263 				case STT_OBJECT:
264 					p->n_type = N_DATA;
265 					break;
266 				case STT_FUNC:
267 					p->n_type = N_TEXT;
268 					break;
269 				case STT_FILE:
270 					p->n_type = N_FN;
271 					break;
272 				}
273 				if (ELF_ST_BIND(s->st_info) == STB_LOCAL)
274 					p->n_type = N_EXT;
275 				p->n_desc = 0;
276 				p->n_other = 0;
277 				if (--nent <= 0)
278 					break;
279 			}
280 		}
281 	}
282 	if (usemalloc)
283 		free(strtab);
284 	else
285 		munmap(strtab, symstrsize);
286 	return (nent);
287 }
288 DEF_STRONG(__fdnlist);
289 
290 int
nlist(const char * name,struct nlist * list)291 nlist(const char *name, struct nlist *list)
292 {
293 	int fd, n;
294 
295 	fd = open(name, O_RDONLY | O_CLOEXEC);
296 	if (fd == -1)
297 		return (-1);
298 	n = __fdnlist(fd, list);
299 	(void)close(fd);
300 	return (n);
301 }
302