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
72static char sym_buf[SPACE * SYMESZ];
73static int num_in_buf = 0;
74static char *next_entry = (char *)0;
75#endif /* COFF_NLIST_SUPPORTED */
76
77static unsigned encode;		/* data encoding if an ELF file */
78static unsigned fvers;		/* object file version if an ELF file */
79
80/* forward declarations */
81static int _elf_nlist(int, struct nlist *);
82static int end_elf_job(int);
83static Elf_Data *elf_read(int, long, size_t, size_t, Elf_Type);
84
85#if COFF_NLIST_SUPPORTED
86static int _coff_nlist(int, struct nlist *);
87static void sym_close(int);
88static int sym_read(int, struct syment *, int);
89static int fill_sym_buf(int, int);
90#endif /* COFF_NLIST_SUPPORTED */
91
92int
93nlist(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
140static 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
293static Elf_Data *
294elf_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
347static int
348end_elf_job(int fd)
349{
350	(void) close(fd);
351	return (-1);
352}
353
354#if COFF_NLIST_SUPPORTED
355static 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
478static void
479sym_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 */
488static int
489sym_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
520static int
521fill_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