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);
}