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 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 * 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 * 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 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 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 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 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 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 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 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 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 * 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