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