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