1178481Sjb/*
2178481Sjb * CDDL HEADER START
3178481Sjb *
4178481Sjb * The contents of this file are subject to the terms of the
5178481Sjb * Common Development and Distribution License (the "License").
6178481Sjb * You may not use this file except in compliance with the License.
7178481Sjb *
8178481Sjb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9178481Sjb * or http://www.opensolaris.org/os/licensing.
10178481Sjb * See the License for the specific language governing permissions
11178481Sjb * and limitations under the License.
12178481Sjb *
13178481Sjb * When distributing Covered Code, include this CDDL HEADER in each
14178481Sjb * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15178481Sjb * If applicable, add the following below this CDDL HEADER, with the
16178481Sjb * fields enclosed by brackets "[]" replaced with your own identifying
17178481Sjb * information: Portions Copyright [yyyy] [name of copyright owner]
18178481Sjb *
19178481Sjb * CDDL HEADER END
20178481Sjb */
21178481Sjb/*
22178481Sjb * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23178481Sjb * Use is subject to license terms.
24178481Sjb */
25178481Sjb
26178481Sjb#pragma ident	"%Z%%M%	%I%	%E% SMI"
27178481Sjb
28178481Sjb/*
29178481Sjb * Routines for retrieving CTF data from a .SUNW_ctf ELF section
30178481Sjb */
31178481Sjb
32178481Sjb#include <stdio.h>
33178481Sjb#include <stdlib.h>
34178481Sjb#include <fcntl.h>
35178481Sjb#include <unistd.h>
36178481Sjb#include <gelf.h>
37178481Sjb#include <strings.h>
38178481Sjb#include <sys/types.h>
39178481Sjb
40178481Sjb#include "ctftools.h"
41178481Sjb#include "memory.h"
42178481Sjb#include "symbol.h"
43178481Sjb
44178481Sjbtypedef int read_cb_f(tdata_t *, char *, void *);
45178481Sjb
46178481Sjb/*
47178481Sjb * Return the source types that the object was generated from.
48178481Sjb */
49178481Sjbsource_types_t
50178481Sjbbuilt_source_types(Elf *elf, char const *file)
51178481Sjb{
52178481Sjb	source_types_t types = SOURCE_NONE;
53178481Sjb	symit_data_t *si;
54178481Sjb
55178481Sjb	if ((si = symit_new(elf, file)) == NULL)
56178481Sjb		return (SOURCE_NONE);
57178481Sjb
58178481Sjb	while (symit_next(si, STT_FILE) != NULL) {
59178481Sjb		char *name = symit_name(si);
60178481Sjb		size_t len = strlen(name);
61178481Sjb		if (len < 2 || name[len - 2] != '.') {
62178481Sjb			types |= SOURCE_UNKNOWN;
63178481Sjb			continue;
64178481Sjb		}
65178481Sjb
66178481Sjb		switch (name[len - 1]) {
67178481Sjb		case 'c':
68178481Sjb			types |= SOURCE_C;
69178481Sjb			break;
70178481Sjb		case 'h':
71178481Sjb			/* ignore */
72178481Sjb			break;
73178481Sjb		case 's':
74178546Sjb		case 'S':
75178481Sjb			types |= SOURCE_S;
76178481Sjb			break;
77178481Sjb		default:
78178481Sjb			types |= SOURCE_UNKNOWN;
79178481Sjb		}
80178481Sjb	}
81178481Sjb
82178481Sjb	symit_free(si);
83178481Sjb	return (types);
84178481Sjb}
85178481Sjb
86178481Sjbstatic int
87178481Sjbread_file(Elf *elf, char *file, char *label, read_cb_f *func, void *arg,
88178481Sjb    int require_ctf)
89178481Sjb{
90178481Sjb	Elf_Scn *ctfscn;
91178546Sjb	Elf_Data *ctfdata = NULL;
92178481Sjb	symit_data_t *si = NULL;
93178481Sjb	int ctfscnidx;
94178481Sjb	tdata_t *td;
95178481Sjb
96178481Sjb	if ((ctfscnidx = findelfsecidx(elf, file, ".SUNW_ctf")) < 0) {
97178481Sjb		if (require_ctf &&
98178481Sjb		    (built_source_types(elf, file) & SOURCE_C)) {
99178481Sjb			terminate("Input file %s was partially built from "
100178481Sjb			    "C sources, but no CTF data was present\n", file);
101178481Sjb		}
102178481Sjb		return (0);
103178481Sjb	}
104178481Sjb
105178481Sjb	if ((ctfscn = elf_getscn(elf, ctfscnidx)) == NULL ||
106178481Sjb	    (ctfdata = elf_getdata(ctfscn, NULL)) == NULL)
107178481Sjb		elfterminate(file, "Cannot read CTF section");
108178481Sjb
109178481Sjb	/* Reconstruction of type tree */
110178481Sjb	if ((si = symit_new(elf, file)) == NULL) {
111178481Sjb		warning("%s has no symbol table - skipping", file);
112178481Sjb		return (0);
113178481Sjb	}
114178481Sjb
115178481Sjb	td = ctf_load(file, ctfdata->d_buf, ctfdata->d_size, si, label);
116178481Sjb	tdata_build_hashes(td);
117178481Sjb
118178481Sjb	symit_free(si);
119178481Sjb
120178481Sjb	if (td != NULL) {
121178481Sjb		if (func(td, file, arg) < 0)
122178481Sjb			return (-1);
123178481Sjb		else
124178481Sjb			return (1);
125178481Sjb	}
126178481Sjb	return (0);
127178481Sjb}
128178481Sjb
129178481Sjbstatic int
130178481Sjbread_archive(int fd, Elf *elf, char *file, char *label, read_cb_f *func,
131178481Sjb    void *arg, int require_ctf)
132178481Sjb{
133178481Sjb	Elf *melf;
134178481Sjb	Elf_Cmd cmd = ELF_C_READ;
135178481Sjb	Elf_Arhdr *arh;
136178481Sjb	int secnum = 1, found = 0;
137178481Sjb
138178481Sjb	while ((melf = elf_begin(fd, cmd, elf)) != NULL) {
139178481Sjb		int rc = 0;
140178481Sjb
141178481Sjb		if ((arh = elf_getarhdr(melf)) == NULL) {
142178481Sjb			elfterminate(file, "Can't get archive header for "
143178481Sjb			    "member %d", secnum);
144178481Sjb		}
145178481Sjb
146178481Sjb		/* skip special sections - their names begin with "/" */
147178481Sjb		if (*arh->ar_name != '/') {
148178481Sjb			size_t memlen = strlen(file) + 1 +
149178481Sjb			    strlen(arh->ar_name) + 1 + 1;
150178481Sjb			char *memname = xmalloc(memlen);
151178481Sjb
152178481Sjb			snprintf(memname, memlen, "%s(%s)", file, arh->ar_name);
153178481Sjb
154178481Sjb			switch (elf_kind(melf)) {
155178481Sjb			case ELF_K_AR:
156178481Sjb				rc = read_archive(fd, melf, memname, label,
157178481Sjb				    func, arg, require_ctf);
158178481Sjb				break;
159178481Sjb			case ELF_K_ELF:
160178481Sjb				rc = read_file(melf, memname, label,
161178481Sjb				    func, arg, require_ctf);
162178481Sjb				break;
163178481Sjb			default:
164178481Sjb				terminate("%s: Unknown elf kind %d\n",
165178481Sjb				    memname, elf_kind(melf));
166178481Sjb			}
167178481Sjb
168178481Sjb			free(memname);
169178481Sjb		}
170178481Sjb
171178481Sjb		cmd = elf_next(melf);
172178481Sjb		(void) elf_end(melf);
173178481Sjb		secnum++;
174178481Sjb
175178481Sjb		if (rc < 0)
176178481Sjb			return (rc);
177178481Sjb		else
178178481Sjb			found += rc;
179178481Sjb	}
180178481Sjb
181178481Sjb	return (found);
182178481Sjb}
183178481Sjb
184178481Sjbstatic int
185178481Sjbread_ctf_common(char *file, char *label, read_cb_f *func, void *arg,
186178481Sjb    int require_ctf)
187178481Sjb{
188178481Sjb	Elf *elf;
189178481Sjb	int found = 0;
190178481Sjb	int fd;
191178481Sjb
192178481Sjb	debug(3, "Reading %s (label %s)\n", file, (label ? label : "NONE"));
193178481Sjb
194178481Sjb	(void) elf_version(EV_CURRENT);
195178481Sjb
196178481Sjb	if ((fd = open(file, O_RDONLY)) < 0)
197178481Sjb		terminate("%s: Cannot open for reading", file);
198178481Sjb	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
199178481Sjb		elfterminate(file, "Cannot read");
200178481Sjb
201178481Sjb	switch (elf_kind(elf)) {
202178481Sjb	case ELF_K_AR:
203178481Sjb		found = read_archive(fd, elf, file, label,
204178481Sjb		    func, arg, require_ctf);
205178481Sjb		break;
206178481Sjb
207178481Sjb	case ELF_K_ELF:
208178481Sjb		found = read_file(elf, file, label,
209178481Sjb		    func, arg, require_ctf);
210178481Sjb		break;
211178481Sjb
212178481Sjb	default:
213178481Sjb		terminate("%s: Unknown elf kind %d\n", file, elf_kind(elf));
214178481Sjb	}
215178481Sjb
216178481Sjb	(void) elf_end(elf);
217178481Sjb	(void) close(fd);
218178481Sjb
219178481Sjb	return (found);
220178481Sjb}
221178481Sjb
222178481Sjb/*ARGSUSED*/
223178481Sjbint
224178546Sjbread_ctf_save_cb(tdata_t *td, char *name __unused, void *retp)
225178481Sjb{
226178481Sjb	tdata_t **tdp = retp;
227178481Sjb
228178481Sjb	*tdp = td;
229178481Sjb
230178481Sjb	return (1);
231178481Sjb}
232178481Sjb
233178481Sjbint
234178481Sjbread_ctf(char **files, int n, char *label, read_cb_f *func, void *private,
235178481Sjb    int require_ctf)
236178481Sjb{
237178481Sjb	int found;
238178481Sjb	int i, rc;
239178481Sjb
240178481Sjb	for (i = 0, found = 0; i < n; i++) {
241178481Sjb		if ((rc = read_ctf_common(files[i], label, func,
242178481Sjb		    private, require_ctf)) < 0)
243178481Sjb			return (rc);
244178481Sjb		found += rc;
245178481Sjb	}
246178481Sjb
247178481Sjb	return (found);
248178481Sjb}
249178481Sjb
250178481Sjbstatic int
251178481Sjbcount_archive(int fd, Elf *elf, char *file)
252178481Sjb{
253178481Sjb	Elf *melf;
254178481Sjb	Elf_Cmd cmd = ELF_C_READ;
255178481Sjb	Elf_Arhdr *arh;
256178481Sjb	int nfiles = 0, err = 0;
257178481Sjb
258178481Sjb	while ((melf = elf_begin(fd, cmd, elf)) != NULL) {
259178481Sjb		if ((arh = elf_getarhdr(melf)) == NULL) {
260178481Sjb			warning("Can't process input archive %s\n",
261178481Sjb			    file);
262178481Sjb			err++;
263178481Sjb		}
264178481Sjb
265178481Sjb		if (*arh->ar_name != '/')
266178481Sjb			nfiles++;
267178481Sjb
268178481Sjb		cmd = elf_next(melf);
269178481Sjb		(void) elf_end(melf);
270178481Sjb	}
271178481Sjb
272178481Sjb	if (err > 0)
273178481Sjb		return (-1);
274178481Sjb
275178481Sjb	return (nfiles);
276178481Sjb}
277178481Sjb
278178481Sjbint
279178481Sjbcount_files(char **files, int n)
280178481Sjb{
281178481Sjb	int nfiles = 0, err = 0;
282178481Sjb	Elf *elf;
283178481Sjb	int fd, rc, i;
284178481Sjb
285178481Sjb	(void) elf_version(EV_CURRENT);
286178481Sjb
287178481Sjb	for (i = 0; i < n; i++) {
288178481Sjb		char *file = files[i];
289178481Sjb
290178481Sjb		if ((fd = open(file, O_RDONLY)) < 0) {
291178481Sjb			warning("Can't read input file %s", file);
292178481Sjb			err++;
293178481Sjb			continue;
294178481Sjb		}
295178481Sjb
296178481Sjb		if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
297178481Sjb			warning("Can't open input file %s: %s\n", file,
298178481Sjb			    elf_errmsg(-1));
299178481Sjb			err++;
300178481Sjb			(void) close(fd);
301178481Sjb			continue;
302178481Sjb		}
303178481Sjb
304178481Sjb		switch (elf_kind(elf)) {
305178481Sjb		case ELF_K_AR:
306178481Sjb			if ((rc = count_archive(fd, elf, file)) < 0)
307178481Sjb				err++;
308178481Sjb			else
309178481Sjb				nfiles += rc;
310178481Sjb			break;
311178481Sjb		case ELF_K_ELF:
312178481Sjb			nfiles++;
313178481Sjb			break;
314178481Sjb		default:
315178481Sjb			warning("Input file %s is corrupt\n", file);
316178481Sjb			err++;
317178481Sjb		}
318178481Sjb
319178481Sjb		(void) elf_end(elf);
320178481Sjb		(void) close(fd);
321178481Sjb	}
322178481Sjb
323178481Sjb	if (err > 0)
324178481Sjb		return (-1);
325178481Sjb
326178481Sjb	debug(2, "Found %d files in %d input files\n", nfiles, n);
327178481Sjb
328178481Sjb	return (nfiles);
329178481Sjb}
330178481Sjb
331178481Sjbstruct symit_data {
332178481Sjb	GElf_Shdr si_shdr;
333178481Sjb	Elf_Data *si_symd;
334178481Sjb	Elf_Data *si_strd;
335178481Sjb	GElf_Sym si_cursym;
336178481Sjb	char *si_curname;
337178481Sjb	char *si_curfile;
338178481Sjb	int si_nument;
339178481Sjb	int si_next;
340178481Sjb};
341178481Sjb
342178481Sjbsymit_data_t *
343178481Sjbsymit_new(Elf *elf, const char *file)
344178481Sjb{
345178481Sjb	symit_data_t *si;
346178481Sjb	Elf_Scn *scn;
347178481Sjb	int symtabidx;
348178481Sjb
349178481Sjb	if ((symtabidx = findelfsecidx(elf, file, ".symtab")) < 0)
350178481Sjb		return (NULL);
351178481Sjb
352178481Sjb	si = xcalloc(sizeof (symit_data_t));
353178481Sjb
354178481Sjb	if ((scn = elf_getscn(elf, symtabidx)) == NULL ||
355178481Sjb	    gelf_getshdr(scn, &si->si_shdr) == NULL ||
356178481Sjb	    (si->si_symd = elf_getdata(scn, NULL)) == NULL)
357178481Sjb		elfterminate(file, "Cannot read .symtab");
358178481Sjb
359178481Sjb	if ((scn = elf_getscn(elf, si->si_shdr.sh_link)) == NULL ||
360178481Sjb	    (si->si_strd = elf_getdata(scn, NULL)) == NULL)
361178481Sjb		elfterminate(file, "Cannot read strings for .symtab");
362178481Sjb
363178481Sjb	si->si_nument = si->si_shdr.sh_size / si->si_shdr.sh_entsize;
364178481Sjb
365178481Sjb	return (si);
366178481Sjb}
367178481Sjb
368178481Sjbvoid
369178481Sjbsymit_free(symit_data_t *si)
370178481Sjb{
371178481Sjb	free(si);
372178481Sjb}
373178481Sjb
374178481Sjbvoid
375178481Sjbsymit_reset(symit_data_t *si)
376178481Sjb{
377178481Sjb	si->si_next = 0;
378178481Sjb}
379178481Sjb
380178481Sjbchar *
381178481Sjbsymit_curfile(symit_data_t *si)
382178481Sjb{
383178481Sjb	return (si->si_curfile);
384178481Sjb}
385178481Sjb
386178481SjbGElf_Sym *
387178481Sjbsymit_next(symit_data_t *si, int type)
388178481Sjb{
389178481Sjb	GElf_Sym sym;
390248991Sdim	char *bname;
391178481Sjb	int check_sym = (type == STT_OBJECT || type == STT_FUNC);
392178481Sjb
393178481Sjb	for (; si->si_next < si->si_nument; si->si_next++) {
394178481Sjb		gelf_getsym(si->si_symd, si->si_next, &si->si_cursym);
395178481Sjb		gelf_getsym(si->si_symd, si->si_next, &sym);
396178481Sjb		si->si_curname = (caddr_t)si->si_strd->d_buf + sym.st_name;
397178481Sjb
398248991Sdim		if (GELF_ST_TYPE(sym.st_info) == STT_FILE) {
399248991Sdim			bname = strrchr(si->si_curname, '/');
400248991Sdim			si->si_curfile = bname == NULL ? si->si_curname : bname + 1;
401248991Sdim		}
402178481Sjb
403178481Sjb		if (GELF_ST_TYPE(sym.st_info) != type ||
404178481Sjb		    sym.st_shndx == SHN_UNDEF)
405178481Sjb			continue;
406178481Sjb
407178481Sjb		if (check_sym && ignore_symbol(&sym, si->si_curname))
408178481Sjb			continue;
409178481Sjb
410178481Sjb		si->si_next++;
411178481Sjb
412178481Sjb		return (&si->si_cursym);
413178481Sjb	}
414178481Sjb
415178481Sjb	return (NULL);
416178481Sjb}
417178481Sjb
418178481Sjbchar *
419178481Sjbsymit_name(symit_data_t *si)
420178481Sjb{
421178481Sjb	return (si->si_curname);
422178481Sjb}
423