xref: /openbsd-src/usr.sbin/installboot/i386_nlist.c (revision 3a50f0a93a2072911d0ba6ababa815fb04bf9a71)
1*3a50f0a9Sjmc /*	$OpenBSD: i386_nlist.c,v 1.9 2022/12/28 21:30:16 jmc Exp $	*/
2b4544c7cSjsing /*
3b4544c7cSjsing  * Copyright (c) 1989, 1993
4b4544c7cSjsing  *	The Regents of the University of California.  All rights reserved.
5b4544c7cSjsing  *
6b4544c7cSjsing  * Redistribution and use in source and binary forms, with or without
7b4544c7cSjsing  * modification, are permitted provided that the following conditions
8b4544c7cSjsing  * are met:
9b4544c7cSjsing  * 1. Redistributions of source code must retain the above copyright
10b4544c7cSjsing  *    notice, this list of conditions and the following disclaimer.
11b4544c7cSjsing  * 2. Redistributions in binary form must reproduce the above copyright
12b4544c7cSjsing  *    notice, this list of conditions and the following disclaimer in the
13b4544c7cSjsing  *    documentation and/or other materials provided with the distribution.
14b4544c7cSjsing  * 3. Neither the name of the University nor the names of its contributors
15b4544c7cSjsing  *    may be used to endorse or promote products derived from this software
16b4544c7cSjsing  *    without specific prior written permission.
17b4544c7cSjsing  *
18b4544c7cSjsing  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19b4544c7cSjsing  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20b4544c7cSjsing  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21b4544c7cSjsing  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22b4544c7cSjsing  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23b4544c7cSjsing  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24b4544c7cSjsing  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25b4544c7cSjsing  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26b4544c7cSjsing  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27b4544c7cSjsing  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28b4544c7cSjsing  * SUCH DAMAGE.
29b4544c7cSjsing  */
30b4544c7cSjsing 
31b4544c7cSjsing #define ELFSIZE 32
32b4544c7cSjsing 
33b4544c7cSjsing #include <sys/types.h>
34b4544c7cSjsing #include <sys/mman.h>
35b4544c7cSjsing #include <sys/stat.h>
36b4544c7cSjsing 
37e9d517b1Smpi #include <elf.h>
384497e208Stobias #include <errno.h>
39b4544c7cSjsing #include <fcntl.h>
40b4544c7cSjsing #include <nlist.h>
414497e208Stobias #include <stdint.h>
42b4544c7cSjsing #include <stdlib.h>
43b4544c7cSjsing #include <string.h>
44b4544c7cSjsing #include <unistd.h>
45b4544c7cSjsing 
469b2ea772Sderaadt #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
479b2ea772Sderaadt 
48b4544c7cSjsing static int	__elf_fdnlist(int, struct nlist *);
49b4544c7cSjsing static int	__elf_is_okay__(Elf_Ehdr *ehdr);
50b4544c7cSjsing 
51b4544c7cSjsing int	nlist_elf32(const char *, struct nlist *);
52b4544c7cSjsing 
53b4544c7cSjsing #define	ISLAST(p)	(p->n_name == 0 || p->n_name[0] == 0)
54b4544c7cSjsing 
55b4544c7cSjsing /*
56b4544c7cSjsing  * __elf_is_okay__ - Determine if ehdr really
57b4544c7cSjsing  * is ELF and valid for the target platform.
58b4544c7cSjsing  *
59b4544c7cSjsing  * WARNING:  This is NOT a ELF ABI function and
60b4544c7cSjsing  * as such its use should be restricted.
61b4544c7cSjsing  */
62b4544c7cSjsing static int
__elf_is_okay__(Elf_Ehdr * ehdr)63b4544c7cSjsing __elf_is_okay__(Elf_Ehdr *ehdr)
64b4544c7cSjsing {
65b4544c7cSjsing 	int retval = 0;
66b4544c7cSjsing 	/*
67*3a50f0a9Sjmc 	 * We need to check magic, class size, endianness,
68b4544c7cSjsing 	 * and version before we look at the rest of the
69b4544c7cSjsing 	 * Elf_Ehdr structure.  These few elements are
70b4544c7cSjsing 	 * represented in a machine independent fashion.
71b4544c7cSjsing 	 */
72b4544c7cSjsing 
73b4544c7cSjsing 	/*
74b4544c7cSjsing 	 * We are constructing a 32-bit executable. So we can't
75b4544c7cSjsing 	 * use the libc nlist.c, which would be upset. Manually
76b4544c7cSjsing 	 * check for the i386 values for EI_CLASS and e_machine.
77b4544c7cSjsing 	 */
78b4544c7cSjsing 
79b4544c7cSjsing 	if (IS_ELF(*ehdr) &&
80b4544c7cSjsing 	    ehdr->e_ident[EI_CLASS] == ELFCLASS32 &&
81b4544c7cSjsing 	    ehdr->e_ident[EI_DATA] == ELF_TARG_DATA &&
82b4544c7cSjsing 	    ehdr->e_ident[EI_VERSION] == ELF_TARG_VER) {
83b4544c7cSjsing 
844497e208Stobias 		/* Now check the machine dependent header */
85b4544c7cSjsing 		if (ehdr->e_machine == EM_386 &&
86b4544c7cSjsing 		    ehdr->e_version == ELF_TARG_VER)
87b4544c7cSjsing 			retval = 1;
88b4544c7cSjsing 	}
89b4544c7cSjsing 
90b4544c7cSjsing 	return retval;
91b4544c7cSjsing }
92b4544c7cSjsing 
93b4544c7cSjsing static int
__elf_fdnlist(int fd,struct nlist * list)94b4544c7cSjsing __elf_fdnlist(int fd, struct nlist *list)
95b4544c7cSjsing {
96b4544c7cSjsing 	struct nlist *p;
97b4544c7cSjsing 	caddr_t strtab;
98b4544c7cSjsing 	Elf_Off symoff = 0, symstroff = 0;
99b4544c7cSjsing 	Elf_Word symsize = 0, symstrsize = 0;
100b4544c7cSjsing 	Elf_Sword nent, cc, i;
101b4544c7cSjsing 	Elf_Sym sbuf[1024];
102b4544c7cSjsing 	Elf_Sym *s;
103b4544c7cSjsing 	Elf_Ehdr ehdr;
104b4544c7cSjsing 	Elf_Shdr *shdr = NULL;
105b4544c7cSjsing 	Elf_Word shdr_size;
106b4544c7cSjsing 	struct stat st;
107b4544c7cSjsing 	int usemalloc = 0;
1084497e208Stobias 	size_t left, len;
109b4544c7cSjsing 
110b4544c7cSjsing 	/* Make sure obj is OK */
111b4544c7cSjsing 	if (pread(fd, &ehdr, sizeof(Elf_Ehdr), (off_t)0) != sizeof(Elf_Ehdr) ||
112df69c215Sderaadt 	    !__elf_is_okay__(&ehdr) || fstat(fd, &st) == -1)
113b4544c7cSjsing 		return (-1);
114b4544c7cSjsing 
115b4544c7cSjsing 	/* calculate section header table size */
116b4544c7cSjsing 	shdr_size = ehdr.e_shentsize * ehdr.e_shnum;
117b4544c7cSjsing 
1184497e208Stobias 	/* Make sure it's not too big to mmap */
1194497e208Stobias 	if (SIZE_MAX - ehdr.e_shoff < shdr_size ||
12017606059Skrw 	    (S_ISREG(st.st_mode) && ehdr.e_shoff + shdr_size > st.st_size)) {
1214497e208Stobias 		errno = EFBIG;
1224497e208Stobias 		return (-1);
1234497e208Stobias 	}
1244497e208Stobias 
125b4544c7cSjsing 	/* mmap section header table */
126b4544c7cSjsing 	shdr = (Elf_Shdr *)mmap(NULL, (size_t)shdr_size, PROT_READ,
127b4544c7cSjsing 	    MAP_SHARED|MAP_FILE, fd, (off_t) ehdr.e_shoff);
128b4544c7cSjsing 	if (shdr == MAP_FAILED) {
129b4544c7cSjsing 		usemalloc = 1;
130b4544c7cSjsing 		if ((shdr = malloc(shdr_size)) == NULL)
131b4544c7cSjsing 			return (-1);
132b4544c7cSjsing 
133b4544c7cSjsing 		if (pread(fd, shdr, shdr_size, (off_t)ehdr.e_shoff) !=
134b4544c7cSjsing 		    shdr_size) {
135b4544c7cSjsing 			free(shdr);
136b4544c7cSjsing 			return (-1);
137b4544c7cSjsing 		}
138b4544c7cSjsing 	}
139b4544c7cSjsing 
140b4544c7cSjsing 	/*
141b4544c7cSjsing 	 * Find the symbol table entry and its corresponding
142b4544c7cSjsing 	 * string table entry.	Version 1.1 of the ABI states
143b4544c7cSjsing 	 * that there is only one symbol table but that this
144b4544c7cSjsing 	 * could change in the future.
145b4544c7cSjsing 	 */
146b4544c7cSjsing 	for (i = 0; i < ehdr.e_shnum; i++) {
147b4544c7cSjsing 		if (shdr[i].sh_type == SHT_SYMTAB) {
1484497e208Stobias 			if (shdr[i].sh_link >= ehdr.e_shnum)
1494497e208Stobias 				continue;
150b4544c7cSjsing 			symoff = shdr[i].sh_offset;
151b4544c7cSjsing 			symsize = shdr[i].sh_size;
152b4544c7cSjsing 			symstroff = shdr[shdr[i].sh_link].sh_offset;
153b4544c7cSjsing 			symstrsize = shdr[shdr[i].sh_link].sh_size;
154b4544c7cSjsing 			break;
155b4544c7cSjsing 		}
156b4544c7cSjsing 	}
157b4544c7cSjsing 
158b4544c7cSjsing 	/* Flush the section header table */
159b4544c7cSjsing 	if (usemalloc)
160b4544c7cSjsing 		free(shdr);
161b4544c7cSjsing 	else
162b4544c7cSjsing 		munmap((caddr_t)shdr, shdr_size);
163b4544c7cSjsing 
164b4544c7cSjsing 	/*
165b4544c7cSjsing 	 * clean out any left-over information for all valid entries.
166b4544c7cSjsing 	 * Type and value defined to be 0 if not found; historical
167b4544c7cSjsing 	 * versions cleared other and desc as well.  Also figure out
168b4544c7cSjsing 	 * the largest string length so don't read any more of the
169b4544c7cSjsing 	 * string table than we have to.
170b4544c7cSjsing 	 *
171b4544c7cSjsing 	 * XXX clearing anything other than n_type and n_value violates
172b4544c7cSjsing 	 * the semantics given in the man page.
173b4544c7cSjsing 	 */
174b4544c7cSjsing 	nent = 0;
175b4544c7cSjsing 	for (p = list; !ISLAST(p); ++p) {
176b4544c7cSjsing 		p->n_type = 0;
177b4544c7cSjsing 		p->n_other = 0;
178b4544c7cSjsing 		p->n_desc = 0;
179b4544c7cSjsing 		p->n_value = 0;
180b4544c7cSjsing 		++nent;
181b4544c7cSjsing 	}
182b4544c7cSjsing 
183b4544c7cSjsing 	/* Don't process any further if object is stripped. */
184b4544c7cSjsing 	/* ELFism - dunno if stripped by looking at header */
185b4544c7cSjsing 	if (symoff == 0)
1864497e208Stobias 		return nent;
187b4544c7cSjsing 
1884497e208Stobias 	/* Check for files too large to mmap. */
1894497e208Stobias 	if (SIZE_MAX - symstrsize < symstroff ||
19017606059Skrw 	    (S_ISREG(st.st_mode) && symstrsize + symstroff > st.st_size)) {
1914497e208Stobias 		errno = EFBIG;
1924497e208Stobias 		return (-1);
1934497e208Stobias 	}
1944497e208Stobias 
1954497e208Stobias 	/*
1964497e208Stobias 	 * Map string table into our address space.  This gives us
1974497e208Stobias 	 * an easy way to randomly access all the strings, without
1984497e208Stobias 	 * making the memory allocation permanent as with malloc/free
1994497e208Stobias 	 * (i.e., munmap will return it to the system).
2004497e208Stobias 	 */
2014497e208Stobias 	if (usemalloc) {
2024497e208Stobias 		if ((strtab = malloc(symstrsize)) == NULL)
2034497e208Stobias 			return (-1);
2044497e208Stobias 		if (pread(fd, strtab, symstrsize, (off_t)symstroff) !=
2054497e208Stobias 		    symstrsize) {
2064497e208Stobias 			free(strtab);
2074497e208Stobias 			return (-1);
2084497e208Stobias 		}
2094497e208Stobias 	} else {
2104497e208Stobias 		strtab = mmap(NULL, (size_t)symstrsize, PROT_READ,
2114497e208Stobias 		    MAP_SHARED|MAP_FILE, fd, (off_t) symstroff);
2124497e208Stobias 		if (strtab == MAP_FAILED)
2134497e208Stobias 			return (-1);
2144497e208Stobias 	}
2154497e208Stobias 
2164497e208Stobias 	while (symsize >= sizeof(Elf_Sym)) {
2179b2ea772Sderaadt 		cc = MINIMUM(symsize, sizeof(sbuf));
218b4544c7cSjsing 		if (pread(fd, sbuf, cc, (off_t)symoff) != cc)
219b4544c7cSjsing 			break;
220b4544c7cSjsing 		symsize -= cc;
221b4544c7cSjsing 		symoff += cc;
222b4544c7cSjsing 		for (s = sbuf; cc > 0; ++s, cc -= sizeof(*s)) {
2234497e208Stobias 			Elf_Word soff = s->st_name;
224b4544c7cSjsing 
2254497e208Stobias 			if (soff == 0 || soff >= symstrsize)
226b4544c7cSjsing 				continue;
2274497e208Stobias 			left = symstrsize - soff;
2284497e208Stobias 
229b4544c7cSjsing 			for (p = list; !ISLAST(p); p++) {
230b4544c7cSjsing 				char *sym;
231b4544c7cSjsing 
232b4544c7cSjsing 				/*
233b4544c7cSjsing 				 * First we check for the symbol as it was
234b4544c7cSjsing 				 * provided by the user. If that fails
235b4544c7cSjsing 				 * and the first char is an '_', skip over
236b4544c7cSjsing 				 * the '_' and try again.
237b4544c7cSjsing 				 * XXX - What do we do when the user really
2384497e208Stobias 				 *       wants '_foo' and there are symbols
239b4544c7cSjsing 				 *       for both 'foo' and '_foo' in the
240b4544c7cSjsing 				 *	 table and 'foo' is first?
241b4544c7cSjsing 				 */
242b4544c7cSjsing 				sym = p->n_name;
2434497e208Stobias 				len = strlen(sym);
2444497e208Stobias 
2454497e208Stobias 				if ((len >= left ||
2464497e208Stobias 				    strcmp(&strtab[soff], sym) != 0) &&
2474497e208Stobias 				    (sym[0] != '_' || len - 1 >= left ||
248b4544c7cSjsing 				     strcmp(&strtab[soff], sym + 1) != 0))
249b4544c7cSjsing 					continue;
250b4544c7cSjsing 
251b4544c7cSjsing 				p->n_value = s->st_value;
252b4544c7cSjsing 
253b4544c7cSjsing 				/* XXX - type conversion */
254b4544c7cSjsing 				/*	 is pretty rude. */
255b4544c7cSjsing 				switch(ELF_ST_TYPE(s->st_info)) {
256b4544c7cSjsing 				case STT_NOTYPE:
257b4544c7cSjsing 					switch (s->st_shndx) {
258b4544c7cSjsing 					case SHN_UNDEF:
259b4544c7cSjsing 						p->n_type = N_UNDF;
260b4544c7cSjsing 						break;
261b4544c7cSjsing 					case SHN_ABS:
262b4544c7cSjsing 						p->n_type = N_ABS;
263b4544c7cSjsing 						break;
264b4544c7cSjsing 					case SHN_COMMON:
265b4544c7cSjsing 						p->n_type = N_COMM;
266b4544c7cSjsing 						break;
267b4544c7cSjsing 					default:
268b4544c7cSjsing 						p->n_type = N_COMM | N_EXT;
269b4544c7cSjsing 						break;
270b4544c7cSjsing 					}
271b4544c7cSjsing 					break;
272b4544c7cSjsing 				case STT_OBJECT:
273b4544c7cSjsing 					p->n_type = N_DATA;
274b4544c7cSjsing 					break;
275b4544c7cSjsing 				case STT_FUNC:
276b4544c7cSjsing 					p->n_type = N_TEXT;
277b4544c7cSjsing 					break;
278b4544c7cSjsing 				case STT_FILE:
279b4544c7cSjsing 					p->n_type = N_FN;
280b4544c7cSjsing 					break;
281b4544c7cSjsing 				}
2824497e208Stobias 				if (ELF_ST_BIND(s->st_info) == STB_LOCAL)
283b4544c7cSjsing 					p->n_type = N_EXT;
284b4544c7cSjsing 				p->n_desc = 0;
285b4544c7cSjsing 				p->n_other = 0;
286b4544c7cSjsing 				if (--nent <= 0)
287b4544c7cSjsing 					break;
288b4544c7cSjsing 			}
289b4544c7cSjsing 		}
290b4544c7cSjsing 	}
291b4544c7cSjsing 	if (usemalloc)
292b4544c7cSjsing 		free(strtab);
293b4544c7cSjsing 	else
294b4544c7cSjsing 		munmap(strtab, symstrsize);
295b4544c7cSjsing 	return (nent);
296b4544c7cSjsing }
297b4544c7cSjsing 
298b4544c7cSjsing int
nlist_elf32(const char * name,struct nlist * list)299b4544c7cSjsing nlist_elf32(const char *name, struct nlist *list)
300b4544c7cSjsing {
301b4544c7cSjsing 	int fd, n;
302b4544c7cSjsing 
303b7041c07Sderaadt 	fd = open(name, O_RDONLY);
304df69c215Sderaadt 	if (fd == -1)
305b4544c7cSjsing 		return (-1);
306b4544c7cSjsing 	n = __elf_fdnlist(fd, list);
307b4544c7cSjsing 	close(fd);
308b4544c7cSjsing 
309b4544c7cSjsing 	return (n);
310b4544c7cSjsing }
311