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