1327Sjkh/*
2379Sjkh * CDDL HEADER START
3327Sjkh *
4327Sjkh * The contents of this file are subject to the terms of the
5327Sjkh * Common Development and Distribution License (the "License").
6327Sjkh * You may not use this file except in compliance with the License.
7327Sjkh *
8327Sjkh * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9327Sjkh * or http://www.opensolaris.org/os/licensing.
10327Sjkh * See the License for the specific language governing permissions
11327Sjkh * and limitations under the License.
12327Sjkh *
13327Sjkh * When distributing Covered Code, include this CDDL HEADER in each
14327Sjkh * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15327Sjkh * If applicable, add the following below this CDDL HEADER, with the
16327Sjkh * fields enclosed by brackets "[]" replaced with your own identifying
17327Sjkh * information: Portions Copyright [yyyy] [name of copyright owner]
18327Sjkh *
19327Sjkh * CDDL HEADER END
20327Sjkh */
21327Sjkh/*
22327Sjkh * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23327Sjkh * Use is subject to license terms.
24327Sjkh */
25327Sjkh
26327Sjkh#pragma ident	"%Z%%M%	%I%	%E% SMI"
27327Sjkh
28327Sjkh/*
29379Sjkh * Routines for retrieving CTF data from a .SUNW_ctf ELF section
30327Sjkh */
31327Sjkh
32327Sjkh#include <stdio.h>
33379Sjkh#include <stdlib.h>
34383Sjkh#include <fcntl.h>
35327Sjkh#include <unistd.h>
36327Sjkh#include <gelf.h>
37327Sjkh#include <strings.h>
38327Sjkh#include <sys/types.h>
39327Sjkh
40327Sjkh#include "ctftools.h"
41327Sjkh#include "memory.h"
42327Sjkh#include "symbol.h"
43327Sjkh
44327Sjkhtypedef int read_cb_f(tdata_t *, char *, void *);
45327Sjkh
46327Sjkh/*
47327Sjkh * Return the source types that the object was generated from.
48327Sjkh */
49327Sjkhsource_types_t
50327Sjkhbuilt_source_types(Elf *elf, char const *file)
51327Sjkh{
52327Sjkh	source_types_t types = SOURCE_NONE;
53327Sjkh	symit_data_t *si;
54327Sjkh
55327Sjkh	if ((si = symit_new(elf, file)) == NULL)
56327Sjkh		return (SOURCE_NONE);
57327Sjkh
58327Sjkh	while (symit_next(si, STT_FILE) != NULL) {
59327Sjkh		char *name = symit_name(si);
60327Sjkh		size_t len = strlen(name);
61327Sjkh		if (len < 2 || name[len - 2] != '.') {
62327Sjkh			types |= SOURCE_UNKNOWN;
63327Sjkh			continue;
64327Sjkh		}
65327Sjkh
66327Sjkh		switch (name[len - 1]) {
67327Sjkh		case 'c':
68327Sjkh			types |= SOURCE_C;
69327Sjkh			break;
70327Sjkh		case 'h':
71327Sjkh			/* ignore */
72327Sjkh			break;
73327Sjkh		case 's':
74327Sjkh		case 'S':
75327Sjkh			types |= SOURCE_S;
76327Sjkh			break;
77327Sjkh		default:
78327Sjkh			types |= SOURCE_UNKNOWN;
79327Sjkh		}
80327Sjkh	}
81327Sjkh
82327Sjkh	symit_free(si);
83327Sjkh	return (types);
84327Sjkh}
85327Sjkh
86327Sjkhstatic int
87327Sjkhread_file(Elf *elf, char *file, char *label, read_cb_f *func, void *arg,
88327Sjkh    int require_ctf)
89379Sjkh{
90379Sjkh	Elf_Scn *ctfscn;
91379Sjkh	Elf_Data *ctfdata = NULL;
92379Sjkh	symit_data_t *si = NULL;
93383Sjkh	int ctfscnidx;
94383Sjkh	tdata_t *td;
95383Sjkh
96383Sjkh	if ((ctfscnidx = findelfsecidx(elf, file, ".SUNW_ctf")) < 0) {
97327Sjkh		if (require_ctf &&
98327Sjkh		    (built_source_types(elf, file) & SOURCE_C)) {
99327Sjkh			terminate("Input file %s was partially built from "
100327Sjkh			    "C sources, but no CTF data was present\n", file);
101327Sjkh		}
102327Sjkh		return (0);
103327Sjkh	}
104327Sjkh
105327Sjkh	if ((ctfscn = elf_getscn(elf, ctfscnidx)) == NULL ||
106327Sjkh	    (ctfdata = elf_getdata(ctfscn, NULL)) == NULL)
107327Sjkh		elfterminate(file, "Cannot read CTF section");
108327Sjkh
109327Sjkh	/* Reconstruction of type tree */
110327Sjkh	if ((si = symit_new(elf, file)) == NULL) {
111327Sjkh		warning("%s has no symbol table - skipping", file);
112327Sjkh		return (0);
113327Sjkh	}
114327Sjkh
115327Sjkh	td = ctf_load(file, ctfdata->d_buf, ctfdata->d_size, si, label);
116327Sjkh	tdata_build_hashes(td);
117327Sjkh
118327Sjkh	symit_free(si);
119327Sjkh
120327Sjkh	if (td != NULL) {
121327Sjkh		if (func(td, file, arg) < 0)
122327Sjkh			return (-1);
123327Sjkh		else
124327Sjkh			return (1);
125327Sjkh	}
126327Sjkh	return (0);
127327Sjkh}
128327Sjkh
129327Sjkhstatic int
130327Sjkhread_archive(int fd, Elf *elf, char *file, char *label, read_cb_f *func,
131327Sjkh    void *arg, int require_ctf)
132327Sjkh{
133327Sjkh	Elf *melf;
134327Sjkh	Elf_Cmd cmd = ELF_C_READ;
135327Sjkh	Elf_Arhdr *arh;
136327Sjkh	int secnum = 1, found = 0;
137327Sjkh
138327Sjkh	while ((melf = elf_begin(fd, cmd, elf)) != NULL) {
139327Sjkh		int rc = 0;
140327Sjkh
141327Sjkh		if ((arh = elf_getarhdr(melf)) == NULL) {
142327Sjkh			elfterminate(file, "Can't get archive header for "
143327Sjkh			    "member %d", secnum);
144327Sjkh		}
145379Sjkh
146327Sjkh		/* skip special sections - their names begin with "/" */
147383Sjkh		if (*arh->ar_name != '/') {
148327Sjkh			size_t memlen = strlen(file) + 1 +
149327Sjkh			    strlen(arh->ar_name) + 1 + 1;
150327Sjkh			char *memname = xmalloc(memlen);
151
152			snprintf(memname, memlen, "%s(%s)", file, arh->ar_name);
153
154			switch (elf_kind(melf)) {
155			case ELF_K_AR:
156				rc = read_archive(fd, melf, memname, label,
157				    func, arg, require_ctf);
158				break;
159			case ELF_K_ELF:
160				rc = read_file(melf, memname, label,
161				    func, arg, require_ctf);
162				break;
163			default:
164				terminate("%s: Unknown elf kind %d\n",
165				    memname, elf_kind(melf));
166			}
167
168			free(memname);
169		}
170
171		cmd = elf_next(melf);
172		(void) elf_end(melf);
173		secnum++;
174
175		if (rc < 0)
176			return (rc);
177		else
178			found += rc;
179	}
180
181	return (found);
182}
183
184static int
185read_ctf_common(char *file, char *label, read_cb_f *func, void *arg,
186    int require_ctf)
187{
188	Elf *elf;
189	int found = 0;
190	int fd;
191
192	debug(3, "Reading %s (label %s)\n", file, (label ? label : "NONE"));
193
194	(void) elf_version(EV_CURRENT);
195
196	if ((fd = open(file, O_RDONLY)) < 0)
197		terminate("%s: Cannot open for reading", file);
198	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
199		elfterminate(file, "Cannot read");
200
201	switch (elf_kind(elf)) {
202	case ELF_K_AR:
203		found = read_archive(fd, elf, file, label,
204		    func, arg, require_ctf);
205		break;
206
207	case ELF_K_ELF:
208		found = read_file(elf, file, label,
209		    func, arg, require_ctf);
210		break;
211
212	default:
213		terminate("%s: Unknown elf kind %d\n", file, elf_kind(elf));
214	}
215
216	(void) elf_end(elf);
217	(void) close(fd);
218
219	return (found);
220}
221
222/*ARGSUSED*/
223int
224read_ctf_save_cb(tdata_t *td, char *name __unused, void *retp)
225{
226	tdata_t **tdp = retp;
227
228	*tdp = td;
229
230	return (1);
231}
232
233int
234read_ctf(char **files, int n, char *label, read_cb_f *func, void *private,
235    int require_ctf)
236{
237	int found;
238	int i, rc;
239
240	for (i = 0, found = 0; i < n; i++) {
241		if ((rc = read_ctf_common(files[i], label, func,
242		    private, require_ctf)) < 0)
243			return (rc);
244		found += rc;
245	}
246
247	return (found);
248}
249
250static int
251count_archive(int fd, Elf *elf, char *file)
252{
253	Elf *melf;
254	Elf_Cmd cmd = ELF_C_READ;
255	Elf_Arhdr *arh;
256	int nfiles = 0, err = 0;
257
258	while ((melf = elf_begin(fd, cmd, elf)) != NULL) {
259		if ((arh = elf_getarhdr(melf)) == NULL) {
260			warning("Can't process input archive %s\n",
261			    file);
262			err++;
263		}
264
265		if (*arh->ar_name != '/')
266			nfiles++;
267
268		cmd = elf_next(melf);
269		(void) elf_end(melf);
270	}
271
272	if (err > 0)
273		return (-1);
274
275	return (nfiles);
276}
277
278int
279count_files(char **files, int n)
280{
281	int nfiles = 0, err = 0;
282	Elf *elf;
283	int fd, rc, i;
284
285	(void) elf_version(EV_CURRENT);
286
287	for (i = 0; i < n; i++) {
288		char *file = files[i];
289
290		if ((fd = open(file, O_RDONLY)) < 0) {
291			warning("Can't read input file %s", file);
292			err++;
293			continue;
294		}
295
296		if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
297			warning("Can't open input file %s: %s\n", file,
298			    elf_errmsg(-1));
299			err++;
300			(void) close(fd);
301			continue;
302		}
303
304		switch (elf_kind(elf)) {
305		case ELF_K_AR:
306			if ((rc = count_archive(fd, elf, file)) < 0)
307				err++;
308			else
309				nfiles += rc;
310			break;
311		case ELF_K_ELF:
312			nfiles++;
313			break;
314		default:
315			warning("Input file %s is corrupt\n", file);
316			err++;
317		}
318
319		(void) elf_end(elf);
320		(void) close(fd);
321	}
322
323	if (err > 0)
324		return (-1);
325
326	debug(2, "Found %d files in %d input files\n", nfiles, n);
327
328	return (nfiles);
329}
330
331struct symit_data {
332	GElf_Shdr si_shdr;
333	Elf_Data *si_symd;
334	Elf_Data *si_strd;
335	GElf_Sym si_cursym;
336	char *si_curname;
337	char *si_curfile;
338	int si_nument;
339	int si_next;
340};
341
342symit_data_t *
343symit_new(Elf *elf, const char *file)
344{
345	symit_data_t *si;
346	Elf_Scn *scn;
347	int symtabidx;
348
349	if ((symtabidx = findelfsecidx(elf, file, ".symtab")) < 0)
350		return (NULL);
351
352	si = xcalloc(sizeof (symit_data_t));
353
354	if ((scn = elf_getscn(elf, symtabidx)) == NULL ||
355	    gelf_getshdr(scn, &si->si_shdr) == NULL ||
356	    (si->si_symd = elf_getdata(scn, NULL)) == NULL)
357		elfterminate(file, "Cannot read .symtab");
358
359	if ((scn = elf_getscn(elf, si->si_shdr.sh_link)) == NULL ||
360	    (si->si_strd = elf_getdata(scn, NULL)) == NULL)
361		elfterminate(file, "Cannot read strings for .symtab");
362
363	si->si_nument = si->si_shdr.sh_size / si->si_shdr.sh_entsize;
364
365	return (si);
366}
367
368void
369symit_free(symit_data_t *si)
370{
371	free(si);
372}
373
374void
375symit_reset(symit_data_t *si)
376{
377	si->si_next = 0;
378}
379
380char *
381symit_curfile(symit_data_t *si)
382{
383	return (si->si_curfile);
384}
385
386GElf_Sym *
387symit_next(symit_data_t *si, int type)
388{
389	GElf_Sym sym;
390	char *bname;
391	int check_sym = (type == STT_OBJECT || type == STT_FUNC);
392
393	for (; si->si_next < si->si_nument; si->si_next++) {
394		gelf_getsym(si->si_symd, si->si_next, &si->si_cursym);
395		gelf_getsym(si->si_symd, si->si_next, &sym);
396		si->si_curname = (caddr_t)si->si_strd->d_buf + sym.st_name;
397
398		if (GELF_ST_TYPE(sym.st_info) == STT_FILE) {
399			bname = strrchr(si->si_curname, '/');
400			si->si_curfile = bname == NULL ? si->si_curname : bname + 1;
401		}
402
403		if (GELF_ST_TYPE(sym.st_info) != type ||
404		    sym.st_shndx == SHN_UNDEF)
405			continue;
406
407		if (check_sym && ignore_symbol(&sym, si->si_curname))
408			continue;
409
410		si->si_next++;
411
412		return (&si->si_cursym);
413	}
414
415	return (NULL);
416}
417
418char *
419symit_name(symit_data_t *si)
420{
421	return (si->si_curname);
422}
423