xref: /illumos-gate/usr/src/ucblib/libucb/port/gen/nlist.c (revision 5bbb4db2c3f208d12bf0fd11769728f9e5ba66a2)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1988 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * University Copyright- Copyright (c) 1982, 1986, 1988
31  * The Regents of the University of California
32  * All Rights Reserved
33  *
34  * University Acknowledgment- Portions of this document are derived from
35  * software developed by the University of California, Berkeley, and its
36  * contributors.
37  */
38 
39 /*LINTLIBRARY*/
40 
41 #include <sys/types.h>
42 #include "libelf.h"
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <sys/fcntl.h>
46 #include <nlist.h>
47 #include <sys/file.h>
48 #include <string.h>
49 
50 #if COFF_NLIST_SUPPORTED
51 #include "aouthdr.h"
52 #include "filehdr.h"
53 #include "scnhdr.h"
54 #include "reloc.h"
55 #endif /* COFF_NLIST_SUPPORTED */
56 
57 #include "linenum.h"
58 #include "syms.h"
59 
60 #undef  BADMAG
61 #define	BADMAG(x)	(!ISCOFF(x))
62 
63 #ifndef FLEXNAMES
64 #define	FLEXNAMES 1
65 #endif
66 #undef n_name		/* this patch causes problems here */
67 
68 #define	SPACE 100		/* number of symbols read at a time */
69 #define	ISELF (strncmp(magic_buf, ELFMAG, SELFMAG) == 0)
70 
71 #if COFF_NLIST_SUPPORTED
72 static char sym_buf[SPACE * SYMESZ];
73 static int num_in_buf = 0;
74 static char *next_entry = (char *)0;
75 #endif /* COFF_NLIST_SUPPORTED */
76 
77 static unsigned encode;		/* data encoding if an ELF file */
78 static unsigned fvers;		/* object file version if an ELF file */
79 
80 /* forward declarations */
81 static int _elf_nlist(int, struct nlist *);
82 static int end_elf_job(int);
83 static Elf_Data *elf_read(int, long, size_t, size_t, Elf_Type);
84 
85 #if COFF_NLIST_SUPPORTED
86 static int _coff_nlist(int, struct nlist *);
87 static void sym_close(int);
88 static int sym_read(int, struct syment *, int);
89 static int fill_sym_buf(int, int);
90 #endif /* COFF_NLIST_SUPPORTED */
91 
92 int
93 nlist(const char *name, struct nlist *list)
94 {
95 	struct nlist *p;
96 	char magic_buf[EI_NIDENT+1];
97 	int fd;
98 
99 	for (p = list; p->n_name && p->n_name[0]; p++) { /* n_name can be ptr */
100 		p->n_type = 0;
101 		p->n_value = 0L;
102 		p->n_scnum = 0;
103 		p->n_sclass = 0;
104 		p->n_numaux = 0;
105 	}
106 
107 	if ((fd = open(name, 0)) < 0)
108 		return (-1);
109 	if (read(fd, magic_buf, (size_t)EI_NIDENT) == -1) {
110 		(void) close(fd);
111 		return (-1);
112 	}
113 	magic_buf[EI_NIDENT] = '\0';
114 	if (lseek(fd, 0L, 0) == -1L) { /* rewind to beginning of object file */
115 		(void) close(fd);
116 		return (-1);
117 	}
118 
119 	if (ISELF) {
120 		/*
121 		 * right now can only handle 32-bit architectures
122 		 * XX64 work to come?  ELFCLASS64?
123 		 */
124 		if (magic_buf[EI_CLASS] != ELFCLASS32) {
125 			(void) close(fd);
126 			return (-1);
127 		}
128 		encode = (unsigned)magic_buf[EI_DATA];
129 		fvers = (unsigned)magic_buf[EI_VERSION];
130 		return (_elf_nlist(fd, list));
131 	}
132 	else
133 #if COFF_NLIST_SUPPORTED
134 		return (_coff_nlist(fd, list));
135 #else /* COFF_NLIST_SUPPORTED */
136 		return (-1);
137 #endif /* COFF_NLIST_SUPPORTED */
138 }
139 
140 static int
141 _elf_nlist(int fd, struct nlist *list)
142 {
143 	Elf_Data   *symdata;	/* buffer points to symbol table */
144 	Elf_Data   *strdata;	/* buffer points to string table */
145 	Elf_Data   *secdata;	/* buffer points to section table */
146 	Elf32_Shdr *symhdr;	/* section table entry for symtab */
147 	Elf32_Shdr *strhdr;	/* section table entry for strtab */
148 	Elf32_Sym  *sym;	/* buffer storing one symbol information */
149 	Elf32_Sym  *sym_end;	/* end of symbol table */
150 	Elf32_Ehdr *ehdr;	/* file header */
151 	Elf_Data   *edata;	/* data buffer for ehdr */
152 	int	i;
153 	int	nreq;
154 	struct  nlist *inl;
155 
156 	if (elf_version(EV_CURRENT) == EV_NONE)
157 		return (end_elf_job(fd));
158 
159 	/* count the number of symbols requested */
160 	for (inl = list, nreq = 0; inl->n_name && inl->n_name[0]; ++inl, nreq++)
161 		;
162 
163 	/* read file header and section header table */
164 	if ((edata = elf_read(fd, 0L, elf32_fsize(ELF_T_EHDR, 1, fvers),
165 		sizeof (Elf32_Ehdr), ELF_T_EHDR)) == 0)
166 		return (end_elf_job(fd));
167 
168 	ehdr = (Elf32_Ehdr *)edata->d_buf;
169 
170 	if (ehdr->e_shoff == 0) {
171 		free(edata->d_buf);
172 		free(edata);
173 		return (end_elf_job(fd));
174 	}
175 
176 	if ((secdata = elf_read(fd, (long)ehdr->e_shoff,
177 		(size_t)(ehdr->e_shentsize * ehdr->e_shnum),
178 		(size_t)(ehdr->e_shnum * sizeof (Elf32_Ehdr)),
179 		ELF_T_SHDR)) == 0) {
180 		free(edata->d_buf);
181 		free(edata);
182 		return (end_elf_job(fd));
183 	}
184 
185 	/* find symbol table section */
186 	symhdr = (Elf32_Shdr *)secdata->d_buf;
187 	for (i = 0; i < (Elf32_Word)ehdr->e_shnum; i++, symhdr++)
188 		if (symhdr->sh_type == SHT_SYMTAB)
189 			break;
190 
191 	if ((symhdr->sh_type != SHT_SYMTAB) ||
192 		(symhdr->sh_link >= ehdr->e_shnum)) {
193 		free(secdata->d_buf);
194 		free(secdata);
195 		free(edata->d_buf);
196 		free(edata);
197 		return (end_elf_job(fd));
198 	}
199 
200 	if ((symdata = elf_read(fd, (long)symhdr->sh_offset,
201 		(size_t)symhdr->sh_size,
202 		(size_t)((symhdr->sh_size / symhdr->sh_entsize) *
203 		sizeof (Elf32_Sym)), ELF_T_SYM)) == 0) {
204 		free(secdata->d_buf);
205 		free(secdata);
206 		free(edata->d_buf);
207 		free(edata);
208 		return (end_elf_job(fd));
209 	}
210 
211 	/* read string table */
212 	strhdr = (Elf32_Shdr *)secdata->d_buf;
213 	strhdr = strhdr + symhdr->sh_link;
214 
215 	if (strhdr->sh_type != SHT_STRTAB) {
216 		free(symdata->d_buf);
217 		free(symdata);
218 		free(secdata->d_buf);
219 		free(secdata);
220 		free(edata->d_buf);
221 		free(edata);
222 		return (end_elf_job(fd));
223 	}
224 
225 	if ((strdata = elf_read(fd, strhdr->sh_offset, strhdr->sh_size,
226 		strhdr->sh_size, ELF_T_BYTE)) == 0) {
227 		free(symdata->d_buf);
228 		free(symdata);
229 		free(secdata->d_buf);
230 		free(secdata);
231 		free(edata->d_buf);
232 		free(edata);
233 		return (end_elf_job(fd));
234 	}
235 
236 	((char *)strdata->d_buf)[0] = '\0';
237 	((char *)strdata->d_buf)[strhdr->sh_size-1] = '\0';
238 
239 	sym = (Elf32_Sym *) (symdata->d_buf);
240 	sym_end = sym + symdata->d_size / sizeof (Elf32_Sym);
241 	for (; sym < sym_end; ++sym) {
242 		struct nlist *p;
243 		char *name;
244 		if (sym->st_name > strhdr->sh_size) {
245 			free(strdata->d_buf);
246 			free(strdata);
247 			free(symdata->d_buf);
248 			free(symdata);
249 			free(secdata->d_buf);
250 			free(secdata);
251 			free(edata->d_buf);
252 			free(edata);
253 			return (end_elf_job(fd));
254 		}
255 		name = (char *)strdata->d_buf + sym->st_name;
256 		if (name == 0)
257 			continue;
258 		for (p = list; p->n_name && p->n_name[0]; ++p) {
259 			if (strcmp(p->n_name, name)) {
260 				continue;
261 			}
262 			p->n_value = sym->st_value;
263 			p->n_type = ELF32_ST_TYPE(sym->st_info);
264 			p->n_scnum = sym->st_shndx;
265 			nreq--;
266 			break;
267 		}
268 	}
269 	/*
270 	 * Currently there is only one symbol table section
271 	 * in an object file, but this restriction may be
272 	 * relaxed in the future.
273 	 */
274 	(void) close(fd);
275 	free(secdata->d_buf);
276 	free(strdata->d_buf);
277 	free(symdata->d_buf);
278 	free(edata->d_buf);
279 	free(secdata);
280 	free(strdata);
281 	free(symdata);
282 	free(edata);
283 	return (nreq);
284 }
285 
286 /*
287  * allocate memory of size memsize and read size bytes
288  * starting at offset from fd - the file data are
289  * translated in place using the low-level libelf
290  * translation routines
291  */
292 
293 static Elf_Data *
294 elf_read(int fd, long offset, size_t size, size_t memsize, Elf_Type dtype)
295 {
296 	Elf_Data *dsrc, *ddst;
297 	Elf_Data srcdata;
298 	size_t maxsize;
299 	char *p;
300 
301 	dsrc = &srcdata;
302 
303 	if (size == 0)
304 		return (0);
305 
306 	if ((maxsize = memsize) < size)
307 		maxsize = size;
308 
309 
310 	if ((ddst = (Elf_Data *)malloc(sizeof (Elf_Data))) == 0)
311 		return (0);
312 
313 	if ((p = malloc(maxsize)) == 0) {
314 		free(ddst);
315 		return (0);
316 	}
317 
318 	if (lseek(fd, offset, 0L) == -1) {
319 		free(ddst);
320 		free(p);
321 		return (0);
322 	}
323 
324 	if (read(fd, p, size) != size) {
325 		free(ddst);
326 		free(p);
327 		return (0);
328 	}
329 
330 	dsrc->d_buf = p;
331 	dsrc->d_type = dtype;
332 	dsrc->d_size = size;
333 	dsrc->d_version = fvers;
334 	ddst->d_buf = p;
335 	ddst->d_size = memsize;
336 	ddst->d_version = EV_CURRENT;
337 
338 	if (elf32_xlatetom(ddst, dsrc, encode) != ddst) {
339 		free(ddst);
340 		free(p);
341 		return (0);
342 	}
343 
344 	return (ddst);
345 }
346 
347 static int
348 end_elf_job(int fd)
349 {
350 	(void) close(fd);
351 	return (-1);
352 }
353 
354 #if COFF_NLIST_SUPPORTED
355 static int
356 _coff_nlist(int fd, struct nlist *list)
357 {
358 	struct	filehdr	buf;
359 	struct	syment	sym;
360 	long n;
361 	int bufsiz = FILHSZ;
362 #if FLEXNAMES
363 	char *strtab = (char *)0;
364 	long strtablen;
365 #endif
366 	struct nlist *p, *inl;
367 	struct syment *q;
368 	long	sa;
369 	int 	nreq;
370 
371 	if (read(fd, (char *)&buf, bufsiz) == -1) {
372 		(void) close(fd);
373 		return (-1);
374 	}
375 
376 	if (BADMAG(buf.f_magic)) {
377 		(void) close(fd);
378 		return (-1);
379 	}
380 	sa = buf.f_symptr;	/* direct pointer to sym tab */
381 	if (lseek(fd, (long)sa, 0) == -1L) {
382 		(void) close(fd);
383 		return (-1);
384 	}
385 	q = &sym;
386 	n = buf.f_nsyms;	/* num. of sym tab entries */
387 
388 	/* count the number of symbols requested */
389 	for (inl = list, nreq = 0; inl->n_name && inl->n_name[0]; ++inl, nreq++)
390 		;
391 
392 	while (n) {
393 		if (sym_read(fd, &sym, SYMESZ) == -1) {
394 			sym_close(fd);
395 			return (-1);
396 		}
397 		n -= (q->n_numaux + 1L);
398 		for (p = list; p->n_name && p->n_name[0]; ++p) {
399 			if (p->n_value != 0L && p->n_sclass == C_EXT)
400 				continue;
401 			/*
402 			 * For 6.0, the name in an object file is
403 			 * either stored in the eight long character
404 			 * array, or in a string table at the end
405 			 * of the object file.  If the name is in the
406 			 * string table, the eight characters are
407 			 * thought of as a pair of longs, (n_zeroes
408 			 * and n_offset) the first of which is zero
409 			 * and the second is the offset of the name
410 			 * in the string table.
411 			 */
412 #if FLEXNAMES
413 			if (q->n_zeroes == 0L)	/* in string table */
414 			{
415 				if (strtab == (char *)0) /* need it */
416 				{
417 					long home = lseek(fd, 0L, 1);
418 					if (home == -1L) {
419 						sym_close(fd);
420 						return (-1);
421 					}
422 					if (lseek(fd, buf.f_symptr +
423 					    buf.f_nsyms * SYMESZ, 0) == -1 ||
424 					    read(fd, (char *)&strtablen,
425 					    sizeof (long)) != sizeof (long) ||
426 					    (strtab = (char *)malloc(
427 					    (unsigned)strtablen)) ==
428 					    (char *)0 ||
429 					    read(fd, strtab + sizeof (long),
430 					    strtablen - sizeof (long)) !=
431 					    strtablen - sizeof (long) ||
432 					    strtab[strtablen - 1] != '\0' ||
433 					    lseek(fd, home, 0) == -1) {
434 						(void) lseek(fd, home, 0);
435 						sym_close(fd);
436 						if (strtab != (char *)0)
437 							free(strtab);
438 						return (-1);
439 					}
440 				}
441 				if (q->n_offset < sizeof (long) ||
442 					q->n_offset >= strtablen) {
443 					sym_close(fd);
444 					if (strtab != (char *)0)
445 						free(strtab);
446 					return (-1);
447 				}
448 				if (strcmp(&strtab[q->n_offset],
449 					p->n_name)) {
450 					continue;
451 				}
452 			}
453 			else
454 #endif /* FLEXNAMES */
455 			{
456 				if (strncmp(q->_n._n_name,
457 					p->n_name, SYMNMLEN)) {
458 					continue;
459 				}
460 			}
461 
462 			p->n_value = q->n_value;
463 			p->n_type = q->n_type;
464 			p->n_scnum = q->n_scnum;
465 			p->n_sclass = q->n_sclass;
466 			nreq--;
467 			break;
468 		}
469 	}
470 #if FLEXNAMES
471 	sym_close(fd);
472 	if (strtab != (char *)0)
473 		free(strtab);
474 #endif
475 	return (nreq);
476 }
477 
478 static void
479 sym_close(int fd)
480 {
481 	num_in_buf = 0;
482 	next_entry = (char *)0;
483 
484 	(void) close(fd);
485 }
486 
487 /* buffered read of symbol table */
488 static int
489 sym_read(int fd, struct syment *sym, int size)
490 {
491 	long where = 0L;
492 
493 	if ((where = lseek(fd, 0L, 1)) == -1L) {
494 		sym_close(fd);
495 		return (-1);
496 	}
497 
498 	if (!num_in_buf) {
499 		if (fill_sym_buf(fd, size) == -1)
500 			return (-1);
501 	}
502 	(void) memcpy((char *)sym, next_entry, size);
503 	num_in_buf--;
504 
505 	if (sym->n_numaux && !num_in_buf) {
506 		if (fill_sym_buf(fd, size) == -1)
507 			return (-1);
508 	}
509 	if ((lseek(fd, where + SYMESZ + (AUXESZ * sym->n_numaux), 0)) == -1L) {
510 		sym_close(fd);
511 		return (-1);
512 	}
513 	size += AUXESZ * sym->n_numaux;
514 	num_in_buf--;
515 
516 	next_entry += size;
517 	return (0);
518 }
519 
520 static int
521 fill_sym_buf(int fd, int size)
522 {
523 	if ((num_in_buf = read(fd, sym_buf, size * SPACE)) == -1)
524 		return (-1);
525 	num_in_buf /= size;
526 	next_entry = &(sym_buf[0]);
527 	return (0);
528 }
529 #endif /* COFF_NLIST_SUPPORTED */
530