diff options
Diffstat (limited to 'usr/src/uts/common/exec/elf/elf.c')
| -rw-r--r-- | usr/src/uts/common/exec/elf/elf.c | 160 |
1 files changed, 82 insertions, 78 deletions
diff --git a/usr/src/uts/common/exec/elf/elf.c b/usr/src/uts/common/exec/elf/elf.c index 986a2d0abd..5e401b91df 100644 --- a/usr/src/uts/common/exec/elf/elf.c +++ b/usr/src/uts/common/exec/elf/elf.c @@ -69,6 +69,8 @@ #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> @@ -108,71 +110,6 @@ 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) { @@ -1974,7 +1911,9 @@ elf_copy_scn(elf_core_ctx_t *ctx, const Shdr *src, vnode_t *src_vp, Shdr *dst) /* * 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, @@ -1992,7 +1931,8 @@ elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr, 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); } @@ -2030,6 +1970,50 @@ elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr, (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) + continue; + + elf_ctx_resize_scratch(ctx, shdr->sh_size); + if (!shstrtab_ndx(shstr, 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; } @@ -2049,7 +2033,8 @@ elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr, 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; } @@ -2059,6 +2044,7 @@ elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr, count += 1; if (symtab != NULL) count += 2; + if (v == NULL || count == 0 || count > remain) { count = MIN(count, remain); goto done; @@ -2068,7 +2054,11 @@ elf_process_obj_scns(elf_core_ctx_t *ctx, vnode_t *mvp, caddr_t saddr, if (ctf != NULL) { elf_ctx_resize_scratch(ctx, ctf->sh_size); - v[idx].sh_name = shstrtab_ndx(shstrtab, STR_CTF); + if (!shstrtab_ndx(shstr, 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; @@ -2164,7 +2154,8 @@ elf_process_scns(elf_core_ctx_t *ctx, Shdr *v, uint_t nv, uint_t *nshdrsp) if (v != NULL) { ASSERT(nv != 0); - shstrtab_init(&shstrtab); + if (!shstrtab_init(&shstrtab)) + return (ENOMEM); remain = nv; } else { ASSERT(nv == 0); @@ -2212,6 +2203,10 @@ elf_process_scns(elf_core_ctx_t *ctx, Shdr *v, uint_t nv, uint_t *nshdrsp) 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); @@ -2228,6 +2223,7 @@ elf_process_scns(elf_core_ctx_t *ctx, Shdr *v, uint_t nv, uint_t *nshdrsp) /* Include room for the shrstrtab at the end */ *nshdrsp = idx + 1; } + /* No need to free up shstrtab so we can just return. */ return (0); } @@ -2235,10 +2231,15 @@ elf_process_scns(elf_core_ctx_t *ctx, Shdr *v, uint_t nv, uint_t *nshdrsp) 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; @@ -2255,6 +2256,9 @@ elf_process_scns(elf_core_ctx_t *ctx, Shdr *v, uint_t nv, uint_t *nshdrsp) ctx->ecc_doffset += v[idx].sh_size; } +done: + if (v != NULL) + shstrtab_fini(&shstrtab); return (error); } @@ -2302,9 +2306,8 @@ top: * 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); /* @@ -2317,10 +2320,11 @@ top: } /* - * 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); |
