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 2006 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*
29 * Routines for retrieving CTF data from a .SUNW_ctf ELF section
30 */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <gelf.h>
37#include <strings.h>
38#include <sys/types.h>
39
40#include "ctftools.h"
41#include "memory.h"
42#include "symbol.h"
43
44typedef int read_cb_f(tdata_t *, char *, void *);
45
46/*
47 * Return the source types that the object was generated from.
48 */
49source_types_t
50built_source_types(Elf *elf, char const *file)
51{
52	source_types_t types = SOURCE_NONE;
53	symit_data_t *si;
54
55	if ((si = symit_new(elf, file)) == NULL)
56		return (SOURCE_NONE);
57
58	while (symit_next(si, STT_FILE) != NULL) {
59		char *name = symit_name(si);
60		size_t len = strlen(name);
61		if (len < 2 || name[len - 2] != '.') {
62			types |= SOURCE_UNKNOWN;
63			continue;
64		}
65
66		switch (name[len - 1]) {
67		case 'c':
68			types |= SOURCE_C;
69			break;
70		case 'h':
71			/* ignore */
72			break;
73		case 's':
74			types |= SOURCE_S;
75			break;
76		default:
77			types |= SOURCE_UNKNOWN;
78		}
79	}
80
81	symit_free(si);
82	return (types);
83}
84
85static int
86read_file(Elf *elf, char *file, char *label, read_cb_f *func, void *arg,
87    int require_ctf)
88{
89	Elf_Scn *ctfscn;
90	Elf_Data *ctfdata;
91	symit_data_t *si = NULL;
92	int ctfscnidx;
93	tdata_t *td;
94
95	if ((ctfscnidx = findelfsecidx(elf, file, ".SUNW_ctf")) < 0) {
96		if (require_ctf &&
97		    (built_source_types(elf, file) & SOURCE_C)) {
98			terminate("Input file %s was partially built from "
99			    "C sources, but no CTF data was present\n", file);
100		}
101		return (0);
102	}
103
104	if ((ctfscn = elf_getscn(elf, ctfscnidx)) == NULL ||
105	    (ctfdata = elf_getdata(ctfscn, NULL)) == NULL)
106		elfterminate(file, "Cannot read CTF section");
107
108	/* Reconstruction of type tree */
109	if ((si = symit_new(elf, file)) == NULL) {
110		warning("%s has no symbol table - skipping", file);
111		return (0);
112	}
113
114	td = ctf_load(file, ctfdata->d_buf, ctfdata->d_size, si, label);
115	tdata_build_hashes(td);
116
117	symit_free(si);
118
119	if (td != NULL) {
120		if (func(td, file, arg) < 0)
121			return (-1);
122		else
123			return (1);
124	}
125	return (0);
126}
127
128static int
129read_archive(int fd, Elf *elf, char *file, char *label, read_cb_f *func,
130    void *arg, int require_ctf)
131{
132	Elf *melf;
133	Elf_Cmd cmd = ELF_C_READ;
134	Elf_Arhdr *arh;
135	int secnum = 1, found = 0;
136
137	while ((melf = elf_begin(fd, cmd, elf)) != NULL) {
138		int rc = 0;
139
140		if ((arh = elf_getarhdr(melf)) == NULL) {
141			elfterminate(file, "Can't get archive header for "
142			    "member %d", secnum);
143		}
144
145		/* skip special sections - their names begin with "/" */
146		if (*arh->ar_name != '/') {
147			size_t memlen = strlen(file) + 1 +
148			    strlen(arh->ar_name) + 1 + 1;
149			char *memname = xmalloc(memlen);
150
151			snprintf(memname, memlen, "%s(%s)", file, arh->ar_name);
152
153			switch (elf_kind(melf)) {
154			case ELF_K_AR:
155				rc = read_archive(fd, melf, memname, label,
156				    func, arg, require_ctf);
157				break;
158			case ELF_K_ELF:
159				rc = read_file(melf, memname, label,
160				    func, arg, require_ctf);
161				break;
162			default:
163				terminate("%s: Unknown elf kind %d\n",
164				    memname, elf_kind(melf));
165			}
166
167			free(memname);
168		}
169
170		cmd = elf_next(melf);
171		(void) elf_end(melf);
172		secnum++;
173
174		if (rc < 0)
175			return (rc);
176		else
177			found += rc;
178	}
179
180	return (found);
181}
182
183static int
184read_ctf_common(char *file, char *label, read_cb_f *func, void *arg,
185    int require_ctf)
186{
187	Elf *elf;
188	int found = 0;
189	int fd;
190
191	debug(3, "Reading %s (label %s)\n", file, (label ? label : "NONE"));
192
193	(void) elf_version(EV_CURRENT);
194
195	if ((fd = open(file, O_RDONLY)) < 0)
196		terminate("%s: Cannot open for reading", file);
197	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
198		elfterminate(file, "Cannot read");
199
200	switch (elf_kind(elf)) {
201	case ELF_K_AR:
202		found = read_archive(fd, elf, file, label,
203		    func, arg, require_ctf);
204		break;
205
206	case ELF_K_ELF:
207		found = read_file(elf, file, label,
208		    func, arg, require_ctf);
209		break;
210
211	default:
212		terminate("%s: Unknown elf kind %d\n", file, elf_kind(elf));
213	}
214
215	(void) elf_end(elf);
216	(void) close(fd);
217
218	return (found);
219}
220
221/*ARGSUSED*/
222int
223read_ctf_save_cb(tdata_t *td, char *name, void *retp)
224{
225	tdata_t **tdp = retp;
226
227	*tdp = td;
228
229	return (1);
230}
231
232int
233read_ctf(char **files, int n, char *label, read_cb_f *func, void *private,
234    int require_ctf)
235{
236	int found;
237	int i, rc;
238
239	for (i = 0, found = 0; i < n; i++) {
240		if ((rc = read_ctf_common(files[i], label, func,
241		    private, require_ctf)) < 0)
242			return (rc);
243		found += rc;
244	}
245
246	return (found);
247}
248
249static int
250count_archive(int fd, Elf *elf, char *file)
251{
252	Elf *melf;
253	Elf_Cmd cmd = ELF_C_READ;
254	Elf_Arhdr *arh;
255	int nfiles = 0, err = 0;
256
257	while ((melf = elf_begin(fd, cmd, elf)) != NULL) {
258		if ((arh = elf_getarhdr(melf)) == NULL) {
259			warning("Can't process input archive %s\n",
260			    file);
261			err++;
262		}
263
264		if (*arh->ar_name != '/')
265			nfiles++;
266
267		cmd = elf_next(melf);
268		(void) elf_end(melf);
269	}
270
271	if (err > 0)
272		return (-1);
273
274	return (nfiles);
275}
276
277int
278count_files(char **files, int n)
279{
280	int nfiles = 0, err = 0;
281	Elf *elf;
282	int fd, rc, i;
283
284	(void) elf_version(EV_CURRENT);
285
286	for (i = 0; i < n; i++) {
287		char *file = files[i];
288
289		if ((fd = open(file, O_RDONLY)) < 0) {
290			warning("Can't read input file %s", file);
291			err++;
292			continue;
293		}
294
295		if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
296			warning("Can't open input file %s: %s\n", file,
297			    elf_errmsg(-1));
298			err++;
299			(void) close(fd);
300			continue;
301		}
302
303		switch (elf_kind(elf)) {
304		case ELF_K_AR:
305			if ((rc = count_archive(fd, elf, file)) < 0)
306				err++;
307			else
308				nfiles += rc;
309			break;
310		case ELF_K_ELF:
311			nfiles++;
312			break;
313		default:
314			warning("Input file %s is corrupt\n", file);
315			err++;
316		}
317
318		(void) elf_end(elf);
319		(void) close(fd);
320	}
321
322	if (err > 0)
323		return (-1);
324
325	debug(2, "Found %d files in %d input files\n", nfiles, n);
326
327	return (nfiles);
328}
329
330struct symit_data {
331	GElf_Shdr si_shdr;
332	Elf_Data *si_symd;
333	Elf_Data *si_strd;
334	GElf_Sym si_cursym;
335	char *si_curname;
336	char *si_curfile;
337	int si_nument;
338	int si_next;
339};
340
341symit_data_t *
342symit_new(Elf *elf, const char *file)
343{
344	symit_data_t *si;
345	Elf_Scn *scn;
346	int symtabidx;
347
348	if ((symtabidx = findelfsecidx(elf, file, ".symtab")) < 0)
349		return (NULL);
350
351	si = xcalloc(sizeof (symit_data_t));
352
353	if ((scn = elf_getscn(elf, symtabidx)) == NULL ||
354	    gelf_getshdr(scn, &si->si_shdr) == NULL ||
355	    (si->si_symd = elf_getdata(scn, NULL)) == NULL)
356		elfterminate(file, "Cannot read .symtab");
357
358	if ((scn = elf_getscn(elf, si->si_shdr.sh_link)) == NULL ||
359	    (si->si_strd = elf_getdata(scn, NULL)) == NULL)
360		elfterminate(file, "Cannot read strings for .symtab");
361
362	si->si_nument = si->si_shdr.sh_size / si->si_shdr.sh_entsize;
363
364	return (si);
365}
366
367void
368symit_free(symit_data_t *si)
369{
370	free(si);
371}
372
373void
374symit_reset(symit_data_t *si)
375{
376	si->si_next = 0;
377}
378
379char *
380symit_curfile(symit_data_t *si)
381{
382	return (si->si_curfile);
383}
384
385GElf_Sym *
386symit_next(symit_data_t *si, int type)
387{
388	GElf_Sym sym;
389	int check_sym = (type == STT_OBJECT || type == STT_FUNC);
390
391	for (; si->si_next < si->si_nument; si->si_next++) {
392		gelf_getsym(si->si_symd, si->si_next, &si->si_cursym);
393		gelf_getsym(si->si_symd, si->si_next, &sym);
394		si->si_curname = (caddr_t)si->si_strd->d_buf + sym.st_name;
395
396		if (GELF_ST_TYPE(sym.st_info) == STT_FILE)
397			si->si_curfile = si->si_curname;
398
399		if (GELF_ST_TYPE(sym.st_info) != type ||
400		    sym.st_shndx == SHN_UNDEF)
401			continue;
402
403		if (check_sym && ignore_symbol(&sym, si->si_curname))
404			continue;
405
406		si->si_next++;
407
408		return (&si->si_cursym);
409	}
410
411	return (NULL);
412}
413
414char *
415symit_name(symit_data_t *si)
416{
417	return (si->si_curname);
418}
419