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