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