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