elfcore.c revision 40803
14374Slars/*- 24374Slars * Copyright (c) 1998 John D. Polstra 34374Slars * All rights reserved. 44374Slars * 511990Speter * Redistribution and use in source and binary forms, with or without 611990Speter * modification, are permitted provided that the following conditions 711990Speter * are met: 834766Speter * 1. Redistributions of source code must retain the above copyright 911990Speter * notice, this list of conditions and the following disclaimer. 1011990Speter * 2. Redistributions in binary form must reproduce the above copyright 1111990Speter * notice, this list of conditions and the following disclaimer in the 1211990Speter * documentation and/or other materials provided with the distribution. 1311990Speter * 144374Slars * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 154374Slars * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1628597Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1734766Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1834766Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1934766Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2034766Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 214374Slars * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2228597Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2328597Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2428597Speter * SUCH DAMAGE. 2528597Speter * 264374Slars * $Id: elfcore.c,v 1.1 1998/10/19 19:42:18 jdp Exp $ 2728597Speter */ 2828597Speter 2928597Speter#include <sys/param.h> 3028597Speter#include <sys/lock.h> 3128597Speter#include <sys/procfs.h> 3228597Speter#include <vm/vm_param.h> 3328597Speter#include <vm/vm.h> 3428597Speter#include <vm/pmap.h> 3528597Speter#include <vm/vm_map.h> 3628597Speter#include <vm/vm_prot.h> 3728597Speter#include <elf.h> 3828597Speter#include <err.h> 3928597Speter#include <errno.h> 4028597Speter#include <fcntl.h> 4128597Speter#include <stdio.h> 4228597Speter#include <stdlib.h> 4328597Speter#include <string.h> 4428597Speter#include <unistd.h> 4528597Speter 4628597Speter#include "extern.h" 4728597Speter 4828597Speter/* 4928597Speter * Code for generating ELF core dumps. 5028597Speter */ 5128597Speter 5211990Spetertypedef void (*segment_callback)(vm_map_entry_t, void *); 5311990Speter 5411990Speter/* Closure for cb_put_phdr(). */ 5534766Speterstruct phdr_closure { 5634766Speter Elf_Phdr *phdr; /* Program header to fill in */ 5734766Speter Elf_Off offset; /* Offset of segment in core file */ 5828597Speter}; 5934766Speter 6028597Speter/* Closure for cb_size_segment(). */ 6128597Speterstruct sseg_closure { 6228597Speter int count; /* Count of writable segments. */ 6328597Speter size_t size; /* Total size of all writable segments. */ 6428597Speter}; 6528597Speter 6628597Speterstatic void cb_put_phdr(vm_map_entry_t, void *); 6728597Speterstatic void cb_size_segment(vm_map_entry_t, void *); 6828597Speterstatic void each_writable_segment(vm_map_entry_t, segment_callback, 694374Slars void *closure); 704374Slarsstatic void elf_corehdr(int fd, pid_t, vm_map_entry_t, int numsegs, 714374Slars void *hdr, size_t hdrsize); 724374Slarsstatic void elf_puthdr(vm_map_entry_t, void *, size_t *, 734374Slars const prstatus_t *, const prfpregset_t *, const prpsinfo_t *, int numsegs); 744374Slarsstatic void elf_putnote(void *dst, size_t *off, const char *name, int type, 754374Slars const void *desc, size_t descsz); 7611990Speterstatic void freemap(vm_map_entry_t); 7728597Speterstatic void readhdrinfo(pid_t, prstatus_t *, prfpregset_t *, prpsinfo_t *); 784374Slarsstatic vm_map_entry_t readmap(pid_t); 794374Slars 8028597Speter/* 8141568Sarchie * Write an ELF coredump for the given pid to the given fd. 8250477Speter */ 8328597Spetervoid 844374Slarself_coredump(int fd, pid_t pid) 854374Slars{ 8628597Speter vm_map_entry_t map; 8711990Speter struct sseg_closure seginfo; 884374Slars void *hdr; 894374Slars size_t hdrsize; 904374Slars char memname[64]; 914374Slars int memfd; 924374Slars Elf_Phdr *php; 9328597Speter int i; 944374Slars 954374Slars /* Get the program's memory map. */ 964374Slars map = readmap(pid); 974374Slars 984374Slars /* Size the program segments. */ 994374Slars seginfo.count = 0; 1004374Slars seginfo.size = 0; 1014374Slars each_writable_segment(map, cb_size_segment, &seginfo); 1024374Slars 1034374Slars /* 1044374Slars * Calculate the size of the core file header area by making 1054374Slars * a dry run of generating it. Nothing is written, but the 1064374Slars * size is calculated. 1074374Slars */ 1084374Slars hdrsize = 0; 1094374Slars elf_puthdr(map, (void *)NULL, &hdrsize, 1104374Slars (const prstatus_t *)NULL, (const prfpregset_t *)NULL, 1114374Slars (const prpsinfo_t *)NULL, seginfo.count); 1124374Slars 1134374Slars /* 1144374Slars * Allocate memory for building the header, fill it up, 1154374Slars * and write it out. 11634766Speter */ 1174374Slars hdr = malloc(hdrsize); 11811990Speter if ((hdr = malloc(hdrsize)) == NULL) 11911990Speter errx(1, "out of memory"); 12011990Speter elf_corehdr(fd, pid, map, seginfo.count, hdr, hdrsize); 12111990Speter 12234766Speter /* Write the contents of all of the writable segments. */ 12334766Speter snprintf(memname, sizeof memname, "/proc/%d/mem", pid); 12434766Speter if ((memfd = open(memname, O_RDONLY)) == -1) 12534766Speter err(1, "cannot open %s", memname); 12634766Speter 12734766Speter php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; 12834766Speter for (i = 0; i < seginfo.count; i++) { 12934766Speter int nleft = php->p_filesz; 1304374Slars 1314374Slars lseek(memfd, (off_t)php->p_vaddr, SEEK_SET); 1324374Slars while (nleft > 0) { 1334374Slars char buf[8*1024]; 1344374Slars int nwant; 1354374Slars int ngot; 1364374Slars 1374374Slars nwant = nleft; 1384374Slars if (nwant > sizeof buf) 1394374Slars nwant = sizeof buf; 1404374Slars ngot = read(memfd, buf, nwant); 1414374Slars if (ngot == -1) 1424374Slars err(1, "read from %s", memname); 14311990Speter if (ngot < nwant) 1444374Slars errx(1, "short read from %s:" 1454374Slars " wanted %d, got %d\n", memname, 14628597Speter nwant, ngot); 14711990Speter ngot = write(fd, buf, nwant); 14834766Speter if (ngot == -1) 14934766Speter err(1, "write of segment %d failed", i); 15028597Speter if (ngot != nwant) 15111990Speter errx(1, "short write"); 15211990Speter nleft -= nwant; 15311990Speter } 15411990Speter php++; 15511990Speter } 15611990Speter close(memfd); 15734766Speter free(hdr); 15834766Speter freemap(map); 15911990Speter} 1604374Slars 1614374Slars/* 16211990Speter * A callback for each_writable_segment() to write out the segment's 1634374Slars * program header entry. 16411990Speter */ 16511990Speterstatic void 16611990Spetercb_put_phdr(vm_map_entry_t entry, void *closure) 1674374Slars{ 1684374Slars struct phdr_closure *phc = (struct phdr_closure *)closure; 16911990Speter Elf_Phdr *phdr = phc->phdr; 1704374Slars 17111990Speter phc->offset = round_page(phc->offset); 17211990Speter 17311990Speter phdr->p_type = PT_LOAD; 1744374Slars phdr->p_offset = phc->offset; 1754374Slars phdr->p_vaddr = entry->start; 1764374Slars phdr->p_paddr = 0; 1774374Slars phdr->p_filesz = phdr->p_memsz = entry->end - entry->start; 1784374Slars phdr->p_align = PAGE_SIZE; 17928597Speter phdr->p_flags = 0; 18028597Speter if (entry->protection & VM_PROT_READ) 1814374Slars phdr->p_flags |= PF_R; 18211990Speter if (entry->protection & VM_PROT_WRITE) 18311990Speter phdr->p_flags |= PF_W; 18411990Speter if (entry->protection & VM_PROT_EXECUTE) 18528597Speter phdr->p_flags |= PF_X; 18611990Speter 18728597Speter phc->offset += phdr->p_filesz; 18828597Speter phc->phdr++; 18992920Simp} 19092920Simp 19192920Simp/* 19292920Simp * A callback for each_writable_segment() to gather information about 19392920Simp * the number of segments and their total size. 19492920Simp */ 19592920Simpstatic void 19692920Simpcb_size_segment(vm_map_entry_t entry, void *closure) 19792920Simp{ 19892920Simp struct sseg_closure *ssc = (struct sseg_closure *)closure; 19992920Simp 20092920Simp ssc->count++; 20192920Simp ssc->size += entry->end - entry->start; 20292920Simp} 20392920Simp 20492920Simp/* 20592920Simp * For each segment in the given memory map, call the given function 20692920Simp * with a pointer to the map entry and some arbitrary caller-supplied 20792920Simp * data. 20892920Simp */ 20992920Simpstatic void 21092920Simpeach_writable_segment(vm_map_entry_t map, segment_callback func, void *closure) 21192920Simp{ 21292920Simp vm_map_entry_t entry; 21392920Simp 21492920Simp for (entry = map; entry != NULL; entry = entry->next) 21592920Simp (*func)(entry, closure); 21692920Simp} 21792920Simp 21892920Simp/* 2194374Slars * Write the core file header to the file, including padding up to 22092920Simp * the page boundary. 22128597Speter */ 2224374Slarsstatic void 2234374Slarself_corehdr(int fd, pid_t pid, vm_map_entry_t map, int numsegs, void *hdr, 2244374Slars size_t hdrsize) 22534766Speter{ 2264374Slars size_t off; 2274374Slars prstatus_t status; 22834766Speter prfpregset_t fpregset; 22934766Speter prpsinfo_t psinfo; 2304374Slars 2314374Slars /* Gather the information for the header. */ 23234766Speter readhdrinfo(pid, &status, &fpregset, &psinfo); 2334374Slars 2344374Slars /* Fill in the header. */ 2354374Slars memset(hdr, 0, hdrsize); 23634766Speter off = 0; 2374374Slars elf_puthdr(map, hdr, &off, &status, &fpregset, &psinfo, numsegs); 23834766Speter 2394374Slars /* Write it to the core file. */ 2404374Slars if (write(fd, hdr, hdrsize) == -1) 24134766Speter err(1, "write"); 24234766Speter} 2434374Slars 2444374Slars/* 2454374Slars * Generate the ELF coredump header into the buffer at "dst". "dst" may 2464374Slars * be NULL, in which case the header is sized but not actually generated. 2474374Slars */ 2484374Slarsstatic void 24934766Speterelf_puthdr(vm_map_entry_t map, void *dst, size_t *off, const prstatus_t *status, 25034766Speter const prfpregset_t *fpregset, const prpsinfo_t *psinfo, int numsegs) 25134766Speter{ 2524374Slars size_t ehoff; 2534374Slars size_t phoff; 2544374Slars size_t noteoff; 25511990Speter size_t notesz; 2564374Slars 25734766Speter ehoff = *off; 25834766Speter *off += sizeof(Elf_Ehdr); 25934766Speter 26034766Speter phoff = *off; 26134766Speter *off += (numsegs + 1) * sizeof(Elf_Phdr); 26228597Speter 26334766Speter noteoff = *off; 26434766Speter elf_putnote(dst, off, "FreeBSD", NT_PRSTATUS, status, 26534766Speter sizeof *status); 2664374Slars elf_putnote(dst, off, "FreeBSD", NT_FPREGSET, fpregset, 26734766Speter sizeof *fpregset); 26834766Speter elf_putnote(dst, off, "FreeBSD", NT_PRPSINFO, psinfo, 26934766Speter sizeof *psinfo); 27028597Speter notesz = *off - noteoff; 27134766Speter 27234766Speter /* Align up to a page boundary for the program segments. */ 27334766Speter *off = round_page(*off); 27434766Speter 27534766Speter if (dst != NULL) { 27634766Speter Elf_Ehdr *ehdr; 27734766Speter Elf_Phdr *phdr; 27834766Speter struct phdr_closure phc; 27934766Speter 28034766Speter /* 2814374Slars * Fill in the ELF header. 28234766Speter */ 28334766Speter ehdr = (Elf_Ehdr *)((char *)dst + ehoff); 28434766Speter ehdr->e_ident[EI_MAG0] = ELFMAG0; 2854374Slars ehdr->e_ident[EI_MAG1] = ELFMAG1; 28634766Speter ehdr->e_ident[EI_MAG2] = ELFMAG2; 28734766Speter ehdr->e_ident[EI_MAG3] = ELFMAG3; 28834766Speter ehdr->e_ident[EI_CLASS] = ELF_CLASS; 28934766Speter ehdr->e_ident[EI_DATA] = ELF_DATA; 29034766Speter ehdr->e_ident[EI_VERSION] = EV_CURRENT; 29134766Speter ehdr->e_ident[EI_PAD] = 0; 2924374Slars strncpy(ehdr->e_ident + EI_BRAND, "FreeBSD", 29334766Speter EI_NIDENT - EI_BRAND); 29434766Speter ehdr->e_type = ET_CORE; 29534766Speter ehdr->e_machine = ELF_ARCH; 29634766Speter ehdr->e_version = EV_CURRENT; 29734766Speter ehdr->e_entry = 0; 29834766Speter ehdr->e_phoff = phoff; 29934766Speter ehdr->e_flags = 0; 30034766Speter ehdr->e_ehsize = sizeof(Elf_Ehdr); 30134766Speter ehdr->e_phentsize = sizeof(Elf_Phdr); 30234766Speter ehdr->e_phnum = numsegs + 1; 30334766Speter ehdr->e_shentsize = sizeof(Elf_Shdr); 30434766Speter ehdr->e_shnum = 0; 30534766Speter ehdr->e_shstrndx = SHN_UNDEF; 30634766Speter 30734766Speter /* 3084374Slars * Fill in the program header entries. 30934766Speter */ 31034766Speter phdr = (Elf_Phdr *)((char *)dst + phoff); 31134766Speter 31234766Speter /* The note segement. */ 3134374Slars phdr->p_type = PT_NOTE; 31434766Speter phdr->p_offset = noteoff; 31534766Speter phdr->p_vaddr = 0; 31634766Speter phdr->p_paddr = 0; 31734766Speter phdr->p_filesz = notesz; 31834766Speter phdr->p_memsz = 0; 31934766Speter phdr->p_flags = 0; 32034766Speter phdr->p_align = 0; 32134766Speter phdr++; 32234766Speter 32334766Speter /* All the writable segments from the program. */ 32434766Speter phc.phdr = phdr; 32534766Speter phc.offset = *off; 32634766Speter each_writable_segment(map, cb_put_phdr, &phc); 32734766Speter } 32811990Speter} 32911990Speter 33011990Speter/* 33111990Speter * Emit one note section to "dst", or just size it if "dst" is NULL. 33211990Speter */ 3334374Slarsstatic void 33434766Speterelf_putnote(void *dst, size_t *off, const char *name, int type, 3354374Slars const void *desc, size_t descsz) 33634766Speter{ 3374374Slars Elf_Note note; 33834766Speter 3394374Slars note.n_namesz = strlen(name) + 1; 34034766Speter note.n_descsz = descsz; 34134766Speter note.n_type = type; 34234766Speter if (dst != NULL) 34334766Speter bcopy(¬e, (char *)dst + *off, sizeof note); 3444374Slars *off += sizeof note; 34534766Speter if (dst != NULL) 3464374Slars bcopy(name, (char *)dst + *off, note.n_namesz); 3474374Slars *off += roundup2(note.n_namesz, sizeof(Elf_Size)); 34811990Speter if (dst != NULL) 34934766Speter bcopy(desc, (char *)dst + *off, note.n_descsz); 3504374Slars *off += roundup2(note.n_descsz, sizeof(Elf_Size)); 3514374Slars} 3524374Slars 3534374Slars/* 3544374Slars * Free the memory map. 35534766Speter */ 35634766Speterstatic void 3574374Slarsfreemap(vm_map_entry_t map) 3584374Slars{ 35928597Speter while (map != NULL) { 3604374Slars vm_map_entry_t next = map->next; 3614374Slars free(map); 36234766Speter map = next; 3634374Slars } 3644374Slars} 36528597Speter 36634766Speter/* 3674374Slars * Read the process information necessary to fill in the core file's header. 3684374Slars */ 3694374Slarsstatic void 3704374Slarsreadhdrinfo(pid_t pid, prstatus_t *status, prfpregset_t *fpregset, 3714374Slars prpsinfo_t *psinfo) 3724374Slars{ 3734374Slars char name[64]; 37434766Speter char line[256]; 37532069Salex int fd; 3764374Slars int i; 3774374Slars int n; 3784374Slars 3794374Slars memset(status, 0, sizeof *status); 38011990Speter status->pr_version = PRSTATUS_VERSION; 38111990Speter status->pr_statussz = sizeof(prstatus_t); 38234766Speter status->pr_gregsetsz = sizeof(gregset_t); 3834374Slars status->pr_fpregsetsz = sizeof(fpregset_t); 3844374Slars status->pr_osreldate = __FreeBSD_version; 3854374Slars status->pr_pid = pid; 3864374Slars 38734766Speter memset(fpregset, 0, sizeof *fpregset); 3884374Slars 3894374Slars memset(psinfo, 0, sizeof *psinfo); 3904374Slars psinfo->pr_version = PRPSINFO_VERSION; 3914374Slars psinfo->pr_psinfosz = sizeof(prpsinfo_t); 3924374Slars 3934374Slars /* Read the general registers. */ 39428597Speter snprintf(name, sizeof name, "/proc/%d/regs", pid); 39528597Speter if ((fd = open(name, O_RDONLY)) == -1) 39628597Speter err(1, "cannot open %s", name); 39734766Speter if ((n = read(fd, &status->pr_reg, sizeof status->pr_reg)) == -1) 39834766Speter err(1, "read error from %s", name); 39928597Speter if (n < sizeof status->pr_reg) 40034766Speter errx(1, "short read from %s: wanted %u, got %d", name, 40134766Speter sizeof status->pr_reg, n); 4024374Slars close(fd); 4034374Slars 40434766Speter /* Read the floating point registers. */ 4054374Slars snprintf(name, sizeof name, "/proc/%d/fpregs", pid); 40634766Speter if ((fd = open(name, O_RDONLY)) == -1) 4074374Slars err(1, "cannot open %s", name); 4084374Slars if ((n = read(fd, fpregset, sizeof *fpregset)) == -1) 40934766Speter err(1, "read error from %s", name); 4104374Slars if (n < sizeof *fpregset) 41134766Speter errx(1, "short read from %s: wanted %u, got %d", name, 41234766Speter sizeof *fpregset, n); 41334766Speter close(fd); 4144374Slars 4154374Slars /* Read and parse the process status. */ 4164374Slars snprintf(name, sizeof name, "/proc/%d/status", pid); 4174374Slars if ((fd = open(name, O_RDONLY)) == -1) 41834766Speter err(1, "cannot open %s", name); 41934766Speter if ((n = read(fd, line, sizeof line - 1)) == -1) 4204374Slars err(1, "read error from %s", name); 4214374Slars if (n > MAXCOMLEN) 4224374Slars n = MAXCOMLEN; 42334766Speter for (i = 0; i < n && line[i] != ' '; i++) 4244374Slars psinfo->pr_fname[i] = line[i]; 4254374Slars strncpy(psinfo->pr_psargs, psinfo->pr_fname, PRARGSZ); 4264374Slars close(fd); 4274374Slars} 4284374Slars 4294374Slars/* 4304374Slars * Read the process's memory map using procfs, and return a list of 4314374Slars * VM map entries. Only the non-device read/writable segments are 4324374Slars * returned. The map entries in the list aren't fully filled in; only 4334374Slars * the items we need are present. 43434766Speter */ 4354374Slarsstatic vm_map_entry_t 43634766Speterreadmap(pid_t pid) 4374374Slars{ 4384374Slars char mapname[64]; 4394374Slars int mapfd; 4404374Slars ssize_t mapsize; 44126880Scharnier size_t bufsize; 44226880Scharnier char *mapbuf; 44334766Speter int pos; 44434766Speter vm_map_entry_t map; 44534766Speter vm_map_entry_t *linkp; 44634766Speter 4474374Slars snprintf(mapname, sizeof mapname, "/proc/%d/map", pid); 44834766Speter if ((mapfd = open(mapname, O_RDONLY)) == -1) 4494374Slars err(1, "cannot open %s", mapname); 45034766Speter 4514374Slars /* 45234766Speter * Procfs requires (for consistency) that the entire memory map 45334766Speter * be read with a single read() call. Start with a reasonbly sized 45434766Speter * buffer, and double it until it is big enough. 45592920Simp */ 45628597Speter bufsize = 8 * 1024; 45734766Speter mapbuf = NULL; 4584374Slars for ( ; ; ) { 45934766Speter if ((mapbuf = realloc(mapbuf, bufsize)) == NULL) 46034766Speter errx(1, "out of memory"); 46134766Speter mapsize = read(mapfd, mapbuf, bufsize); 46228597Speter if (mapsize != -1 || errno != EFBIG) 46334766Speter break; 46434766Speter bufsize *= 2; 46528597Speter /* This lseek shouldn't be necessary, but it is. */ 4664374Slars lseek(mapfd, (off_t)0, SEEK_SET); 4674374Slars } 4684374Slars if (mapsize == -1) 4694374Slars err(1, "read error from %s", mapname); 4704374Slars if (mapsize == 0) 47192920Simp errx(1, "empty map file %s", mapname); 47234766Speter close(mapfd); 47334766Speter 4744374Slars pos = 0; 47534766Speter map = NULL; 47634766Speter linkp = ↦ 47734766Speter while (pos < mapsize) { 47834766Speter vm_map_entry_t ent; 47934766Speter vm_offset_t start; 48034766Speter vm_offset_t end; 48134766Speter char prot[4]; 48234766Speter char type[16]; 4834374Slars int n; 4844374Slars int len; 4854374Slars 4864374Slars len = 0; 4874374Slars n = sscanf(mapbuf + pos, "%x %x %*d %*d %*d %3[-rwx]" 48834766Speter " %*d %*d %*x %*s %*s %16s%*[\n]%n", 4894374Slars &start, &end, prot, type, &len); 4904374Slars if (n != 4) 4914374Slars errx(1, "ill-formed line in %s", mapname); 4924374Slars pos += len; 4934374Slars 4944374Slars /* Ignore segments of the wrong kind, and unwritable ones */ 4954374Slars if (strncmp(prot, "rw", 2) != 0 || 49634766Speter (strcmp(type, "default") != 0 && 4974374Slars strcmp(type, "vnode") != 0 && 49834766Speter strcmp(type, "swap") != 0)) 49934766Speter continue; 50034766Speter 5014374Slars if ((ent = (vm_map_entry_t)calloc(1, sizeof *ent)) == NULL) 50234766Speter errx(1, "out of memory"); 50334766Speter ent->start = start; 5044374Slars ent->end = end; 5054374Slars ent->protection = VM_PROT_READ | VM_PROT_WRITE; 50634766Speter if (prot[2] == 'x') 5074374Slars ent->protection |= VM_PROT_EXECUTE; 5084374Slars 5094374Slars *linkp = ent; 51034766Speter linkp = &ent->next; 5114374Slars } 51234766Speter free(mapbuf); 51334766Speter return map; 51434766Speter} 51534766Speter