xref: /illumos-gate/usr/src/lib/libctf/common/ctf_convert.c (revision dd4422524768709a579a2a93a10c78a88a6b0ecb)
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 		return (CHR_NO_C_SOURCE);
58 
59 	if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL) {
60 		(void) snprintf(errmsg, errlen, "failed to get str section: %s",
61 		    elf_errmsg(elf_errno()));
62 		return (CHR_ERROR);
63 	}
64 
65 	if ((data = elf_getdata(scn, NULL)) == NULL) {
66 		(void) snprintf(errmsg, errlen, "failed to read section: %s",
67 		    elf_errmsg(elf_errno()));
68 		return (CHR_ERROR);
69 	}
70 
71 	if ((strdata = elf_getdata(strscn, NULL)) == NULL) {
72 		(void) snprintf(errmsg, errlen,
73 		    "failed to read string table: %s", elf_errmsg(elf_errno()));
74 		return (CHR_ERROR);
75 	}
76 
77 	for (i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
78 		GElf_Sym sym;
79 		const char *file;
80 		size_t len;
81 
82 		if (gelf_getsym(data, i, &sym) == NULL) {
83 			(void) snprintf(errmsg, errlen,
84 			    "failed to read sym %lu: %s",
85 			    i, elf_errmsg(elf_errno()));
86 			return (CHR_ERROR);
87 		}
88 
89 		if (GELF_ST_TYPE(sym.st_info) != STT_FILE)
90 			continue;
91 
92 		file = (const char *)((uintptr_t)strdata->d_buf + sym.st_name);
93 		len = strlen(file);
94 		if (len >= 2 && strncmp(".c", &file[len - 2], 2) == 0) {
95 			ret = CHR_HAS_C_SOURCE;
96 			break;
97 		}
98 	}
99 
100 	return (ret);
101 }
102 
103 static ctf_file_t *
104 ctf_elfconvert(ctf_convert_t *cch, int fd, Elf *elf, int *errp, char *errbuf,
105     size_t errlen)
106 {
107 	int err, i;
108 	ctf_file_t *fp = NULL;
109 
110 	if (errp == NULL)
111 		errp = &err;
112 
113 	if (elf == NULL) {
114 		*errp = EINVAL;
115 		return (NULL);
116 	}
117 
118 	if (elf_kind(elf) != ELF_K_ELF) {
119 		*errp = ECTF_FMT;
120 		return (NULL);
121 	}
122 
123 	switch (ctf_has_c_source(elf, errbuf, errlen)) {
124 	case CHR_ERROR:
125 		*errp = ECTF_ELF;
126 		return (NULL);
127 
128 	case CHR_NO_C_SOURCE:
129 		*errp = ECTF_CONVNOCSRC;
130 		return (NULL);
131 
132 	default:
133 		break;
134 	}
135 
136 	for (i = 0; i < NCONVERTS; i++) {
137 		fp = NULL;
138 		err = ctf_converters[i](cch, fd, elf, &fp, errbuf, errlen);
139 
140 		if (err != ECTF_CONVNODEBUG)
141 			break;
142 	}
143 
144 	if (err != 0) {
145 		assert(fp == NULL);
146 		*errp = err;
147 		return (NULL);
148 	}
149 
150 	if (cch->cch_label != NULL) {
151 		if (ctf_add_label(fp, cch->cch_label, fp->ctf_typemax, 0) ==
152 		    CTF_ERR) {
153 			*errp = ctf_errno(fp);
154 			ctf_close(fp);
155 			return (NULL);
156 		}
157 		if (ctf_update(fp) == CTF_ERR) {
158 			*errp = ctf_errno(fp);
159 			ctf_close(fp);
160 			return (NULL);
161 		}
162 	}
163 
164 	return (fp);
165 }
166 
167 ctf_convert_t *
168 ctf_convert_init(int *errp)
169 {
170 	struct ctf_convert_handle *cch;
171 	int err;
172 
173 	if (errp == NULL)
174 		errp = &err;
175 	*errp = 0;
176 
177 	cch = ctf_alloc(sizeof (struct ctf_convert_handle));
178 	if (cch == NULL) {
179 		*errp = ENOMEM;
180 		return (NULL);
181 	}
182 
183 	cch->cch_label = NULL;
184 	cch->cch_flags = 0;
185 	cch->cch_nthreads = CTF_CONVERT_DEFAULT_NTHREADS;
186 	cch->cch_batchsize = CTF_CONVERT_DEFAULT_BATCHSIZE;
187 	cch->cch_warncb = NULL;
188 	cch->cch_warncb_arg = NULL;
189 
190 	return (cch);
191 }
192 
193 void
194 ctf_convert_fini(ctf_convert_t *cch)
195 {
196 	if (cch->cch_label != NULL) {
197 		size_t len = strlen(cch->cch_label) + 1;
198 		ctf_free(cch->cch_label, len);
199 	}
200 	ctf_free(cch, sizeof (struct ctf_convert_handle));
201 }
202 
203 int
204 ctf_convert_set_nthreads(ctf_convert_t *cch, uint_t nthrs)
205 {
206 	if (nthrs == 0)
207 		return (EINVAL);
208 	cch->cch_nthreads = nthrs;
209 	return (0);
210 }
211 
212 int
213 ctf_convert_set_batchsize(ctf_convert_t *cch, uint_t bsize)
214 {
215 	if (bsize == 0)
216 		return (EINVAL);
217 	cch->cch_batchsize = bsize;
218 	return (0);
219 }
220 
221 int
222 ctf_convert_set_flags(ctf_convert_t *cch, uint_t flags)
223 {
224 	if ((flags & ~CTF_CONVERT_ALL_FLAGS) != 0)
225 		return (EINVAL);
226 	cch->cch_flags = flags;
227 	return (0);
228 }
229 
230 int
231 ctf_convert_set_label(ctf_convert_t *cch, const char *label)
232 {
233 	char *dup;
234 
235 	if (label == NULL)
236 		return (EINVAL);
237 
238 	dup = ctf_strdup(label);
239 	if (dup == NULL)
240 		return (ENOMEM);
241 
242 	if (cch->cch_label != NULL) {
243 		size_t len = strlen(cch->cch_label) + 1;
244 		ctf_free(cch->cch_label, len);
245 	}
246 
247 	cch->cch_label = dup;
248 	return (0);
249 }
250 
251 int
252 ctf_convert_set_warncb(ctf_convert_t *cch, ctf_convert_warn_f cb, void *arg)
253 {
254 	cch->cch_warncb = cb;
255 	cch->cch_warncb_arg = arg;
256 	return (0);
257 }
258 
259 ctf_file_t *
260 ctf_fdconvert(ctf_convert_t *cch, int fd, int *errp,
261     char *errbuf, size_t errlen)
262 {
263 	int err;
264 	Elf *elf;
265 	ctf_file_t *fp;
266 
267 	if (errp == NULL)
268 		errp = &err;
269 
270 	elf = elf_begin(fd, ELF_C_READ, NULL);
271 	if (elf == NULL) {
272 		*errp = ECTF_FMT;
273 		return (NULL);
274 	}
275 
276 	fp = ctf_elfconvert(cch, fd, elf, errp, errbuf, errlen);
277 
278 	(void) elf_end(elf);
279 	return (fp);
280 }
281