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 */
15
16/*
17 * Main conversion entry points. This has been designed such that there can be
18 * any number of different conversion backends. Currently we only have one that
19 * understands DWARFv2 (and bits of DWARFv4). Each backend should be placed in
20 * the ctf_converters list and each will be tried in turn.
21 */
22
23#include <libctf_impl.h>
24#include <assert.h>
25#include <gelf.h>
26
27ctf_convert_f ctf_converters[] = {
28	ctf_dwarf_convert
29};
30
31#define	NCONVERTS	(sizeof (ctf_converters) / sizeof (ctf_convert_f))
32
33ctf_hsc_ret_t
34ctf_has_c_source(Elf *elf, char *errmsg, size_t errlen)
35{
36	ctf_hsc_ret_t ret = CHR_NO_C_SOURCE;
37	Elf_Scn *scn, *strscn;
38	Elf_Data *data, *strdata;
39	GElf_Shdr shdr;
40	ulong_t i;
41
42	scn = NULL;
43	while ((scn = elf_nextscn(elf, scn)) != NULL) {
44		if (gelf_getshdr(scn, &shdr) == NULL) {
45			(void) snprintf(errmsg, errlen,
46			    "failed to get section header: %s",
47			    elf_errmsg(elf_errno()));
48			return (CHR_ERROR);
49		}
50
51		if (shdr.sh_type == SHT_SYMTAB)
52			break;
53	}
54
55	if (scn == NULL)
56		return (CHR_NO_C_SOURCE);
57
58	if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL) {
59		(void) snprintf(errmsg, errlen, "failed to get str section: %s",
60		    elf_errmsg(elf_errno()));
61		return (CHR_ERROR);
62	}
63
64	if ((data = elf_getdata(scn, NULL)) == NULL) {
65		(void) snprintf(errmsg, errlen, "failed to read section: %s",
66		    elf_errmsg(elf_errno()));
67		return (CHR_ERROR);
68	}
69
70	if ((strdata = elf_getdata(strscn, NULL)) == NULL) {
71		(void) snprintf(errmsg, errlen,
72		    "failed to read string table: %s", elf_errmsg(elf_errno()));
73		return (CHR_ERROR);
74	}
75
76	for (i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
77		GElf_Sym sym;
78		const char *file;
79		size_t len;
80
81		if (gelf_getsym(data, i, &sym) == NULL) {
82			(void) snprintf(errmsg, errlen,
83			    "failed to read sym %lu: %s",
84			    i, elf_errmsg(elf_errno()));
85			return (CHR_ERROR);
86		}
87
88		if (GELF_ST_TYPE(sym.st_info) != STT_FILE)
89			continue;
90
91		file = (const char *)((uintptr_t)strdata->d_buf + sym.st_name);
92		len = strlen(file);
93		if (len >= 2 && strncmp(".c", &file[len - 2], 2) == 0) {
94			ret = CHR_HAS_C_SOURCE;
95			break;
96		}
97	}
98
99	return (ret);
100}
101
102static ctf_file_t *
103ctf_elfconvert(int fd, Elf *elf, const char *label, uint_t nthrs, uint_t flags,
104    int *errp, char *errbuf, size_t errlen)
105{
106	int err, i;
107	ctf_file_t *fp = NULL;
108
109	if (errp == NULL)
110		errp = &err;
111
112	if (elf == NULL) {
113		*errp = EINVAL;
114		return (NULL);
115	}
116
117	if (flags & ~CTF_ALLOW_MISSING_DEBUG) {
118		*errp = EINVAL;
119		return (NULL);
120	}
121
122	if (elf_kind(elf) != ELF_K_ELF) {
123		*errp = ECTF_FMT;
124		return (NULL);
125	}
126
127	switch (ctf_has_c_source(elf, errbuf, errlen)) {
128	case CHR_ERROR:
129		*errp = ECTF_ELF;
130		return (NULL);
131
132	case CHR_NO_C_SOURCE:
133		*errp = ECTF_CONVNOCSRC;
134		return (NULL);
135
136	default:
137		break;
138	}
139
140	for (i = 0; i < NCONVERTS; i++) {
141		fp = NULL;
142		err = ctf_converters[i](fd, elf, nthrs, flags,
143		    &fp, errbuf, errlen);
144
145		if (err != ECTF_CONVNODEBUG)
146			break;
147	}
148
149	if (err != 0) {
150		assert(fp == NULL);
151		*errp = err;
152		return (NULL);
153	}
154
155	if (label != NULL) {
156		if (ctf_add_label(fp, label, fp->ctf_typemax, 0) == CTF_ERR) {
157			*errp = ctf_errno(fp);
158			ctf_close(fp);
159			return (NULL);
160		}
161		if (ctf_update(fp) == CTF_ERR) {
162			*errp = ctf_errno(fp);
163			ctf_close(fp);
164			return (NULL);
165		}
166	}
167
168	return (fp);
169}
170
171ctf_file_t *
172ctf_fdconvert(int fd, const char *label, uint_t nthrs, uint_t flags, int *errp,
173    char *errbuf, size_t errlen)
174{
175	int err;
176	Elf *elf;
177	ctf_file_t *fp;
178
179	if (errp == NULL)
180		errp = &err;
181
182	elf = elf_begin(fd, ELF_C_READ, NULL);
183	if (elf == NULL) {
184		*errp = ECTF_FMT;
185		return (NULL);
186	}
187
188	fp = ctf_elfconvert(fd, elf, label, nthrs, flags, errp, errbuf, errlen);
189
190	(void) elf_end(elf);
191	return (fp);
192}
193