Print this page
13925 core files should include DWARF (fix mismerge)
13925 core files should include DWARF
Reviewed by: Rich Lowe <richlowe@richlowe.net>
Reviewed by: C Fraire <cfraire@me.com>
Reviewed by: Adam Leventhal <adam.leventhal@gmail.com>
Approved by: Dan McDonald <danmcd@joyent.com>
        
@@ -67,10 +67,12 @@
 #include "elf_impl.h"
 #include <sys/sdt.h>
 #include <sys/siginfo.h>
 #include <sys/random.h>
 
+#include <core_shstrtab.h>
+
 #if defined(__x86)
 #include <sys/comm_page_util.h>
 #include <sys/fp.h>
 #endif /* defined(__x86) */
 
@@ -106,75 +108,10 @@
 uint_t elf_nphdr_max = 1000;
 uint_t elf_nshdr_max = 10000;
 size_t elf_shstrtab_max = 100 * 1024;
 #endif
 
-
-
-typedef enum {
-        STR_CTF,
-        STR_SYMTAB,
-        STR_DYNSYM,
-        STR_STRTAB,
-        STR_DYNSTR,
-        STR_SHSTRTAB,
-        STR_NUM
-} shstrtype_t;
-
-static const char *shstrtab_data[] = {
-        ".SUNW_ctf",
-        ".symtab",
-        ".dynsym",
-        ".strtab",
-        ".dynstr",
-        ".shstrtab"
-};
-
-typedef struct shstrtab {
-        uint_t  sst_ndx[STR_NUM];
-        uint_t  sst_cur;
-} shstrtab_t;
-
-static void
-shstrtab_init(shstrtab_t *s)
-{
-        bzero(&s->sst_ndx, sizeof (s->sst_ndx));
-        s->sst_cur = 1;
-}
-
-static uint_t
-shstrtab_ndx(shstrtab_t *s, shstrtype_t type)
-{
-        uint_t ret;
-
-        if ((ret = s->sst_ndx[type]) != 0)
-                return (ret);
-
-        ret = s->sst_ndx[type] = s->sst_cur;
-        s->sst_cur += strlen(shstrtab_data[type]) + 1;
-
-        return (ret);
-}
-
-static size_t
-shstrtab_size(const shstrtab_t *s)
-{
-        return (s->sst_cur);
-}
-
-static void
-shstrtab_dump(const shstrtab_t *s, char *buf)
-{
-        uint_t i, ndx;
-
-        *buf = '\0';
-        for (i = 0; i < STR_NUM; i++) {
-                if ((ndx = s->sst_ndx[i]) != 0)
-                        (void) strcpy(buf + ndx, shstrtab_data[i]);
-        }
-}
-
 static int
 dtrace_safe_phdr(Phdr *phdrp, struct uarg *args, uintptr_t base)
 {
         ASSERT(phdrp->p_type == PT_SUNWDTRACE);
 
@@ -1972,11 +1909,13 @@
         ctx->ecc_doffset += src->sh_size;
 }
 
 /*
  * Walk sections for a given ELF object, counting (or copying) those of
- * interest (CTF, symtab, strtab).
+ * interest (CTF, symtab, strtab, DWARF debug).
+ *
+ * Returns UINT_MAX upon low-memory.
  */
 static uint_t
 elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr,
     Shdr *v, uint_t idx, uint_t remain, shstrtab_t *shstrtab)
 {
@@ -1990,11 +1929,12 @@
         boolean_t ctf_link = B_FALSE;
         caddr_t shbase;
         size_t shsize, shstrsize;
         char *shstrbase;
 
-        if ((content & (CC_CONTENT_CTF | CC_CONTENT_SYMTAB)) == 0) {
+        if ((content & (CC_CONTENT_CTF | CC_CONTENT_SYMTAB | CC_CONTENT_DEBUG))
+            == 0) {
                 return (0);
         }
 
         if (getelfhead(mvp, credp, &ehdr, &nshdrs, &shstrndx, &nphdrs) != 0 ||
             getelfshdr(mvp, credp, &ehdr, nshdrs, shstrndx, &shbase, &shsize,
@@ -2028,10 +1968,56 @@
                         }
                 } else if (symtab == NULL &&
                     (content & CC_CONTENT_SYMTAB) != 0 &&
                     strcmp(name, shstrtab_data[STR_SYMTAB]) == 0) {
                         symchk = shdr;
+                } else if ((content & CC_CONTENT_DEBUG) != 0 &&
+                    strncmp(name, ".debug_", strlen(".debug_")) == 0) {
+                        /*
+                         * The design of the above check is intentional.  In
+                         * particular, we want to capture any sections that
+                         * begin with '.debug_' for a few reasons:
+                         *
+                         * 1) Various revisions to the DWARF spec end up
+                         * changing the set of section headers that
+                         * exist. This ensures that we don't need to change
+                         * the kernel to get a new version.
+                         *
+                         * 2) Other software uses .debug_ sections for things
+                         * which aren't DWARF. This allows them to be captured
+                         * as well.
+                         *
+                         * Because of this, we emit straight here, unlike the
+                         * other two sections where we wait until we're done
+                         * scanning.
+                         */
+
+                        /* We're only counting, don't emit! */
+                        if (v == NULL) {
+                                count++;
+                                continue;
+                        }
+
+                        elf_ctx_resize_scratch(ctx, shdr->sh_size);
+                        if (!shstrtab_ndx(shstrtab, name, &v[idx].sh_name)) {
+                                count = UINT_MAX;
+                                goto done;
+                        }
+                        v[idx].sh_addr = (Addr)(uintptr_t)saddr;
+                        v[idx].sh_type = shdr->sh_type;
+                        v[idx].sh_addralign = shdr->sh_addralign;
+                        *doffp = roundup(*doffp, v[idx].sh_addralign);
+                        v[idx].sh_offset = *doffp;
+                        v[idx].sh_size = shdr->sh_size;
+                        v[idx].sh_link = 0;
+                        v[idx].sh_entsize = shdr->sh_entsize;
+                        v[idx].sh_info = shdr->sh_info;
+
+                        elf_copy_scn(ctx, shdr, mvp, &v[idx]);
+                        count++;
+                        idx++;
+                        continue;
                 } else {
                         continue;
                 }
 
                 ASSERT(symchk != NULL);
@@ -2047,30 +2033,37 @@
                         continue;
                 }
                 symtab = symchk;
                 strtab = strchk;
 
-                if (symtab != NULL && ctf != NULL) {
+                if (symtab != NULL && ctf != NULL &&
+                    (content & CC_CONTENT_DEBUG) == 0) {
                         /* No other shdrs are of interest at this point */
                         break;
                 }
         }
 
         if (ctf != NULL)
                 count += 1;
         if (symtab != NULL)
                 count += 2;
+
         if (v == NULL || count == 0 || count > remain) {
                 count = MIN(count, remain);
                 goto done;
         }
 
         /* output CTF section */
         if (ctf != NULL) {
                 elf_ctx_resize_scratch(ctx, ctf->sh_size);
 
-                v[idx].sh_name = shstrtab_ndx(shstrtab, STR_CTF);
+                if (!shstrtab_ndx(shstrtab, shstrtab_data[STR_CTF],
+                    &v[idx].sh_name)) {
+                        count = UINT_MAX;
+                        goto done;
+                }
+
                 v[idx].sh_addr = (Addr)(uintptr_t)saddr;
                 v[idx].sh_type = SHT_PROGBITS;
                 v[idx].sh_addralign = 4;
                 *doffp = roundup(*doffp, v[idx].sh_addralign);
                 v[idx].sh_offset = *doffp;
@@ -2097,16 +2090,26 @@
 
                 elf_ctx_resize_scratch(ctx,
                     MAX(symtab->sh_size, strtab->sh_size));
 
                 if (symtab->sh_type == SHT_DYNSYM) {
-                        symtab_name = shstrtab_ndx(shstrtab, STR_DYNSYM);
-                        strtab_name = shstrtab_ndx(shstrtab, STR_DYNSTR);
+                        if (!shstrtab_ndx(shstrtab, shstrtab_data[STR_DYNSYM],
+                            &symtab_name) ||
+                            !shstrtab_ndx(shstrtab, shstrtab_data[STR_DYNSTR],
+                            &strtab_name)) {
+                                count = UINT_MAX;
+                                goto done;
+                        }
                 } else {
-                        symtab_name = shstrtab_ndx(shstrtab, STR_SYMTAB);
-                        strtab_name = shstrtab_ndx(shstrtab, STR_STRTAB);
+                        if (!shstrtab_ndx(shstrtab, shstrtab_data[STR_SYMTAB],
+                            &symtab_name) ||
+                            !shstrtab_ndx(shstrtab, shstrtab_data[STR_STRTAB],
+                            &strtab_name)) {
+                                count = UINT_MAX;
+                                goto done;
                 }
+                }
 
                 v[idx].sh_name = symtab_name;
                 v[idx].sh_type = symtab->sh_type;
                 v[idx].sh_addr = symtab->sh_addr;
                 if (ehdr.e_type == ET_DYN || v[idx].sh_addr == 0)
@@ -2162,11 +2165,12 @@
         ASSERT(AS_WRITE_HELD(as));
 
         if (v != NULL) {
                 ASSERT(nv != 0);
 
-                shstrtab_init(&shstrtab);
+                if (!shstrtab_init(&shstrtab))
+                        return (ENOMEM);
                 remain = nv;
         } else {
                 ASSERT(nv == 0);
 
                 /*
@@ -2210,10 +2214,14 @@
                 if ((prot & (PROT_WRITE | PROT_EXEC)) != PROT_EXEC)
                         continue;
 
                 count = elf_process_obj_scns(ctx, mvp, saddr, v, idx, remain,
                     &shstrtab);
+                if (count == UINT_MAX) {
+                        error = ENOMEM;
+                        goto done;
+                }
 
                 ASSERT(count <= remain);
                 ASSERT(v == NULL || (idx + count) < nv);
 
                 remain -= count;
@@ -2226,21 +2234,27 @@
                         *nshdrsp = 0;
                 } else {
                         /* Include room for the shrstrtab at the end */
                         *nshdrsp = idx + 1;
                 }
+                /* No need to free up shstrtab so we can just return. */
                 return (0);
         }
 
         if (idx != nv - 1) {
                 cmn_err(CE_WARN, "elfcore: core dump failed for "
                     "process %d; address space is changing",
                     ctx->ecc_p->p_pid);
-                return (EIO);
+                error = EIO;
+                goto done;
         }
 
-        v[idx].sh_name = shstrtab_ndx(&shstrtab, STR_SHSTRTAB);
+        if (!shstrtab_ndx(&shstrtab, shstrtab_data[STR_SHSTRTAB],
+            &v[idx].sh_name)) {
+                error = ENOMEM;
+                goto done;
+        }
         v[idx].sh_size = shstrtab_size(&shstrtab);
         v[idx].sh_addralign = 1;
         v[idx].sh_offset = ctx->ecc_doffset;
         v[idx].sh_flags = SHF_STRINGS;
         v[idx].sh_type = SHT_STRTAB;
@@ -2253,10 +2267,13 @@
             ctx->ecc_buf, v[idx].sh_size, ctx->ecc_rlimit, ctx->ecc_credp);
         if (error == 0) {
                 ctx->ecc_doffset += v[idx].sh_size;
         }
 
