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  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
15  */
16 
17 /*
18  * Main conversion entry points. This has been designed such that there can be
19  * any number of different conversion backends. Currently we only have one that
20  * understands DWARFv2 and DWARFv4. Each backend should be placed in
21  * the ctf_converters list and each will be tried in turn.
22  */
23 
24 #include <libctf_impl.h>
25 #include <assert.h>
26 #include <gelf.h>
27 
28 static ctf_convert_f ctf_converters[] = {
29 	ctf_dwarf_convert
30 };
31 
32 #define	NCONVERTS	(sizeof (ctf_converters) / sizeof (ctf_convert_f))
33 
34 ctf_hsc_ret_t
35 ctf_has_c_source(Elf *elf, char *errmsg, size_t errlen)
36 {
37 	ctf_hsc_ret_t ret = CHR_NO_C_SOURCE;
38 	Elf_Scn *scn, *strscn;
39 	Elf_Data *data, *strdata;
40 	GElf_Shdr shdr;
41 	ulong_t i;
42 
43 	scn = NULL;
44 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
45 		if (gelf_getshdr(scn, &shdr) == NULL) {
46 			(void) snprintf(errmsg, errlen,
47 			    "failed to get section header: %s",
48 			    elf_errmsg(elf_errno()));
49 			return (CHR_ERROR);
50 		}
51 
52 		if (shdr.sh_type == SHT_SYMTAB)
53 			break;
54 	}
55 
56 	if (scn == NULL) {
57 		ctf_dprintf("Could not find symbol table section\n");
58 		return (CHR_NO_C_SOURCE);
59 	}
60 
61 	if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL) {
62 		(void) snprintf(errmsg, errlen, "failed to get str section: %s",
63 		    elf_errmsg(elf_errno()));
64 		return (CHR_ERROR);
65 	}
66 
67 	if ((data = elf_getdata(scn, NULL)) == NULL) {
68 		(void) snprintf(errmsg, errlen, "failed to read section: %s",
69 		    elf_errmsg(elf_errno()));
70 		return (CHR_ERROR);
71 	}
72 
73 	if ((strdata = elf_getdata(strscn, NULL)) == NULL) {
74 		(void) snprintf(errmsg, errlen,
75 		    "failed to read string table: %s", elf_errmsg(elf_errno()));
76 		return (CHR_ERROR);
77 	}
78 
79 	ctf_dprintf("Walking string table looking for .c files\n");
80 
81 	for (i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
82 		GElf_Sym sym;
83 		const char *file;
84 		size_t len;
85 
86 		if (gelf_getsym(data, i, &sym) == NULL) {
87 			(void) snprintf(errmsg, errlen,
88 			    "failed to read sym %lu: %s",
89 			    i, elf_errmsg(elf_errno()));
90 			return (CHR_ERROR);
91 		}
92 
93 		file = (const char *)((uintptr_t)strdata->d_buf + sym.st_name);
94 
95 		if (GELF_ST_TYPE(sym.st_info) != STT_FILE) {
96 			ctf_dprintf("'%s'\n", file);
97 			continue;
98 		}
99 
100 		ctf_dprintf("'%s'; is a file\n", file);
101 
102 		len = strlen(file);
103 		if (len >= 2 && strncmp(".c", &file[len - 2], 2) == 0) {
104 			ret = CHR_HAS_C_SOURCE;
105 			ctf_dprintf("Found .c file - '%s'\n", file);
106 			break;
107 		}
108 	}
109 
110 	return (ret);
111 }
112 
113 static ctf_file_t *
114 ctf_elfconvert(ctf_convert_t *cch, int fd, Elf *elf, int *errp, char *errbuf,
115     size_t errlen)
116 {
117 	int err, i;
118 	ctf_file_t *fp = NULL;
119 	boolean_t no_c_src = B_FALSE;
120 
121 	if (errp == NULL)
122 		errp = &err;
123 
124 	if (elf == NULL) {
125 		*errp = EINVAL;
126 		return (NULL);
127 	}
128 
129 	if (elf_kind(elf) != ELF_K_ELF) {
130 		*errp = ECTF_FMT;
131 		return (NULL);
132 	}
133 
134 	switch (ctf_has_c_source(elf, errbuf, errlen)) {
135 	case CHR_ERROR:
136 		*errp = ECTF_ELF;
137 		return (NULL);
138 
139 	case CHR_NO_C_SOURCE:
140 		if ((cch->cch_flags & CTF_FORCE_CONVERSION) == 0) {
141 			*errp = ECTF_CONVNOCSRC;
142 			return (NULL);
143 		}
144 		no_c_src = B_TRUE;
145 		break;
146 
147 	default:
148 		break;
149 	}
150 
151 	for (i = 0; i < NCONVERTS; i++) {
152 		fp = NULL;
153 		err = ctf_converters[i](cch, fd, elf, &fp, errbuf, errlen);
154 
155 		if (err != ECTF_CONVNODEBUG)
156 			break;
157 	}
158 
159 	if (err != 0) {
160 		assert(fp == NULL);
161 		/*
162 		 * If no C source was found but we attempted conversion anyway
163 		 * due to CTF_FORCE_CONVERSION, and none of the converters
164 		 * was able to process the object, return ECTF_CONVNOCSRC.
165 		 */
166 		if (no_c_src && err == ECTF_CONVNODEBUG)
167 			*errp = ECTF_CONVNOCSRC;
168 		else
169 			*errp = err;
170 		return (NULL);
171 	}
172 
173 	if (cch->cch_label != NULL) {
174 		if (ctf_add_label(fp, cch->cch_label, fp->ctf_typemax, 0) ==
175 		    CTF_ERR) {
176 			*errp = ctf_errno(fp);
177 			ctf_close(fp);
178 			return (NULL);
179 		}
180 		if (ctf_update(fp) == CTF_ERR) {
181 			*errp = ctf_errno(fp);
182 			ctf_close(fp);
183 			return (NULL);
184 		}
185 	}
186 
187 	return (fp);
188 }
189 
190 ctf_convert_t *
191 ctf_convert_init(int *errp)
192 {
193 	struct ctf_convert_handle *cch;
194 	int err;
195 
196 	if (errp == NULL)
197 		errp = &err;
198 	*errp = 0;
199 
200 	cch = ctf_alloc(sizeof (struct ctf_convert_handle));
201 	if (cch == NULL) {
202 		*errp = ENOMEM;
203 		return (NULL);
204 	}
205 
206 	cch->cch_label = NULL;
207 	cch->cch_flags = 0;
208 	cch->cch_nthreads = CTF_CONVERT_DEFAULT_NTHREADS;
209 	cch->cch_batchsize = CTF_CONVERT_DEFAULT_BATCHSIZE;
210 	cch->cch_warncb = NULL;
211 	cch->cch_warncb_arg = NULL;
212 
213 	return (cch);
214 }
215 
216 void
217 ctf_convert_fini(ctf_convert_t *cch)
218 {
219 	if (cch->cch_label != NULL) {
220 		size_t len = strlen(cch->cch_label) + 1;
221 		ctf_free(cch->cch_label, len);
222 	}
223 	ctf_free(cch, sizeof (struct ctf_convert_handle));
224 }
225 
226 int
227 ctf_convert_set_nthreads(ctf_convert_t *cch, uint_t nthrs)
228 {
229 	if (nthrs == 0)
230 		return (EINVAL);
231 	cch->cch_nthreads = nthrs;
232 	return (0);
233 }
234 
235 int
236 ctf_convert_set_batchsize(ctf_convert_t *cch, uint_t bsize)
237 {
238 	if (bsize == 0)
239 		return (EINVAL);
240 	cch->cch_batchsize = bsize;
241 	return (0);
242 }
243 
244 int
245 ctf_convert_set_flags(ctf_convert_t *cch, uint_t flags)
246 {
247 	if ((flags & ~CTF_CONVERT_ALL_FLAGS) != 0)
248 		return (EINVAL);
249 	cch->cch_flags = flags;
250 	return (0);
251 }
252 
253 int
254 ctf_convert_set_label(ctf_convert_t *cch, const char *label)
255 {
256 	char *dup;
257 
258 	if (label == NULL)
259 		return (EINVAL);
260 
261 	dup = ctf_strdup(label);
262 	if (dup == NULL)
263 		return (ENOMEM);
264 
265 	if (cch->cch_label != NULL) {
266 		size_t len = strlen(cch->cch_label) + 1;
267 		ctf_free(cch->cch_label, len);
268 	}
269 
270 	cch->cch_label = dup;
271 	return (0);
272 }
273 
274 int
275 ctf_convert_set_warncb(ctf_convert_t *cch, ctf_convert_warn_f cb, void *arg)
276 {
277 	cch->cch_warncb = cb;
278 	cch->cch_warncb_arg = arg;
279 	return (0);
280 }
281 
282 ctf_file_t *
283 ctf_fdconvert(ctf_convert_t *cch, int fd, int *errp,
284     char *errbuf, size_t errlen)
285 {
286 	int err;
287 	Elf *elf;
288 	ctf_file_t *fp;
289 
290 	if (errp == NULL)
291 		errp = &err;
292 
293 	elf = elf_begin(fd, ELF_C_READ, NULL);
294 	if (elf == NULL) {
295 		*errp = ECTF_FMT;
296 		return (NULL);
297 	}
298 
299 	fp = ctf_elfconvert(cch, fd, elf, errp, errbuf, errlen);
300 
301 	(void) elf_end(elf);
302 	return (fp);
303 }
304