1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2019 Joyent, Inc.
14  */
15 
16 /*
17  * Main conversion entry points. This has been designed such that there can be
18  * any number of different conversion backends. Currently we only have one that
19  * understands DWARFv2 (and bits of DWARFv4). Each backend should be placed in
20  * the ctf_converters list and each will be tried in turn.
21  */
22 
23 #include <libctf_impl.h>
24 #include <gelf.h>
25 
26 ctf_convert_f ctf_converters[] = {
27 	ctf_dwarf_convert
28 };
29 
30 #define	NCONVERTS	(sizeof (ctf_converters) / sizeof (ctf_convert_f))
31 
32 typedef enum ctf_convert_source {
33 	CTFCONV_SOURCE_NONE = 0x0,
34 	CTFCONV_SOURCE_UNKNOWN = 0x01,
35 	CTFCONV_SOURCE_C = 0x02,
36 	CTFCONV_SOURCE_S = 0x04
37 } ctf_convert_source_t;
38 
39 static void
40 ctf_convert_ftypes(Elf *elf, ctf_convert_source_t *types)
41 {
42 	int i;
43 	Elf_Scn *scn = NULL, *strscn;
44 	*types = CTFCONV_SOURCE_NONE;
45 	GElf_Shdr shdr;
46 	Elf_Data *data, *strdata;
47 
48 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
49 
50 		if (gelf_getshdr(scn, &shdr) == NULL)
51 			return;
52 
53 		if (shdr.sh_type == SHT_SYMTAB)
54 			break;
55 	}
56 
57 	if (scn == NULL)
58 		return;
59 
60 	if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL)
61 		return;
62 
63 	if ((data = elf_getdata(scn, NULL)) == NULL)
64 		return;
65 
66 	if ((strdata = elf_getdata(strscn, NULL)) == NULL)
67 		return;
68 
69 	for (i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
70 		GElf_Sym sym;
71 		const char *file;
72 		size_t len;
73 
74 		if (gelf_getsym(data, i, &sym) == NULL)
75 			return;
76 
77 		if (GELF_ST_TYPE(sym.st_info) != STT_FILE)
78 			continue;
79 
80 		file = (const char *)((uintptr_t)strdata->d_buf + sym.st_name);
81 		len = strlen(file);
82 		if (len < 2 || file[len - 2] != '.') {
83 			*types |= CTFCONV_SOURCE_UNKNOWN;
84 			continue;
85 		}
86 
87 		switch (file[len - 1]) {
88 		case 'c':
89 			*types |= CTFCONV_SOURCE_C;
90 			break;
91 		case 'h':
92 			/* We traditionally ignore header files... */
93 			break;
94 		case 's':
95 			*types |= CTFCONV_SOURCE_S;
96 			break;
97 		default:
98 			*types |= CTFCONV_SOURCE_UNKNOWN;
99 			break;
100 		}
101 	}
102 }
103 
104 static ctf_file_t *
105 ctf_elfconvert(int fd, Elf *elf, const char *label, uint_t nthrs, uint_t flags,
106     int *errp, char *errbuf, size_t errlen)
107 {
108 	int err, i;
109 	ctf_file_t *fp = NULL;
110 	boolean_t notsup = B_TRUE;
111 	ctf_convert_source_t type;
112 
113 	if (errp == NULL)
114 		errp = &err;
115 
116 	if (elf == NULL) {
117 		*errp = EINVAL;
118 		return (NULL);
119 	}
120 
121 	if (flags & ~CTF_CONVERT_F_IGNNONC) {
122 		*errp = EINVAL;
123 		return (NULL);
124 	}
125 
126 	if (elf_kind(elf) != ELF_K_ELF) {
127 		*errp = ECTF_FMT;
128 		return (NULL);
129 	}
130 
131 	ctf_convert_ftypes(elf, &type);
132 	ctf_dprintf("got types: %d\n", type);
133 	if (flags & CTF_CONVERT_F_IGNNONC) {
134 		if (type == CTFCONV_SOURCE_NONE ||
135 		    (type & CTFCONV_SOURCE_UNKNOWN)) {
136 			*errp = ECTF_CONVNOCSRC;
137 			return (NULL);
138 		}
139 	}
140 
141 	for (i = 0; i < NCONVERTS; i++) {
142 		ctf_conv_status_t cs;
143 
144 		fp = NULL;
145 		cs = ctf_converters[i](fd, elf, nthrs, errp, &fp, errbuf,
146 		    errlen);
147 		if (cs == CTF_CONV_SUCCESS) {
148 			notsup = B_FALSE;
149 			break;
150 		}
151 		if (cs == CTF_CONV_ERROR) {
152 			fp = NULL;
153 			notsup = B_FALSE;
154 			break;
155 		}
156 	}
157 
158 	if (notsup == B_TRUE) {
159 		if ((flags & CTF_CONVERT_F_IGNNONC) != 0 &&
160 		    (type & CTFCONV_SOURCE_C) == 0) {
161 			*errp = ECTF_CONVNOCSRC;
162 			return (NULL);
163 		}
164 		*errp = ECTF_NOCONVBKEND;
165 		return (NULL);
166 	}
167 
168 	/*
169 	 * Succsesful conversion.
170 	 */
171 	if (fp != NULL && label != NULL) {
172 		if (ctf_add_label(fp, label, fp->ctf_typemax, 0) == CTF_ERR) {
173 			*errp = ctf_errno(fp);
174 			ctf_close(fp);
175 			return (NULL);
176 		}
177 		if (ctf_update(fp) == CTF_ERR) {
178 			*errp = ctf_errno(fp);
179 			ctf_close(fp);
180 			return (NULL);
181 		}
182 	}
183 
184 	return (fp);
185 }
186 
187 ctf_file_t *
188 ctf_fdconvert(int fd, const char *label, uint_t nthrs, uint_t flags, int *errp,
189     char *errbuf, size_t errlen)
190 {
191 	int err;
192 	Elf *elf;
193 	ctf_file_t *fp;
194 
195 	if (errp == NULL)
196 		errp = &err;
197 
198 	elf = elf_begin(fd, ELF_C_READ, NULL);
199 	if (elf == NULL) {
200 		*errp = ECTF_FMT;
201 		return (NULL);
202 	}
203 
204 	fp = ctf_elfconvert(fd, elf, label, nthrs, flags, errp, errbuf, errlen);
205 
206 	(void) elf_end(elf);
207 	return (fp);
208 }
209