2 * Copyright (c) 2016 Apple Inc. All rights reserved.
5 typedef char *kobject_description_t
[512];
11 #include "dyld_shared_cache.h"
14 #include <sys/types.h>
17 #include <sys/queue.h>
18 #include <sys/param.h>
19 #include <mach-o/fat.h>
20 #include <uuid/uuid.h>
33 /* Declare xpc_create_from_plist manually because xpc/private.h is closed source */
34 xpc_object_t
xpc_create_from_plist(void *data
, size_t size
);
35 #include <sys/event.h>
38 #if defined(CONFIG_GCORE_MAP) || defined(CONFIG_GCORE_CONV) || defined(CONFIG_GCORE_FREF)
41 mmapfile(int fd
, off_t off
, off_t
*filesize
)
44 if (-1 == fstat(fd
, &st
))
45 errc(EX_OSERR
, errno
, "can't stat input file");
47 const size_t size
= (size_t)(st
.st_size
- off
);
48 if ((off_t
)size
!= (st
.st_size
- off
))
49 errc(EX_OSERR
, EOVERFLOW
, "input file too large?");
51 const void *addr
= mmap(0, size
, PROT_READ
, MAP_PRIVATE
, fd
, off
);
52 if ((void *)-1 == addr
)
53 errc(EX_OSERR
, errno
, "can't mmap input file");
54 *filesize
= st
.st_size
;
60 const native_mach_header_t
*mh
,
61 void (^coreinfo
)(const struct proto_coreinfo_command
*),
62 void (^frefdata
)(const struct proto_fileref_command
*),
63 void (^coredata
)(const struct proto_coredata_command
*),
64 void (^segdata
)(const native_segment_command_t
*),
65 void (^thrdata
)(const struct thread_command
*))
67 const struct load_command
*lc
= (const void *)(mh
+ 1);
68 for (unsigned i
= 0; i
< mh
->ncmds
; i
++) {
70 case proto_LC_COREINFO
:
72 coreinfo((const void *)lc
);
74 case proto_LC_FILEREF
:
76 frefdata((const void *)lc
);
78 case proto_LC_COREDATA
:
80 coredata((const void *)lc
);
82 case NATIVE_LC_SEGMENT
:
84 segdata((const void *)lc
);
88 thrdata((const void *)lc
);
93 if (NULL
== (lc
= next_lc(lc
)))
100 #ifdef CONFIG_GCORE_FREF
106 const void *corebase
= mmapfile(fd
, 0, &filesize
);
110 STAILQ_ENTRY(flist
) f_linkage
;
112 unsigned long f_nmhash
;
114 STAILQ_HEAD(flisthead
, flist
) __flh
, *flh
= &__flh
;
117 walkcore(corebase
, NULL
, ^(const struct proto_fileref_command
*fc
) {
118 const char *nm
= fc
->filename
.offset
+ (const char *)fc
;
119 const unsigned long nmhash
= simple_namehash(nm
);
121 STAILQ_FOREACH(f
, flh
, f_linkage
) {
122 if (nmhash
== f
->f_nmhash
&& 0 == strcmp(f
->f_nm
, nm
))
123 return; /* skip duplicates */
125 struct flist
*nf
= calloc(1, sizeof (*nf
));
127 nf
->f_nmhash
= nmhash
;
128 STAILQ_INSERT_TAIL(flh
, nf
, f_linkage
);
129 }, NULL
, NULL
, NULL
);
131 struct flist
*f
, *tf
;
132 STAILQ_FOREACH_SAFE(f
, flh
, f_linkage
, tf
) {
133 printf("%s\n", f
->f_nm
);
138 munmap((void *)corebase
, (size_t)filesize
);
142 #endif /* CONFIG_GCORE_FREF */
144 #ifdef CONFIG_GCORE_MAP
147 * A pale imitation of vmmap, but for core files
153 const void *corebase
= mmapfile(fd
, 0, &filesize
);
155 __block
int coreversion
= 0;
157 walkcore(corebase
, ^(const struct proto_coreinfo_command
*ci
) {
158 coreversion
= ci
->version
;
159 }, NULL
, NULL
, NULL
, NULL
);
161 if (0 == coreversion
) {
162 const char titlfmt
[] = "%16s-%-16s [%7s] %3s/%3s\n";
163 const char *segcfmt
= "%016llx-%016llx [%7s] %3s/%3s\n";
165 printf(titlfmt
, "start ", " end", "vsize", "prt", "max");
166 walkcore(corebase
, NULL
, NULL
, NULL
, ^(const native_segment_command_t
*sc
) {
168 printf(segcfmt
, (mach_vm_offset_t
)sc
->vmaddr
, (mach_vm_offset_t
)sc
->vmaddr
+ sc
->vmsize
, str_hsize(vstr
, sc
->vmsize
), str_prot(sc
->initprot
), str_prot(sc
->maxprot
));
171 const char titlfmt
[] = "%-23s %16s-%-16s [%7s] %3s/%3s %6s %4s %-14s\n";
172 const char *freffmt
= "%-23s %016llx-%016llx [%7s] %3s/%3s %6s %4s %-14s @%lld\n";
173 const char *datafmt
= "%-23s %016llx-%016llx [%7s] %3s/%3s %6s %4s %-14s\n";
175 printf(titlfmt
, "region type", "start ", " end", "vsize", "prt", "max", "shrmod", "purge", "region detail");
176 walkcore(corebase
, NULL
, ^(const struct proto_fileref_command
*fc
) {
177 const char *nm
= fc
->filename
.offset
+ (const char *)fc
;
180 printf(freffmt
, str_tag(tstr
, fc
->tag
, fc
->share_mode
, fc
->prot
, fc
->extp
),
181 fc
->vmaddr
, fc
->vmaddr
+ fc
->vmsize
,
182 str_hsize(vstr
, fc
->vmsize
), str_prot(fc
->prot
),
183 str_prot(fc
->maxprot
), str_shared(fc
->share_mode
),
184 str_purgable(fc
->purgable
, fc
->share_mode
), nm
, fc
->fileoff
);
185 }, ^(const struct proto_coredata_command
*cc
) {
188 printf(datafmt
, str_tag(tstr
, cc
->tag
, cc
->share_mode
, cc
->prot
, cc
->extp
),
189 cc
->vmaddr
, cc
->vmaddr
+ cc
->vmsize
,
190 str_hsize(vstr
, cc
->vmsize
), str_prot(cc
->prot
),
191 str_prot(cc
->maxprot
), str_shared(cc
->share_mode
),
192 str_purgable(cc
->purgable
, cc
->share_mode
),
193 cc
->vmsize
&& 0 == cc
->filesize
? "(zfod)" : "");
194 }, ^(const native_segment_command_t
*sc
) {
196 printf(datafmt
, "", (mach_vm_offset_t
)sc
->vmaddr
,
197 (mach_vm_offset_t
)sc
->vmaddr
+ sc
->vmsize
,
198 str_hsize(vstr
, sc
->vmsize
), str_prot(sc
->initprot
),
199 str_prot(sc
->maxprot
), "", "",
200 sc
->vmsize
&& 0 == sc
->filesize
? "(zfod)" : "");
204 munmap((void *)corebase
, (size_t)filesize
);
210 #ifdef CONFIG_GCORE_CONV
213 * Convert an input core file into an "old" format core file
214 * (a) convert all fileref segments into regular segments
215 * (b) uncompress anything we find compressed.
216 * This should be equivalent to a copy for an "old" format core file.
220 machocmp(const native_mach_header_t
*tmh
, const native_mach_header_t
*mh
, const struct proto_fileref_command
*fr
)
222 if (tmh
->magic
== mh
->magic
) {
223 const struct load_command
*lc
= (const void *)(tmh
+ 1);
224 for (unsigned i
= 0; i
< tmh
->ncmds
; i
++) {
225 if (LC_UUID
== lc
->cmd
&& lc
->cmdsize
>= sizeof (struct uuid_command
)) {
226 const struct uuid_command
*uc
= (const void *)lc
;
227 return uuid_compare(uc
->uuid
, fr
->id
);
229 if (NULL
== (lc
= next_lc(lc
)))
237 fat_machocmp(const struct fat_header
*fh
, const native_mach_header_t
*mh
, const struct proto_fileref_command
*fr
, off_t
*reloff
)
239 const uint32_t (^get32
)(uint32_t);
241 if (FAT_MAGIC
== fh
->magic
) {
242 get32
= ^(uint32_t val
) {
246 get32
= ^(uint32_t val
) {
248 for (unsigned i
= 0; i
< sizeof (uint32_t); i
++)
249 ((uint8_t *)&result
)[i
] = ((uint8_t *)&val
)[3-i
];
254 assert(FAT_MAGIC
== get32(fh
->magic
));
255 assert(kFREF_ID_UUID
== FREF_ID_TYPE(fr
->flags
) && !uuid_is_null(fr
->id
));
257 const struct fat_arch
*fa
= (const struct fat_arch
*)(fh
+ 1);
258 uint32_t narch
= get32(fh
->nfat_arch
);
259 for (unsigned n
= 0; n
< narch
; n
++, fa
++) {
260 const native_mach_header_t
*tmh
= (const void *)(((const char *)fh
) + get32(fa
->offset
));
261 if (tmh
->magic
== mh
->magic
&& 0 == machocmp(tmh
, mh
, fr
)) {
262 *reloff
= get32(fa
->offset
);
275 static struct convstats
{
279 int64_t uncompressed
;
280 } cstat
, *cstats
= &cstat
;
283 * A fileref segment references a read-only file that contains pages from
284 * the image. The file may be a Mach binary or dylib identified with a uuid.
287 convert_fileref_with_file(const char *filename
, const native_mach_header_t
*inmh
, const struct proto_fileref_command
*infr
, const struct vm_range
*invr
, struct load_command
*lc
, struct output_info
*oi
)
289 assert(invr
->addr
== infr
->vmaddr
&& invr
->size
== infr
->vmsize
);
292 const int rfd
= open(filename
, O_RDONLY
);
293 if (-1 == rfd
|| -1 == fstat(rfd
, &st
)) {
294 warnc(errno
, "%s: open", filename
);
297 const size_t rlen
= (size_t)st
.st_size
;
298 void *raddr
= mmap(NULL
, rlen
, PROT_READ
, MAP_PRIVATE
, rfd
, 0);
299 if ((void *)-1 == raddr
) {
300 warnc(errno
, "%s: mmap", filename
);
306 off_t fatoff
= 0; /* for FAT objects */
307 int ecode
= EX_DATAERR
;
309 switch (FREF_ID_TYPE(infr
->flags
)) {
310 case kFREF_ID_UUID
: {
311 /* file should be a mach binary: check that uuid matches */
312 const uint32_t magic
= *(uint32_t *)raddr
;
316 if (0 == fat_machocmp(raddr
, inmh
, infr
, &fatoff
))
319 case NATIVE_MH_MAGIC
:
320 if (0 == machocmp(raddr
, inmh
, infr
))
325 * Maybe this is the shared cache?
328 if (get_uuid_from_shared_cache_mapping(raddr
, rlen
, uu
) && uuid_compare(uu
, infr
->id
) == 0)
335 case kFREF_ID_MTIMESPEC_LE
:
336 /* file should have the same mtime */
337 if (0 == memcmp(&st
.st_mtimespec
, infr
->id
, sizeof (infr
->id
)))
341 /* file has no uniquifier, copy it anyway */
347 warnx("%s doesn't match corefile content", filename
);
351 const off_t fileoff
= fatoff
+ infr
->fileoff
;
352 const void *start
= (const char *)raddr
+ fileoff
;
353 const size_t len
= (size_t)infr
->filesize
;
357 if (fileoff
+ (off_t
)infr
->filesize
> (off_t
)rlen
) {
359 * the file content needed (as described on machine with
360 * larger pagesize) extends beyond the end of the mapped
361 * file using our smaller pagesize. Zero pad it.
363 const size_t pagesize_host
= 1ul << pageshift_host
;
364 void *endaddr
= (caddr_t
)raddr
+ roundup(rlen
, pagesize_host
);
365 zlen
= (size_t)(fileoff
+ infr
->filesize
- rlen
);
366 zaddr
= mmap(endaddr
, zlen
, PROT_READ
, MAP_FIXED
| MAP_PRIVATE
| MAP_ANON
, -1, 0);
367 if ((void *)-1 == zaddr
) {
369 warnc(errno
, "cannot zero-pad %s mapping for %s", str_hsize(hstr
, zlen
),filename
);
375 if (-1 == madvise((void *)start
, len
, MADV_SEQUENTIAL
))
376 warnc(errno
, "%s: madvise", filename
);
378 const int error
= bounded_pwrite(oi
->oi_fd
, start
, len
, oi
->oi_foffset
, &oi
->oi_nocache
, NULL
);
381 if (-1 == munmap(zaddr
, zlen
))
382 warnc(errno
, "%s: munmap zero pad", filename
);
384 if (-1 == munmap(raddr
, rlen
))
385 warnc(errno
, "%s: munmap", filename
);
387 warnc(error
, "while copying %s to core file", filename
);
391 const struct file_range fr
= {
392 .off
= oi
->oi_foffset
,
393 .size
= infr
->filesize
,
395 make_native_segment_command(lc
, invr
, &fr
, infr
->maxprot
, infr
->prot
);
396 oi
->oi_foffset
+= fr
.size
;
397 cstats
->added
+= infr
->filesize
;
402 * expanduser tries to expand the leading '~' (if there is any) in the given
403 * path and returns a copy of the expanded path; it returns NULL on failures.
404 * The caller is responsible for freeing the returned string.
407 expanduser(const char *path
)
412 if (path
[0] != '~') {
414 * For consistency, still dup the string so that the caller always
415 * needs to free the string.
420 char *expanded
= NULL
;
422 if (OPTIONS_DEBUG(opt
, 1)) {
423 printf("Expanding %s\n", path
);
425 int rc
= glob(path
, GLOB_TILDE
, NULL
, &globbuf
);
427 if (OPTIONS_DEBUG(opt
, 3)) {
428 printf("expanduser - gl_pathc: %zu\n", globbuf
.gl_pathc
);
429 for (size_t i
= 0; i
< globbuf
.gl_pathc
; i
++) {
430 printf("expanduser - gl_pathv[%zu]: %s\n", i
, globbuf
.gl_pathv
[i
]);
433 if (globbuf
.gl_pathc
== 1) {
434 expanded
= strdup(globbuf
.gl_pathv
[0]);
435 if (OPTIONS_DEBUG(opt
, 1)) {
436 printf("Expanded path: %s\n", expanded
);
445 #define RESPONSE_BUFF_SIZE (2048)
448 * read_response dynamically allocates buffer for reading bytes from the given
449 * fd. Upon success, this function sets response to point to the buffer and
450 * returns bytes being read; otherwise, it returns -1. The caller is
451 * responsible for freeing the response buffer.
454 read_response(int fd
, char **response
)
456 if (response
== NULL
|| *response
) {
457 warnx("Invalid response buffer pointer");
461 ssize_t bytes_read
= 0;
462 size_t buff_size
= RESPONSE_BUFF_SIZE
;
464 if (OPTIONS_DEBUG(opt
, 3)) {
465 printf("Allocating response buffer (%zu)\n", buff_size
);
467 char *buff
= malloc(buff_size
);
469 warn("Failed to allocate response buffer (%zu)", buff_size
);
477 bytes_read
= read(fd
, buff
+ total
, buff_size
- total
);
478 if (bytes_read
== -1) {
479 if (errno
== EINTR
) {
486 total
+= (size_t)bytes_read
;
487 if (total
== buff_size
) {
488 size_t new_buff_size
= buff_size
* 2;
489 if (OPTIONS_DEBUG(opt
, 3)) {
490 printf("Reallocating response buffer (%zu)\n", new_buff_size
);
492 char *new_buff
= realloc(buff
, new_buff_size
);
493 if (new_buff
== NULL
) {
494 warn("Failed to reallocate response buffer (%zu)", new_buff_size
);
498 buff_size
= new_buff_size
;
501 } while (bytes_read
!= 0);
510 assert(total
< buff_size
);
514 return (ssize_t
)total
;
517 #define WAITPID_WTO_SIGALRM (100) /* alternative for SIGALRM for kevent timeout */
518 #define WAITPID_WTO_SIGERR (101) /* sig for error when waiting for pid */
521 * waitpid_with_timeout returns true if the process exits successfully within
522 * timeout; otherwise, it returns false along with setting exitstatus and
523 * signal_no if the pointers are given.
526 waitpid_with_timeout(pid_t pid
, int *exitstatus
, int *signal_no
, int timeout
)
533 struct kevent64_s event
= {
534 .ident
= (uint64_t)pid
,
535 .filter
= EVFILT_PROC
,
536 .flags
= EV_ADD
| EV_ONESHOT
,
539 struct timespec tmout
= {
542 int ret
= kevent64(kq
, &event
, 1, &event
, 1, 0, &tmout
);
543 int kevent64_errno
= errno
;
546 if (ret
== 0) { /* timeout */
551 *signal_no
= WAITPID_WTO_SIGALRM
;
557 warnx("kevent64(): errno=%d (%s)\n", kevent64_errno
, strerror(kevent64_errno
));
561 if (event
.flags
== EV_ERROR
&& event
.data
!= ESRCH
) {
562 warnx("event.data (%lld) is not ESRCH when event.flags is EV_ERROR\n", event
.data
);
566 if (event
.ident
!= (uint64_t)pid
) {
567 warnx("event.ident is %lld (should be pid %d)\n", event
.ident
, pid
);
571 if (event
.filter
!= EVFILT_PROC
) {
572 warnx("event.filter (%d) is not EVFILT_PROC\n", event
.filter
);
577 while (waitpid(pid
, &status
, 0) < 0) {
578 if (errno
== EINTR
) {
581 warnx("waitpid(): errno=%d (%s)\n", errno
, strerror(errno
));
584 if (WIFEXITED(status
)) {
586 *exitstatus
= WEXITSTATUS(status
);
591 return WEXITSTATUS(status
) == 0;
593 if (WIFSIGNALED(status
)) {
598 *signal_no
= WTERMSIG(status
);
604 if (exitstatus
) *exitstatus
= 0;
605 if (signal_no
) *signal_no
= WAITPID_WTO_SIGERR
;
609 #define DSYMFORUUID_PATH "/usr/local/bin/dsymForUUID"
612 * exec_dsymForUUID spawns dsymForUUID to query dsym UUID info and responds the
613 * result plist. Upon success, this function sets response point to the buffer
614 * and returns bytes being read; otherwise, it returns -1. The caller is
615 * responsible for freeing the response buffer.
618 exec_dsymForUUID(uuid_string_t id
, char **response
)
620 int pipe_fds
[2] = {-1, -1};
621 bool file_actions_inited
= false;
622 ssize_t bytes_read
= -1;
630 posix_spawn_file_actions_t file_actions
;
631 rc
= posix_spawn_file_actions_init(&file_actions
);
635 file_actions_inited
= true;
637 rc
= posix_spawn_file_actions_addclose(&file_actions
, pipe_fds
[0]);
642 rc
= posix_spawn_file_actions_adddup2(&file_actions
, pipe_fds
[1], STDOUT_FILENO
);
647 rc
= posix_spawn_file_actions_addclose(&file_actions
, pipe_fds
[1]);
652 char *command
[] = {DSYMFORUUID_PATH
, id
, NULL
};
654 rc
= posix_spawn(&child
, command
[0], &file_actions
, NULL
, command
, NULL
);
662 bytes_read
= read_response(pipe_fds
[0], response
);
664 waitpid_with_timeout(child
, NULL
, NULL
, 3);
667 if (pipe_fds
[1] != -1) {
670 if (pipe_fds
[0] != -1) {
673 if (file_actions_inited
) {
674 posix_spawn_file_actions_destroy(&file_actions
);
681 * get_symbol_rich_executable_path_via_dsymForUUID spawns dsymForUUID to query
682 * dsym uuid info for the given uuid and returns the string of
683 * DBGSymbolRichExecutable; otherwise, it returns NULL on failures. The caller
684 * is responsible for freeing the returned string.
687 get_symbol_rich_executable_path_via_dsymForUUID(const uuid_t uuid
)
691 uuid_string_t uuid_str
;
692 xpc_object_t plist
= NULL
;
693 xpc_object_t uuid_info
= NULL
;
694 xpc_object_t exec_path
= NULL
;
695 char *expanded_exec_path
= NULL
;
697 uuid_unparse_upper(uuid
, uuid_str
);
699 size
= exec_dsymForUUID(uuid_str
, &response
);
704 if (OPTIONS_DEBUG(opt
, 3)) {
705 printf("dsymForUUID response:\n%s\n", response
);
708 plist
= xpc_create_from_plist(response
, (size_t)size
);
712 if (xpc_get_type(plist
) != XPC_TYPE_DICTIONARY
) {
716 uuid_info
= xpc_dictionary_get_value(plist
, uuid_str
);
717 if (uuid_info
== NULL
) {
720 if (xpc_get_type(uuid_info
) != XPC_TYPE_DICTIONARY
) {
724 exec_path
= xpc_dictionary_get_value(uuid_info
, "DBGSymbolRichExecutable");
725 if (exec_path
== NULL
) {
728 if (xpc_get_type(exec_path
) != XPC_TYPE_STRING
) {
732 expanded_exec_path
= expanduser(xpc_string_get_string_ptr(exec_path
));
742 return expanded_exec_path
;
746 * bind the file reference into the output core file.
747 * filename optionally prefixed with names from a ':'-separated PATH variable
750 convert_fileref(const char *path
, bool zf
, const native_mach_header_t
*inmh
, const struct proto_fileref_command
*infr
, struct load_command
*lc
, struct output_info
*oi
)
752 const char *nm
= infr
->filename
.offset
+ (const char *)infr
;
754 const struct vm_range invr
= {
755 .addr
= infr
->vmaddr
,
756 .size
= infr
->vmsize
,
761 printvr(&invr
, "adding %s from '%s'",
762 str_hsize(hstr
, (off_t
)infr
->filesize
), nm
);
763 switch (FREF_ID_TYPE(infr
->flags
)) {
767 uuid_unparse_lower(infr
->id
, uustr
);
768 printf(" (%s)", uustr
);
770 case kFREF_ID_MTIMESPEC_LE
: {
773 char tbuf
[4 + 2 + 2 + 2 + 2 + 1 + 2 + 1]; /* touch -t */
774 memcpy(&mts
, &infr
->id
, sizeof (mts
));
775 localtime_r(&mts
.tv_sec
, &tm
);
776 strftime(tbuf
, sizeof (tbuf
), "%Y%m%d%H%M.%S", &tm
);
777 printf(" (%s)", tbuf
);
784 if (opt
->dsymforuuid
&& (FREF_ID_TYPE(infr
->flags
) == kFREF_ID_UUID
)) {
785 /* Try to use dsymForUUID to get the symbol-rich executable */
786 char *symrich_filepath
= get_symbol_rich_executable_path_via_dsymForUUID(infr
->id
);
787 if (symrich_filepath
) {
789 printf("\tTrying %s from dsymForUUID\n", symrich_filepath
);
791 ecode
= convert_fileref_with_file(symrich_filepath
, inmh
, infr
, &invr
, lc
, oi
);
792 free(symrich_filepath
);
796 warnx("Failed to convert fileref with dsymForUUID. Fall back to local paths");
800 const size_t pathsize
= path
? strlen(path
) : 0;
803 ecode
= convert_fileref_with_file(nm
, inmh
, infr
, &invr
, lc
, oi
);
805 /* search the : separated path (-L) for possible matches */
806 char *pathcopy
= strdup(path
);
807 char *searchpath
= pathcopy
;
810 while ((token
= strsep(&searchpath
, ":")) != NULL
) {
811 const size_t buflen
= strlen(token
) + 1 + strlen(nm
) + 1;
812 char *buf
= malloc(buflen
);
813 snprintf(buf
, buflen
, "%s%s%s", token
, '/' == nm
[0] ? "" : "/", nm
);
815 printf("\tTrying '%s'", buf
);
816 if (0 == access(buf
, R_OK
)) {
819 ecode
= convert_fileref_with_file(buf
, inmh
, infr
, &invr
, lc
, oi
);
824 } else if (opt
->verbose
)
826 0 == access(buf
, F_OK
) ? "Unreadable" : "Not present");
832 if (0 != ecode
&& zf
) {
834 * Failed to find the file reference. If this was a fileref that uses
835 * a file metadata tagging method (e.g. mtime), allow the user to subsitute a
836 * zfod region: assumes that it's better to have something to debug
837 * vs. nothing. UUID-tagged filerefs are Mach-O tags, and are
838 * assumed to be never substitutable.
840 switch (FREF_ID_TYPE(infr
->flags
)) {
842 case kFREF_ID_MTIMESPEC_LE
: { // weak tagging, allow zfod substitution
843 const struct file_range outfr
= {
844 .off
= oi
->oi_foffset
,
848 printf("\tWARNING: no file matched. Missing content is now zfod\n");
850 printvr(&invr
, "WARNING: missing content (%s) now zfod\n", nm
);
851 make_native_segment_command(lc
, &invr
, &outfr
, infr
->maxprot
, infr
->prot
);
863 segment_uncompflags(unsigned algnum
, compression_algorithm
*ca
)
867 *ca
= COMPRESSION_LZ4
;
870 *ca
= COMPRESSION_ZLIB
;
873 *ca
= COMPRESSION_LZMA
;
876 *ca
= COMPRESSION_LZFSE
;
879 warnx("unknown compression flavor %d", algnum
);
886 convert_region(const void *inbase
, const struct vm_range
*invr
, const struct file_range
*infr
, const vm_prot_t prot
, const vm_prot_t maxprot
, const int flavor
, struct load_command
*lc
, struct output_info
*oi
)
891 void *input
= (const caddr_t
)inbase
+ F_OFF(infr
);
898 printvr(invr
, "copying %s\n", str_hsize(hstr
, F_SIZE(infr
)));
901 compression_algorithm ca
;
903 if (0 != (ecode
= segment_uncompflags(flavor
, &ca
)))
906 hsize_str_t hstr1
, hstr2
;
907 printvr(invr
, "uncompressing %s to %s\n",
908 str_hsize(hstr1
, F_SIZE(infr
)), str_hsize(hstr2
, V_SIZE(invr
)));
910 const size_t buflen
= V_SIZEOF(invr
);
911 buf
= malloc(buflen
);
912 const size_t dstsize
= compression_decode_buffer(buf
, buflen
, input
, (size_t)F_SIZE(infr
), NULL
, ca
);
913 if (buflen
!= dstsize
) {
914 warnx("failed to uncompress segment");
918 cstats
->compressed
+= F_SIZE(infr
);
920 const int error
= bounded_pwrite(oi
->oi_fd
, buf
, V_SIZEOF(invr
), oi
->oi_foffset
, &oi
->oi_nocache
, NULL
);
922 warnc(error
, "failed to write data to core file");
930 const struct file_range outfr
= {
931 .off
= oi
->oi_foffset
,
932 .size
= V_SIZE(invr
),
934 make_native_segment_command(lc
, invr
, &outfr
, maxprot
, prot
);
935 oi
->oi_foffset
+= outfr
.size
;
938 cstats
->copied
+= outfr
.size
;
940 cstats
->uncompressed
+= outfr
.size
;
944 printvr(invr
, "%s remains zfod\n", str_hsize(hstr
, V_SIZE(invr
)));
946 const struct file_range outfr
= {
947 .off
= oi
->oi_foffset
,
950 make_native_segment_command(lc
, invr
, &outfr
, maxprot
, prot
);
956 convert_coredata(const void *inbase
, const native_mach_header_t
*__unused inmh
, const struct proto_coredata_command
*cc
, struct load_command
*lc
, struct output_info
*oi
)
958 const struct vm_range vr
= {
962 const struct file_range fr
= {
964 .size
= cc
->filesize
,
966 return convert_region(inbase
, &vr
, &fr
, cc
->prot
, cc
->maxprot
, COMP_ALG_TYPE(cc
->flags
), lc
, oi
);
970 convert_segment(const void *inbase
, const native_mach_header_t
*__unused inmh
, const native_segment_command_t
*sc
, struct load_command
*lc
, struct output_info
*oi
)
972 const struct vm_range vr
= {
976 const struct file_range fr
= {
978 .size
= sc
->filesize
,
980 return convert_region(inbase
, &vr
, &fr
, sc
->initprot
, sc
->maxprot
, 0, lc
, oi
);
983 /* pass-through - content is all in the header */
986 convert_thread(struct thread_command
*dst
, const struct thread_command
*src
)
988 assert(LC_THREAD
== src
->cmd
);
989 memcpy(dst
, src
, src
->cmdsize
);
990 cstats
->copied
+= src
->cmdsize
;
995 gcore_conv(int infd
, const char *searchpath
, bool zf
, int fd
)
998 const void *corebase
= mmapfile(infd
, 0, &filesize
);
1001 * Check to see if the input file is "sane" as far as we're concerned.
1002 * XXX Note that this -won't- necessarily work for other ISAs than
1005 const native_mach_header_t
*inmh
= corebase
;
1006 validate_core_header(inmh
, filesize
);
1009 * The sparse file may have created many more segments, but there's no
1010 * attempt to change their numbers here. Just count all the segment
1011 * types needed to figure out the size of the output file header.
1013 * (Size assertions to be deleted once data structures stable!)
1015 __block
size_t headersize
= sizeof (native_mach_header_t
);
1016 __block
unsigned pageshift_target
= pageshift_host
;
1018 walkcore(inmh
, ^(const struct proto_coreinfo_command
*ci
) {
1019 assert(sizeof (*ci
) == ci
->cmdsize
);
1021 printf("Converting version %d core file to pre-versioned format\n", ci
->version
);
1022 if (0 < ci
->pageshift
&& ci
->pageshift
< 31)
1023 pageshift_target
= ci
->pageshift
;
1024 else if (CPU_TYPE_ARM64
== inmh
->cputype
)
1025 pageshift_target
= 14; // compatibility hack, should go soon
1026 }, ^(const struct proto_fileref_command
*__unused fc
) {
1027 const char *nm
= fc
->filename
.offset
+ (const char *)fc
;
1028 size_t nmlen
= strlen(nm
) + 1;
1029 size_t cmdsize
= sizeof (*fc
) + roundup(nmlen
, sizeof (long));
1030 assert(cmdsize
== fc
->cmdsize
);
1032 headersize
+= sizeof (native_segment_command_t
);
1033 }, ^(const struct proto_coredata_command
*__unused cc
) {
1034 assert(sizeof (*cc
) == cc
->cmdsize
);
1035 headersize
+= sizeof (native_segment_command_t
);
1036 }, ^(const native_segment_command_t
*sc
) {
1037 headersize
+= sc
->cmdsize
;
1038 }, ^(const struct thread_command
*tc
) {
1039 headersize
+= tc
->cmdsize
;
1042 void *header
= calloc(1, headersize
);
1044 errx(EX_OSERR
, "out of memory for header");
1046 native_mach_header_t
*mh
= memcpy(header
, inmh
, sizeof (*mh
));
1050 assert(0 < pageshift_target
&& pageshift_target
< 31);
1051 const vm_offset_t pagesize_target
= ((vm_offset_t
)1 << pageshift_target
);
1052 const vm_offset_t pagemask_target
= pagesize_target
- 1;
1054 const struct load_command
*inlc
= (const void *)(inmh
+ 1);
1055 struct load_command
*lc
= (void *)(mh
+ 1);
1058 struct output_info oi
= {
1060 .oi_foffset
= ((vm_offset_t
)headersize
+ pagemask_target
) & ~pagemask_target
,
1061 .oi_nocache
= false,
1064 for (unsigned i
= 0; i
< inmh
->ncmds
; i
++) {
1065 switch (inlc
->cmd
) {
1066 case proto_LC_FILEREF
:
1067 ecode
= convert_fileref(searchpath
, zf
, inmh
, (const void *)inlc
, lc
, &oi
);
1069 case proto_LC_COREDATA
:
1070 ecode
= convert_coredata(corebase
, inmh
, (const void *)inlc
, lc
, &oi
);
1072 case NATIVE_LC_SEGMENT
:
1073 ecode
= convert_segment(corebase
, inmh
, (const void *)inlc
, lc
, &oi
);
1076 ecode
= convert_thread((void *)lc
, (const void *)inlc
);
1079 if (OPTIONS_DEBUG(opt
, 1))
1080 printf("discarding load command %d\n", inlc
->cmd
);
1085 if (NATIVE_LC_SEGMENT
== lc
->cmd
|| LC_THREAD
== lc
->cmd
) {
1086 mach_header_inc_ncmds(mh
, 1);
1087 mach_header_inc_sizeofcmds(mh
, lc
->cmdsize
);
1088 lc
= (void *)next_lc(lc
);
1090 if (NULL
== (inlc
= next_lc(inlc
)))
1095 * Even if we've encountered an error, try and write out the header
1097 if (0 != bounded_pwrite(fd
, header
, headersize
, 0, &oi
.oi_nocache
, NULL
))
1099 if (0 == ecode
&& sizeof (*mh
) + mh
->sizeofcmds
!= headersize
)
1100 ecode
= EX_SOFTWARE
;
1101 validate_core_header(mh
, oi
.oi_foffset
);
1103 warnx("failed to write new core file correctly");
1104 else if (opt
->verbose
) {
1106 printf("Conversion complete: %s copied", str_hsize(hstr
, cstats
->copied
));
1107 const int64_t delta
= cstats
->uncompressed
- cstats
->compressed
;
1109 printf(", %s uncompressed", str_hsize(hstr
, delta
));
1110 const int64_t added
= cstats
->added
+ ((int)mh
->sizeofcmds
- (int)inmh
->sizeofcmds
);
1112 printf(", %s added", str_hsize(hstr
, added
));
1116 munmap((void *)corebase
, (size_t)filesize
);