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