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