/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Pcontrol.h" #include "P32ton.h" #include "Putil.h" /* * Pcore.c - Code to initialize a ps_prochandle from a core dump. We * allocate an additional structure to hold information from the core * file, and attach this to the standard ps_prochandle in place of the * ability to examine /proc// files. */ /* * Basic i/o function for reading and writing from the process address space * stored in the core file and associated shared libraries. We compute the * appropriate fd and offsets, and let the provided prw function do the rest. */ static ssize_t core_rw(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr, ssize_t (*prw)(int, void *, size_t, off64_t)) { ssize_t resid = n; while (resid != 0) { map_info_t *mp = Paddr2mptr(P, addr); uintptr_t mapoff; ssize_t len; off64_t off; int fd; if (mp == NULL) break; /* No mapping for this address */ if (mp->map_pmap.pr_mflags & MA_RESERVED1) { if (mp->map_file == NULL || mp->map_file->file_fd < 0) break; /* No file or file not open */ fd = mp->map_file->file_fd; } else fd = P->asfd; mapoff = addr - mp->map_pmap.pr_vaddr; len = MIN(resid, mp->map_pmap.pr_size - mapoff); off = mp->map_offset + mapoff; if ((len = prw(fd, buf, len, off)) <= 0) break; resid -= len; addr += len; buf = (char *)buf + len; } /* * Important: Be consistent with the behavior of i/o on the as file: * writing to an invalid address yields EIO; reading from an invalid * address falls through to returning success and zero bytes. */ if (resid == n && n != 0 && prw != pread64) { errno = EIO; return (-1); } return (n - resid); } static ssize_t Pread_core(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr) { return (core_rw(P, buf, n, addr, pread64)); } static ssize_t Pwrite_core(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr) { return (core_rw(P, (void *)buf, n, addr, (ssize_t (*)(int, void *, size_t, off64_t)) pwrite64)); } static const ps_rwops_t P_core_ops = { Pread_core, Pwrite_core }; /* * Return the lwp_info_t for the given lwpid. If no such lwpid has been * encountered yet, allocate a new structure and return a pointer to it. * Create a list of lwp_info_t structures sorted in decreasing lwp_id order. */ static lwp_info_t * lwpid2info(struct ps_prochandle *P, lwpid_t id) { lwp_info_t *lwp = list_next(&P->core->core_lwp_head); lwp_info_t *next; uint_t i; for (i = 0; i < P->core->core_nlwp; i++, lwp = list_next(lwp)) { if (lwp->lwp_id == id) { P->core->core_lwp = lwp; return (lwp); } if (lwp->lwp_id < id) { break; } } next = lwp; if ((lwp = calloc(1, sizeof (lwp_info_t))) == NULL) return (NULL); list_link(lwp, next); lwp->lwp_id = id; P->core->core_lwp = lwp; P->core->core_nlwp++; return (lwp); } /* * The core file itself contains a series of NOTE segments containing saved * structures from /proc at the time the process died. For each note we * comprehend, we define a function to read it in from the core file, * convert it to our native data model if necessary, and store it inside * the ps_prochandle. Each function is invoked by Pfgrab_core() with the * seek pointer on P->asfd positioned appropriately. We populate a table * of pointers to these note functions below. */ static int note_pstatus(struct ps_prochandle *P, size_t nbytes) { #ifdef _LP64 if (P->core->core_dmodel == PR_MODEL_ILP32) { pstatus32_t ps32; if (nbytes < sizeof (pstatus32_t) || read(P->asfd, &ps32, sizeof (ps32)) != sizeof (ps32)) goto err; pstatus_32_to_n(&ps32, &P->status); } else #endif if (nbytes < sizeof (pstatus_t) || read(P->asfd, &P->status, sizeof (pstatus_t)) != sizeof (pstatus_t)) goto err; P->orig_status = P->status; P->pid = P->status.pr_pid; return (0); err: dprintf("Pgrab_core: failed to read NT_PSTATUS\n"); return (-1); } static int note_lwpstatus(struct ps_prochandle *P, size_t nbytes) { lwp_info_t *lwp; lwpstatus_t lps; #ifdef _LP64 if (P->core->core_dmodel == PR_MODEL_ILP32) { lwpstatus32_t l32; if (nbytes < sizeof (lwpstatus32_t) || read(P->asfd, &l32, sizeof (l32)) != sizeof (l32)) goto err; lwpstatus_32_to_n(&l32, &lps); } else #endif if (nbytes < sizeof (lwpstatus_t) || read(P->asfd, &lps, sizeof (lps)) != sizeof (lps)) goto err; if ((lwp = lwpid2info(P, lps.pr_lwpid)) == NULL) { dprintf("Pgrab_core: failed to add NT_LWPSTATUS\n"); return (-1); } /* * Erase a useless and confusing artifact of the kernel implementation: * the lwps which did *not* create the core will show SIGKILL. We can * be assured this is bogus because SIGKILL can't produce core files. */ if (lps.pr_cursig == SIGKILL) lps.pr_cursig = 0; (void) memcpy(&lwp->lwp_status, &lps, sizeof (lps)); return (0); err: dprintf("Pgrab_core: failed to read NT_LWPSTATUS\n"); return (-1); } static int note_psinfo(struct ps_prochandle *P, size_t nbytes) { #ifdef _LP64 if (P->core->core_dmodel == PR_MODEL_ILP32) { psinfo32_t ps32; if (nbytes < sizeof (psinfo32_t) || read(P->asfd, &ps32, sizeof (ps32)) != sizeof (ps32)) goto err; psinfo_32_to_n(&ps32, &P->psinfo); } else #endif if (nbytes < sizeof (psinfo_t) || read(P->asfd, &P->psinfo, sizeof (psinfo_t)) != sizeof (psinfo_t)) goto err; dprintf("pr_fname = <%s>\n", P->psinfo.pr_fname); dprintf("pr_psargs = <%s>\n", P->psinfo.pr_psargs); dprintf("pr_wstat = 0x%x\n", P->psinfo.pr_wstat); return (0); err: dprintf("Pgrab_core: failed to read NT_PSINFO\n"); return (-1); } static int note_lwpsinfo(struct ps_prochandle *P, size_t nbytes) { lwp_info_t *lwp; lwpsinfo_t lps; #ifdef _LP64 if (P->core->core_dmodel == PR_MODEL_ILP32) { lwpsinfo32_t l32; if (nbytes < sizeof (lwpsinfo32_t) || read(P->asfd, &l32, sizeof (l32)) != sizeof (l32)) goto err; lwpsinfo_32_to_n(&l32, &lps); } else #endif if (nbytes < sizeof (lwpsinfo_t) || read(P->asfd, &lps, sizeof (lps)) != sizeof (lps)) goto err; if ((lwp = lwpid2info(P, lps.pr_lwpid)) == NULL) { dprintf("Pgrab_core: failed to add NT_LWPSINFO\n"); return (-1); } (void) memcpy(&lwp->lwp_psinfo, &lps, sizeof (lps)); return (0); err: dprintf("Pgrab_core: failed to read NT_LWPSINFO\n"); return (-1); } static int note_platform(struct ps_prochandle *P, size_t nbytes) { char *plat; if (P->core->core_platform != NULL) return (0); /* Already seen */ if (nbytes != 0 && ((plat = malloc(nbytes + 1)) != NULL)) { if (read(P->asfd, plat, nbytes) != nbytes) { dprintf("Pgrab_core: failed to read NT_PLATFORM\n"); free(plat); return (-1); } plat[nbytes - 1] = '\0'; P->core->core_platform = plat; } return (0); } static int note_utsname(struct ps_prochandle *P, size_t nbytes) { size_t ubytes = sizeof (struct utsname); struct utsname *utsp; if (P->core->core_uts != NULL || nbytes < ubytes) return (0); /* Already seen or bad size */ if ((utsp = malloc(ubytes)) == NULL) return (-1); if (read(P->asfd, utsp, ubytes) != ubytes) { dprintf("Pgrab_core: failed to read NT_UTSNAME\n"); free(utsp); return (-1); } if (_libproc_debug) { dprintf("uts.sysname = \"%s\"\n", utsp->sysname); dprintf("uts.nodename = \"%s\"\n", utsp->nodename); dprintf("uts.release = \"%s\"\n", utsp->release); dprintf("uts.version = \"%s\"\n", utsp->version); dprintf("uts.machine = \"%s\"\n", utsp->machine); } P->core->core_uts = utsp; return (0); } static int note_content(struct ps_prochandle *P, size_t nbytes) { core_content_t content; if (sizeof (P->core->core_content) != nbytes) return (-1); if (read(P->asfd, &content, sizeof (content)) != sizeof (content)) return (-1); P->core->core_content = content; dprintf("core content = %llx\n", content); return (0); } static int note_cred(struct ps_prochandle *P, size_t nbytes) { prcred_t *pcrp; int ngroups; const size_t min_size = sizeof (prcred_t) - sizeof (gid_t); /* * We allow for prcred_t notes that are actually smaller than a * prcred_t since the last member isn't essential if there are * no group memberships. This allows for more flexibility when it * comes to slightly malformed -- but still valid -- notes. */ if (P->core->core_cred != NULL || nbytes < min_size) return (0); /* Already seen or bad size */ ngroups = (nbytes - min_size) / sizeof (gid_t); nbytes = sizeof (prcred_t) + (ngroups - 1) * sizeof (gid_t); if ((pcrp = malloc(nbytes)) == NULL) return (-1); if (read(P->asfd, pcrp, nbytes) != nbytes) { dprintf("Pgrab_core: failed to read NT_PRCRED\n"); free(pcrp); return (-1); } if (pcrp->pr_ngroups > ngroups) { dprintf("pr_ngroups = %d; resetting to %d based on note size\n", pcrp->pr_ngroups, ngroups); pcrp->pr_ngroups = ngroups; } P->core->core_cred = pcrp; return (0); } #if defined(__i386) || defined(__amd64) static int note_ldt(struct ps_prochandle *P, size_t nbytes) { struct ssd *pldt; uint_t nldt; if (P->core->core_ldt != NULL || nbytes < sizeof (struct ssd)) return (0); /* Already seen or bad size */ nldt = nbytes / sizeof (struct ssd); nbytes = nldt * sizeof (struct ssd); if ((pldt = malloc(nbytes)) == NULL) return (-1); if (read(P->asfd, pldt, nbytes) != nbytes) { dprintf("Pgrab_core: failed to read NT_LDT\n"); free(pldt); return (-1); } P->core->core_ldt = pldt; P->core->core_nldt = nldt; return (0); } #endif /* __i386 */ static int note_priv(struct ps_prochandle *P, size_t nbytes) { prpriv_t *pprvp; if (P->core->core_priv != NULL || nbytes < sizeof (prpriv_t)) return (0); /* Already seen or bad size */ if ((pprvp = malloc(nbytes)) == NULL) return (-1); if (read(P->asfd, pprvp, nbytes) != nbytes) { dprintf("Pgrab_core: failed to read NT_PRPRIV\n"); free(pprvp); return (-1); } P->core->core_priv = pprvp; P->core->core_priv_size = nbytes; return (0); } static int note_priv_info(struct ps_prochandle *P, size_t nbytes) { extern void *__priv_parse_info(); priv_impl_info_t *ppii; if (P->core->core_privinfo != NULL || nbytes < sizeof (priv_impl_info_t)) return (0); /* Already seen or bad size */ if ((ppii = malloc(nbytes)) == NULL) return (-1); if (read(P->asfd, ppii, nbytes) != nbytes || PRIV_IMPL_INFO_SIZE(ppii) != nbytes) { dprintf("Pgrab_core: failed to read NT_PRPRIVINFO\n"); free(ppii); return (-1); } P->core->core_privinfo = __priv_parse_info(ppii); P->core->core_ppii = ppii; return (0); } static int note_zonename(struct ps_prochandle *P, size_t nbytes) { char *zonename; if (P->core->core_zonename != NULL) return (0); /* Already seen */ if (nbytes != 0) { if ((zonename = malloc(nbytes)) == NULL) return (-1); if (read(P->asfd, zonename, nbytes) != nbytes) { dprintf("Pgrab_core: failed to read NT_ZONENAME\n"); free(zonename); return (-1); } zonename[nbytes - 1] = '\0'; P->core->core_zonename = zonename; } return (0); } static int note_auxv(struct ps_prochandle *P, size_t nbytes) { size_t n, i; #ifdef _LP64 if (P->core->core_dmodel == PR_MODEL_ILP32) { auxv32_t *a32; n = nbytes / sizeof (auxv32_t); nbytes = n * sizeof (auxv32_t); a32 = alloca(nbytes); if (read(P->asfd, a32, nbytes) != nbytes) { dprintf("Pgrab_core: failed to read NT_AUXV\n"); return (-1); } if ((P->auxv = malloc(sizeof (auxv_t) * (n + 1))) == NULL) return (-1); for (i = 0; i < n; i++) auxv_32_to_n(&a32[i], &P->auxv[i]); } else { #endif n = nbytes / sizeof (auxv_t); nbytes = n * sizeof (auxv_t); if ((P->auxv = malloc(nbytes + sizeof (auxv_t))) == NULL) return (-1); if (read(P->asfd, P->auxv, nbytes) != nbytes) { free(P->auxv); P->auxv = NULL; return (-1); } #ifdef _LP64 } #endif if (_libproc_debug) { for (i = 0; i < n; i++) { dprintf("P->auxv[%lu] = ( %d, 0x%lx )\n", (ulong_t)i, P->auxv[i].a_type, P->auxv[i].a_un.a_val); } } /* * Defensive coding for loops which depend upon the auxv array being * terminated by an AT_NULL element; in each case, we've allocated * P->auxv to have an additional element which we force to be AT_NULL. */ P->auxv[n].a_type = AT_NULL; P->auxv[n].a_un.a_val = 0L; P->nauxv = (int)n; return (0); } #ifdef __sparc static int note_xreg(struct ps_prochandle *P, size_t nbytes) { lwp_info_t *lwp = P->core->core_lwp; size_t xbytes = sizeof (prxregset_t); prxregset_t *xregs; if (lwp == NULL || lwp->lwp_xregs != NULL || nbytes < xbytes) return (0); /* No lwp yet, already seen, or bad size */ if ((xregs = malloc(xbytes)) == NULL) return (-1); if (read(P->asfd, xregs, xbytes) != xbytes) { dprintf("Pgrab_core: failed to read NT_PRXREG\n"); free(xregs); return (-1); } lwp->lwp_xregs = xregs; return (0); } static int note_gwindows(struct ps_prochandle *P, size_t nbytes) { lwp_info_t *lwp = P->core->core_lwp; if (lwp == NULL || lwp->lwp_gwins != NULL || nbytes == 0) return (0); /* No lwp yet or already seen or no data */ if ((lwp->lwp_gwins = malloc(sizeof (gwindows_t))) == NULL) return (-1); /* * Since the amount of gwindows data varies with how many windows were * actually saved, we just read up to the minimum of the note size * and the size of the gwindows_t type. It doesn't matter if the read * fails since we have to zero out gwindows first anyway. */ #ifdef _LP64 if (P->core->core_dmodel == PR_MODEL_ILP32) { gwindows32_t g32; (void) memset(&g32, 0, sizeof (g32)); (void) read(P->asfd, &g32, MIN(nbytes, sizeof (g32))); gwindows_32_to_n(&g32, lwp->lwp_gwins); } else { #endif (void) memset(lwp->lwp_gwins, 0, sizeof (gwindows_t)); (void) read(P->asfd, lwp->lwp_gwins, MIN(nbytes, sizeof (gwindows_t))); #ifdef _LP64 } #endif return (0); } #ifdef __sparcv9 static int note_asrs(struct ps_prochandle *P, size_t nbytes) { lwp_info_t *lwp = P->core->core_lwp; int64_t *asrs; if (lwp == NULL || lwp->lwp_asrs != NULL || nbytes < sizeof (asrset_t)) return (0); /* No lwp yet, already seen, or bad size */ if ((asrs = malloc(sizeof (asrset_t))) == NULL) return (-1); if (read(P->asfd, asrs, sizeof (asrset_t)) != sizeof (asrset_t)) { dprintf("Pgrab_core: failed to read NT_ASRS\n"); free(asrs); return (-1); } lwp->lwp_asrs = asrs; return (0); } #endif /* __sparcv9 */ #endif /* __sparc */ /*ARGSUSED*/ static int note_notsup(struct ps_prochandle *P, size_t nbytes) { dprintf("skipping unsupported note type\n"); return (0); } /* * Populate a table of function pointers indexed by Note type with our * functions to process each type of core file note: */ static int (*nhdlrs[])(struct ps_prochandle *, size_t) = { note_notsup, /* 0 unassigned */ note_notsup, /* 1 NT_PRSTATUS (old) */ note_notsup, /* 2 NT_PRFPREG (old) */ note_notsup, /* 3 NT_PRPSINFO (old) */ #ifdef __sparc note_xreg, /* 4 NT_PRXREG */ #else note_notsup, /* 4 NT_PRXREG */ #endif note_platform, /* 5 NT_PLATFORM */ note_auxv, /* 6 NT_AUXV */ #ifdef __sparc note_gwindows, /* 7 NT_GWINDOWS */ #ifdef __sparcv9 note_asrs, /* 8 NT_ASRS */ #else note_notsup, /* 8 NT_ASRS */ #endif #else note_notsup, /* 7 NT_GWINDOWS */ note_notsup, /* 8 NT_ASRS */ #endif #if defined(__i386) || defined(__amd64) note_ldt, /* 9 NT_LDT */ #else note_notsup, /* 9 NT_LDT */ #endif note_pstatus, /* 10 NT_PSTATUS */ note_notsup, /* 11 unassigned */ note_notsup, /* 12 unassigned */ note_psinfo, /* 13 NT_PSINFO */ note_cred, /* 14 NT_PRCRED */ note_utsname, /* 15 NT_UTSNAME */ note_lwpstatus, /* 16 NT_LWPSTATUS */ note_lwpsinfo, /* 17 NT_LWPSINFO */ note_priv, /* 18 NT_PRPRIV */ note_priv_info, /* 19 NT_PRPRIVINFO */ note_content, /* 20 NT_CONTENT */ note_zonename, /* 21 NT_ZONENAME */ }; /* * Add information on the address space mapping described by the given * PT_LOAD program header. We fill in more information on the mapping later. */ static int core_add_mapping(struct ps_prochandle *P, GElf_Phdr *php) { int err = 0; prmap_t pmap; dprintf("mapping base %llx filesz %llu memsz %llu offset %llu\n", (u_longlong_t)php->p_vaddr, (u_longlong_t)php->p_filesz, (u_longlong_t)php->p_memsz, (u_longlong_t)php->p_offset); pmap.pr_vaddr = (uintptr_t)php->p_vaddr; pmap.pr_size = php->p_memsz; /* * If Pgcore() or elfcore() fail to write a mapping, they will set * PF_SUNW_FAILURE in the Phdr and try to stash away the errno for us. */ if (php->p_flags & PF_SUNW_FAILURE) { (void) pread64(P->asfd, &err, sizeof (err), (off64_t)php->p_offset); Perror_printf(P, "core file data for mapping at %p not saved: " "%s\n", (void *)(uintptr_t)php->p_vaddr, strerror(err)); dprintf("core file data for mapping at %p not saved: %s\n", (void *)(uintptr_t)php->p_vaddr, strerror(err)); } else if (php->p_filesz != 0 && php->p_offset >= P->core->core_size) { Perror_printf(P, "core file may be corrupt -- data for mapping " "at %p is missing\n", (void *)(uintptr_t)php->p_vaddr); dprintf("core file may be corrupt -- data for mapping " "at %p is missing\n", (void *)(uintptr_t)php->p_vaddr); } /* * The mapping name and offset will hopefully be filled in * by the librtld_db agent. Unfortunately, if it isn't a * shared library mapping, this information is gone forever. */ pmap.pr_mapname[0] = '\0'; pmap.pr_offset = 0; pmap.pr_mflags = 0; if (php->p_flags & PF_R) pmap.pr_mflags |= MA_READ; if (php->p_flags & PF_W) pmap.pr_mflags |= MA_WRITE; if (php->p_flags & PF_X) pmap.pr_mflags |= MA_EXEC; if (php->p_filesz == 0) pmap.pr_mflags |= MA_RESERVED1; /* * At the time of adding this mapping, we just zero the pagesize. * Once we've processed more of the core file, we'll have the * pagesize from the auxv's AT_PAGESZ element and we can fill this in. */ pmap.pr_pagesize = 0; /* * Unfortunately whether or not the mapping was a System V * shared memory segment is lost. We use -1 to mark it as not shm. */ pmap.pr_shmid = -1; return (Padd_mapping(P, php->p_offset, NULL, &pmap)); } /* * Given a virtual address, name the mapping at that address using the * specified name, and return the map_info_t pointer. */ static map_info_t * core_name_mapping(struct ps_prochandle *P, uintptr_t addr, const char *name) { map_info_t *mp = Paddr2mptr(P, addr); if (mp != NULL) { (void) strncpy(mp->map_pmap.pr_mapname, name, PRMAPSZ); mp->map_pmap.pr_mapname[PRMAPSZ - 1] = '\0'; } return (mp); } /* * libproc uses libelf for all of its symbol table manipulation. This function * takes a symbol table and string table from a core file and places them * in a memory backed elf file. */ static void fake_up_symtab(struct ps_prochandle *P, const elf_file_header_t *ehdr, GElf_Shdr *symtab, GElf_Shdr *strtab) { size_t size; off64_t off, base; map_info_t *mp; file_info_t *fp; Elf_Scn *scn; Elf_Data *data; if (symtab->sh_addr == 0 || (mp = Paddr2mptr(P, symtab->sh_addr)) == NULL || (fp = mp->map_file) == NULL || fp->file_symtab.sym_data_pri != NULL) { dprintf("fake_up_symtab: invalid section\n"); return; } if (P->status.pr_dmodel == PR_MODEL_ILP32) { struct { Elf32_Ehdr ehdr; Elf32_Shdr shdr[3]; char data[1]; } *b; base = sizeof (b->ehdr) + sizeof (b->shdr); size = base + symtab->sh_size + strtab->sh_size; if ((b = calloc(1, size)) == NULL) return; (void) memcpy(b->ehdr.e_ident, ehdr->e_ident, sizeof (ehdr->e_ident)); b->ehdr.e_type = ehdr->e_type; b->ehdr.e_machine = ehdr->e_machine; b->ehdr.e_version = ehdr->e_version; b->ehdr.e_flags = ehdr->e_flags; b->ehdr.e_ehsize = sizeof (b->ehdr); b->ehdr.e_shoff = sizeof (b->ehdr); b->ehdr.e_shentsize = sizeof (b->shdr[0]); b->ehdr.e_shnum = 3; off = 0; b->shdr[1].sh_size = symtab->sh_size; b->shdr[1].sh_type = SHT_SYMTAB; b->shdr[1].sh_offset = off + base; b->shdr[1].sh_entsize = sizeof (Elf32_Sym); b->shdr[1].sh_link = 2; b->shdr[1].sh_info = symtab->sh_info; b->shdr[1].sh_addralign = symtab->sh_addralign; if (pread64(P->asfd, &b->data[off], b->shdr[1].sh_size, symtab->sh_offset) != b->shdr[1].sh_size) { dprintf("fake_up_symtab: pread of symtab[1] failed\n"); free(b); return; } off += b->shdr[1].sh_size; b->shdr[2].sh_flags = SHF_STRINGS; b->shdr[2].sh_size = strtab->sh_size; b->shdr[2].sh_type = SHT_STRTAB; b->shdr[2].sh_offset = off + base; b->shdr[2].sh_info = strtab->sh_info; b->shdr[2].sh_addralign = 1; if (pread64(P->asfd, &b->data[off], b->shdr[2].sh_size, strtab->sh_offset) != b->shdr[2].sh_size) { dprintf("fake_up_symtab: pread of symtab[2] failed\n"); free(b); return; } off += b->shdr[2].sh_size; fp->file_symtab.sym_elf = elf_memory((char *)b, size); if (fp->file_symtab.sym_elf == NULL) { free(b); return; } fp->file_symtab.sym_elfmem = b; #ifdef _LP64 } else { struct { Elf64_Ehdr ehdr; Elf64_Shdr shdr[3]; char data[1]; } *b; base = sizeof (b->ehdr) + sizeof (b->shdr); size = base + symtab->sh_size + strtab->sh_size; if ((b = calloc(1, size)) == NULL) return; (void) memcpy(b->ehdr.e_ident, ehdr->e_ident, sizeof (ehdr->e_ident)); b->ehdr.e_type = ehdr->e_type; b->ehdr.e_machine = ehdr->e_machine; b->ehdr.e_version = ehdr->e_version; b->ehdr.e_flags = ehdr->e_flags; b->ehdr.e_ehsize = sizeof (b->ehdr); b->ehdr.e_shoff = sizeof (b->ehdr); b->ehdr.e_shentsize = sizeof (b->shdr[0]); b->ehdr.e_shnum = 3; off = 0; b->shdr[1].sh_size = symtab->sh_size; b->shdr[1].sh_type = SHT_SYMTAB; b->shdr[1].sh_offset = off + base; b->shdr[1].sh_entsize = sizeof (Elf64_Sym); b->shdr[1].sh_link = 2; b->shdr[1].sh_info = symtab->sh_info; b->shdr[1].sh_addralign = symtab->sh_addralign; if (pread64(P->asfd, &b->data[off], b->shdr[1].sh_size, symtab->sh_offset) != b->shdr[1].sh_size) { free(b); return; } off += b->shdr[1].sh_size; b->shdr[2].sh_flags = SHF_STRINGS; b->shdr[2].sh_size = strtab->sh_size; b->shdr[2].sh_type = SHT_STRTAB; b->shdr[2].sh_offset = off + base; b->shdr[2].sh_info = strtab->sh_info; b->shdr[2].sh_addralign = 1; if (pread64(P->asfd, &b->data[off], b->shdr[2].sh_size, strtab->sh_offset) != b->shdr[2].sh_size) { free(b); return; } off += b->shdr[2].sh_size; fp->file_symtab.sym_elf = elf_memory((char *)b, size); if (fp->file_symtab.sym_elf == NULL) { free(b); return; } fp->file_symtab.sym_elfmem = b; #endif } if ((scn = elf_getscn(fp->file_symtab.sym_elf, 1)) == NULL || (fp->file_symtab.sym_data_pri = elf_getdata(scn, NULL)) == NULL || (scn = elf_getscn(fp->file_symtab.sym_elf, 2)) == NULL || (data = elf_getdata(scn, NULL)) == NULL) { dprintf("fake_up_symtab: failed to get section data at %p\n", (void *)scn); goto err; } fp->file_symtab.sym_strs = data->d_buf; fp->file_symtab.sym_strsz = data->d_size; fp->file_symtab.sym_symn = symtab->sh_size / symtab->sh_entsize; fp->file_symtab.sym_hdr_pri = *symtab; fp->file_symtab.sym_strhdr = *strtab; optimize_symtab(&fp->file_symtab); return; err: (void) elf_end(fp->file_symtab.sym_elf); free(fp->file_symtab.sym_elfmem); fp->file_symtab.sym_elf = NULL; fp->file_symtab.sym_elfmem = NULL; } static void core_phdr_to_gelf(const Elf32_Phdr *src, GElf_Phdr *dst) { dst->p_type = src->p_type; dst->p_flags = src->p_flags; dst->p_offset = (Elf64_Off)src->p_offset; dst->p_vaddr = (Elf64_Addr)src->p_vaddr; dst->p_paddr = (Elf64_Addr)src->p_paddr; dst->p_filesz = (Elf64_Xword)src->p_filesz; dst->p_memsz = (Elf64_Xword)src->p_memsz; dst->p_align = (Elf64_Xword)src->p_align; } static void core_shdr_to_gelf(const Elf32_Shdr *src, GElf_Shdr *dst) { dst->sh_name = src->sh_name; dst->sh_type = src->sh_type; dst->sh_flags = (Elf64_Xword)src->sh_flags; dst->sh_addr = (Elf64_Addr)src->sh_addr; dst->sh_offset = (Elf64_Off)src->sh_offset; dst->sh_size = (Elf64_Xword)src->sh_size; dst->sh_link = src->sh_link; dst->sh_info = src->sh_info; dst->sh_addralign = (Elf64_Xword)src->sh_addralign; dst->sh_entsize = (Elf64_Xword)src->sh_entsize; } /* * Perform elf_begin on efp->e_fd and verify the ELF file's type and class. */ static int core_elf_fdopen(elf_file_t *efp, GElf_Half type, int *perr) { #ifdef _BIG_ENDIAN uchar_t order = ELFDATA2MSB; #else uchar_t order = ELFDATA2LSB; #endif Elf32_Ehdr e32; int is_noelf = -1; int isa_err = 0; /* * Because 32-bit libelf cannot deal with large files, we need to read, * check, and convert the file header manually in case type == ET_CORE. */ if (pread64(efp->e_fd, &e32, sizeof (e32), 0) != sizeof (e32)) { if (perr != NULL) *perr = G_FORMAT; goto err; } if ((is_noelf = memcmp(&e32.e_ident[EI_MAG0], ELFMAG, SELFMAG)) != 0 || e32.e_type != type || (isa_err = (e32.e_ident[EI_DATA] != order)) || e32.e_version != EV_CURRENT) { if (perr != NULL) { if (is_noelf == 0 && isa_err) { *perr = G_ISAINVAL; } else { *perr = G_FORMAT; } } goto err; } /* * If the file is 64-bit and we are 32-bit, fail with G_LP64. If the * file is 64-bit and we are 64-bit, re-read the header as a Elf64_Ehdr, * and convert it to a elf_file_header_t. Otherwise, the file is * 32-bit, so convert e32 to a elf_file_header_t. */ if (e32.e_ident[EI_CLASS] == ELFCLASS64) { #ifdef _LP64 Elf64_Ehdr e64; if (pread64(efp->e_fd, &e64, sizeof (e64), 0) != sizeof (e64)) { if (perr != NULL) *perr = G_FORMAT; goto err; } (void) memcpy(efp->e_hdr.e_ident, e64.e_ident, EI_NIDENT); efp->e_hdr.e_type = e64.e_type; efp->e_hdr.e_machine = e64.e_machine; efp->e_hdr.e_version = e64.e_version; efp->e_hdr.e_entry = e64.e_entry; efp->e_hdr.e_phoff = e64.e_phoff; efp->e_hdr.e_shoff = e64.e_shoff; efp->e_hdr.e_flags = e64.e_flags; efp->e_hdr.e_ehsize = e64.e_ehsize; efp->e_hdr.e_phentsize = e64.e_phentsize; efp->e_hdr.e_phnum = (Elf64_Word)e64.e_phnum; efp->e_hdr.e_shentsize = e64.e_shentsize; efp->e_hdr.e_shnum = (Elf64_Word)e64.e_shnum; efp->e_hdr.e_shstrndx = (Elf64_Word)e64.e_shstrndx; #else /* _LP64 */ if (perr != NULL) *perr = G_LP64; goto err; #endif /* _LP64 */ } else { (void) memcpy(efp->e_hdr.e_ident, e32.e_ident, EI_NIDENT); efp->e_hdr.e_type = e32.e_type; efp->e_hdr.e_machine = e32.e_machine; efp->e_hdr.e_version = e32.e_version; efp->e_hdr.e_entry = (Elf64_Addr)e32.e_entry; efp->e_hdr.e_phoff = (Elf64_Off)e32.e_phoff; efp->e_hdr.e_shoff = (Elf64_Off)e32.e_shoff; efp->e_hdr.e_flags = e32.e_flags; efp->e_hdr.e_ehsize = e32.e_ehsize; efp->e_hdr.e_phentsize = e32.e_phentsize; efp->e_hdr.e_phnum = (Elf64_Word)e32.e_phnum; efp->e_hdr.e_shentsize = e32.e_shentsize; efp->e_hdr.e_shnum = (Elf64_Word)e32.e_shnum; efp->e_hdr.e_shstrndx = (Elf64_Word)e32.e_shstrndx; } /* * If the number of section headers or program headers or the section * header string table index would overflow their respective fields * in the ELF header, they're stored in the section header at index * zero. To simplify use elsewhere, we look for those sentinel values * here. */ if ((efp->e_hdr.e_shnum == 0 && efp->e_hdr.e_shoff != 0) || efp->e_hdr.e_shstrndx == SHN_XINDEX || efp->e_hdr.e_phnum == PN_XNUM) { GElf_Shdr shdr; dprintf("extended ELF header\n"); if (efp->e_hdr.e_shoff == 0) { if (perr != NULL) *perr = G_FORMAT; goto err; } if (efp->e_hdr.e_ident[EI_CLASS] == ELFCLASS32) { Elf32_Shdr shdr32; if (pread64(efp->e_fd, &shdr32, sizeof (shdr32), efp->e_hdr.e_shoff) != sizeof (shdr32)) { if (perr != NULL) *perr = G_FORMAT; goto err; } core_shdr_to_gelf(&shdr32, &shdr); } else { if (pread64(efp->e_fd, &shdr, sizeof (shdr), efp->e_hdr.e_shoff) != sizeof (shdr)) { if (perr != NULL) *perr = G_FORMAT; goto err; } } if (efp->e_hdr.e_shnum == 0) { efp->e_hdr.e_shnum = shdr.sh_size; dprintf("section header count %lu\n", (ulong_t)shdr.sh_size); } if (efp->e_hdr.e_shstrndx == SHN_XINDEX) { efp->e_hdr.e_shstrndx = shdr.sh_link; dprintf("section string index %u\n", shdr.sh_link); } if (efp->e_hdr.e_phnum == PN_XNUM && shdr.sh_info != 0) { efp->e_hdr.e_phnum = shdr.sh_info; dprintf("program header count %u\n", shdr.sh_info); } } else if (efp->e_hdr.e_phoff != 0) { GElf_Phdr phdr; uint64_t phnum; /* * It's possible this core file came from a system that * accidentally truncated the e_phnum field without correctly * using the extended format in the section header at index * zero. We try to detect and correct that specific type of * corruption by using the knowledge that the core dump * routines usually place the data referenced by the first * program header immediately after the last header element. */ if (efp->e_hdr.e_ident[EI_CLASS] == ELFCLASS32) { Elf32_Phdr phdr32; if (pread64(efp->e_fd, &phdr32, sizeof (phdr32), efp->e_hdr.e_phoff) != sizeof (phdr32)) { if (perr != NULL) *perr = G_FORMAT; goto err; } core_phdr_to_gelf(&phdr32, &phdr); } else { if (pread64(efp->e_fd, &phdr, sizeof (phdr), efp->e_hdr.e_phoff) != sizeof (phdr)) { if (perr != NULL) *perr = G_FORMAT; goto err; } } phnum = phdr.p_offset - efp->e_hdr.e_ehsize - (uint64_t)efp->e_hdr.e_shnum * efp->e_hdr.e_shentsize; phnum /= efp->e_hdr.e_phentsize; if (phdr.p_offset != 0 && phnum != efp->e_hdr.e_phnum) { dprintf("suspicious program header count %u %u\n", (uint_t)phnum, efp->e_hdr.e_phnum); /* * If the new program header count we computed doesn't * jive with count in the ELF header, we'll use the * data that's there and hope for the best. * * If it does, it's also possible that the section * header offset is incorrect; we'll check that and * possibly try to fix it. */ if (phnum <= INT_MAX && (uint16_t)phnum == efp->e_hdr.e_phnum) { if (efp->e_hdr.e_shoff == efp->e_hdr.e_phoff + efp->e_hdr.e_phentsize * (uint_t)efp->e_hdr.e_phnum) { efp->e_hdr.e_shoff = efp->e_hdr.e_phoff + efp->e_hdr.e_phentsize * phnum; } efp->e_hdr.e_phnum = (Elf64_Word)phnum; dprintf("using new program header count\n"); } else { dprintf("inconsistent program header count\n"); } } } /* * The libelf implementation was never ported to be large-file aware. * This is typically not a problem for your average executable or * shared library, but a large 32-bit core file can exceed 2GB in size. * So if type is ET_CORE, we don't bother doing elf_begin; the code * in Pfgrab_core() below will do its own i/o and struct conversion. */ if (type == ET_CORE) { efp->e_elf = NULL; return (0); } if ((efp->e_elf = elf_begin(efp->e_fd, ELF_C_READ, NULL)) == NULL) { if (perr != NULL) *perr = G_ELF; goto err; } return (0); err: efp->e_elf = NULL; return (-1); } /* * Open the specified file and then do a core_elf_fdopen on it. */ static int core_elf_open(elf_file_t *efp, const char *path, GElf_Half type, int *perr) { (void) memset(efp, 0, sizeof (elf_file_t)); if ((efp->e_fd = open64(path, O_RDONLY)) >= 0) { if (core_elf_fdopen(efp, type, perr) == 0) return (0); (void) close(efp->e_fd); efp->e_fd = -1; } return (-1); } /* * Close the ELF handle and file descriptor. */ static void core_elf_close(elf_file_t *efp) { if (efp->e_elf != NULL) { (void) elf_end(efp->e_elf); efp->e_elf = NULL; } if (efp->e_fd != -1) { (void) close(efp->e_fd); efp->e_fd = -1; } } /* * Given an ELF file for a statically linked executable, locate the likely * primary text section and fill in rl_base with its virtual address. */ static map_info_t * core_find_text(struct ps_prochandle *P, Elf *elf, rd_loadobj_t *rlp) { GElf_Phdr phdr; uint_t i; size_t nphdrs; if (elf_getphnum(elf, &nphdrs) == 0) return (NULL); for (i = 0; i < nphdrs; i++) { if (gelf_getphdr(elf, i, &phdr) != NULL && phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X)) { rlp->rl_base = phdr.p_vaddr; return (Paddr2mptr(P, rlp->rl_base)); } } return (NULL); } /* * Given an ELF file and the librtld_db structure corresponding to its primary * text mapping, deduce where its data segment was loaded and fill in * rl_data_base and prmap_t.pr_offset accordingly. */ static map_info_t * core_find_data(struct ps_prochandle *P, Elf *elf, rd_loadobj_t *rlp) { GElf_Ehdr ehdr; GElf_Phdr phdr; map_info_t *mp; uint_t i, pagemask; size_t nphdrs; rlp->rl_data_base = NULL; /* * Find the first loadable, writeable Phdr and compute rl_data_base * as the virtual address at which is was loaded. */ if (gelf_getehdr(elf, &ehdr) == NULL || elf_getphnum(elf, &nphdrs) == 0) return (NULL); for (i = 0; i < nphdrs; i++) { if (gelf_getphdr(elf, i, &phdr) != NULL && phdr.p_type == PT_LOAD && (phdr.p_flags & PF_W)) { rlp->rl_data_base = phdr.p_vaddr; if (ehdr.e_type == ET_DYN) rlp->rl_data_base += rlp->rl_base; break; } } /* * If we didn't find an appropriate phdr or if the address we * computed has no mapping, return NULL. */ if (rlp->rl_data_base == NULL || (mp = Paddr2mptr(P, rlp->rl_data_base)) == NULL) return (NULL); /* * It wouldn't be procfs-related code if we didn't make use of * unclean knowledge of segvn, even in userland ... the prmap_t's * pr_offset field will be the segvn offset from mmap(2)ing the * data section, which will be the file offset & PAGEMASK. */ pagemask = ~(mp->map_pmap.pr_pagesize - 1); mp->map_pmap.pr_offset = phdr.p_offset & pagemask; return (mp); } /* * Librtld_db agent callback for iterating over load object mappings. * For each load object, we allocate a new file_info_t, perform naming, * and attempt to construct a symbol table for the load object. */ static int core_iter_mapping(const rd_loadobj_t *rlp, struct ps_prochandle *P) { char lname[PATH_MAX]; file_info_t *fp; map_info_t *mp; if (Pread_string(P, lname, PATH_MAX, (off_t)rlp->rl_nameaddr) <= 0) { dprintf("failed to read name %p\n", (void *)rlp->rl_nameaddr); return (1); /* Keep going; forget this if we can't get a name */ } dprintf("rd_loadobj name = \"%s\" rl_base = %p\n", lname, (void *)rlp->rl_base); if ((mp = Paddr2mptr(P, rlp->rl_base)) == NULL) { dprintf("no mapping for %p\n", (void *)rlp->rl_base); return (1); /* No mapping; advance to next mapping */ } if ((fp = mp->map_file) == NULL) { if ((fp = malloc(sizeof (file_info_t))) == NULL) { P->core->core_errno = errno; dprintf("failed to malloc mapping data\n"); return (0); /* Abort */ } (void) memset(fp, 0, sizeof (file_info_t)); list_link(fp, &P->file_head); mp->map_file = fp; P->num_files++; fp->file_ref = 1; fp->file_fd = -1; } if ((fp->file_lo = malloc(sizeof (rd_loadobj_t))) == NULL) { P->core->core_errno = errno; dprintf("failed to malloc mapping data\n"); return (0); /* Abort */ } *fp->file_lo = *rlp; if (fp->file_lname == NULL && strcmp(mp->map_pmap.pr_mapname, "a.out") == 0) { /* * Naming dance part 1: if the file_info_t is unnamed and * it represents the main executable, name it after the * execname. */ fp->file_lname = P->execname ? strdup(P->execname) : strdup("a.out"); } if (lname[0] != '\0') { /* * Naming dance part 2: if we got a name from librtld_db, then * copy this name to the prmap_t if it is unnamed. If the * file_info_t is unnamed, name it after the lname. */ if (mp->map_pmap.pr_mapname[0] == '\0') { (void) strncpy(mp->map_pmap.pr_mapname, lname, PRMAPSZ); mp->map_pmap.pr_mapname[PRMAPSZ - 1] = '\0'; } if (fp->file_lname == NULL) fp->file_lname = strdup(lname); } else if (fp->file_lname == NULL && mp->map_pmap.pr_mapname[0] != '\0') { /* * Naming dance part 3: if the mapping is named and the * file_info_t is not, name the file after the mapping. */ fp->file_lname = strdup(mp->map_pmap.pr_mapname); } if (fp->file_lname != NULL) fp->file_lbase = basename(fp->file_lname); /* * Associate the file and the mapping, and attempt to build * a symbol table for this file. */ (void) strcpy(fp->file_pname, mp->map_pmap.pr_mapname); fp->file_map = mp; Pbuild_file_symtab(P, fp); if (fp->file_elf == NULL) { dprintf("core_iter_mapping: no symtab - going to next\n"); return (1); /* No symbol table; advance to next mapping */ } /* * Locate the start of a data segment associated with this file. * Starting with that data segment, name all mappings that * fall within this file's address range after the file and * establish their mp->map_file links. */ if ((mp = core_find_data(P, fp->file_elf, fp->file_lo)) != NULL) { dprintf("found data for %s at %p (pr_offset 0x%llx)\n", fp->file_pname, (void *)fp->file_lo->rl_data_base, mp->map_pmap.pr_offset); for (; mp < P->mappings + P->map_count; mp++) { if (mp->map_pmap.pr_vaddr > fp->file_lo->rl_bend) break; if (mp->map_file == NULL) { dprintf("%s: associating segment at %p\n", fp->file_pname, (void *)mp->map_pmap.pr_vaddr); mp->map_file = fp; fp->file_ref++; } else { dprintf("%s: segment at %p already associated " "with %s\n", fp->file_pname, (void *)mp->map_pmap.pr_vaddr, mp->map_file->file_pname); } if (!(mp->map_pmap.pr_mflags & MA_BREAK)) (void) strcpy(mp->map_pmap.pr_mapname, fp->file_pname); } } else { dprintf("core_iter_mapping: no data found for %s\n", fp->file_pname); } return (1); /* Advance to next mapping */ } /* * Callback function for Pfindexec(). In order to confirm a given pathname, * we verify that we can open it as an ELF file of type ET_EXEC. */ static int core_exec_open(const char *path, void *efp) { return (core_elf_open(efp, path, ET_EXEC, NULL) == 0); } /* * Attempt to load any section headers found in the core file. If present, * this will refer to non-loadable data added to the core file by the kernel * based on coreadm(1M) settings, including CTF data and the symbol table. */ static void core_load_shdrs(struct ps_prochandle *P, elf_file_t *efp) { GElf_Shdr *shp, *shdrs = NULL; char *shstrtab = NULL; ulong_t shstrtabsz; const char *name; map_info_t *mp; size_t nbytes; void *buf; int i; if (efp->e_hdr.e_shstrndx >= efp->e_hdr.e_shnum) { dprintf("corrupt shstrndx (%u) exceeds shnum (%u)\n", efp->e_hdr.e_shstrndx, efp->e_hdr.e_shnum); return; } /* * Read the section header table from the core file and then iterate * over the section headers, converting each to a GElf_Shdr. */ if ((shdrs = malloc(efp->e_hdr.e_shnum * sizeof (GElf_Shdr))) == NULL) { dprintf("failed to malloc %u section headers: %s\n", (uint_t)efp->e_hdr.e_shnum, strerror(errno)); return; } nbytes = efp->e_hdr.e_shnum * efp->e_hdr.e_shentsize; if ((buf = malloc(nbytes)) == NULL) { dprintf("failed to malloc %d bytes: %s\n", (int)nbytes, strerror(errno)); free(shdrs); goto out; } if (pread64(efp->e_fd, buf, nbytes, efp->e_hdr.e_shoff) != nbytes) { dprintf("failed to read section headers at off %lld: %s\n", (longlong_t)efp->e_hdr.e_shoff, strerror(errno)); free(buf); goto out; } for (i = 0; i < efp->e_hdr.e_shnum; i++) { void *p = (uchar_t *)buf + efp->e_hdr.e_shentsize * i; if (efp->e_hdr.e_ident[EI_CLASS] == ELFCLASS32) core_shdr_to_gelf(p, &shdrs[i]); else (void) memcpy(&shdrs[i], p, sizeof (GElf_Shdr)); } free(buf); buf = NULL; /* * Read the .shstrtab section from the core file, terminating it with * an extra \0 so that a corrupt section will not cause us to die. */ shp = &shdrs[efp->e_hdr.e_shstrndx]; shstrtabsz = shp->sh_size; if ((shstrtab = malloc(shstrtabsz + 1)) == NULL) { dprintf("failed to allocate %lu bytes for shstrtab\n", (ulong_t)shstrtabsz); goto out; } if (pread64(efp->e_fd, shstrtab, shstrtabsz, shp->sh_offset) != shstrtabsz) { dprintf("failed to read %lu bytes of shstrs at off %lld: %s\n", shstrtabsz, (longlong_t)shp->sh_offset, strerror(errno)); goto out; } shstrtab[shstrtabsz] = '\0'; /* * Now iterate over each section in the section header table, locating * sections of interest and initializing more of the ps_prochandle. */ for (i = 0; i < efp->e_hdr.e_shnum; i++) { shp = &shdrs[i]; name = shstrtab + shp->sh_name; if (shp->sh_name >= shstrtabsz) { dprintf("skipping section [%d]: corrupt sh_name\n", i); continue; } if (shp->sh_link >= efp->e_hdr.e_shnum) { dprintf("skipping section [%d]: corrupt sh_link\n", i); continue; } dprintf("found section header %s (sh_addr 0x%llx)\n", name, (u_longlong_t)shp->sh_addr); if (strcmp(name, ".SUNW_ctf") == 0) { if ((mp = Paddr2mptr(P, shp->sh_addr)) == NULL) { dprintf("no map at addr 0x%llx for %s [%d]\n", (u_longlong_t)shp->sh_addr, name, i); continue; } if (mp->map_file == NULL || mp->map_file->file_ctf_buf != NULL) { dprintf("no mapping file or duplicate buffer " "for %s [%d]\n", name, i); continue; } if ((buf = malloc(shp->sh_size)) == NULL || pread64(efp->e_fd, buf, shp->sh_size, shp->sh_offset) != shp->sh_size) { dprintf("skipping section %s [%d]: %s\n", name, i, strerror(errno)); free(buf); continue; } mp->map_file->file_ctf_size = shp->sh_size; mp->map_file->file_ctf_buf = buf; if (shdrs[shp->sh_link].sh_type == SHT_DYNSYM) mp->map_file->file_ctf_dyn = 1; } else if (strcmp(name, ".symtab") == 0) { fake_up_symtab(P, &efp->e_hdr, shp, &shdrs[shp->sh_link]); } } out: free(shstrtab); free(shdrs); } /* * Main engine for core file initialization: given an fd for the core file * and an optional pathname, construct the ps_prochandle. The aout_path can * either be a suggested executable pathname, or a suggested directory to * use as a possible current working directory. */ struct ps_prochandle * Pfgrab_core(int core_fd, const char *aout_path, int *perr) { struct ps_prochandle *P; map_info_t *stk_mp, *brk_mp; const char *execname; char *interp; int i, notes, pagesize; uintptr_t addr, base_addr; struct stat64 stbuf; void *phbuf, *php; size_t nbytes; elf_file_t aout; elf_file_t core; Elf_Scn *scn, *intp_scn = NULL; Elf_Data *dp; GElf_Phdr phdr, note_phdr; GElf_Shdr shdr; GElf_Xword nleft; if (elf_version(EV_CURRENT) == EV_NONE) { dprintf("libproc ELF version is more recent than libelf\n"); *perr = G_ELF; return (NULL); } aout.e_elf = NULL; aout.e_fd = -1; core.e_elf = NULL; core.e_fd = core_fd; /* * Allocate and initialize a ps_prochandle structure for the core. * There are several key pieces of initialization here: * * 1. The PS_DEAD state flag marks this prochandle as a core file. * PS_DEAD also thus prevents all operations which require state * to be PS_STOP from operating on this handle. * * 2. We keep the core file fd in P->asfd since the core file contains * the remnants of the process address space. * * 3. We set the P->info_valid bit because all information about the * core is determined by the end of this function; there is no need * for proc_update_maps() to reload mappings at any later point. * * 4. The read/write ops vector uses our core_rw() function defined * above to handle i/o requests. */ if ((P = malloc(sizeof (struct ps_prochandle))) == NULL) { *perr = G_STRANGE; return (NULL); } (void) memset(P, 0, sizeof (struct ps_prochandle)); (void) mutex_init(&P->proc_lock, USYNC_THREAD, NULL); P->state = PS_DEAD; P->pid = (pid_t)-1; P->asfd = core.e_fd; P->ctlfd = -1; P->statfd = -1; P->agentctlfd = -1; P->agentstatfd = -1; P->info_valid = 1; P->ops = &P_core_ops; Pinitsym(P); /* * Fstat and open the core file and make sure it is a valid ELF core. */ if (fstat64(P->asfd, &stbuf) == -1) { *perr = G_STRANGE; goto err; } if (core_elf_fdopen(&core, ET_CORE, perr) == -1) goto err; /* * Allocate and initialize a core_info_t to hang off the ps_prochandle * structure. We keep all core-specific information in this structure. */ if ((P->core = malloc(sizeof (core_info_t))) == NULL) { *perr = G_STRANGE; goto err; } list_link(&P->core->core_lwp_head, NULL); P->core->core_errno = 0; P->core->core_lwp = NULL; P->core->core_nlwp = 0; P->core->core_size = stbuf.st_size; P->core->core_platform = NULL; P->core->core_uts = NULL; P->core->core_cred = NULL; /* * In the days before adjustable core file content, this was the * default core file content. For new core files, this value will * be overwritten by the NT_CONTENT note section. */ P->core->core_content = CC_CONTENT_STACK | CC_CONTENT_HEAP | CC_CONTENT_DATA | CC_CONTENT_RODATA | CC_CONTENT_ANON | CC_CONTENT_SHANON; P->core->core_priv = NULL; P->core->core_priv_size = 0; P->core->core_privinfo = NULL; P->core->core_zonename = NULL; P->core->core_ppii = NULL; #if defined(__i386) || defined(__amd64) P->core->core_ldt = NULL; P->core->core_nldt = 0; #endif switch (core.e_hdr.e_ident[EI_CLASS]) { case ELFCLASS32: P->core->core_dmodel = PR_MODEL_ILP32; break; case ELFCLASS64: P->core->core_dmodel = PR_MODEL_LP64; break; default: *perr = G_FORMAT; goto err; } /* * Because the core file may be a large file, we can't use libelf to * read the Phdrs. We use e_phnum and e_phentsize to simplify things. */ nbytes = core.e_hdr.e_phnum * core.e_hdr.e_phentsize; if ((phbuf = malloc(nbytes)) == NULL) { *perr = G_STRANGE; goto err; } if (pread64(core_fd, phbuf, nbytes, core.e_hdr.e_phoff) != nbytes) { *perr = G_STRANGE; free(phbuf); goto err; } /* * Iterate through the program headers in the core file. * We're interested in two types of Phdrs: PT_NOTE (which * contains a set of saved /proc structures), and PT_LOAD (which * represents a memory mapping from the process's address space). * In the case of PT_NOTE, we're interested in the last PT_NOTE * in the core file; currently the first PT_NOTE (if present) * contains /proc structs in the pre-2.6 unstructured /proc format. */ for (php = phbuf, notes = 0, i = 0; i < core.e_hdr.e_phnum; i++) { if (core.e_hdr.e_ident[EI_CLASS] == ELFCLASS64) (void) memcpy(&phdr, php, sizeof (GElf_Phdr)); else core_phdr_to_gelf(php, &phdr); switch (phdr.p_type) { case PT_NOTE: note_phdr = phdr; notes++; break; case PT_LOAD: if (core_add_mapping(P, &phdr) == -1) { *perr = G_STRANGE; free(phbuf); goto err; } break; } php = (char *)php + core.e_hdr.e_phentsize; } free(phbuf); Psort_mappings(P); /* * If we couldn't find anything of type PT_NOTE, or only one PT_NOTE * was present, abort. The core file is either corrupt or too old. */ if (notes == 0 || notes == 1) { *perr = G_NOTE; goto err; } /* * Advance the seek pointer to the start of the PT_NOTE data */ if (lseek64(P->asfd, note_phdr.p_offset, SEEK_SET) == (off64_t)-1) { dprintf("Pgrab_core: failed to lseek to PT_NOTE data\n"); *perr = G_STRANGE; goto err; } /* * Now process the PT_NOTE structures. Each one is preceded by * an Elf{32/64}_Nhdr structure describing its type and size. * * +--------+ * | header | * +--------+ * | name | * | ... | * +--------+ * | desc | * | ... | * +--------+ */ for (nleft = note_phdr.p_filesz; nleft > 0; ) { Elf64_Nhdr nhdr; off64_t off, namesz; /* * Although defines both Elf32_Nhdr and Elf64_Nhdr * as different types, they are both of the same content and * size, so we don't need to worry about 32/64 conversion here. */ if (read(P->asfd, &nhdr, sizeof (nhdr)) != sizeof (nhdr)) { dprintf("Pgrab_core: failed to read ELF note header\n"); *perr = G_NOTE; goto err; } /* * According to the System V ABI, the amount of padding * following the name field should align the description * field on a 4 byte boundary for 32-bit binaries or on an 8 * byte boundary for 64-bit binaries. However, this change * was not made correctly during the 64-bit port so all * descriptions can assume only 4-byte alignment. We ignore * the name field and the padding to 4-byte alignment. */ namesz = P2ROUNDUP((off64_t)nhdr.n_namesz, (off64_t)4); if (lseek64(P->asfd, namesz, SEEK_CUR) == (off64_t)-1) { dprintf("failed to seek past name and padding\n"); *perr = G_STRANGE; goto err; } dprintf("Note hdr n_type=%u n_namesz=%u n_descsz=%u\n", nhdr.n_type, nhdr.n_namesz, nhdr.n_descsz); off = lseek64(P->asfd, (off64_t)0L, SEEK_CUR); /* * Invoke the note handler function from our table */ if (nhdr.n_type < sizeof (nhdlrs) / sizeof (nhdlrs[0])) { if (nhdlrs[nhdr.n_type](P, nhdr.n_descsz) < 0) { *perr = G_NOTE; goto err; } } else (void) note_notsup(P, nhdr.n_descsz); /* * Seek past the current note data to the next Elf_Nhdr */ if (lseek64(P->asfd, off + nhdr.n_descsz, SEEK_SET) == (off64_t)-1) { dprintf("Pgrab_core: failed to seek to next nhdr\n"); *perr = G_STRANGE; goto err; } /* * Subtract the size of the header and its data from what * we have left to process. */ nleft -= sizeof (nhdr) + namesz + nhdr.n_descsz; } if (nleft != 0) { dprintf("Pgrab_core: note section malformed\n"); *perr = G_STRANGE; goto err; } if ((pagesize = Pgetauxval(P, AT_PAGESZ)) == -1) { pagesize = getpagesize(); dprintf("AT_PAGESZ missing; defaulting to %d\n", pagesize); } /* * Locate and label the mappings corresponding to the end of the * heap (MA_BREAK) and the base of the stack (MA_STACK). */ if ((P->status.pr_brkbase != 0 || P->status.pr_brksize != 0) && (brk_mp = Paddr2mptr(P, P->status.pr_brkbase + P->status.pr_brksize - 1)) != NULL) brk_mp->map_pmap.pr_mflags |= MA_BREAK; else brk_mp = NULL; if ((stk_mp = Paddr2mptr(P, P->status.pr_stkbase)) != NULL) stk_mp->map_pmap.pr_mflags |= MA_STACK; /* * At this point, we have enough information to look for the * executable and open it: we have access to the auxv, a psinfo_t, * and the ability to read from mappings provided by the core file. */ (void) Pfindexec(P, aout_path, core_exec_open, &aout); dprintf("P->execname = \"%s\"\n", P->execname ? P->execname : "NULL"); execname = P->execname ? P->execname : "a.out"; /* * Iterate through the sections, looking for the .dynamic and .interp * sections. If we encounter them, remember their section pointers. */ for (scn = NULL; (scn = elf_nextscn(aout.e_elf, scn)) != NULL; ) { char *sname; if ((gelf_getshdr(scn, &shdr) == NULL) || (sname = elf_strptr(aout.e_elf, aout.e_hdr.e_shstrndx, (size_t)shdr.sh_name)) == NULL) continue; if (strcmp(sname, ".interp") == 0) intp_scn = scn; } /* * Get the AT_BASE auxv element. If this is missing (-1), then * we assume this is a statically-linked executable. */ base_addr = Pgetauxval(P, AT_BASE); /* * In order to get librtld_db initialized, we'll need to identify * and name the mapping corresponding to the run-time linker. The * AT_BASE auxv element tells us the address where it was mapped, * and the .interp section of the executable tells us its path. * If for some reason that doesn't pan out, just use ld.so.1. */ if (intp_scn != NULL && (dp = elf_getdata(intp_scn, NULL)) != NULL && dp->d_size != 0) { dprintf(".interp = <%s>\n", (char *)dp->d_buf); interp = dp->d_buf; } else if (base_addr != (uintptr_t)-1L) { if (P->core->core_dmodel == PR_MODEL_LP64) interp = "/usr/lib/64/ld.so.1"; else interp = "/usr/lib/ld.so.1"; dprintf(".interp section is missing or could not be read; " "defaulting to %s\n", interp); } else dprintf("detected statically linked executable\n"); /* * If we have an AT_BASE element, name the mapping at that address * using the interpreter pathname. Name the corresponding data * mapping after the interpreter as well. */ if (base_addr != (uintptr_t)-1L) { elf_file_t intf; P->map_ldso = core_name_mapping(P, base_addr, interp); if (core_elf_open(&intf, interp, ET_DYN, NULL) == 0) { rd_loadobj_t rl; map_info_t *dmp; rl.rl_base = base_addr; dmp = core_find_data(P, intf.e_elf, &rl); if (dmp != NULL) { dprintf("renamed data at %p to %s\n", (void *)rl.rl_data_base, interp); (void) strncpy(dmp->map_pmap.pr_mapname, interp, PRMAPSZ); dmp->map_pmap.pr_mapname[PRMAPSZ - 1] = '\0'; } } core_elf_close(&intf); } /* * If we have an AT_ENTRY element, name the mapping at that address * using the special name "a.out" just like /proc does. */ if ((addr = Pgetauxval(P, AT_ENTRY)) != (uintptr_t)-1L) P->map_exec = core_name_mapping(P, addr, "a.out"); /* * If we're a statically linked executable, then just locate the * executable's text and data and name them after the executable. */ if (base_addr == (uintptr_t)-1L) { map_info_t *tmp, *dmp; file_info_t *fp; rd_loadobj_t rl; if ((tmp = core_find_text(P, aout.e_elf, &rl)) != NULL && (dmp = core_find_data(P, aout.e_elf, &rl)) != NULL) { (void) strncpy(tmp->map_pmap.pr_mapname, execname, PRMAPSZ); tmp->map_pmap.pr_mapname[PRMAPSZ - 1] = '\0'; (void) strncpy(dmp->map_pmap.pr_mapname, execname, PRMAPSZ); dmp->map_pmap.pr_mapname[PRMAPSZ - 1] = '\0'; } if ((P->map_exec = tmp) != NULL && (fp = malloc(sizeof (file_info_t))) != NULL) { (void) memset(fp, 0, sizeof (file_info_t)); list_link(fp, &P->file_head); tmp->map_file = fp; P->num_files++; fp->file_ref = 1; fp->file_fd = -1; fp->file_lo = malloc(sizeof (rd_loadobj_t)); fp->file_lname = strdup(execname); if (fp->file_lo) *fp->file_lo = rl; if (fp->file_lname) fp->file_lbase = basename(fp->file_lname); (void) strcpy(fp->file_pname, P->mappings[0].map_pmap.pr_mapname); fp->file_map = tmp; Pbuild_file_symtab(P, fp); if (dmp != NULL) { dmp->map_file = fp; fp->file_ref++; } } } core_elf_close(&aout); /* * We now have enough information to initialize librtld_db. * After it warms up, we can iterate through the load object chain * in the core, which will allow us to construct the file info * we need to provide symbol information for the other shared * libraries, and also to fill in the missing mapping names. */ rd_log(_libproc_debug); if ((P->rap = rd_new(P)) != NULL) { (void) rd_loadobj_iter(P->rap, (rl_iter_f *) core_iter_mapping, P); if (P->core->core_errno != 0) { errno = P->core->core_errno; *perr = G_STRANGE; goto err; } } else dprintf("failed to initialize rtld_db agent\n"); /* * If there are sections, load them and process the data from any * sections that we can use to annotate the file_info_t's. */ core_load_shdrs(P, &core); /* * If we previously located a stack or break mapping, and they are * still anonymous, we now assume that they were MAP_ANON mappings. * If brk_mp turns out to now have a name, then the heap is still * sitting at the end of the executable's data+bss mapping: remove * the previous MA_BREAK setting to be consistent with /proc. */ if (stk_mp != NULL && stk_mp->map_pmap.pr_mapname[0] == '\0') stk_mp->map_pmap.pr_mflags |= MA_ANON; if (brk_mp != NULL && brk_mp->map_pmap.pr_mapname[0] == '\0') brk_mp->map_pmap.pr_mflags |= MA_ANON; else if (brk_mp != NULL) brk_mp->map_pmap.pr_mflags &= ~MA_BREAK; *perr = 0; return (P); err: Pfree(P); core_elf_close(&aout); return (NULL); } /* * Grab a core file using a pathname. We just open it and call Pfgrab_core(). */ struct ps_prochandle * Pgrab_core(const char *core, const char *aout, int gflag, int *perr) { int fd, oflag = (gflag & PGRAB_RDONLY) ? O_RDONLY : O_RDWR; if ((fd = open64(core, oflag)) >= 0) return (Pfgrab_core(fd, aout, perr)); if (errno != ENOENT) *perr = G_STRANGE; else *perr = G_NOCORE; return (NULL); }