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	"@(#)input.c	1.9	06/05/03 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#if defined(__APPLE__)
160			case ELF_K_MACHO: /* Underlying file is Mach-o */
161#endif /* __APPLE__ */
162				rc = read_file(melf, memname, label,
163				    func, arg, require_ctf);
164				break;
165			default:
166				terminate("%s: Unknown elf kind %d\n",
167				    memname, elf_kind(melf));
168			}
169
170			free(memname);
171		}
172
173		cmd = elf_next(melf);
174		(void) elf_end(melf);
175		secnum++;
176
177		if (rc < 0)
178			return (rc);
179		else
180			found += rc;
181	}
182
183	return (found);
184}
185
186static int
187read_ctf_common(char *file, char *label, read_cb_f *func, void *arg,
188    int require_ctf)
189{
190	Elf *elf;
191	int found = 0;
192	int fd;
193
194	debug(3, "Reading %s (label %s)\n", file, (label ? label : "NONE"));
195
196	(void) elf_version(EV_CURRENT);
197
198	if ((fd = open(file, O_RDONLY)) < 0)
199		terminate("%s: Cannot open for reading", file);
200	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
201		elfterminate(file, "Cannot read");
202
203	switch (elf_kind(elf)) {
204	case ELF_K_AR:
205		found = read_archive(fd, elf, file, label,
206		    func, arg, require_ctf);
207		break;
208
209	case ELF_K_ELF:
210#if defined(__APPLE__)
211	case ELF_K_MACHO: /* Underlying file is Mach-o */
212#endif /* __APPLE__ */
213		found = read_file(elf, file, label,
214		    func, arg, require_ctf);
215		break;
216
217	default:
218		terminate("%s: Unknown elf kind %d\n", file, elf_kind(elf));
219	}
220
221	(void) elf_end(elf);
222	(void) close(fd);
223
224	return (found);
225}
226
227/*ARGSUSED*/
228int
229read_ctf_save_cb(tdata_t *td, char *name, void *retp)
230{
231	tdata_t **tdp = retp;
232
233	*tdp = td;
234
235	return (1);
236}
237
238int
239read_ctf(char **files, int n, char *label, read_cb_f *func, void *private,
240    int require_ctf)
241{
242	int found;
243	int i, rc;
244
245	for (i = 0, found = 0; i < n; i++) {
246		if ((rc = read_ctf_common(files[i], label, func,
247		    private, require_ctf)) < 0)
248			return (rc);
249		found += rc;
250	}
251
252	return (found);
253}
254
255static int
256count_archive(int fd, Elf *elf, char *file)
257{
258	Elf *melf;
259	Elf_Cmd cmd = ELF_C_READ;
260	Elf_Arhdr *arh;
261	int nfiles = 0, err = 0;
262
263	while ((melf = elf_begin(fd, cmd, elf)) != NULL) {
264		if ((arh = elf_getarhdr(melf)) == NULL) {
265			warning("Can't process input archive %s\n",
266			    file);
267			err++;
268		}
269
270		if (*arh->ar_name != '/')
271			nfiles++;
272
273		cmd = elf_next(melf);
274		(void) elf_end(melf);
275	}
276
277	if (err > 0)
278		return (-1);
279
280	return (nfiles);
281}
282
283int
284count_files(char **files, int n)
285{
286	int nfiles = 0, err = 0;
287	Elf *elf;
288	int fd, rc, i;
289
290	(void) elf_version(EV_CURRENT);
291
292	for (i = 0; i < n; i++) {
293		char *file = files[i];
294
295		if ((fd = open(file, O_RDONLY)) < 0) {
296			warning("Can't read input file %s", file);
297			err++;
298			continue;
299		}
300
301		if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
302			warning("Can't open input file %s: %s\n", file,
303			    elf_errmsg(-1));
304			err++;
305			(void) close(fd);
306			continue;
307		}
308
309		switch (elf_kind(elf)) {
310		case ELF_K_AR:
311			if ((rc = count_archive(fd, elf, file)) < 0)
312				err++;
313			else
314				nfiles += rc;
315			break;
316		case ELF_K_ELF:
317#if defined(__APPLE__)
318		case ELF_K_MACHO: /* Underlying file is Mach-o */
319#endif /* __APPLE__ */
320			nfiles++;
321			break;
322		default:
323			warning("Input file %s is corrupt\n", file);
324			err++;
325		}
326
327		(void) elf_end(elf);
328		(void) close(fd);
329	}
330
331	if (err > 0)
332		return (-1);
333
334	debug(2, "Found %d files in %d input files\n", nfiles, n);
335
336	return (nfiles);
337}
338
339struct symit_data {
340	GElf_Shdr si_shdr;
341	Elf_Data *si_symd;
342	Elf_Data *si_strd;
343	GElf_Sym si_cursym;
344	char *si_curname;
345	char *si_curfile;
346	int si_nument;
347	int si_next;
348};
349
350symit_data_t *
351symit_new(Elf *elf, const char *file)
352{
353	symit_data_t *si;
354	Elf_Scn *scn;
355	int symtabidx;
356
357	if ((symtabidx = findelfsecidx(elf, file, ".symtab")) < 0)
358		return (NULL);
359
360	si = xcalloc(sizeof (symit_data_t));
361
362	if ((scn = elf_getscn(elf, symtabidx)) == NULL ||
363	    gelf_getshdr(scn, &si->si_shdr) == NULL ||
364	    (si->si_symd = elf_getdata(scn, NULL)) == NULL)
365		elfterminate(file, "Cannot read .symtab");
366
367#if !defined(__APPLE__)
368	if ((scn = elf_getscn(elf, si->si_shdr.sh_link)) == NULL ||
369	    (si->si_strd = elf_getdata(scn, NULL)) == NULL)
370		elfterminate(file, "Cannot read strings for .symtab");
371#else
372	if (SHN_MACHO != si->si_shdr.sh_link && SHN_MACHO_64 != si->si_shdr.sh_link) {
373		if ((scn = elf_getscn(elf, si->si_shdr.sh_link)) == NULL ||
374		    (si->si_strd = elf_getdata(scn, NULL)) == NULL)
375			elfterminate(file, "Cannot read strings for .symtab");
376	} else {
377		/* Underlying file is Mach-o */
378		int dir_idx;
379
380		if ((dir_idx = findelfsecidx(elf, file, ".dir_str_table")) < 0 ||
381		    (scn = elf_getscn(elf, dir_idx)) == NULL ||
382		    (si->si_strd = elf_getdata(scn, NULL)) == NULL)
383			elfterminate(file, "Cannot read strings for .dir_str_table");
384	}
385#endif /* __APPLE__ */
386	si->si_nument = si->si_shdr.sh_size / si->si_shdr.sh_entsize;
387
388	return (si);
389}
390
391void
392symit_free(symit_data_t *si)
393{
394	free(si);
395}
396
397void
398symit_reset(symit_data_t *si)
399{
400	si->si_next = 0;
401}
402
403char *
404symit_curfile(symit_data_t *si)
405{
406	return (si->si_curfile);
407}
408
409#if defined(__APPLE__)
410#include <mach-o/loader.h>
411#include <mach-o/nlist.h>
412
413static GElf_Sym *
414gelf_getsym_macho(Elf_Data * data, int ndx, GElf_Sym * sym, const char *base)
415{
416	const struct nlist *nsym = (const struct nlist *)(data->d_buf);
417	const char *name;
418	char *tmp;
419
420	nsym += ndx;
421	name = base + nsym->n_un.n_strx;
422
423	if (0 == nsym->n_un.n_strx) // iff a null, "", name.
424		name = "null name"; // return NULL;
425
426	if ('_' == name[0])
427		name++; // Lop off omnipresent underscore to match DWARF convention
428
429	sym->st_name = (GElf_Sxword)(name - base);
430	sym->st_value = nsym->n_value;
431	sym->st_size = 0;
432	sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_NOTYPE));
433	sym->st_other = 0;
434	sym->st_shndx = SHN_MACHO; /* Mark underlying file as Mach-o */
435
436	if (nsym->n_type & N_STAB) { /* Detect C++ methods */
437
438		switch(nsym->n_type) {
439		case N_FUN:
440			sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_FUNC));
441			break;
442		case N_GSYM:
443			sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_OBJECT));
444			break;
445		default:
446			break;
447		}
448
449	} else if ((N_ABS | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT)) ||
450		(N_SECT | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT))) {
451
452		sym->st_info = GELF_ST_INFO((STB_GLOBAL), (nsym->n_desc));
453	} else if ((N_UNDF | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT)) &&
454				nsym->n_sect == NO_SECT) {
455		sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_OBJECT)); /* Common */
456	}
457
458	return sym;
459}
460
461static GElf_Sym *
462gelf_getsym_macho_64(Elf_Data * data, int ndx, GElf_Sym * sym, const char *base)
463{
464	const struct nlist_64 *nsym = (const struct nlist_64 *)(data->d_buf);
465	const char *name;
466	char *tmp;
467
468	nsym += ndx;
469	name = base + nsym->n_un.n_strx;
470
471	if (0 == nsym->n_un.n_strx) // iff a null, "", name.
472		name = "null name"; // return NULL;
473
474	if ('_' == name[0])
475		name++; // Lop off omnipresent underscore to match DWARF convention
476
477	sym->st_name = (GElf_Sxword)(name - base);
478	sym->st_value = nsym->n_value;
479	sym->st_size = 0;
480	sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_NOTYPE));
481	sym->st_other = 0;
482	sym->st_shndx = SHN_MACHO_64; /* Mark underlying file as Mach-o 64 */
483
484	if (nsym->n_type & N_STAB) { /* Detect C++ methods */
485
486		switch(nsym->n_type) {
487		case N_FUN:
488			sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_FUNC));
489			break;
490		case N_GSYM:
491			sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_OBJECT));
492			break;
493		default:
494			break;
495		}
496
497	} else if ((N_ABS | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT)) ||
498		(N_SECT | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT))) {
499
500		sym->st_info = GELF_ST_INFO((STB_GLOBAL), (nsym->n_desc));
501	} else if ((N_UNDF | N_EXT) == (nsym->n_type & (N_TYPE | N_EXT)) &&
502				nsym->n_sect == NO_SECT) {
503		sym->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_OBJECT)); /* Common */
504	}
505
506	return sym;
507}
508
509#endif /* __APPLE__ */
510
511GElf_Sym *
512symit_next(symit_data_t *si, int type)
513{
514	GElf_Sym sym;
515	int check_sym = (type == STT_OBJECT || type == STT_FUNC);
516
517	for (; si->si_next < si->si_nument; si->si_next++) {
518#if !defined(__APPLE__)
519		gelf_getsym(si->si_symd, si->si_next, &si->si_cursym);
520		gelf_getsym(si->si_symd, si->si_next, &sym);
521#else
522		if (si->si_shdr.sh_link == SHN_MACHO) { /* Underlying file is Mach-o */
523			gelf_getsym_macho(si->si_symd, si->si_next, &si->si_cursym, (const char *)(si->si_strd->d_buf));
524			gelf_getsym_macho(si->si_symd, si->si_next, &sym, (const char *)(si->si_strd->d_buf));
525		} else if (si->si_shdr.sh_link == SHN_MACHO_64) { /* Underlying file is Mach-o 64 */
526			gelf_getsym_macho_64(si->si_symd, si->si_next, &si->si_cursym, (const char *)(si->si_strd->d_buf));
527			gelf_getsym_macho_64(si->si_symd, si->si_next, &sym, (const char *)(si->si_strd->d_buf));
528		} else {
529			gelf_getsym(si->si_symd, si->si_next, &si->si_cursym);
530			gelf_getsym(si->si_symd, si->si_next, &sym);
531		}
532#endif /* __APPLE__ */
533		si->si_curname = (caddr_t)si->si_strd->d_buf + sym.st_name;
534
535		if (GELF_ST_TYPE(sym.st_info) == STT_FILE)
536			si->si_curfile = si->si_curname;
537
538		if (GELF_ST_TYPE(sym.st_info) != type ||
539		    sym.st_shndx == SHN_UNDEF)
540			continue;
541
542		if (check_sym && ignore_symbol(&sym, si->si_curname))
543			continue;
544
545		si->si_next++;
546
547		return (&si->si_cursym);
548	}
549
550	return (NULL);
551}
552
553char *
554symit_name(symit_data_t *si)
555{
556	return (si->si_curname);
557}
558