2 * Copyright (c) 2016 Apple Inc. All rights reserved.
14 #include <sys/types.h>
15 #include <sys/sysctl.h>
18 #include <sys/mount.h>
34 #include <mach/mach.h>
37 * (Another optimization to consider is merging adjacent regions with
38 * the same properties.)
42 simple_region_optimization(struct region
*r
, __unused
void *arg
)
44 assert(0 != R_SIZE(r
));
47 * Elide unreadable regions
49 if ((r
->r_info
.max_protection
& VM_PROT_READ
) != VM_PROT_READ
) {
50 if (OPTIONS_DEBUG(opt
, 2))
51 printr(r
, "eliding unreadable region\n");
52 return WALK_DELETE_REGION
;
56 * Elide submaps (here for debugging purposes?)
58 if (r
->r_info
.is_submap
) {
59 if (OPTIONS_DEBUG(opt
))
60 printr(r
, "eliding submap\n");
61 return WALK_DELETE_REGION
;
67 if (r
->r_info
.protection
== VM_PROT_NONE
&&
68 (VM_MEMORY_STACK
== r
->r_info
.user_tag
||
69 VM_MEMORY_MALLOC
== r
->r_info
.user_tag
)) {
70 if (OPTIONS_DEBUG(opt
, 2)) {
73 printr(r
, "elide %s - %s\n", str_hsize(hstr
, R_SIZE(r
)), str_tagr(tstr
, r
));
75 return WALK_DELETE_REGION
;
79 * Regions full of clean zfod data e.g. VM_MEMORY_MALLOC_LARGE can be recorded as zfod
81 if (r
->r_info
.share_mode
== SM_PRIVATE
&&
82 0 == r
->r_info
.external_pager
&&
83 0 == r
->r_info
.pages_dirtied
) {
84 if (OPTIONS_DEBUG(opt
, 2)) {
87 printr(r
, "convert to zfod %s - %s\n", str_hsize(hstr
, R_SIZE(r
)), str_tagr(tstr
, r
));
89 r
->r_inzfodregion
= true;
97 * (Paranoid validation + debugging assistance.)
100 validate_core_header(const native_mach_header_t
*mh
, off_t corefilesize
)
102 assert(NATIVE_MH_MAGIC
== mh
->magic
);
103 assert(MH_CORE
== mh
->filetype
);
105 if (OPTIONS_DEBUG(opt
, 2))
106 printf("%s: core file: mh %p ncmds %u sizeofcmds %u\n",
107 __func__
, mh
, mh
->ncmds
, mh
->sizeofcmds
);
109 unsigned sizeofcmds
= 0;
110 off_t corefilemaxoff
= 0;
111 const struct load_command
*lc
= (const void *)(mh
+ 1);
112 for (unsigned i
= 0; i
< mh
->ncmds
; i
++) {
114 if ((uintptr_t)lc
< (uintptr_t)mh
||
115 (uintptr_t)lc
> (uintptr_t)mh
+ mh
->sizeofcmds
) {
116 warnx("load command %p outside mach header range [%p, 0x%lx]?",
117 lc
, mh
, (uintptr_t)mh
+ mh
->sizeofcmds
);
120 if (OPTIONS_DEBUG(opt
, 2))
121 printf("lc %p cmd %3u size %3u ", lc
, lc
->cmd
, lc
->cmdsize
);
122 sizeofcmds
+= lc
->cmdsize
;
125 case NATIVE_LC_SEGMENT
: {
126 const native_segment_command_t
*sc
= (const void *)lc
;
127 if (OPTIONS_DEBUG(opt
, 2)) {
128 printf("%8s: mem %llx-%llx file %lld-%lld %s/%s nsect %u flags %x\n",
130 (mach_vm_offset_t
)sc
->vmaddr
,
131 (mach_vm_offset_t
)sc
->vmaddr
+ sc
->vmsize
,
133 (off_t
)sc
->fileoff
+ (off_t
)sc
->filesize
,
134 str_prot(sc
->initprot
), str_prot(sc
->maxprot
),
135 sc
->nsects
, sc
->flags
);
137 if ((off_t
)sc
->fileoff
< mh
->sizeofcmds
||
138 (off_t
)sc
->filesize
< 0) {
139 warnx("bad segment command");
142 const off_t endoff
= (off_t
)sc
->fileoff
+ (off_t
)sc
->filesize
;
143 if ((off_t
)sc
->fileoff
> corefilesize
|| endoff
> corefilesize
) {
145 * We may have run out of space to write the data
147 warnx("segment command points beyond end of file");
149 corefilemaxoff
= MAX(corefilemaxoff
, endoff
);
152 case proto_LC_COREINFO
: {
153 const struct proto_coreinfo_command
*cic
= (const void *)lc
;
154 if (OPTIONS_DEBUG(opt
, 2)) {
156 uuid_unparse_lower(cic
->uuid
, uustr
);
157 printf("%8s: version %d type %d uuid %s addr %llx dyninfo %llx\n",
158 "COREINFO", cic
->version
, cic
->type
, uustr
, cic
->address
, cic
->dyninfo
);
160 if (cic
->version
< 1 ||
161 cic
->type
!= proto_CORETYPE_USER
) {
162 warnx("bad coreinfo command");
167 case proto_LC_FILEREF
: {
168 const struct proto_fileref_command
*frc
= (const void *)lc
;
169 const char *nm
= frc
->filename
.offset
+ (char *)lc
;
170 if (OPTIONS_DEBUG(opt
, 2)) {
171 printf("%8s: mem %llx-%llx file %lld-%lld %s/%s '%s'\n",
173 frc
->vmaddr
, frc
->vmaddr
+ frc
->vmsize
,
175 (off_t
)frc
->fileoff
+ (off_t
)frc
->filesize
,
176 str_prot(frc
->prot
), str_prot(frc
->maxprot
), nm
);
178 switch (FREF_ID_TYPE(frc
->flags
)) {
180 case kFREF_ID_MTIMESPEC_LE
:
184 warnx("unknown fref id type: flags %x", frc
->flags
);
187 if (nm
<= (caddr_t
)lc
||
188 nm
> (caddr_t
)lc
+ lc
->cmdsize
||
189 (off_t
)frc
->fileoff
< 0 || (off_t
)frc
->filesize
< 0) {
190 warnx("bad fileref command");
195 case proto_LC_COREDATA
: {
196 const struct proto_coredata_command
*cc
= (const void *)lc
;
197 if (OPTIONS_DEBUG(opt
, 2)) {
198 printf("%8s: mem %llx-%llx file %lld-%lld %s/%s flags %x\n",
200 cc
->vmaddr
, cc
->vmaddr
+ cc
->vmsize
,
202 (off_t
)cc
->fileoff
+ (off_t
)cc
->filesize
,
203 str_prot(cc
->prot
), str_prot(cc
->maxprot
), cc
->flags
);
205 if ((off_t
)cc
->fileoff
< mh
->sizeofcmds
||
206 (off_t
)cc
->filesize
< 0) {
207 warnx("bad COREDATA command");
210 const off_t endoff
= (off_t
)cc
->fileoff
+ (off_t
)cc
->filesize
;
211 if ((off_t
)cc
->fileoff
> corefilesize
|| endoff
> corefilesize
) {
213 * We may have run out of space to write the data
215 warnx("segment command points beyond end of file");
217 corefilemaxoff
= MAX(corefilemaxoff
, endoff
);
221 const struct thread_command
*tc
= (const void *)lc
;
222 if (OPTIONS_DEBUG(opt
, 2))
223 printf("%8s:\n", "THREAD");
224 uint32_t *wbuf
= (void *)(tc
+ 1);
226 const uint32_t flavor
= *wbuf
++;
227 const uint32_t count
= *wbuf
++;
229 if (OPTIONS_DEBUG(opt
, 2)) {
230 printf(" flavor %u count %u\n", flavor
, count
);
233 for (unsigned k
= 0; k
< count
; k
++) {
235 printf(" [%3u] ", k
);
236 printf("%08x ", *wbuf
++);
249 if (!VALID_THREAD_STATE_FLAVOR(flavor
)) {
250 warnx("bad thread state flavor");
253 } while ((caddr_t
) wbuf
< (caddr_t
)tc
+ tc
->cmdsize
);
257 warnx("unknown cmd %u in header", lc
->cmd
);
261 lc
= (const void *)((caddr_t
)lc
+ lc
->cmdsize
);
265 if (corefilemaxoff
< corefilesize
)
266 warnx("unused data after corefile offset %lld", corefilemaxoff
);
267 if (sizeofcmds
!= mh
->sizeofcmds
) {
268 warnx("inconsistent mach header %u vs. %u", sizeofcmds
, mh
->sizeofcmds
);
274 * The vanilla Mach-O core file consists of:
276 * - A Mach-O header of type MH_CORE
278 * A set of load commands of the following types:
280 * - LC_SEGMENT{,_64} pointing at memory content in the file,
281 * each chunk consisting of a contiguous region. Regions may be zfod
282 * (no file content present).
284 * - proto_LC_COREDATA pointing at memory content in the file,
285 * each chunk consisting of a contiguous region. Regions may be zfod
286 * (no file content present) or content may be compressed (experimental)
288 * - proto_LC_COREINFO (experimental), pointing at dyld (10.12 onwards)
290 * - proto_LC_FILEREF (experimental) pointing at memory
291 * content to be mapped in from another uuid-tagged file at various offsets
293 * - LC_THREAD commands with state for each thread
295 * These load commands are followed by the relevant contents of memory,
296 * pointed to by the various commands.
303 struct regionhead
*rhead
,
304 const uuid_t aout_uuid
,
305 mach_vm_offset_t aout_load_addr
,
306 mach_vm_offset_t dyld_aii_addr
)
308 struct size_segment_data ssda
;
309 bzero(&ssda
, sizeof (ssda
));
311 if (walk_region_list(rhead
, region_size_memory
, &ssda
) < 0) {
312 warnx(0, "cannot count segments");
316 unsigned thread_count
= 0;
317 mach_port_t
*threads
= NULL
;
318 kern_return_t ret
= task_threads(task
, &threads
, &thread_count
);
319 if (KERN_SUCCESS
!= ret
|| thread_count
< 1) {
320 err_mach(ret
, NULL
, "cannot retrieve threads");
324 if (OPTIONS_DEBUG(opt
, 3)) {
325 print_memory_region_header();
326 walk_region_list(rhead
, region_print_memory
, NULL
);
327 printf("\nmach header %lu\n", sizeof (native_mach_header_t
));
328 printf("threadcount %u threadsize %lu\n", thread_count
, thread_count
* sizeof_LC_THREAD());
329 printf("fileref %lu %lu %llu\n", ssda
.ssd_fileref
.count
, ssda
.ssd_fileref
.headersize
, ssda
.ssd_fileref
.memsize
);
330 printf("zfod %lu %lu %llu\n", ssda
.ssd_zfod
.count
, ssda
.ssd_zfod
.headersize
, ssda
.ssd_zfod
.memsize
);
331 printf("vanilla %lu %lu %llu\n", ssda
.ssd_vanilla
.count
, ssda
.ssd_vanilla
.headersize
, ssda
.ssd_vanilla
.memsize
);
332 printf("sparse %lu %lu %llu\n", ssda
.ssd_sparse
.count
, ssda
.ssd_sparse
.headersize
, ssda
.ssd_sparse
.memsize
);
335 size_t headersize
= sizeof (native_mach_header_t
) +
336 thread_count
* sizeof_LC_THREAD() +
337 ssda
.ssd_fileref
.headersize
+
338 ssda
.ssd_zfod
.headersize
+
339 ssda
.ssd_vanilla
.headersize
+
340 ssda
.ssd_sparse
.headersize
;
342 headersize
+= sizeof (struct proto_coreinfo_command
);
344 void *header
= calloc(1, headersize
);
346 errx(EX_OSERR
, "out of memory for header");
348 native_mach_header_t
*mh
= make_corefile_mach_header(header
);
349 struct load_command
*lc
= (void *)(mh
+ 1);
352 const struct proto_coreinfo_command
*cc
=
353 make_coreinfo_command(mh
, lc
, aout_uuid
, aout_load_addr
, dyld_aii_addr
);
354 lc
= (void *)((caddr_t
)cc
+ cc
->cmdsize
);
358 const unsigned long fileref_count
= ssda
.ssd_fileref
.count
;
359 const unsigned long segment_count
= fileref_count
+
360 ssda
.ssd_zfod
.count
+ ssda
.ssd_vanilla
.count
+ ssda
.ssd_sparse
.count
;
361 printf("Writing %lu segments", segment_count
);
362 if (0 != fileref_count
)
363 printf(" (including %lu file reference%s (%lu bytes))",
364 fileref_count
, 1 == fileref_count
? "" : "s",
365 ssda
.ssd_fileref
.headersize
);
369 mach_vm_offset_t pagesize
= ((mach_vm_offset_t
)1 << pageshift_host
);
370 mach_vm_offset_t pagemask
= pagesize
- 1;
372 struct write_segment_data wsda
= {
377 .wsd_nocache
= false,
378 .wsd_foffset
= ((mach_vm_offset_t
)headersize
+ pagemask
) & ~pagemask
,
383 if (0 != walk_region_list(rhead
, region_write_memory
, &wsda
))
386 del_region_list(rhead
);
388 struct thread_command
*tc
= (void *)wsda
.wsd_lc
;
390 for (unsigned t
= 0; t
< thread_count
; t
++) {
391 dump_thread_state(mh
, tc
, threads
[t
]);
392 mach_port_deallocate(mach_task_self(), threads
[t
]);
393 tc
= (void *)((caddr_t
)tc
+ tc
->cmdsize
);
397 * Even if we've run out of space, try our best to
398 * write out the header.
400 if (0 != bounded_pwrite(fd
, header
, headersize
, 0, &wsda
.wsd_nocache
, NULL
))
402 if (0 == ecode
&& headersize
!= sizeof (*mh
) + mh
->sizeofcmds
)
405 wsda
.wsd_nwritten
+= headersize
;
407 validate_core_header(mh
, wsda
.wsd_foffset
);
410 warnx("failed to write core file correctly");
411 else if (opt
->verbose
) {
413 printf("Wrote %s to corefile ", str_hsize(hsz
, wsda
.wsd_nwritten
));
414 printf("(memory image %s", str_hsize(hsz
, ssda
.ssd_vanilla
.memsize
));
415 if (ssda
.ssd_sparse
.memsize
)
416 printf("+%s", str_hsize(hsz
, ssda
.ssd_sparse
.memsize
));
417 if (ssda
.ssd_fileref
.memsize
)
418 printf(", referenced %s", str_hsize(hsz
, ssda
.ssd_fileref
.memsize
));
419 if (ssda
.ssd_zfod
.memsize
)
420 printf(", zfod %s", str_hsize(hsz
, ssda
.ssd_zfod
.memsize
));
428 addfileref(struct region
*r
, const struct libent
*le
, const char *nm
)
430 r
->r_fileref
= calloc(1, sizeof (*r
->r_fileref
));
434 r
->r_fileref
->fr_libent
= le
;
435 r
->r_fileref
->fr_pathname
= le
->le_pathname
;
438 r
->r_fileref
->fr_pathname
= strdup(nm
);
440 r
->r_fileref
->fr_offset
= r
->r_pageinfo
.offset
;
441 r
->r_op
= &fileref_ops
;
446 * Once all the info about the shared cache (filerefs) and the information from
447 * dyld (filerefs and subregions), take one last look for mappings
448 * of filesystem content to convert to additional filerefs.
450 * By default we are pessimistic: read-only mappings on read-only root.
453 label_mapped_files(struct region
*r
, void *arg
)
455 const struct proc_bsdinfo
*pbi
= arg
;
457 if (r
->r_fileref
|| r
->r_insharedregion
|| r
->r_incommregion
|| r
->r_inzfodregion
)
458 return WALK_CONTINUE
;
459 if (r
->r_nsubregions
)
460 return WALK_CONTINUE
;
461 if (!r
->r_info
.external_pager
)
462 return WALK_CONTINUE
;
463 if (!opt
->allfilerefs
) {
464 /* must be mapped without write permission */
465 if (0 != (r
->r_info
.protection
& VM_PROT_WRITE
))
466 return WALK_CONTINUE
;
469 char pathbuf
[MAXPATHLEN
+1];
471 int len
= proc_regionfilename(pbi
->pbi_pid
, R_ADDR(r
), pathbuf
, sizeof (pathbuf
)-1);
472 if (len
<= 0 || len
> MAXPATHLEN
)
473 return WALK_CONTINUE
;
478 * On the desktop, only refer to files beginning with particular
479 * prefixes to increase the likelihood that we'll be able to
480 * find the content later on.
482 * XXX Not practical with a writable root, but helpful for testing.
484 static const char *white
[] = {
489 const unsigned nwhite
= sizeof (white
) / sizeof (white
[0]);
491 for (unsigned i
= 0; skip
&& i
< nwhite
; i
++)
492 skip
= 0 != strncmp(white
[i
], pathbuf
, strlen(white
[i
]));
494 if (OPTIONS_DEBUG(opt
, 3))
495 printf("\t(%s not included)\n", pathbuf
);
496 return WALK_CONTINUE
;
498 static const char *black
[] = {
499 "/System/Library/Caches",
503 const unsigned nblack
= sizeof (black
) / sizeof (black
[0]);
504 for (unsigned i
= 0; !skip
&& i
< nblack
; i
++)
505 skip
= 0 == strncmp(black
[i
], pathbuf
, strlen(black
[i
]));
507 if (OPTIONS_DEBUG(opt
, 3))
508 printf("\t(%s excluded)\n", pathbuf
);
509 return WALK_CONTINUE
;
514 if (-1 == statfs(pathbuf
, &stfs
)) {
521 warnc(errno
, "statfs: %s", pathbuf
);
524 return WALK_CONTINUE
;
528 if (OPTIONS_DEBUG(opt
, 2))
529 printr(r
, "found mapped file %s\n", pathbuf
);
530 if (!opt
->allfilerefs
) {
531 if ((stfs
.f_flags
& MNT_ROOTFS
) != MNT_ROOTFS
)
532 break; // must be on the root filesystem
533 if ((stfs
.f_flags
& MNT_RDONLY
) != MNT_RDONLY
)
534 break; // must be on a read-only filesystem
536 if (OPTIONS_DEBUG(opt
, 2))
537 print_memory_region(r
);
538 addfileref(r
, NULL
, pathbuf
);
541 return WALK_CONTINUE
;
545 coredump(task_t task
, int fd
, const struct proc_bsdinfo
*__unused pbi
)
547 /* this is the shared cache id, if any */
551 dyld_process_info dpi
= NULL
;
553 dpi
= get_task_dyld_info(task
);
555 get_sc_uuid(dpi
, sc_uuid
);
559 /* this group is for LC_COREINFO */
560 mach_vm_offset_t dyld_addr
= 0; // all_image_infos -or- dyld mach header
561 mach_vm_offset_t aout_load_addr
= 0;
563 uuid_clear(aout_uuid
);
566 * Walk the address space
569 struct regionhead
*rhead
= coredump_prepare(task
, sc_uuid
);
575 if (OPTIONS_DEBUG(opt
, 1))
576 printf("Optimizing dump content\n");
577 walk_region_list(rhead
, simple_region_optimization
, NULL
);
581 * Snapshot dyld's info ..
583 if (!libent_build_nametable(task
, dpi
))
584 warnx("error parsing dyld data => ignored");
587 * Find the a.out load address and uuid, and the dyld mach header for the coreinfo
589 const struct libent
*le
;
590 if (NULL
!= (le
= libent_lookup_first_bytype(MH_EXECUTE
))) {
591 aout_load_addr
= le
->le_mhaddr
;
592 uuid_copy(aout_uuid
, le
->le_uuid
);
594 if (NULL
!= (le
= libent_lookup_first_bytype(MH_DYLINKER
))) {
595 dyld_addr
= le
->le_mhaddr
;
599 * Use dyld's view of what's being used in the address
600 * space to shrink the dump.
602 if (OPTIONS_DEBUG(opt
, 1))
603 printf("Decorating dump with dyld-derived data\n");
604 if (0 == walk_region_list(rhead
, decorate_memory_region
, (void *)dpi
)) {
605 if (OPTIONS_DEBUG(opt
, 1))
606 printf("Sparse dump optimization(s)\n");
607 walk_region_list(rhead
, sparse_region_optimization
, NULL
);
609 walk_region_list(rhead
, undecorate_memory_region
, NULL
);
610 warnx("error parsing dyld data => ignored");
613 free_task_dyld_info(dpi
);
617 * Hunt for any memory mapped files that we can include by reference
618 * Depending on whether the bsd part of the task is still present
619 * we might be able to determine filenames of other regions mapping
620 * them here - this allows fonts, images, and other read-only content
621 * to be converted into file references, further reducing the size
624 * NOTE: Even though the corpse snapshots the VM, the filesystem is
625 * not correspondingly snapshotted and thus may mutate while the dump
626 * proceeds - so be pessimistic about inclusion.
628 if (opt
->extended
&& NULL
!= pbi
) {
629 if (OPTIONS_DEBUG(opt
, 1))
630 printf("Mapped file optimization\n");
631 walk_region_list(rhead
, label_mapped_files
, (void *)pbi
);
634 if (OPTIONS_DEBUG(opt
, 1))
635 printf("Optimization(s) done\n");
639 ecode
= coredump_write(task
, fd
, rhead
, aout_uuid
, aout_load_addr
, dyld_addr
);
643 struct find_shared_cache_args
{
645 vm_object_id_t fsc_object_id
;
646 vm32_object_id_t fsc_region_object_id
;
648 const struct libent
*fsc_le
;
653 * This is "find the objid of the first shared cache" in the shared region.
656 find_shared_cache(struct region
*r
, void *arg
)
658 struct find_shared_cache_args
*fsc
= arg
;
660 if (!r
->r_insharedregion
)
661 return WALK_CONTINUE
; /* wrong address range! */
662 if (0 != r
->r_info
.user_tag
)
663 return WALK_CONTINUE
; /* must be tag zero */
664 if ((VM_PROT_READ
| VM_PROT_EXECUTE
) != r
->r_info
.protection
||
665 r
->r_info
.protection
!= r
->r_info
.max_protection
)
666 return WALK_CONTINUE
; /* must be r-x / r-x */
667 if (r
->r_pageinfo
.offset
!= 0)
668 return WALK_CONTINUE
; /* must map beginning of file */
670 if (OPTIONS_DEBUG(opt
, 1)) {
672 printr(r
, "examining %s shared cache candidate\n", str_hsize(hstr
, R_SIZE(r
)));
675 struct copied_dyld_cache_header
*ch
;
676 mach_msg_type_number_t chlen
= sizeof (*ch
);
677 kern_return_t ret
= mach_vm_read(fsc
->fsc_task
, R_ADDR(r
), sizeof (*ch
), (vm_offset_t
*)&ch
, &chlen
);
679 if (KERN_SUCCESS
!= ret
) {
680 err_mach(ret
, NULL
, "mach_vm_read() candidate shared region header");
681 return WALK_CONTINUE
;
685 if (get_uuid_from_shared_cache_mapping(ch
, chlen
, scuuid
) &&
686 uuid_compare(scuuid
, fsc
->fsc_uuid
) == 0) {
687 if (OPTIONS_DEBUG(opt
, 1)) {
689 uuid_unparse_lower(fsc
->fsc_uuid
, uustr
);
690 printr(r
, "found shared cache %s here\n", uustr
);
692 if (!r
->r_info
.external_pager
) {
693 if (OPTIONS_DEBUG(opt
, 1))
694 printf("Hmm. Found shared cache magic# + uuid, but not externally paged?\n");
696 return WALK_CONTINUE
; /* should be "paged" from a file */
699 // This is the ID associated with the first page of the mapping
700 fsc
->fsc_object_id
= r
->r_pageinfo
.object_id
;
701 // This is the ID associated with the region
702 fsc
->fsc_region_object_id
= r
->r_info
.object_id
;
704 mach_vm_deallocate(mach_task_self(), (vm_offset_t
)ch
, chlen
);
705 if (fsc
->fsc_object_id
) {
706 if (OPTIONS_DEBUG(opt
, 3)) {
708 uuid_unparse_lower(fsc
->fsc_uuid
, uu
);
709 printf("Shared cache objid %llx uuid %s\n",
710 fsc
->fsc_object_id
, uu
);
712 return WALK_TERMINATE
;
714 return WALK_CONTINUE
;
718 compare_region_with_shared_cache(const struct region
*r
, struct find_shared_cache_args
*fsc
)
721 if (-1 == fstat(fsc
->fsc_fd
, &st
)) {
722 if (OPTIONS_DEBUG(opt
, 1))
723 printr(r
, "cannot fstat %s - %s\n",
724 fsc
->fsc_le
->le_filename
, strerror(errno
));
727 void *file
= mmap(NULL
, R_SIZEOF(r
), PROT_READ
, MAP_PRIVATE
, fsc
->fsc_fd
, r
->r_pageinfo
.offset
);
728 if ((void *)-1L == file
) {
729 if (OPTIONS_DEBUG(opt
, 1))
730 printr(r
, "mmap %s - %s\n", fsc
->fsc_le
->le_filename
, strerror(errno
));
733 madvise(file
, R_SIZEOF(r
), MADV_SEQUENTIAL
);
735 vm_offset_t data
= 0;
736 mach_msg_type_number_t data_count
;
737 const kern_return_t kr
= mach_vm_read(fsc
->fsc_task
, R_ADDR(r
), R_SIZE(r
), &data
, &data_count
);
739 if (KERN_SUCCESS
!= kr
|| data_count
< R_SIZE(r
)) {
740 err_mach(kr
, r
, "mach_vm_read()");
741 munmap(file
, R_SIZEOF(r
));
745 mach_vm_size_t cmpsize
= data_count
;
749 * Now we have the corresponding regions mapped, we should be
750 * able to compare them. There's just one last twist that relates
751 * to heterogenous pagesize systems: rdar://23744374
753 if (st
.st_size
< (off_t
)(r
->r_pageinfo
.offset
+ cmpsize
) &&
754 pageshift_host
< pageshift_app
) {
756 * Looks like we're about to map close to the end of the object.
757 * Check what's really mapped there and reduce the size accordingly.
759 if (!is_actual_size(fsc
->fsc_task
, r
, &cmpsize
)) {
760 if (OPTIONS_DEBUG(opt
, 3))
761 printr(r
, "narrowing the comparison (%llu "
762 "-> %llu)\n", R_SIZE(r
), cmpsize
);
767 mach_vm_behavior_set(mach_task_self(), data
, data_count
, VM_BEHAVIOR_SEQUENTIAL
);
769 const bool thesame
= memcmp(file
, (void *)data
, (size_t)cmpsize
) == 0;
774 const char *f
= file
;
775 const char *d
= (void *)data
;
776 for (mach_vm_size_t off
= 0; off
< cmpsize
; off
+= 4096) {
777 if (memcmp(f
, d
, 4096) != 0) {
784 printr(r
, "%d of %d pages different\n", diffcount
, diffcount
+ samecount
);
787 mach_vm_deallocate(mach_task_self(), data
, data_count
);
788 munmap(file
, R_SIZEOF(r
));
790 if (!thesame
&& OPTIONS_DEBUG(opt
, 3))
791 printr(r
, "mapped file (%s) region is modified\n", fsc
->fsc_le
->le_filename
);
796 label_shared_cache(struct region
*r
, void *arg
)
798 struct find_shared_cache_args
*fsc
= arg
;
800 if (!r
->r_insharedregion
)
801 return WALK_CONTINUE
;
802 if (!r
->r_info
.external_pager
)
803 return WALK_CONTINUE
;
804 if (r
->r_pageinfo
.object_id
!= fsc
->fsc_object_id
) {
805 /* wrong object, or first page already modified */
806 return WALK_CONTINUE
;
808 if (((r
->r_info
.protection
| r
->r_info
.max_protection
) & VM_PROT_WRITE
) != 0) {
809 /* potentially writable, but was it written? */
810 if (0 != r
->r_info
.pages_dirtied
)
811 return WALK_CONTINUE
;
812 if (0 != r
->r_info
.pages_swapped_out
)
813 return WALK_CONTINUE
;
814 if (0 != r
->r_info
.pages_resident
&& !r
->r_info
.external_pager
)
815 return WALK_CONTINUE
;
816 if (OPTIONS_DEBUG(opt
, 1))
817 printr(r
, "verifying shared cache content against memory image\n");
818 if (!compare_region_with_shared_cache(r
, fsc
)) {
819 /* bits don't match */
820 if (OPTIONS_DEBUG(opt
, 1))
821 printr(r
, "hmm .. mismatch: using memory image\n");
822 return WALK_CONTINUE
;
827 * This mapped file segment will be represented as a reference
828 * to the file, rather than as a copy of the mapped file.
830 addfileref(r
, libent_lookup_byuuid(fsc
->fsc_uuid
), NULL
);
831 return WALK_CONTINUE
;
835 coredump_prepare(task_t task
, uuid_t sc_uuid
)
837 struct regionhead
*rhead
= build_region_list(task
);
839 if (OPTIONS_DEBUG(opt
, 2)) {
840 printf("Region list built\n");
841 print_memory_region_header();
842 walk_region_list(rhead
, region_print_memory
, NULL
);
845 if (uuid_is_null(sc_uuid
))
849 * Name the shared cache, if we can
851 char *nm
= shared_cache_filename(sc_uuid
);
852 const struct libent
*le
;
855 le
= libent_insert(nm
, sc_uuid
, 0, NULL
, NULL
, 0);
857 libent_insert("(anonymous shared cache)", sc_uuid
, 0, NULL
, NULL
, 0);
859 printf("Warning: cannot name the shared cache ");
860 if (OPTIONS_DEBUG(opt
, 1)) {
862 uuid_unparse_lower(sc_uuid
, uustr
);
863 printf("(%s) ", uustr
);
865 printf("- dump may be large!\n");
872 * See if we can replace entire regions with references to the shared cache
873 * by looking at the VM meta-data about those regions.
875 if (OPTIONS_DEBUG(opt
, 1)) {
877 uuid_unparse_lower(sc_uuid
, uustr
);
878 printf("Searching for shared cache with uuid %s\n", uustr
);
882 * Identify the regions mapping the shared cache by comparing the UUID via
883 * dyld with the UUID of likely-looking mappings in the right address range
885 struct find_shared_cache_args fsca
;
886 bzero(&fsca
, sizeof (fsca
));
887 fsca
.fsc_task
= task
;
888 uuid_copy(fsca
.fsc_uuid
, sc_uuid
);
891 walk_region_list(rhead
, find_shared_cache
, &fsca
);
893 if (0 == fsca
.fsc_object_id
) {
894 printf("Cannot identify the shared cache region(s) => ignored\n");
897 printf("Referenced %s\n", nm
);
899 fsca
.fsc_fd
= open(fsca
.fsc_le
->le_pathname
, O_RDONLY
);
900 if (-1 == fsca
.fsc_fd
)
901 errc(EX_SOFTWARE
, errno
, "open %s", fsca
.fsc_le
->le_pathname
);
903 walk_region_list(rhead
, label_shared_cache
, &fsca
);