xref: /freebsd/cddl/contrib/opensolaris/tools/ctf/cvt/input.c (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
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 
44 typedef int read_cb_f(tdata_t *, char *, void *);
45 
46 /*
47  * Return the source types that the object was generated from.
48  */
49 source_types_t
50 built_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 		case 'S':
75 			types |= SOURCE_S;
76 			break;
77 		default:
78 			types |= SOURCE_UNKNOWN;
79 		}
80 	}
81 
82 	symit_free(si);
83 	return (types);
84 }
85 
86 static int
87 read_file(Elf *elf, char *file, char *label, read_cb_f *func, void *arg,
88     int require_ctf)
89 {
90 	Elf_Scn *ctfscn;
91 	Elf_Data *ctfdata = NULL;
92 	symit_data_t *si = NULL;
93 	int ctfscnidx;
94 	tdata_t *td;
95 
96 	if ((ctfscnidx = findelfsecidx(elf, file, ".SUNW_ctf")) < 0) {
97 		if (require_ctf &&
98 		    (built_source_types(elf, file) & SOURCE_C)) {
99 			terminate("Input file %s was partially built from "
100 			    "C sources, but no CTF data was present\n", file);
101 		}
102 		return (0);
103 	}
104 
105 	if ((ctfscn = elf_getscn(elf, ctfscnidx)) == NULL ||
106 	    (ctfdata = elf_getdata(ctfscn, NULL)) == NULL)
107 		elfterminate(file, "Cannot read CTF section");
108 
109 	/* Reconstruction of type tree */
110 	if ((si = symit_new(elf, file)) == NULL) {
111 		warning("%s has no symbol table - skipping", file);
112 		return (0);
113 	}
114 
115 	td = ctf_load(file, ctfdata->d_buf, ctfdata->d_size, si, label);
116 	tdata_build_hashes(td);
117 
118 	symit_free(si);
119 
120 	if (td != NULL) {
121 		if (func(td, file, arg) < 0)
122 			return (-1);
123 		else
124 			return (1);
125 	}
126 	return (0);
127 }
128 
129 static int
130 read_archive(int fd, Elf *elf, char *file, char *label, read_cb_f *func,
131     void *arg, int require_ctf)
132 {
133 	Elf *melf;
134 	Elf_Cmd cmd = ELF_C_READ;
135 	Elf_Arhdr *arh;
136 	int secnum = 1, found = 0;
137 
138 	while ((melf = elf_begin(fd, cmd, elf)) != NULL) {
139 		int rc = 0;
140 
141 		if ((arh = elf_getarhdr(melf)) == NULL) {
142 			elfterminate(file, "Can't get archive header for "
143 			    "member %d", secnum);
144 		}
145 
146 		/* skip special sections - their names begin with "/" */
147 		if (*arh->ar_name != '/') {
148 			size_t memlen = strlen(file) + 1 +
149 			    strlen(arh->ar_name) + 1 + 1;
150 			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 
184 static int
185 read_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*/
223 int
224 read_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 
233 int
234 read_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 
250 static int
251 count_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 
278 int
279 count_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 
331 struct 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 
342 symit_data_t *
343 symit_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 
368 void
369 symit_free(symit_data_t *si)
370 {
371 	free(si);
372 }
373 
374 void
375 symit_reset(symit_data_t *si)
376 {
377 	si->si_next = 0;
378 }
379 
380 char *
381 symit_curfile(symit_data_t *si)
382 {
383 	return (si->si_curfile);
384 }
385 
386 GElf_Sym *
387 symit_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 
418 char *
419 symit_name(symit_data_t *si)
420 {
421 	return (si->si_curname);
422 }
423