xref: /openbsd/usr.sbin/kvm_mkdb/nlist.c (revision 07ea8d15)
1 /*-
2  * Copyright (c) 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 /*static char sccsid[] = "from: @(#)nlist.c	8.1 (Berkeley) 6/6/93";*/
36 static char *rcsid = "$Id: nlist.c,v 1.5 1996/07/31 17:21:47 deraadt Exp $";
37 #endif /* not lint */
38 
39 #include <sys/param.h>
40 
41 #include <a.out.h>
42 #include <db.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <kvm.h>
47 #include <limits.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 
53 #include "extern.h"
54 
55 #include <sys/mman.h>
56 #include <sys/stat.h>
57 #include <sys/file.h>
58 
59 #ifdef DO_ELF
60 #include <elf_abi.h>
61 #endif
62 
63 typedef struct nlist NLIST;
64 #define	_strx	n_un.n_strx
65 #define	_name	n_un.n_name
66 
67 #define	badfmt(str)	errx(1, "%s: %s: %s", kfile, str, strerror(EFTYPE))
68 static char *kfile;
69 
70 #if defined(DO_AOUT)
71 
72 static void badread __P((int, char *));
73 static u_long get_kerntext __P((char *kfn, u_int magic));
74 
75 int
76 __aout_knlist(name, db)
77 	char *name;
78 	DB *db;
79 {
80 	register int nsyms;
81 	struct exec ebuf;
82 	FILE *fp;
83 	NLIST nbuf;
84 	DBT data, key;
85 	int fd, nr, strsize;
86 	u_long kerntextoff;
87 	char *strtab, buf[1024];
88 
89 	kfile = name;
90 	if ((fd = open(name, O_RDONLY, 0)) < 0)
91 		err(1, "%s", name);
92 
93 	/* Read in exec structure. */
94 	nr = read(fd, &ebuf, sizeof(struct exec));
95 	if (nr != sizeof(struct exec))
96 		return(-1);
97 
98 	/* Check magic number and symbol count. */
99 	if (N_BADMAG(ebuf))
100 		return(-1);
101 
102 	if (!ebuf.a_syms)
103 		badfmt("stripped");
104 
105 	/* Seek to string table. */
106 	if (lseek(fd, N_STROFF(ebuf), SEEK_SET) == -1)
107 		badfmt("corrupted string table");
108 
109 	/* Read in the size of the symbol table. */
110 	nr = read(fd, (char *)&strsize, sizeof(strsize));
111 	if (nr != sizeof(strsize))
112 		badread(nr, "no symbol table");
113 
114 	/* Read in the string table. */
115 	strsize -= sizeof(strsize);
116 	if (!(strtab = malloc(strsize)))
117 		err(1, NULL);
118 	if ((nr = read(fd, strtab, strsize)) != strsize)
119 		badread(nr, "corrupted symbol table");
120 
121 	/* Seek to symbol table. */
122 	if (!(fp = fdopen(fd, "r")))
123 		err(1, "%s", name);
124 	if (fseek(fp, N_SYMOFF(ebuf), SEEK_SET) == -1)
125 		err(1, "%s", name);
126 
127 	data.data = (u_char *)&nbuf;
128 	data.size = sizeof(NLIST);
129 
130 	kerntextoff = get_kerntext(name, N_GETMAGIC(ebuf));
131 
132 	/* Read each symbol and enter it into the database. */
133 	nsyms = ebuf.a_syms / sizeof(struct nlist);
134 	while (nsyms--) {
135 		if (fread((char *)&nbuf, sizeof (NLIST), 1, fp) != 1) {
136 			if (feof(fp))
137 				badfmt("corrupted symbol table");
138 			err(1, "%s", name);
139 		}
140 		if (!nbuf._strx || nbuf.n_type&N_STAB)
141 			continue;
142 
143 		key.data = (u_char *)strtab + nbuf._strx - sizeof(long);
144 		key.size = strlen((char *)key.data);
145 		if (db->put(db, &key, &data, 0))
146 			err(1, "record enter");
147 
148 		if (strcmp((char *)key.data, VRS_SYM) == 0) {
149 			long cur_off, voff;
150 			/*
151 			 * Calculate offset relative to a normal (non-kernel)
152 			 * a.out.  Kerntextoff is where the kernel is really
153 			 * loaded; N_TXTADDR is where a normal file is loaded.
154 			 * From there, locate file offset in text or data.
155 			 */
156 			voff = nbuf.n_value - kerntextoff + N_TXTADDR(ebuf);
157 			if ((nbuf.n_type & N_TYPE) == N_TEXT)
158 				voff += N_TXTOFF(ebuf) - N_TXTADDR(ebuf);
159 			else
160 				voff += N_DATOFF(ebuf) - N_DATADDR(ebuf);
161 			cur_off = ftell(fp);
162 			if (fseek(fp, voff, SEEK_SET) == -1)
163 				badfmt("corrupted string table");
164 
165 			/*
166 			 * Read version string up to, and including newline.
167 			 * This code assumes that a newline terminates the
168 			 * version line.
169 			 */
170 			if (fgets(buf, sizeof(buf), fp) == NULL)
171 				badfmt("corrupted string table");
172 
173 			key.data = (u_char *)VRS_KEY;
174 			key.size = sizeof(VRS_KEY) - 1;
175 			data.data = (u_char *)buf;
176 			data.size = strlen(buf);
177 			if (db->put(db, &key, &data, 0))
178 				err(1, "record enter");
179 
180 			/* Restore to original values. */
181 			data.data = (u_char *)&nbuf;
182 			data.size = sizeof(NLIST);
183 			if (fseek(fp, cur_off, SEEK_SET) == -1)
184 				badfmt("corrupted string table");
185 		}
186 	}
187 	(void)fclose(fp);
188 	return(0);
189 }
190 
191 /*
192  * XXX: Using this value from machine/param.h introduces a
193  * XXX: machine dependency on this program, so /usr can not
194  * XXX: be shared between (i.e.) several m68k machines.
195  * Instead of compiling in KERNTEXTOFF or KERNBASE, try to
196  * determine the text start address from a standard symbol.
197  * For backward compatibility, use the old compiled-in way
198  * when the standard symbol name is not found.
199  */
200 #ifndef KERNTEXTOFF
201 #define KERNTEXTOFF KERNBASE
202 #endif
203 
204 static u_long
205 get_kerntext(name, magic)
206 	char *name;
207 	u_int magic;
208 {
209 	NLIST nl[2];
210 
211 	bzero((caddr_t)nl, sizeof(nl));
212 	nl[0]._name = "_kernel_text";
213 
214 	if (nlist(name, nl) != 0)
215 		return (KERNTEXTOFF);
216 
217 	if (magic == ZMAGIC || magic == QMAGIC)
218 		return (nl[0].n_value - sizeof(struct exec));
219 	return (nl[0].n_value);
220 }
221 
222 static void
223 badread(nr, p)
224 	int nr;
225 	char *p;
226 {
227 	if (nr < 0)
228 		err(1, "%s", kfile);
229 	badfmt(p);
230 }
231 
232 #endif /* DO_AOUT */
233 
234 #ifdef DO_ELF
235 int
236 __elf_knlist(name, db)
237 	char *name;
238 	DB *db;
239 {
240 	register struct nlist *p;
241 	register caddr_t strtab;
242 	register off_t symstroff, symoff;
243 	register u_long symsize;
244 	register u_long kernvma, kernoffs;
245 	register int cc, i;
246 	Elf32_Sym sbuf;
247 	Elf32_Sym *s;
248 	size_t symstrsize;
249 	char *shstr, buf[1024];
250 	Elf32_Ehdr eh;
251 	Elf32_Shdr *sh = NULL;
252 	struct stat st;
253 	DBT data, key;
254 	NLIST nbuf;
255 	FILE *fp;
256 
257 	kfile = name;
258 	if ((fp = fopen(name, "r")) < 0)
259 		err(1, "%s", name);
260 
261 	if (fseek(fp, (off_t)0, SEEK_SET) == -1 ||
262 	    fread(&eh, sizeof(eh), 1, fp) != 1 ||
263 	    !IS_ELF(eh))
264 		return(-1);
265 
266 	sh = (Elf32_Shdr *)malloc(sizeof(Elf32_Shdr) * eh.e_shnum);
267 
268 	if (fseek (fp, eh.e_shoff, SEEK_SET) < 0)
269 		badfmt("no exec header");
270 
271 	if (fread(sh, sizeof(Elf32_Shdr) * eh.e_shnum, 1, fp) != 1)
272 		badfmt("no exec header");
273 
274 	shstr = (char *)malloc(sh[eh.e_shstrndx].sh_size);
275 	if (fseek (fp, sh[eh.e_shstrndx].sh_offset, SEEK_SET) < 0)
276 		badfmt("corrupt file");
277 	if (fread(shstr, sh[eh.e_shstrndx].sh_size, 1, fp) != 1)
278 		badfmt("corrupt file");
279 
280 	for (i = 0; i < eh.e_shnum; i++) {
281 		if (strcmp (shstr + sh[i].sh_name, ".strtab") == 0) {
282 			symstroff = sh[i].sh_offset;
283 			symstrsize = sh[i].sh_size;
284 		}
285 		else if (strcmp (shstr + sh[i].sh_name, ".symtab") == 0) {
286 			symoff = sh[i].sh_offset;
287 			symsize = sh[i].sh_size;
288 		}
289 		else if (strcmp (shstr + sh[i].sh_name, ".text") == 0) {
290 			kernvma = sh[i].sh_addr;
291 			kernoffs = sh[i].sh_offset;
292 		}
293 	}
294 
295 
296 	/* Check for files too large to mmap. */
297 	/* XXX is this really possible? */
298 	if (symstrsize > SIZE_T_MAX) {
299 		badfmt("corrupt file");
300 	}
301 	/*
302 	 * Map string table into our address space.  This gives us
303 	 * an easy way to randomly access all the strings, without
304 	 * making the memory allocation permanent as with malloc/free
305 	 * (i.e., munmap will return it to the system).
306 	 */
307 	strtab = mmap(NULL, (size_t)symstrsize, PROT_READ, 0, fileno(fp), symstroff);
308 	if (strtab == (char *)-1)
309 		badfmt("corrupt file");
310 
311 	if (fseek(fp, symoff, SEEK_SET) == -1)
312 		badfmt("corrupt file");
313 
314 	data.data = (u_char *)&nbuf;
315 	data.size = sizeof(NLIST);
316 
317 	/* Read each symbol and enter it into the database. */
318 	while (symsize > 0) {
319 		symsize -= sizeof(Elf32_Sym);
320 		if (fread((char *)&sbuf, sizeof(sbuf), 1, fp) != 1) {
321 			if (feof(fp))
322 				badfmt("corrupted symbol table");
323 			err(1, "%s", name);
324 		}
325 		if (!sbuf.st_name)
326 			continue;
327 
328 		nbuf.n_value = sbuf.st_value;
329 
330 		/*XXX type conversion is pretty rude... */
331 		switch(ELF32_ST_TYPE(sbuf.st_info)) {
332 		case STT_NOTYPE:
333 			nbuf.n_type = N_UNDF;
334 			break;
335 		case STT_FUNC:
336 			nbuf.n_type = N_TEXT;
337 			break;
338 		case STT_OBJECT:
339 			nbuf.n_type = N_DATA;
340 			break;
341 		}
342 		if(ELF32_ST_BIND(sbuf.st_info) == STB_LOCAL)
343 			nbuf.n_type = N_EXT;
344 
345 		if(eh.e_machine == EM_MIPS) {
346 			*buf = '_';
347 			strcpy(buf+1,strtab + sbuf.st_name);
348 			key.data = (u_char *)buf;
349 		}
350 		else {
351 			key.data = (u_char *)(strtab + sbuf.st_name);
352 		}
353 		key.size = strlen((char *)key.data);
354 		if (db->put(db, &key, &data, 0))
355 			err(1, "record enter");
356 
357 		if (strcmp((char *)key.data, VRS_SYM) == 0) {
358 			long cur_off, voff;
359 			/*
360 			 * Calculate offset to the version string in the
361 			 * file.  kernvma is where the kernel is really
362 			 * loaded; kernoffs is where in the file it starts.
363 			 */
364 			voff = nbuf.n_value - kernvma + kernoffs;
365 			cur_off = ftell(fp);
366 			if (fseek(fp, voff, SEEK_SET) == -1)
367 				badfmt("corrupted string table");
368 
369 			/*
370 			 * Read version string up to, and including newline.
371 			 * This code assumes that a newline terminates the
372 			 * version line.
373 			 */
374 			if (fgets(buf, sizeof(buf), fp) == NULL)
375 				badfmt("corrupted string table");
376 
377 			key.data = (u_char *)VRS_KEY;
378 			key.size = sizeof(VRS_KEY) - 1;
379 			data.data = (u_char *)buf;
380 			data.size = strlen(buf);
381 			if (db->put(db, &key, &data, 0))
382 				err(1, "record enter");
383 
384 			/* Restore to original values. */
385 			data.data = (u_char *)&nbuf;
386 			data.size = sizeof(NLIST);
387 			if (fseek(fp, cur_off, SEEK_SET) == -1)
388 				badfmt("corrupted string table");
389 		}
390 	}
391 	munmap(strtab, symstrsize);
392 	(void)fclose(fp);
393 	return(0);
394 }
395 #endif /* DO_ELF */
396 
397 #ifdef DO_ECOFF
398 int
399 __ecoff_knlist(name, db)
400 	char *name;
401 	DB *db;
402 {
403 	return (-1);
404 }
405 #endif /* DO_ECOFF */
406 
407 static struct knlist_handlers {
408 	int	(*fn) __P((char *name, DB *db));
409 } nlist_fn[] = {
410 #ifdef DO_AOUT
411 	{ __aout_knlist },
412 #endif
413 #ifdef DO_ELF
414 	{ __elf_knlist },
415 #endif
416 #ifdef DO_ECOFF
417 	{ __ecoff_knlist },
418 #endif
419 };
420 
421 void
422 create_knlist(name, db)
423 	char *name;
424 	DB *db;
425 {
426 	int n, i;
427 
428 	for (i = 0; i < sizeof(nlist_fn)/sizeof(nlist_fn[0]); i++) {
429 		n = (nlist_fn[i].fn)(name, db);
430 		if (n != -1)
431 			break;
432 	}
433 }
434