xref: /dragonfly/usr.bin/gcore/elfcore.c (revision 67e7cb85324fc590b088e24491b7cb82318306d5)
1 /*-
2  * Copyright (c) 1998 John D. Polstra
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/usr.bin/gcore/elfcore.c,v 1.5.2.3 2001/07/11 23:59:11 obrien Exp $
27  */
28 
29 #define _KERNEL_STRUCTURES
30 
31 #include <sys/param.h>
32 #include <sys/lock.h>
33 #include <sys/procfs.h>
34 #include <machine/elf.h>
35 #include <vm/vm_param.h>
36 #include <vm/vm.h>
37 #include <vm/pmap.h>
38 #include <vm/vm_map.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #include "extern.h"
48 
49 extern ssize_t gcore_seg_limit;
50 extern int gcore_verbose;
51 
52 /*
53  * Code for generating ELF core dumps.
54  */
55 
56 typedef void (*segment_callback)(vm_map_entry_t, void *);
57 
58 /* Closure for cb_put_phdr(). */
59 struct phdr_closure {
60           Elf_Phdr *phdr;               /* Program header to fill in */
61           Elf_Off offset;               /* Offset of segment in core file */
62 };
63 
64 /* Closure for cb_size_segment(). */
65 struct sseg_closure {
66           int count;                    /* Count of writable segments. */
67           size_t size;                  /* Total size of all writable segments. */
68 };
69 
70 static void cb_put_phdr(vm_map_entry_t, void *);
71 static void cb_size_segment(vm_map_entry_t, void *);
72 static void each_writable_segment(vm_map_entry_t, segment_callback,
73     void *closure);
74 static void elf_corehdr(int fd, pid_t, vm_map_entry_t, int numsegs,
75     void *hdr, size_t hdrsize);
76 static void elf_puthdr(vm_map_entry_t, void *, size_t *,
77     const prstatus_t *, const prfpregset_t *, const prpsinfo_t *, int numsegs);
78 static void elf_putnote(void *dst, size_t *off, const char *name, int type,
79     const void *desc, size_t descsz);
80 static void freemap(vm_map_entry_t);
81 static void readhdrinfo(pid_t, prstatus_t *, prfpregset_t *, prpsinfo_t *);
82 static vm_map_entry_t readmap(pid_t);
83 
84 /*
85  * Write an ELF coredump for the given pid to the given fd.
86  */
87 void
elf_coredump(int fd,pid_t pid)88 elf_coredump(int fd, pid_t pid)
89 {
90           vm_map_entry_t map;
91           struct sseg_closure seginfo;
92           void *hdr;
93           size_t hdrsize;
94           char memname[64];
95           int memfd;
96           Elf_Phdr *php;
97           int i;
98 
99           /* Get the program's memory map. */
100           map = readmap(pid);
101 
102           /* Size the program segments. */
103           seginfo.count = 0;
104           seginfo.size = 0;
105           each_writable_segment(map, cb_size_segment, &seginfo);
106 
107           /*
108            * Calculate the size of the core file header area by making
109            * a dry run of generating it.  Nothing is written, but the
110            * size is calculated.
111            */
112           hdrsize = 0;
113           elf_puthdr(map, NULL, &hdrsize, NULL, NULL, NULL, seginfo.count);
114 
115           /*
116            * Allocate memory for building the header, fill it up,
117            * and write it out.
118            */
119           hdr = malloc(hdrsize);
120           if ((hdr = malloc(hdrsize)) == NULL)
121                     errx(1, "out of memory");
122           elf_corehdr(fd, pid, map, seginfo.count, hdr, hdrsize);
123 
124           /* Write the contents of all of the writable segments. */
125           snprintf(memname, sizeof memname, "/proc/%d/mem", pid);
126           if ((memfd = open(memname, O_RDONLY)) == -1)
127                     err(1, "cannot open %s", memname);
128 
129           php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1;
130           for (i = 0;  i < seginfo.count;  i++) {
131                     long nleft = php->p_filesz;
132 
133                     lseek(memfd, (off_t)php->p_vaddr, SEEK_SET);
134                     while (nleft > 0) {
135                               char buf[65536];
136                               ssize_t nwant;
137                               ssize_t ngot;
138 
139                               nwant = (ssize_t)nleft;
140                               if (nwant > sizeof buf)
141                                         nwant = sizeof buf;
142                               ngot = read(memfd, buf, nwant);
143                               if (ngot == -1)
144                                         err(1, "read from %s", memname);
145                               if (ngot < nwant)
146                                         errx(1, "short read from %s:"
147                                             " wanted %zd, got %zd\n", memname,
148                                             nwant, ngot);
149                               ngot = write(fd, buf, nwant);
150                               if (ngot == -1)
151                                         err(1, "write of segment %d failed", i);
152                               if (ngot != nwant)
153                                         errx(1, "short write");
154                               nleft -= nwant;
155                     }
156                     php++;
157           }
158           close(memfd);
159           free(hdr);
160           freemap(map);
161 }
162 
163 /*
164  * A callback for each_writable_segment() to write out the segment's
165  * program header entry.
166  */
167 static void
cb_put_phdr(vm_map_entry_t entry,void * closure)168 cb_put_phdr(vm_map_entry_t entry, void *closure)
169 {
170           struct phdr_closure *phc = (struct phdr_closure *)closure;
171           Elf_Phdr *phdr = phc->phdr;
172 
173           phc->offset = round_page(phc->offset);
174 
175           phdr->p_type = PT_LOAD;
176           phdr->p_offset = phc->offset;
177           phdr->p_vaddr = entry->ba.start;
178           phdr->p_paddr = 0;
179           phdr->p_filesz = phdr->p_memsz = entry->ba.end - entry->ba.start;
180           phdr->p_align = PAGE_SIZE;
181           phdr->p_flags = 0;
182           if (entry->protection & VM_PROT_READ)
183                     phdr->p_flags |= PF_R;
184           if (entry->protection & VM_PROT_WRITE)
185                     phdr->p_flags |= PF_W;
186           if (entry->protection & VM_PROT_EXECUTE)
187                     phdr->p_flags |= PF_X;
188 
189           phc->offset += phdr->p_filesz;
190           phc->phdr++;
191 }
192 
193 /*
194  * A callback for each_writable_segment() to gather information about
195  * the number of segments and their total size.
196  */
197 static void
cb_size_segment(vm_map_entry_t entry,void * closure)198 cb_size_segment(vm_map_entry_t entry, void *closure)
199 {
200           struct sseg_closure *ssc = (struct sseg_closure *)closure;
201 
202           ssc->count++;
203           ssc->size += entry->ba.end - entry->ba.start;
204 }
205 
206 /*
207  * For each segment in the given memory map, call the given function
208  * with a pointer to the map entry and some arbitrary caller-supplied
209  * data.
210  */
211 static void
each_writable_segment(vm_map_entry_t map,segment_callback func,void * closure)212 each_writable_segment(vm_map_entry_t map, segment_callback func, void *closure)
213 {
214           vm_map_entry_t entry;
215 
216           for (entry = map; entry; entry = entry->rb_entry.rbe_parent)
217                     (*func)(entry, closure);
218 }
219 
220 /*
221  * Write the core file header to the file, including padding up to
222  * the page boundary.
223  */
224 static void
elf_corehdr(int fd,pid_t pid,vm_map_entry_t map,int numsegs,void * hdr,size_t hdrsize)225 elf_corehdr(int fd, pid_t pid, vm_map_entry_t map, int numsegs, void *hdr,
226     size_t hdrsize)
227 {
228           size_t off;
229           prstatus_t status;
230           prfpregset_t fpregset;
231           prpsinfo_t psinfo;
232 
233           /* Gather the information for the header. */
234           readhdrinfo(pid, &status, &fpregset, &psinfo);
235 
236           /* Fill in the header. */
237           memset(hdr, 0, hdrsize);
238           off = 0;
239           elf_puthdr(map, hdr, &off, &status, &fpregset, &psinfo, numsegs);
240 
241           /* Write it to the core file. */
242           if (write(fd, hdr, hdrsize) == -1)
243                     err(1, "write");
244 }
245 
246 /*
247  * Generate the ELF coredump header into the buffer at "dst".  "dst" may
248  * be NULL, in which case the header is sized but not actually generated.
249  */
250 static void
elf_puthdr(vm_map_entry_t map,void * dst,size_t * off,const prstatus_t * status,const prfpregset_t * fpregset,const prpsinfo_t * psinfo,int numsegs)251 elf_puthdr(vm_map_entry_t map, void *dst, size_t *off, const prstatus_t *status,
252     const prfpregset_t *fpregset, const prpsinfo_t *psinfo, int numsegs)
253 {
254           size_t ehoff;
255           size_t phoff;
256           size_t noteoff;
257           size_t notesz;
258 
259           ehoff = *off;
260           *off += sizeof(Elf_Ehdr);
261 
262           phoff = *off;
263           *off += (numsegs + 1) * sizeof(Elf_Phdr);
264 
265           noteoff = *off;
266           elf_putnote(dst, off, "FreeBSD", NT_PRSTATUS, status,
267               sizeof *status);
268           elf_putnote(dst, off, "FreeBSD", NT_FPREGSET, fpregset,
269               sizeof *fpregset);
270           elf_putnote(dst, off, "FreeBSD", NT_PRPSINFO, psinfo,
271               sizeof *psinfo);
272           notesz = *off - noteoff;
273 
274           /* Align up to a page boundary for the program segments. */
275           *off = round_page(*off);
276 
277           if (dst != NULL) {
278                     Elf_Ehdr *ehdr;
279                     Elf_Phdr *phdr;
280                     struct phdr_closure phc;
281 
282                     /*
283                      * Fill in the ELF header.
284                      */
285                     ehdr = (Elf_Ehdr *)((char *)dst + ehoff);
286                     ehdr->e_ident[EI_MAG0] = ELFMAG0;
287                     ehdr->e_ident[EI_MAG1] = ELFMAG1;
288                     ehdr->e_ident[EI_MAG2] = ELFMAG2;
289                     ehdr->e_ident[EI_MAG3] = ELFMAG3;
290                     ehdr->e_ident[EI_CLASS] = ELF_CLASS;
291                     ehdr->e_ident[EI_DATA] = ELF_DATA;
292                     ehdr->e_ident[EI_VERSION] = EV_CURRENT;
293                     ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
294                     ehdr->e_ident[EI_ABIVERSION] = 0;
295                     ehdr->e_ident[EI_PAD] = 0;
296                     ehdr->e_type = ET_CORE;
297                     ehdr->e_machine = ELF_ARCH;
298                     ehdr->e_version = EV_CURRENT;
299                     ehdr->e_entry = 0;
300                     ehdr->e_phoff = phoff;
301                     ehdr->e_flags = 0;
302                     ehdr->e_ehsize = sizeof(Elf_Ehdr);
303                     ehdr->e_phentsize = sizeof(Elf_Phdr);
304                     ehdr->e_phnum = numsegs + 1;
305                     ehdr->e_shentsize = sizeof(Elf_Shdr);
306                     ehdr->e_shnum = 0;
307                     ehdr->e_shstrndx = SHN_UNDEF;
308 
309                     /*
310                      * Fill in the program header entries.
311                      */
312                     phdr = (Elf_Phdr *)((char *)dst + phoff);
313 
314                     /* The note segement. */
315                     phdr->p_type = PT_NOTE;
316                     phdr->p_offset = noteoff;
317                     phdr->p_vaddr = 0;
318                     phdr->p_paddr = 0;
319                     phdr->p_filesz = notesz;
320                     phdr->p_memsz = 0;
321                     phdr->p_flags = 0;
322                     phdr->p_align = 0;
323                     phdr++;
324 
325                     /* All the writable segments from the program. */
326                     phc.phdr = phdr;
327                     phc.offset = *off;
328                     each_writable_segment(map, cb_put_phdr, &phc);
329           }
330 }
331 
332 /*
333  * Emit one note section to "dst", or just size it if "dst" is NULL.
334  */
335 static void
elf_putnote(void * dst,size_t * off,const char * name,int type,const void * desc,size_t descsz)336 elf_putnote(void *dst, size_t *off, const char *name, int type,
337     const void *desc, size_t descsz)
338 {
339           Elf_Note note;
340 
341           note.n_namesz = strlen(name) + 1;
342           note.n_descsz = descsz;
343           note.n_type = type;
344           if (dst != NULL)
345                     bcopy(&note, (char *)dst + *off, sizeof note);
346           *off += sizeof note;
347           if (dst != NULL)
348                     bcopy(name, (char *)dst + *off, note.n_namesz);
349           *off += roundup2(note.n_namesz, sizeof(Elf_Size));
350           if (dst != NULL)
351                     bcopy(desc, (char *)dst + *off, note.n_descsz);
352           *off += roundup2(note.n_descsz, sizeof(Elf_Size));
353 }
354 
355 /*
356  * Free the memory map.
357  */
358 static void
freemap(vm_map_entry_t map)359 freemap(vm_map_entry_t map)
360 {
361           while (map != NULL) {
362                     vm_map_entry_t next = map->rb_entry.rbe_parent;
363                     free(map);
364                     map = next;
365           }
366 }
367 
368 /*
369  * Read the process information necessary to fill in the core file's header.
370  */
371 static void
readhdrinfo(pid_t pid,prstatus_t * status,prfpregset_t * fpregset,prpsinfo_t * psinfo)372 readhdrinfo(pid_t pid, prstatus_t *status, prfpregset_t *fpregset,
373     prpsinfo_t *psinfo)
374 {
375           char name[64];
376           char line[256];
377           int fd;
378           int i;
379           int n;
380 
381           memset(status, 0, sizeof *status);
382           status->pr_version = PRSTATUS_VERSION;
383           status->pr_statussz = sizeof(prstatus_t);
384           status->pr_gregsetsz = sizeof(gregset_t);
385           status->pr_fpregsetsz = sizeof(fpregset_t);
386           status->pr_osreldate = __DragonFly_version;
387           status->pr_pid = pid;
388 
389           memset(fpregset, 0, sizeof *fpregset);
390 
391           memset(psinfo, 0, sizeof *psinfo);
392           psinfo->pr_version = PRPSINFO_VERSION;
393           psinfo->pr_psinfosz = sizeof(prpsinfo_t);
394 
395           /* Read the general registers. */
396           snprintf(name, sizeof name, "/proc/%d/regs", pid);
397           if ((fd = open(name, O_RDONLY)) == -1)
398                     err(1, "cannot open %s", name);
399           if ((n = read(fd, &status->pr_reg, sizeof status->pr_reg)) == -1)
400                     err(1, "read error from %s", name);
401           if (n < sizeof status->pr_reg)
402                     errx(1, "short read from %s: wanted %zu, got %d",
403                          name, sizeof(status->pr_reg), n);
404           close(fd);
405 
406           /* Read the floating point registers. */
407           snprintf(name, sizeof name, "/proc/%d/fpregs", pid);
408           if ((fd = open(name, O_RDONLY)) == -1)
409                     err(1, "cannot open %s", name);
410           if ((n = read(fd, fpregset, sizeof *fpregset)) == -1)
411                     err(1, "read error from %s", name);
412           if (n < sizeof *fpregset)
413                     errx(1, "short read from %s: wanted %zu, got %d",
414                          name, sizeof(*fpregset), n);
415           close(fd);
416 
417           /* Read and parse the process status. */
418           snprintf(name, sizeof name, "/proc/%d/status", pid);
419           if ((fd = open(name, O_RDONLY)) == -1)
420                     err(1, "cannot open %s", name);
421           if ((n = read(fd, line, sizeof line - 1)) == -1)
422                     err(1, "read error from %s", name);
423           if (n > MAXCOMLEN)
424                     n = MAXCOMLEN;
425           for (i = 0;  i < n && line[i] != ' ';  i++)
426                     psinfo->pr_fname[i] = line[i];
427           strncpy(psinfo->pr_psargs, psinfo->pr_fname, PRARGSZ);
428           close(fd);
429 }
430 
431 /*
432  * Read the process's memory map using procfs, and return a list of
433  * VM map entries.  Only the non-device read/writable segments are
434  * returned.  The map entries in the list aren't fully filled in; only
435  * the items we need are present.
436  */
437 static vm_map_entry_t
readmap(pid_t pid)438 readmap(pid_t pid)
439 {
440           char mapname[64];
441           int mapfd;
442           ssize_t mapsize;
443           size_t bufsize;
444           char *mapbuf;
445           int pos;
446           vm_map_entry_t map;
447           vm_map_entry_t *linkp;
448 
449           snprintf(mapname, sizeof mapname, "/proc/%d/map", pid);
450           if ((mapfd = open(mapname, O_RDONLY)) == -1)
451                     err(1, "cannot open %s", mapname);
452 
453           /*
454            * Procfs requires (for consistency) that the entire memory map
455            * be read with a single read() call.  Start with a reasonbly sized
456            * buffer, and double it until it is big enough.
457            */
458           bufsize = 65536;
459           mapbuf = NULL;
460           for ( ; ; ) {
461                     if ((mapbuf = realloc(mapbuf, bufsize + 1)) == NULL)
462                               errx(1, "out of memory");
463                     mapsize = read(mapfd, mapbuf, bufsize);
464                     if ((mapsize != -1 || errno != EFBIG) && mapsize != bufsize) {
465                               break;
466                     }
467                     bufsize *= 2;
468                     /* This lseek shouldn't be necessary, but it is. */
469                     lseek(mapfd, (off_t)0, SEEK_SET);
470           }
471           if (mapsize == -1)
472                     err(1, "read error from %s", mapname);
473           if (mapsize == 0)
474                     errx(1, "empty map file %s", mapname);
475           mapbuf[mapsize] = 0;
476           close(mapfd);
477 
478           pos = 0;
479           map = NULL;
480           linkp = &map;
481           while (pos < mapsize) {
482                     vm_map_entry_t ent;
483                     vm_offset_t start;
484                     vm_offset_t end;
485                     char prot[4];
486                     char type[16];
487                     int n;
488                     int len;
489                     int skipme = 0;
490 
491                     len = 0;
492                     n = sscanf(mapbuf + pos, "%lx %lx %*d %*d %*x %3[-rwx]"
493                         " %*d %*d %*x %*s %*s %15s %*s%*[\n]%n",
494                         &start, &end, prot, type, &len);
495                     if (n != 4)
496                               errx(1, "ill-formed line in %s: '%s'",
497                                   mapname, mapbuf);
498                     pos += len;
499 
500                     /* Ignore segments of the wrong kind, and unwritable ones */
501                     if (gcore_seg_limit >= 0 && end - start > gcore_seg_limit) {
502                               skipme = 1;
503                     }
504                     if (strncmp(prot, "rw", 2) != 0 ||
505                         (strcmp(type, "default") != 0 &&
506                         strcmp(type, "vnode") != 0 &&
507                         strcmp(type, "swap") != 0)) {
508                               skipme = 2;
509                     }
510                     if (gcore_verbose || skipme == 1)
511                               printf("%016lx-%016lx (%ldM) %s,%s",
512                                      start, end,
513                                         (end - start) / (1024 * 1024),
514                                         prot, type);
515                     if (skipme) {
516                               if (skipme == 1)
517                                         printf(" (ignored - seglimit)\n");
518                               else if (gcore_verbose)
519                                         printf(" (ignored)\n");
520                               continue;
521                     }
522                     if (gcore_verbose)
523                               printf("\n");
524 
525                     if ((ent = (vm_map_entry_t)calloc(1, sizeof *ent)) == NULL)
526                               errx(1, "out of memory");
527                     ent->ba.start = start;
528                     ent->ba.end = end;
529                     ent->protection = VM_PROT_READ | VM_PROT_WRITE;
530                     if (prot[2] == 'x')
531                         ent->protection |= VM_PROT_EXECUTE;
532 
533                     *linkp = ent;
534                     linkp = &ent->rb_entry.rbe_parent;
535           }
536           free(mapbuf);
537           return map;
538 }
539