Print this page
10816 ctf_dwarf_convert_type() relies on un-initialized id
10817 ctfconvert -i option is mis-handled
10818 Improve ctfconvert error messages
10819 ctfconvert should handle empty dies
10820 ctfconvert -i never converts
10821 bad free in ctf_dwarf_init_die
10815 shouldn't build gcore.c as part of kmdb
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>

*** 881,891 **** if ((ret = ctf_dwarf_float_base(cup, type, enc)) != 0) return (ret); break; default: (void) snprintf(cup->cu_errbuf, cup->cu_errlen, ! "encountered unkown DWARF encoding: %d", type); return (ECTF_CONVBKERR); } return (0); } --- 881,891 ---- if ((ret = ctf_dwarf_float_base(cup, type, enc)) != 0) return (ret); break; default: (void) snprintf(cup->cu_errbuf, cup->cu_errlen, ! "encountered unknown DWARF encoding: %d", type); return (ECTF_CONVBKERR); } return (0); }
*** 1776,1785 **** --- 1776,1786 ---- ret = ctf_dwarf_create_reference(cup, die, idp, CTF_K_RESTRICT, isroot); break; default: ctf_dprintf("ignoring tag type %x\n", tag); + *idp = CTF_ERR; ret = 0; break; } ctf_dprintf("ctf_dwarf_convert_type tag specific handler returned %d\n", ret);
*** 2675,2684 **** --- 2676,2686 ---- ndb = ctf_list_next(cdb); ctf_free(cdb, sizeof (ctf_dwbitf_t)); } ctf_dprintf("Trying to clean up dwarf_t: %p\n", cup->cu_dwarf); + if (cup->cu_dwarf != NULL) (void) dwarf_finish(cup->cu_dwarf, &derr); cup->cu_dwarf = NULL; ctf_close(cup->cu_ctfp); cookie = NULL;
*** 2725,2741 **** return (ECTF_CONVBKERR); } *ndies = *ndies + 1; } - if (*ndies == 0) { - (void) snprintf(errbuf, errlen, - "file does not contain valid DWARF data: %s\n", - dwarf_errmsg(*derr)); - return (ECTF_CONVBKERR); - } - return (0); } static int ctf_dwarf_init_die(int fd, Elf *elf, ctf_cu_t *cup, int ndie, char *errbuf, --- 2727,2736 ----
*** 2767,2829 **** cup->cu_voidtid = CTF_ERR; cup->cu_longtid = CTF_ERR; cup->cu_elf = elf; cup->cu_maxoff = nexthdr - 1; cup->cu_ctfp = ctf_fdcreate(fd, &ret); ! if (cup->cu_ctfp == NULL) { ! ctf_free(cup, sizeof (ctf_cu_t)); return (ret); ! } avl_create(&cup->cu_map, ctf_dwmap_comp, sizeof (ctf_dwmap_t), offsetof(ctf_dwmap_t, cdm_avl)); cup->cu_errbuf = errbuf; cup->cu_errlen = errlen; bzero(&cup->cu_vars, sizeof (ctf_list_t)); bzero(&cup->cu_funcs, sizeof (ctf_list_t)); bzero(&cup->cu_bitfields, sizeof (ctf_list_t)); if ((ret = ctf_dwarf_die_elfenc(elf, cup, errbuf, ! errlen)) != 0) { ! avl_destroy(&cup->cu_map); ! ctf_free(cup, sizeof (ctf_cu_t)); return (ret); - } ! if ((ret = ctf_dwarf_sib(cup, NULL, &cu)) != 0) { ! avl_destroy(&cup->cu_map); ! ctf_free(cup, sizeof (ctf_cu_t)); return (ret); ! } if (cu == NULL) { (void) snprintf(errbuf, errlen, ! "file does not contain DWARF data\n"); ! avl_destroy(&cup->cu_map); ! ctf_free(cup, sizeof (ctf_cu_t)); ! return (ECTF_CONVBKERR); } ! if ((ret = ctf_dwarf_child(cup, cu, &child)) != 0) { ! avl_destroy(&cup->cu_map); ! ctf_free(cup, sizeof (ctf_cu_t)); return (ret); ! } if (child == NULL) { (void) snprintf(errbuf, errlen, ! "file does not contain DWARF data\n"); ! avl_destroy(&cup->cu_map); ! ctf_free(cup, sizeof (ctf_cu_t)); ! return (ECTF_CONVBKERR); } cup->cu_cuoff = offset; cup->cu_cu = child; ! if ((cup->cu_cmh = ctf_merge_init(fd, &ret)) == NULL) { ! avl_destroy(&cup->cu_map); ! ctf_free(cup, sizeof (ctf_cu_t)); return (ret); - } if (ctf_dwarf_string(cup, cu, DW_AT_name, &name) == 0) { size_t len = strlen(name) + 1; char *b = basename(name); cup->cu_name = strdup(b); --- 2762,2809 ---- cup->cu_voidtid = CTF_ERR; cup->cu_longtid = CTF_ERR; cup->cu_elf = elf; cup->cu_maxoff = nexthdr - 1; cup->cu_ctfp = ctf_fdcreate(fd, &ret); ! if (cup->cu_ctfp == NULL) return (ret); ! avl_create(&cup->cu_map, ctf_dwmap_comp, sizeof (ctf_dwmap_t), offsetof(ctf_dwmap_t, cdm_avl)); cup->cu_errbuf = errbuf; cup->cu_errlen = errlen; bzero(&cup->cu_vars, sizeof (ctf_list_t)); bzero(&cup->cu_funcs, sizeof (ctf_list_t)); bzero(&cup->cu_bitfields, sizeof (ctf_list_t)); if ((ret = ctf_dwarf_die_elfenc(elf, cup, errbuf, ! errlen)) != 0) return (ret); ! if ((ret = ctf_dwarf_sib(cup, NULL, &cu)) != 0) return (ret); ! if (cu == NULL) { (void) snprintf(errbuf, errlen, ! "file does not contain DWARF data"); ! return (ECTF_CONVNODEBUG); } ! if ((ret = ctf_dwarf_child(cup, cu, &child)) != 0) return (ret); ! if (child == NULL) { (void) snprintf(errbuf, errlen, ! "file does not contain DWARF data"); ! return (ECTF_CONVNODEBUG); } cup->cu_cuoff = offset; cup->cu_cu = child; ! if ((cup->cu_cmh = ctf_merge_init(fd, &ret)) == NULL) return (ret); if (ctf_dwarf_string(cup, cu, DW_AT_name, &name) == 0) { size_t len = strlen(name) + 1; char *b = basename(name); cup->cu_name = strdup(b);
*** 2833,2877 **** } return (0); } ! ctf_conv_status_t ! ctf_dwarf_convert(int fd, Elf *elf, uint_t nthrs, int *errp, ctf_file_t **fpp, char *errmsg, size_t errlen) { int err, ret, ndies, i; Dwarf_Debug dw; Dwarf_Error derr; ctf_cu_t *cdies = NULL, *cup; workq_t *wqp = NULL; - if (errp == NULL) - errp = &err; - *errp = 0; *fpp = NULL; ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, &dw, &derr); if (ret != DW_DLV_OK) { - /* - * We may want to expect DWARF data here and fail conversion if - * it's missing. In this case, if we actually have some amount - * of DWARF, but no section, for now, just go ahead and create - * an empty CTF file. - */ if (ret == DW_DLV_NO_ENTRY || dwarf_errno(derr) == DW_DLE_DEBUG_INFO_NULL) { ! *fpp = ctf_create(errp); ! return (*fpp != NULL ? CTF_CONV_SUCCESS : ! CTF_CONV_ERROR); } ! (void) snprintf(errmsg, errlen, ! "failed to initialize DWARF: %s\n", ! dwarf_errmsg(derr)); ! *errp = ECTF_CONVBKERR; ! return (CTF_CONV_ERROR); } /* * Iterate over all of the compilation units and create a ctf_cu_t for * each of them. This is used to determine if we have zero, one, or --- 2813,2959 ---- } return (0); } + /* + * This is our only recourse to identify a C source file that is missing debug + * info: it will be mentioned as an STT_FILE, but not have a compile unit entry. + * (A traditional ctfmerge works on individual files, so can identify missing + * DWARF more directly, via ctf_has_c_source() on the .o file.) + * + * As we operate on basenames, this can of course miss some cases, but it's + * better than not checking at all. + * + * We explicitly whitelist some CRT components. Failing that, there's always + * the -m option. + */ + static boolean_t + c_source_has_debug(const char *file, ctf_cu_t *cus, size_t nr_cus) + { + const char *basename = strrchr(file, '/'); ! if (basename == NULL) ! basename = file; ! else ! basename++; ! ! if (strcmp(basename, "common-crt.c") == 0 || ! strcmp(basename, "gmon.c") == 0 || ! strcmp(basename, "dlink_init.c") == 0 || ! strcmp(basename, "dlink_common.c") == 0 || ! strncmp(basename, "crt", strlen("crt")) == 0 || ! strncmp(basename, "values-", strlen("values-")) == 0) ! return (B_TRUE); ! ! for (size_t i = 0; i < nr_cus; i++) { ! if (strcmp(basename, cus[i].cu_name) == 0) ! return (B_TRUE); ! } ! ! return (B_FALSE); ! } ! ! static int ! ctf_dwarf_check_missing(ctf_cu_t *cus, size_t nr_cus, Elf *elf, char *errmsg, size_t errlen) { + Elf_Scn *scn, *strscn; + Elf_Data *data, *strdata; + GElf_Shdr shdr; + ulong_t i; + + scn = NULL; + while ((scn = elf_nextscn(elf, scn)) != NULL) { + if (gelf_getshdr(scn, &shdr) == NULL) { + (void) snprintf(errmsg, errlen, + "failed to get section header: %s\n", + elf_errmsg(elf_errno())); + return (EINVAL); + } + + if (shdr.sh_type == SHT_SYMTAB) + break; + } + + if (scn == NULL) + return (0); + + if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL) { + (void) snprintf(errmsg, errlen, + "failed to get str section: %s\n", + elf_errmsg(elf_errno())); + return (EINVAL); + } + + if ((data = elf_getdata(scn, NULL)) == NULL) { + (void) snprintf(errmsg, errlen, "failed to read section: %s\n", + elf_errmsg(elf_errno())); + return (EINVAL); + } + + if ((strdata = elf_getdata(strscn, NULL)) == NULL) { + (void) snprintf(errmsg, errlen, + "failed to read string table: %s\n", + elf_errmsg(elf_errno())); + return (EINVAL); + } + + for (i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) { + GElf_Sym sym; + const char *file; + size_t len; + + if (gelf_getsym(data, i, &sym) == NULL) { + (void) snprintf(errmsg, errlen, + "failed to read sym %lu: %s\n", + i, elf_errmsg(elf_errno())); + return (EINVAL); + } + + if (GELF_ST_TYPE(sym.st_info) != STT_FILE) + continue; + + file = (const char *)((uintptr_t)strdata->d_buf + sym.st_name); + len = strlen(file); + if (len < 2 || strncmp(".c", &file[len - 2], 2) != 0) + continue; + + if (!c_source_has_debug(file, cus, nr_cus)) { + (void) snprintf(errmsg, errlen, + "file %s is missing debug info\n", file); + return (ECTF_CONVNODEBUG); + } + } + + return (0); + } + + int + ctf_dwarf_convert(int fd, Elf *elf, uint_t nthrs, uint_t flags, + ctf_file_t **fpp, char *errbuf, size_t errlen) + { int err, ret, ndies, i; Dwarf_Debug dw; Dwarf_Error derr; ctf_cu_t *cdies = NULL, *cup; workq_t *wqp = NULL; *fpp = NULL; ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, &dw, &derr); if (ret != DW_DLV_OK) { if (ret == DW_DLV_NO_ENTRY || dwarf_errno(derr) == DW_DLE_DEBUG_INFO_NULL) { ! (void) snprintf(errbuf, errlen, ! "file does not contain DWARF data\n"); ! return (ECTF_CONVNODEBUG); } ! ! (void) snprintf(errbuf, errlen, ! "dwarf_elf_init() failed: %s\n", dwarf_errmsg(derr)); ! return (ECTF_CONVBKERR); } /* * Iterate over all of the compilation units and create a ctf_cu_t for * each of them. This is used to determine if we have zero, one, or
*** 2878,2923 **** * multiple dies to convert. If we have zero, that's an error. If * there's only one die, that's the simple case. No merge needed and * only a single Dwarf_Debug as well. */ ndies = 0; ! ret = ctf_dwarf_count_dies(dw, &derr, &ndies, errmsg, errlen); ! if (ret != 0) { ! *errp = ret; ! goto out; } (void) dwarf_finish(dw, &derr); cdies = ctf_alloc(sizeof (ctf_cu_t) * ndies); if (cdies == NULL) { ! *errp = ENOMEM; ! return (CTF_CONV_ERROR); } for (i = 0; i < ndies; i++) { cup = &cdies[i]; ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, &cup->cu_dwarf, &derr); if (ret != 0) { ctf_free(cdies, sizeof (ctf_cu_t) * ndies); ! (void) snprintf(errmsg, errlen, "failed to initialize DWARF: %s\n", dwarf_errmsg(derr)); ! *errp = ECTF_CONVBKERR; ! return (CTF_CONV_ERROR); } ! ret = ctf_dwarf_init_die(fd, elf, &cdies[i], i, errmsg, errlen); ! if (ret != 0) { ! *errp = ret; goto out; - } cup->cu_doweaks = ndies > 1 ? B_FALSE : B_TRUE; } ! ctf_dprintf("found %d DWARF CUs\n", ndies); /* * If we only have one compilation unit, there's no reason to use * multiple threads, even if the user requested them. After all, they * just gave us an upper bound. --- 2960,3010 ---- * multiple dies to convert. If we have zero, that's an error. If * there's only one die, that's the simple case. No merge needed and * only a single Dwarf_Debug as well. */ ndies = 0; ! err = ctf_dwarf_count_dies(dw, &derr, &ndies, errbuf, errlen); ! ! ctf_dprintf("found %d DWARF CUs\n", ndies); ! ! if (ndies == 0) { ! (void) snprintf(errbuf, errlen, ! "file does not contain DWARF data\n"); ! return (ECTF_CONVNODEBUG); } (void) dwarf_finish(dw, &derr); cdies = ctf_alloc(sizeof (ctf_cu_t) * ndies); if (cdies == NULL) { ! return (ENOMEM); } + bzero(cdies, sizeof (ctf_cu_t) * ndies); + for (i = 0; i < ndies; i++) { cup = &cdies[i]; ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, &cup->cu_dwarf, &derr); if (ret != 0) { ctf_free(cdies, sizeof (ctf_cu_t) * ndies); ! (void) snprintf(errbuf, errlen, "failed to initialize DWARF: %s\n", dwarf_errmsg(derr)); ! return (ECTF_CONVBKERR); } ! err = ctf_dwarf_init_die(fd, elf, cup, i, errbuf, errlen); ! if (err != 0) goto out; cup->cu_doweaks = ndies > 1 ? B_FALSE : B_TRUE; } ! if (!(flags & CTF_ALLOW_MISSING_DEBUG) && ! (err = ctf_dwarf_check_missing(cdies, ndies, ! elf, errbuf, errlen)) != 0) ! goto out; /* * If we only have one compilation unit, there's no reason to use * multiple threads, even if the user requested them. After all, they * just gave us an upper bound.
*** 2924,3004 **** */ if (ndies == 1) nthrs = 1; if (workq_init(&wqp, nthrs) == -1) { ! *errp = errno; goto out; } for (i = 0; i < ndies; i++) { cup = &cdies[i]; ctf_dprintf("adding cu %s: %p, %x %x\n", cup->cu_name, cup->cu_cu, cup->cu_cuoff, cup->cu_maxoff); if (workq_add(wqp, cup) == -1) { ! *errp = errno; goto out; } } ! ret = workq_work(wqp, ctf_dwarf_convert_one, NULL, errp); if (ret == WORKQ_ERROR) { ! *errp = errno; goto out; } else if (ret == WORKQ_UERROR) { ctf_dprintf("internal convert failed: %s\n", ! ctf_errmsg(*errp)); goto out; } ctf_dprintf("Determining next phase: have %d CUs\n", ndies); if (ndies != 1) { ctf_merge_t *cmp; ! cmp = ctf_merge_init(fd, &ret); ! if (cmp == NULL) { ! *errp = ret; goto out; - } ctf_dprintf("setting threads\n"); ! if ((ret = ctf_merge_set_nthreads(cmp, nthrs)) != 0) { ctf_merge_fini(cmp); - *errp = ret; goto out; } for (i = 0; i < ndies; i++) { cup = &cdies[i]; ! ctf_dprintf("adding cu %s (%p)\n", cup->cu_name, ! cup->cu_ctfp); ! if ((ret = ctf_merge_add(cmp, cup->cu_ctfp)) != 0) { ctf_merge_fini(cmp); - *errp = ret; goto out; } } ctf_dprintf("performing merge\n"); ! ret = ctf_merge_merge(cmp, fpp); ! if (ret != 0) { ctf_dprintf("failed merge!\n"); *fpp = NULL; ctf_merge_fini(cmp); - *errp = ret; goto out; } ctf_merge_fini(cmp); ! *errp = 0; ctf_dprintf("successfully converted!\n"); } else { ! *errp = 0; *fpp = cdies->cu_ctfp; cdies->cu_ctfp = NULL; ctf_dprintf("successfully converted!\n"); } out: workq_fini(wqp); ctf_dwarf_free_dies(cdies, ndies); ! return (*fpp != NULL ? CTF_CONV_SUCCESS : CTF_CONV_ERROR); } --- 3011,3084 ---- */ if (ndies == 1) nthrs = 1; if (workq_init(&wqp, nthrs) == -1) { ! err = errno; goto out; } for (i = 0; i < ndies; i++) { cup = &cdies[i]; ctf_dprintf("adding cu %s: %p, %x %x\n", cup->cu_name, cup->cu_cu, cup->cu_cuoff, cup->cu_maxoff); if (workq_add(wqp, cup) == -1) { ! err = errno; goto out; } } ! ret = workq_work(wqp, ctf_dwarf_convert_one, NULL, &err); if (ret == WORKQ_ERROR) { ! err = errno; goto out; } else if (ret == WORKQ_UERROR) { ctf_dprintf("internal convert failed: %s\n", ! ctf_errmsg(err)); goto out; } ctf_dprintf("Determining next phase: have %d CUs\n", ndies); if (ndies != 1) { ctf_merge_t *cmp; ! cmp = ctf_merge_init(fd, &err); ! if (cmp == NULL) goto out; ctf_dprintf("setting threads\n"); ! if ((err = ctf_merge_set_nthreads(cmp, nthrs)) != 0) { ctf_merge_fini(cmp); goto out; } for (i = 0; i < ndies; i++) { cup = &cdies[i]; ! if ((err = ctf_merge_add(cmp, cup->cu_ctfp)) != 0) { ctf_merge_fini(cmp); goto out; } } ctf_dprintf("performing merge\n"); ! err = ctf_merge_merge(cmp, fpp); ! if (err != 0) { ctf_dprintf("failed merge!\n"); *fpp = NULL; ctf_merge_fini(cmp); goto out; } ctf_merge_fini(cmp); ! err = 0; ctf_dprintf("successfully converted!\n"); } else { ! err = 0; *fpp = cdies->cu_ctfp; cdies->cu_ctfp = NULL; ctf_dprintf("successfully converted!\n"); } out: workq_fini(wqp); ctf_dwarf_free_dies(cdies, ndies); ! return (err); }