xref: /illumos-gate/usr/src/lib/libctf/common/ctf_convert.c (revision 17a5fa85fe0c34b1146222e40a80b42f2aae8500)
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 
27 ctf_convert_f ctf_converters[] = {
28 	ctf_dwarf_convert
29 };
30 
31 #define	NCONVERTS	(sizeof (ctf_converters) / sizeof (ctf_convert_f))
32 
33 ctf_hsc_ret_t
34 ctf_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 
102 static ctf_file_t *
103 ctf_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 
171 ctf_file_t *
172 ctf_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