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,11 +881,11 @@
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);
+ "encountered unknown DWARF encoding: %d", type);
return (ECTF_CONVBKERR);
}
return (0);
}
@@ -1776,10 +1776,11 @@
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,10 +2676,11 @@
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,17 +2727,10 @@
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,
@@ -2767,63 +2762,48 @@
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));
+ 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) {
- avl_destroy(&cup->cu_map);
- ctf_free(cup, sizeof (ctf_cu_t));
+ errlen)) != 0)
return (ret);
- }
- if ((ret = ctf_dwarf_sib(cup, NULL, &cu)) != 0) {
- avl_destroy(&cup->cu_map);
- ctf_free(cup, sizeof (ctf_cu_t));
+ if ((ret = ctf_dwarf_sib(cup, NULL, &cu)) != 0)
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);
+ "file does not contain DWARF data");
+ return (ECTF_CONVNODEBUG);
}
- if ((ret = ctf_dwarf_child(cup, cu, &child)) != 0) {
- avl_destroy(&cup->cu_map);
- ctf_free(cup, sizeof (ctf_cu_t));
+ if ((ret = ctf_dwarf_child(cup, cu, &child)) != 0)
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);
+ "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) {
- avl_destroy(&cup->cu_map);
- ctf_free(cup, sizeof (ctf_cu_t));
+ 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,45 +2813,147 @@
}
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, '/');
-ctf_conv_status_t
-ctf_dwarf_convert(int fd, Elf *elf, uint_t nthrs, int *errp, ctf_file_t **fpp,
+ 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;
- 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(errbuf, errlen,
+ "file does not contain DWARF data\n");
+ return (ECTF_CONVNODEBUG);
}
- (void) snprintf(errmsg, errlen,
- "failed to initialize DWARF: %s\n",
- dwarf_errmsg(derr));
- *errp = ECTF_CONVBKERR;
- return (CTF_CONV_ERROR);
+
+ (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,46 +2960,51 @@
* 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;
+ 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) {
- *errp = ENOMEM;
- return (CTF_CONV_ERROR);
+ 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(errmsg, errlen,
+ (void) snprintf(errbuf, errlen,
"failed to initialize DWARF: %s\n",
dwarf_errmsg(derr));
- *errp = ECTF_CONVBKERR;
- return (CTF_CONV_ERROR);
+ return (ECTF_CONVBKERR);
}
- ret = ctf_dwarf_init_die(fd, elf, &cdies[i], i, errmsg, errlen);
- if (ret != 0) {
- *errp = ret;
+ 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;
}
- ctf_dprintf("found %d DWARF CUs\n", ndies);
+ 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,81 +3011,74 @@
*/
if (ndies == 1)
nthrs = 1;
if (workq_init(&wqp, nthrs) == -1) {
- *errp = errno;
+ 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) {
- *errp = errno;
+ err = errno;
goto out;
}
}
- ret = workq_work(wqp, ctf_dwarf_convert_one, NULL, errp);
+ ret = workq_work(wqp, ctf_dwarf_convert_one, NULL, &err);
if (ret == WORKQ_ERROR) {
- *errp = errno;
+ err = errno;
goto out;
} else if (ret == WORKQ_UERROR) {
ctf_dprintf("internal convert failed: %s\n",
- ctf_errmsg(*errp));
+ 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, &ret);
- if (cmp == NULL) {
- *errp = ret;
+ cmp = ctf_merge_init(fd, &err);
+ if (cmp == NULL)
goto out;
- }
ctf_dprintf("setting threads\n");
- if ((ret = ctf_merge_set_nthreads(cmp, nthrs)) != 0) {
+ if ((err = 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) {
+ if ((err = 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) {
+ err = ctf_merge_merge(cmp, fpp);
+ if (err != 0) {
ctf_dprintf("failed merge!\n");
*fpp = NULL;
ctf_merge_fini(cmp);
- *errp = ret;
goto out;
}
ctf_merge_fini(cmp);
- *errp = 0;
+ err = 0;
ctf_dprintf("successfully converted!\n");
} else {
- *errp = 0;
+ 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 (*fpp != NULL ? CTF_CONV_SUCCESS : CTF_CONV_ERROR);
+ return (err);
}