1bc1f688bSRobert Mustacchi /*
2bc1f688bSRobert Mustacchi  * This file and its contents are supplied under the terms of the
3bc1f688bSRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4bc1f688bSRobert Mustacchi  * You may only use this file in accordance with the terms of version
5bc1f688bSRobert Mustacchi  * 1.0 of the CDDL.
6bc1f688bSRobert Mustacchi  *
7bc1f688bSRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8bc1f688bSRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9bc1f688bSRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10bc1f688bSRobert Mustacchi  */
11bc1f688bSRobert Mustacchi 
12bc1f688bSRobert Mustacchi /*
1337e82d12SRobert Mustacchi  * Copyright 2019 Joyent, Inc.
14dd442252SAndy Fiddaman  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
15bc1f688bSRobert Mustacchi  */
16bc1f688bSRobert Mustacchi 
17bc1f688bSRobert Mustacchi /*
18bc1f688bSRobert Mustacchi  * Main conversion entry points. This has been designed such that there can be
19bc1f688bSRobert Mustacchi  * any number of different conversion backends. Currently we only have one that
20dd442252SAndy Fiddaman  * understands DWARFv2 and DWARFv4. Each backend should be placed in
21bc1f688bSRobert Mustacchi  * the ctf_converters list and each will be tried in turn.
22bc1f688bSRobert Mustacchi  */
23bc1f688bSRobert Mustacchi 
24bc1f688bSRobert Mustacchi #include <libctf_impl.h>
253eca6103SJohn Levon #include <assert.h>
26bc1f688bSRobert Mustacchi #include <gelf.h>
2788a08813SAndy Fiddaman #include <sys/list.h>
28bc1f688bSRobert Mustacchi 
29dd442252SAndy Fiddaman static ctf_convert_f ctf_converters[] = {
30bc1f688bSRobert Mustacchi 	ctf_dwarf_convert
31bc1f688bSRobert Mustacchi };
32bc1f688bSRobert Mustacchi 
33bc1f688bSRobert Mustacchi #define	NCONVERTS	(sizeof (ctf_converters) / sizeof (ctf_convert_f))
34bc1f688bSRobert Mustacchi 
353eca6103SJohn Levon ctf_hsc_ret_t
ctf_has_c_source(Elf * elf,char * errmsg,size_t errlen)363eca6103SJohn Levon ctf_has_c_source(Elf *elf, char *errmsg, size_t errlen)
37bc1f688bSRobert Mustacchi {
383eca6103SJohn Levon 	ctf_hsc_ret_t ret = CHR_NO_C_SOURCE;
393eca6103SJohn Levon 	Elf_Scn *scn, *strscn;
40bc1f688bSRobert Mustacchi 	Elf_Data *data, *strdata;
413eca6103SJohn Levon 	GElf_Shdr shdr;
423eca6103SJohn Levon 	ulong_t i;
43bc1f688bSRobert Mustacchi 
443eca6103SJohn Levon 	scn = NULL;
45bc1f688bSRobert Mustacchi 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
463eca6103SJohn Levon 		if (gelf_getshdr(scn, &shdr) == NULL) {
473eca6103SJohn Levon 			(void) snprintf(errmsg, errlen,
48*16d40492SRobert Mustacchi 			    "failed to get section header: %s\n",
493eca6103SJohn Levon 			    elf_errmsg(elf_errno()));
503eca6103SJohn Levon 			return (CHR_ERROR);
513eca6103SJohn Levon 		}
52bc1f688bSRobert Mustacchi 
53bc1f688bSRobert Mustacchi 		if (shdr.sh_type == SHT_SYMTAB)
54bc1f688bSRobert Mustacchi 			break;
55bc1f688bSRobert Mustacchi 	}
56bc1f688bSRobert Mustacchi 
5773197b54SAndy Fiddaman 	if (scn == NULL) {
5873197b54SAndy Fiddaman 		ctf_dprintf("Could not find symbol table section\n");
593eca6103SJohn Levon 		return (CHR_NO_C_SOURCE);
6073197b54SAndy Fiddaman 	}
61bc1f688bSRobert Mustacchi 
623eca6103SJohn Levon 	if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL) {
63*16d40492SRobert Mustacchi 		(void) snprintf(errmsg, errlen, "failed to get str section: "
64*16d40492SRobert Mustacchi 		    "%s\n", elf_errmsg(elf_errno()));
653eca6103SJohn Levon 		return (CHR_ERROR);
663eca6103SJohn Levon 	}
67bc1f688bSRobert Mustacchi 
683eca6103SJohn Levon 	if ((data = elf_getdata(scn, NULL)) == NULL) {
69*16d40492SRobert Mustacchi 		(void) snprintf(errmsg, errlen, "failed to read section: %s\n",
703eca6103SJohn Levon 		    elf_errmsg(elf_errno()));
713eca6103SJohn Levon 		return (CHR_ERROR);
723eca6103SJohn Levon 	}
73bc1f688bSRobert Mustacchi 
743eca6103SJohn Levon 	if ((strdata = elf_getdata(strscn, NULL)) == NULL) {
753eca6103SJohn Levon 		(void) snprintf(errmsg, errlen,
76*16d40492SRobert Mustacchi 		    "failed to read string table: %s\n",
77*16d40492SRobert Mustacchi 		    elf_errmsg(elf_errno()));
783eca6103SJohn Levon 		return (CHR_ERROR);
793eca6103SJohn Levon 	}
80bc1f688bSRobert Mustacchi 
8173197b54SAndy Fiddaman 	ctf_dprintf("Walking string table looking for .c files\n");
8273197b54SAndy Fiddaman 
83bc1f688bSRobert Mustacchi 	for (i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
84bc1f688bSRobert Mustacchi 		GElf_Sym sym;
85bc1f688bSRobert Mustacchi 		const char *file;
86bc1f688bSRobert Mustacchi 		size_t len;
87bc1f688bSRobert Mustacchi 
883eca6103SJohn Levon 		if (gelf_getsym(data, i, &sym) == NULL) {
893eca6103SJohn Levon 			(void) snprintf(errmsg, errlen,
90*16d40492SRobert Mustacchi 			    "failed to read sym %lu: %s\n",
913eca6103SJohn Levon 			    i, elf_errmsg(elf_errno()));
923eca6103SJohn Levon 			return (CHR_ERROR);
933eca6103SJohn Levon 		}
94bc1f688bSRobert Mustacchi 
9573197b54SAndy Fiddaman 		file = (const char *)((uintptr_t)strdata->d_buf + sym.st_name);
9673197b54SAndy Fiddaman 
9773197b54SAndy Fiddaman 		if (GELF_ST_TYPE(sym.st_info) != STT_FILE) {
9873197b54SAndy Fiddaman 			ctf_dprintf("'%s'\n", file);
99bc1f688bSRobert Mustacchi 			continue;
10073197b54SAndy Fiddaman 		}
10173197b54SAndy Fiddaman 
10273197b54SAndy Fiddaman 		ctf_dprintf("'%s'; is a file\n", file);
103bc1f688bSRobert Mustacchi 
104bc1f688bSRobert Mustacchi 		len = strlen(file);
1053eca6103SJohn Levon 		if (len >= 2 && strncmp(".c", &file[len - 2], 2) == 0) {
1063eca6103SJohn Levon 			ret = CHR_HAS_C_SOURCE;
10773197b54SAndy Fiddaman 			ctf_dprintf("Found .c file - '%s'\n", file);
108bc1f688bSRobert Mustacchi 			break;
109bc1f688bSRobert Mustacchi 		}
110bc1f688bSRobert Mustacchi 	}
1113eca6103SJohn Levon 
1123eca6103SJohn Levon 	return (ret);
113bc1f688bSRobert Mustacchi }
114bc1f688bSRobert Mustacchi 
115bc1f688bSRobert Mustacchi static ctf_file_t *
ctf_elfconvert(ctf_convert_t * cch,int fd,Elf * elf,int * errp,char * errbuf,size_t errlen)116dd442252SAndy Fiddaman ctf_elfconvert(ctf_convert_t *cch, int fd, Elf *elf, int *errp, char *errbuf,
117dd442252SAndy Fiddaman     size_t errlen)
118bc1f688bSRobert Mustacchi {
119bc1f688bSRobert Mustacchi 	int err, i;
120bc1f688bSRobert Mustacchi 	ctf_file_t *fp = NULL;
12173197b54SAndy Fiddaman 	boolean_t no_c_src = B_FALSE;
122bc1f688bSRobert Mustacchi 
123bc1f688bSRobert Mustacchi 	if (errp == NULL)
124bc1f688bSRobert Mustacchi 		errp = &err;
125bc1f688bSRobert Mustacchi 
126bc1f688bSRobert Mustacchi 	if (elf == NULL) {
127bc1f688bSRobert Mustacchi 		*errp = EINVAL;
128bc1f688bSRobert Mustacchi 		return (NULL);
129bc1f688bSRobert Mustacchi 	}
130bc1f688bSRobert Mustacchi 
131bc1f688bSRobert Mustacchi 	if (elf_kind(elf) != ELF_K_ELF) {
132bc1f688bSRobert Mustacchi 		*errp = ECTF_FMT;
133bc1f688bSRobert Mustacchi 		return (NULL);
134bc1f688bSRobert Mustacchi 	}
135bc1f688bSRobert Mustacchi 
1363eca6103SJohn Levon 	switch (ctf_has_c_source(elf, errbuf, errlen)) {
1373eca6103SJohn Levon 	case CHR_ERROR:
1383eca6103SJohn Levon 		*errp = ECTF_ELF;
1393eca6103SJohn Levon 		return (NULL);
1403eca6103SJohn Levon 
1413eca6103SJohn Levon 	case CHR_NO_C_SOURCE:
14273197b54SAndy Fiddaman 		if ((cch->cch_flags & CTF_FORCE_CONVERSION) == 0) {
14373197b54SAndy Fiddaman 			*errp = ECTF_CONVNOCSRC;
14473197b54SAndy Fiddaman 			return (NULL);
14573197b54SAndy Fiddaman 		}
14673197b54SAndy Fiddaman 		no_c_src = B_TRUE;
14773197b54SAndy Fiddaman 		break;
1483eca6103SJohn Levon 
1493eca6103SJohn Levon 	default:
1503eca6103SJohn Levon 		break;
151bc1f688bSRobert Mustacchi 	}
152bc1f688bSRobert Mustacchi 
153bc1f688bSRobert Mustacchi 	for (i = 0; i < NCONVERTS; i++) {
154bc1f688bSRobert Mustacchi 		fp = NULL;
155dd442252SAndy Fiddaman 		err = ctf_converters[i](cch, fd, elf, &fp, errbuf, errlen);
1563eca6103SJohn Levon 
1573eca6103SJohn Levon 		if (err != ECTF_CONVNODEBUG)
158bc1f688bSRobert Mustacchi 			break;
159bc1f688bSRobert Mustacchi 	}
160bc1f688bSRobert Mustacchi 
1613eca6103SJohn Levon 	if (err != 0) {
1623eca6103SJohn Levon 		assert(fp == NULL);
16373197b54SAndy Fiddaman 		/*
16473197b54SAndy Fiddaman 		 * If no C source was found but we attempted conversion anyway
16573197b54SAndy Fiddaman 		 * due to CTF_FORCE_CONVERSION, and none of the converters
16673197b54SAndy Fiddaman 		 * was able to process the object, return ECTF_CONVNOCSRC.
16773197b54SAndy Fiddaman 		 */
16873197b54SAndy Fiddaman 		if (no_c_src && err == ECTF_CONVNODEBUG)
16973197b54SAndy Fiddaman 			*errp = ECTF_CONVNOCSRC;
17073197b54SAndy Fiddaman 		else
17173197b54SAndy Fiddaman 			*errp = err;
172bc1f688bSRobert Mustacchi 		return (NULL);
173bc1f688bSRobert Mustacchi 	}
174bc1f688bSRobert Mustacchi 
175dd442252SAndy Fiddaman 	if (cch->cch_label != NULL) {
176dd442252SAndy Fiddaman 		if (ctf_add_label(fp, cch->cch_label, fp->ctf_typemax, 0) ==
177dd442252SAndy Fiddaman 		    CTF_ERR) {
178bc1f688bSRobert Mustacchi 			*errp = ctf_errno(fp);
179bc1f688bSRobert Mustacchi 			ctf_close(fp);
180bc1f688bSRobert Mustacchi 			return (NULL);
181bc1f688bSRobert Mustacchi 		}
182bc1f688bSRobert Mustacchi 		if (ctf_update(fp) == CTF_ERR) {
183bc1f688bSRobert Mustacchi 			*errp = ctf_errno(fp);
184bc1f688bSRobert Mustacchi 			ctf_close(fp);
185bc1f688bSRobert Mustacchi 			return (NULL);
186bc1f688bSRobert Mustacchi 		}
187bc1f688bSRobert Mustacchi 	}
188bc1f688bSRobert Mustacchi 
189bc1f688bSRobert Mustacchi 	return (fp);
190bc1f688bSRobert Mustacchi }
191bc1f688bSRobert Mustacchi 
192dd442252SAndy Fiddaman ctf_convert_t *
ctf_convert_init(int * errp)193dd442252SAndy Fiddaman ctf_convert_init(int *errp)
194dd442252SAndy Fiddaman {
195dd442252SAndy Fiddaman 	struct ctf_convert_handle *cch;
196dd442252SAndy Fiddaman 	int err;
197dd442252SAndy Fiddaman 
198dd442252SAndy Fiddaman 	if (errp == NULL)
199dd442252SAndy Fiddaman 		errp = &err;
200dd442252SAndy Fiddaman 	*errp = 0;
201dd442252SAndy Fiddaman 
202dd442252SAndy Fiddaman 	cch = ctf_alloc(sizeof (struct ctf_convert_handle));
203dd442252SAndy Fiddaman 	if (cch == NULL) {
204dd442252SAndy Fiddaman 		*errp = ENOMEM;
205dd442252SAndy Fiddaman 		return (NULL);
206dd442252SAndy Fiddaman 	}
207dd442252SAndy Fiddaman 
208dd442252SAndy Fiddaman 	cch->cch_label = NULL;
209dd442252SAndy Fiddaman 	cch->cch_flags = 0;
210dd442252SAndy Fiddaman 	cch->cch_nthreads = CTF_CONVERT_DEFAULT_NTHREADS;
211dd442252SAndy Fiddaman 	cch->cch_batchsize = CTF_CONVERT_DEFAULT_BATCHSIZE;
212dd442252SAndy Fiddaman 	cch->cch_warncb = NULL;
213dd442252SAndy Fiddaman 	cch->cch_warncb_arg = NULL;
21488a08813SAndy Fiddaman 	list_create(&cch->cch_nodebug, sizeof (ctf_convert_filelist_t),
21588a08813SAndy Fiddaman 	    offsetof(ctf_convert_filelist_t, ccf_node));
216dd442252SAndy Fiddaman 
217dd442252SAndy Fiddaman 	return (cch);
218dd442252SAndy Fiddaman }
219dd442252SAndy Fiddaman 
22088a08813SAndy Fiddaman static void
ctf_convert_fini_filelist(ctf_convert_filelist_t * ccf)22188a08813SAndy Fiddaman ctf_convert_fini_filelist(ctf_convert_filelist_t *ccf)
22288a08813SAndy Fiddaman {
22388a08813SAndy Fiddaman 	ctf_strfree(ccf->ccf_basename);
22488a08813SAndy Fiddaman 	ctf_free(ccf, sizeof (ctf_convert_filelist_t));
22588a08813SAndy Fiddaman }
22688a08813SAndy Fiddaman 
227dd442252SAndy Fiddaman void
ctf_convert_fini(ctf_convert_t * cch)228dd442252SAndy Fiddaman ctf_convert_fini(ctf_convert_t *cch)
229dd442252SAndy Fiddaman {
23088a08813SAndy Fiddaman 	ctf_convert_filelist_t *ccf;
23188a08813SAndy Fiddaman 
23288a08813SAndy Fiddaman 	ctf_strfree(cch->cch_label);
23388a08813SAndy Fiddaman 	while ((ccf = list_remove_head(&cch->cch_nodebug)) != NULL)
23488a08813SAndy Fiddaman 		ctf_convert_fini_filelist(ccf);
23588a08813SAndy Fiddaman 	list_destroy(&cch->cch_nodebug);
23688a08813SAndy Fiddaman 
237dd442252SAndy Fiddaman 	ctf_free(cch, sizeof (struct ctf_convert_handle));
238dd442252SAndy Fiddaman }
239dd442252SAndy Fiddaman 
240dd442252SAndy Fiddaman int
ctf_convert_set_nthreads(ctf_convert_t * cch,uint_t nthrs)241dd442252SAndy Fiddaman ctf_convert_set_nthreads(ctf_convert_t *cch, uint_t nthrs)
242dd442252SAndy Fiddaman {
243dd442252SAndy Fiddaman 	if (nthrs == 0)
244dd442252SAndy Fiddaman 		return (EINVAL);
245dd442252SAndy Fiddaman 	cch->cch_nthreads = nthrs;
246dd442252SAndy Fiddaman 	return (0);
247dd442252SAndy Fiddaman }
248dd442252SAndy Fiddaman 
249dd442252SAndy Fiddaman int
ctf_convert_set_batchsize(ctf_convert_t * cch,uint_t bsize)250dd442252SAndy Fiddaman ctf_convert_set_batchsize(ctf_convert_t *cch, uint_t bsize)
251dd442252SAndy Fiddaman {
252dd442252SAndy Fiddaman 	if (bsize == 0)
253dd442252SAndy Fiddaman 		return (EINVAL);
254dd442252SAndy Fiddaman 	cch->cch_batchsize = bsize;
255dd442252SAndy Fiddaman 	return (0);
256dd442252SAndy Fiddaman }
257dd442252SAndy Fiddaman 
258dd442252SAndy Fiddaman int
ctf_convert_set_flags(ctf_convert_t * cch,ctf_convert_flag_t flags)259037765caSToomas Soome ctf_convert_set_flags(ctf_convert_t *cch, ctf_convert_flag_t flags)
260dd442252SAndy Fiddaman {
261dd442252SAndy Fiddaman 	if ((flags & ~CTF_CONVERT_ALL_FLAGS) != 0)
262dd442252SAndy Fiddaman 		return (EINVAL);
263dd442252SAndy Fiddaman 	cch->cch_flags = flags;
264dd442252SAndy Fiddaman 	return (0);
265dd442252SAndy Fiddaman }
266dd442252SAndy Fiddaman 
267dd442252SAndy Fiddaman int
ctf_convert_set_label(ctf_convert_t * cch,const char * label)268dd442252SAndy Fiddaman ctf_convert_set_label(ctf_convert_t *cch, const char *label)
269dd442252SAndy Fiddaman {
270dd442252SAndy Fiddaman 	char *dup;
271dd442252SAndy Fiddaman 
272dd442252SAndy Fiddaman 	if (label == NULL)
273dd442252SAndy Fiddaman 		return (EINVAL);
274dd442252SAndy Fiddaman 
275dd442252SAndy Fiddaman 	dup = ctf_strdup(label);
276dd442252SAndy Fiddaman 	if (dup == NULL)
277dd442252SAndy Fiddaman 		return (ENOMEM);
278dd442252SAndy Fiddaman 
27988a08813SAndy Fiddaman 	ctf_strfree(cch->cch_label);
280dd442252SAndy Fiddaman 	cch->cch_label = dup;
281dd442252SAndy Fiddaman 	return (0);
282dd442252SAndy Fiddaman }
283dd442252SAndy Fiddaman 
284dd442252SAndy Fiddaman int
ctf_convert_set_warncb(ctf_convert_t * cch,ctf_convert_warn_f cb,void * arg)285dd442252SAndy Fiddaman ctf_convert_set_warncb(ctf_convert_t *cch, ctf_convert_warn_f cb, void *arg)
286dd442252SAndy Fiddaman {
287dd442252SAndy Fiddaman 	cch->cch_warncb = cb;
288dd442252SAndy Fiddaman 	cch->cch_warncb_arg = arg;
289dd442252SAndy Fiddaman 	return (0);
290dd442252SAndy Fiddaman }
291dd442252SAndy Fiddaman 
29288a08813SAndy Fiddaman int
ctf_convert_add_ignore(ctf_convert_t * cch,const char * basename)29388a08813SAndy Fiddaman ctf_convert_add_ignore(ctf_convert_t *cch, const char *basename)
29488a08813SAndy Fiddaman {
29588a08813SAndy Fiddaman 	ctf_convert_filelist_t *ccf;
29688a08813SAndy Fiddaman 
29788a08813SAndy Fiddaman 	if (strchr(basename, '/') != NULL)
29888a08813SAndy Fiddaman 		return (EINVAL);
29988a08813SAndy Fiddaman 
30088a08813SAndy Fiddaman 	ccf = ctf_alloc(sizeof (ctf_convert_filelist_t));
30188a08813SAndy Fiddaman 	if (ccf == NULL)
30288a08813SAndy Fiddaman 		return (ENOMEM);
30388a08813SAndy Fiddaman 
30488a08813SAndy Fiddaman 	ccf->ccf_basename = ctf_strdup(basename);
30588a08813SAndy Fiddaman 	if (ccf->ccf_basename == NULL) {
30688a08813SAndy Fiddaman 		ctf_free(ccf, sizeof (ctf_convert_filelist_t));
30788a08813SAndy Fiddaman 		return (ENOMEM);
30888a08813SAndy Fiddaman 	}
30988a08813SAndy Fiddaman 	list_insert_tail(&cch->cch_nodebug, ccf);
31088a08813SAndy Fiddaman 
31188a08813SAndy Fiddaman 	return (0);
31288a08813SAndy Fiddaman }
31388a08813SAndy Fiddaman 
314bc1f688bSRobert Mustacchi ctf_file_t *
ctf_fdconvert(ctf_convert_t * cch,int fd,int * errp,char * errbuf,size_t errlen)315dd442252SAndy Fiddaman ctf_fdconvert(ctf_convert_t *cch, int fd, int *errp,
316dd442252SAndy Fiddaman     char *errbuf, size_t errlen)
317bc1f688bSRobert Mustacchi {
318bc1f688bSRobert Mustacchi 	int err;
319bc1f688bSRobert Mustacchi 	Elf *elf;
320bc1f688bSRobert Mustacchi 	ctf_file_t *fp;
321bc1f688bSRobert Mustacchi 
322bc1f688bSRobert Mustacchi 	if (errp == NULL)
323bc1f688bSRobert Mustacchi 		errp = &err;
324bc1f688bSRobert Mustacchi 
325bc1f688bSRobert Mustacchi 	elf = elf_begin(fd, ELF_C_READ, NULL);
326bc1f688bSRobert Mustacchi 	if (elf == NULL) {
327bc1f688bSRobert Mustacchi 		*errp = ECTF_FMT;
328bc1f688bSRobert Mustacchi 		return (NULL);
329bc1f688bSRobert Mustacchi 	}
330bc1f688bSRobert Mustacchi 
331dd442252SAndy Fiddaman 	fp = ctf_elfconvert(cch, fd, elf, errp, errbuf, errlen);
332bc1f688bSRobert Mustacchi 
333bc1f688bSRobert Mustacchi 	(void) elf_end(elf);
334bc1f688bSRobert Mustacchi 	return (fp);
335bc1f688bSRobert Mustacchi }
336