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