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