+done:
+        if (v != NULL)
+                shstrtab_fini(&shstrtab);
         return (error);
 }
 
 int
 elfcore(vnode_t *vp, proc_t *p, cred_t *credp, rlim64_t rlimit, int sig,
@@ -2300,13 +2317,12 @@
 
         /*
          * Count the number of section headers we're going to need.
          */
         nshdrs = 0;
-        if (content & (CC_CONTENT_CTF | CC_CONTENT_SYMTAB)) {
+        if (content & (CC_CONTENT_CTF | CC_CONTENT_SYMTAB | CC_CONTENT_DEBUG))
                 VERIFY0(elf_process_scns(&ctx, NULL, 0, &nshdrs));
-        }
         AS_LOCK_EXIT(as);
 
         /*
          * The core file contents may require zero section headers, but if
          * we overflow the 16 bits allotted to the program header count in
@@ -2315,14 +2331,15 @@
         if (nshdrs == 0 && nphdrs >= PN_XNUM) {
                 nshdrs = 1;
         }
 
         /*
-         * Allocate a buffer which is sized adequately to hold the ehdr, phdrs
-         * or shdrs needed to produce the core file.  It is used for the three
-         * tasks sequentially, not simultaneously, so it does not need space
-         * for all three data at once, only the largest one.
+         * Allocate a buffer which is sized adequately to hold the ehdr,
+         * phdrs, DWARF debug, or shdrs needed to produce the core file.  It
+         * is used for the four tasks sequentially, not simultaneously, so it
+         * does not need space for all four data at once, only the largest
+         * one.
          */
         VERIFY(nphdrs >= 2);
         phdrsz = nphdrs * sizeof (Phdr);
         shdrsz = nshdrs * sizeof (Shdr);
         bigsize = MAX(sizeof (Ehdr), MAX(phdrsz, shdrsz));