2 * Copyright (c) 2016 Apple Inc. All rights reserved.
10 #include "dyld_shared_cache.h"
13 #include <sys/types.h>
16 #include <sys/queue.h>
17 #include <sys/param.h>
18 #include <mach-o/fat.h>
19 #include <uuid/uuid.h>
32 /* Declare xpc_create_from_plist manually because xpc/private.h is closed source */
33 xpc_object_t
xpc_create_from_plist(void *data
, size_t size
);
34 #include <sys/event.h>
37 #if defined(CONFIG_GCORE_MAP) || defined(CONFIG_GCORE_CONV) || defined(CONFIG_GCORE_FREF)
40 mmapfile(int fd
, off_t off
, off_t
*filesize
)
43 if (-1 == fstat(fd
, &st
))
44 errc(EX_OSERR
, errno
, "can't stat input file");
46 const size_t size
= (size_t)(st
.st_size
- off
);
47 if ((off_t
)size
!= (st
.st_size
- off
))
48 errc(EX_OSERR
, EOVERFLOW
, "input file too large?");
50 const void *addr
= mmap(0, size
, PROT_READ
, MAP_PRIVATE
, fd
, off
);
51 if ((void *)-1 == addr
)
52 errc(EX_OSERR
, errno
, "can't mmap input file");
53 *filesize
= st
.st_size
;
59 const native_mach_header_t
*mh
,
60 void (^coreinfo
)(const struct proto_coreinfo_command
*),
61 void (^frefdata
)(const struct proto_fileref_command
*),
62 void (^coredata
)(const struct proto_coredata_command
*),
63 void (^segdata
)(const native_segment_command_t
*),
64 void (^thrdata
)(const struct thread_command
*))
66 const struct load_command
*lc
= (const void *)(mh
+ 1);
67 for (unsigned i
= 0; i
< mh
->ncmds
; i
++) {
69 case proto_LC_COREINFO
:
71 coreinfo((const void *)lc
);
73 case proto_LC_FILEREF
:
75 frefdata((const void *)lc
);
77 case proto_LC_COREDATA
:
79 coredata((const void *)lc
);
81 case NATIVE_LC_SEGMENT
:
83 segdata((const void *)lc
);
87 thrdata((const void *)lc
);
92 if (NULL
== (lc
= next_lc(lc
)))
99 #ifdef CONFIG_GCORE_FREF
105 const void *corebase
= mmapfile(fd
, 0, &filesize
);
109 STAILQ_ENTRY(flist
) f_linkage
;
111 unsigned long f_nmhash
;
113 STAILQ_HEAD(flisthead
, flist
) __flh
, *flh
= &__flh
;
116 walkcore(corebase
, NULL
, ^(const struct proto_fileref_command
*fc
) {
117 const char *nm
= fc
->filename
.offset
+ (const char *)fc
;
118 const unsigned long nmhash
= simple_namehash(nm
);
120 STAILQ_FOREACH(f
, flh
, f_linkage
) {
121 if (nmhash
== f
->f_nmhash
&& 0 == strcmp(f
->f_nm
, nm
))
122 return; /* skip duplicates */
124 struct flist
*nf
= calloc(1, sizeof (*nf
));
126 nf
->f_nmhash
= nmhash
;
127 STAILQ_INSERT_TAIL(flh
, nf
, f_linkage
);
128 }, NULL
, NULL
, NULL
);
130 struct flist
*f
, *tf
;
131 STAILQ_FOREACH_SAFE(f
, flh
, f_linkage
, tf
) {
132 printf("%s\n", f
->f_nm
);
137 munmap((void *)corebase
, (size_t)filesize
);
141 #endif /* CONFIG_GCORE_FREF */
143 #ifdef CONFIG_GCORE_MAP
146 * A pale imitation of vmmap, but for core files
152 const void *corebase
= mmapfile(fd
, 0, &filesize
);
154 __block
int coreversion
= 0;
156 walkcore(corebase
, ^(const struct proto_coreinfo_command
*ci
) {
157 coreversion
= ci
->version
;
158 }, NULL
, NULL
, NULL
, NULL
);
160 if (0 == coreversion
) {
161 const char titlfmt
[] = "%16s-%-16s [%7s] %3s/%3s\n";
162 const char *segcfmt
= "%016llx-%016llx [%7s] %3s/%3s\n";
164 printf(titlfmt
, "start ", " end", "vsize", "prt", "max");
165 walkcore(corebase
, NULL
, NULL
, NULL
, ^(const native_segment_command_t
*sc
) {
167 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
));
170 const char titlfmt
[] = "%-23s %16s-%-16s [%7s] %3s/%3s %6s %4s %-14s\n";
171 const char *freffmt
= "%-23s %016llx-%016llx [%7s] %3s/%3s %6s %4s %-14s @%lld\n";
172 const char *datafmt
= "%-23s %016llx-%016llx [%7s] %3s/%3s %6s %4s %-14s\n";
174 printf(titlfmt
, "region type", "start ", " end", "vsize", "prt", "max", "shrmod", "purge", "region detail");
175 walkcore(corebase
, NULL
, ^(const struct proto_fileref_command
*fc
) {
176 const char *nm
= fc
->filename
.offset
+ (const char *)fc
;
179 printf(freffmt
, str_tag(tstr
, fc
->tag
, fc
->share_mode
, fc
->prot
, fc
->extp
),
180 fc
->vmaddr
, fc
->vmaddr
+ fc
->vmsize
,
181 str_hsize(vstr
, fc
->vmsize
), str_prot(fc
->prot
),
182 str_prot(fc
->maxprot
), str_shared(fc
->share_mode
),
183 str_purgable(fc
->purgable
, fc
->share_mode
), nm
, fc
->fileoff
);
184 }, ^(const struct proto_coredata_command
*cc
) {
187 printf(datafmt
, str_tag(tstr
, cc
->tag
, cc
->share_mode
, cc
->prot
, cc
->extp
),
188 cc
->vmaddr
, cc
->vmaddr
+ cc
->vmsize
,
189 str_hsize(vstr
, cc
->vmsize
), str_prot(cc
->prot
),
190 str_prot(cc
->maxprot
), str_shared(cc
->share_mode
),
191 str_purgable(cc
->purgable
, cc
->share_mode
),
192 cc
->vmsize
&& 0 == cc
->filesize
? "(zfod)" : "");
193 }, ^(const native_segment_command_t
*sc
) {
195 printf(datafmt
, "", (mach_vm_offset_t
)sc
->vmaddr
,
196 (mach_vm_offset_t
)sc
->vmaddr
+ sc
->vmsize
,
197 str_hsize(vstr
, sc
->vmsize
), str_prot(sc
->initprot
),
198 str_prot(sc
->maxprot
), "", "",
199 sc
->vmsize
&& 0 == sc
->filesize
? "(zfod)" : "");
203 munmap((void *)corebase
, (size_t)filesize
);
209 #ifdef CONFIG_GCORE_CONV
212 * Convert an input core file into an "old" format core file
213 * (a) convert all fileref segments into regular segments
214 * (b) uncompress anything we find compressed.
215 * This should be equivalent to a copy for an "old" format core file.
219 machocmp(const native_mach_header_t
*tmh
, const native_mach_header_t
*mh
, const struct proto_fileref_command
*fr
)
221 if (tmh
->magic
== mh
->magic
) {
222 const struct load_command
*lc
= (const void *)(tmh
+ 1);
223 for (unsigned i
= 0; i
< tmh
->ncmds
; i
++) {
224 if (LC_UUID
== lc
->cmd
&& lc
->cmdsize
>= sizeof (struct uuid_command
)) {
225 const struct uuid_command
*uc
= (const void *)lc
;
226 return uuid_compare(uc
->uuid
, fr
->id
);
228 if (NULL
== (lc
= next_lc(lc
)))
236 fat_machocmp(const struct fat_header
*fh
, const native_mach_header_t
*mh
, const struct proto_fileref_command
*fr
, off_t
*reloff
)
238 const uint32_t (^get32
)(uint32_t);
240 if (FAT_MAGIC
== fh
->magic
) {
241 get32
= ^(uint32_t val
) {
245 get32
= ^(uint32_t val
) {
247 for (unsigned i
= 0; i
< sizeof (uint32_t); i
++)
248 ((uint8_t *)&result
)[i
] = ((uint8_t *)&val
)[3-i
];
253 assert(FAT_MAGIC
== get32(fh
->magic
));
254 assert(kFREF_ID_UUID
== FREF_ID_TYPE(fr
->flags
) && !uuid_is_null(fr
->id
));
256 const struct fat_arch
*fa
= (const struct fat_arch
*)(fh
+ 1);
257 uint32_t narch
= get32(fh
->nfat_arch
);
258 for (unsigned n
= 0; n
< narch
; n
++, fa
++) {
259 const native_mach_header_t
*tmh
= (const void *)(((const char *)fh
) + get32(fa
->offset
));
260 if (tmh
->magic
== mh
->magic
&& 0 == machocmp(tmh
, mh
, fr
)) {
261 *reloff
= get32(fa
->offset
);
274 static struct convstats
{
278 int64_t uncompressed
;
279 } cstat
, *cstats
= &cstat
;
282 * A fileref segment references a read-only file that contains pages from
283 * the image. The file may be a Mach binary or dylib identified with a uuid.
286 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
)
288 assert(invr
->addr
== infr
->vmaddr
&& invr
->size
== infr
->vmsize
);
291 const int rfd
= open(filename
, O_RDONLY
);
292 if (-1 == rfd
|| -1 == fstat(rfd
, &st
)) {
293 warnc(errno
, "%s: open", filename
);
296 const size_t rlen
= (size_t)st
.st_size
;
297 void *raddr
= mmap(NULL
, rlen
, PROT_READ
, MAP_PRIVATE
, rfd
, 0);
298 if ((void *)-1 == raddr
) {
299 warnc(errno
, "%s: mmap", filename
);
305 off_t fatoff
= 0; /* for FAT objects */
306 int ecode
= EX_DATAERR
;
308 switch (FREF_ID_TYPE(infr
->flags
)) {
309 case kFREF_ID_UUID
: {
310 /* file should be a mach binary: check that uuid matches */
311 const uint32_t magic
= *(uint32_t *)raddr
;
315 if (0 == fat_machocmp(raddr
, inmh
, infr
, &fatoff
))
318 case NATIVE_MH_MAGIC
:
319 if (0 == machocmp(raddr
, inmh
, infr
))
324 * Maybe this is the shared cache?
327 if (get_uuid_from_shared_cache_mapping(raddr
, rlen
, uu
) && uuid_compare(uu
, infr
->id
) == 0)
334 case kFREF_ID_MTIMESPEC_LE
:
335 /* file should have the same mtime */
336 if (0 == memcmp(&st
.st_mtimespec
, infr
->id
, sizeof (infr
->id
)))
340 /* file has no uniquifier, copy it anyway */
346 warnx("%s doesn't match corefile content", filename
);
350 const off_t fileoff
= fatoff
+ infr
->fileoff
;
351 const void *start
= (const char *)raddr
+ fileoff
;
352 const size_t len
= (size_t)infr
->filesize
;
356 if (fileoff
+ (off_t
)infr
->filesize
> (off_t
)rlen
) {
358 * the file content needed (as described on machine with
359 * larger pagesize) extends beyond the end of the mapped
360 * file using our smaller pagesize. Zero pad it.
362 const size_t pagesize_host
= 1ul << pageshift_host
;
363 void *endaddr
= (caddr_t
)raddr
+ roundup(rlen
, pagesize_host
);
364 zlen
= (size_t)(fileoff
+ infr
->filesize
- rlen
);
365 zaddr
= mmap(endaddr
, zlen
, PROT_READ
, MAP_FIXED
| MAP_PRIVATE
| MAP_ANON
, -1, 0);
366 if ((void *)-1 == zaddr
) {
368 warnc(errno
, "cannot zero-pad %s mapping for %s", str_hsize(hstr
, zlen
),filename
);
374 if (-1 == madvise((void *)start
, len
, MADV_SEQUENTIAL
))
375 warnc(errno
, "%s: madvise", filename
);
377 const int error
= bounded_pwrite(oi
->oi_fd
, start
, len
, oi
->oi_foffset
, &oi
->oi_nocache
, NULL
);
380 if (-1 == munmap(zaddr
, zlen
))
381 warnc(errno
, "%s: munmap zero pad", filename
);
383 if (-1 == munmap(raddr
, rlen
))
384 warnc(errno
, "%s: munmap", filename
);
386 warnc(error
, "while copying %s to core file", filename
);
390 const struct file_range fr
= {
391 .off
= oi
->oi_foffset
,
392 .size
= infr
->filesize
,
394 make_native_segment_command(lc
, invr
, &fr
, infr
->maxprot
, infr
->prot
);
395 oi
->oi_foffset
+= fr
.size
;
396 cstats
->added
+= infr
->filesize
;
401 * expanduser tries to expand the leading '~' (if there is any) in the given
402 * path and returns a copy of the expanded path; it returns NULL on failures.
403 * The caller is responsible for freeing the returned string.
406 expanduser(const char *path
)
411 if (path
[0] != '~') {
413 * For consistency, still dup the string so that the caller always
414 * needs to free the string.
419 char *expanded
= NULL
;
421 if (OPTIONS_DEBUG(opt
, 1)) {
422 printf("Expanding %s\n", path
);
424 int rc
= glob(path
, GLOB_TILDE
, NULL
, &globbuf
);
426 if (OPTIONS_DEBUG(opt
, 3)) {
427 printf("expanduser - gl_pathc: %zu\n", globbuf
.gl_pathc
);
428 for (size_t i
= 0; i
< globbuf
.gl_pathc
; i
++) {
429 printf("expanduser - gl_pathv[%zu]: %s\n", i
, globbuf
.gl_pathv
[i
]);
432 if (globbuf
.gl_pathc
== 1) {
433 expanded
= strdup(globbuf
.gl_pathv
[0]);
434 if (OPTIONS_DEBUG(opt
, 1)) {
435 printf("Expanded path: %s\n", expanded
);
444 #define RESPONSE_BUFF_SIZE (2048)
447 * read_response dynamically allocates buffer for reading bytes from the given
448 * fd. Upon success, this function sets response to point to the buffer and
449 * returns bytes being read; otherwise, it returns -1. The caller is
450 * responsible for freeing the response buffer.
453 read_response(int fd
, char **response
)
455 if (response
== NULL
|| *response
) {
456 warnx("Invalid response buffer pointer");
460 ssize_t bytes_read
= 0;
461 size_t buff_size
= RESPONSE_BUFF_SIZE
;
463 if (OPTIONS_DEBUG(opt
, 3)) {
464 printf("Allocating response buffer (%zu)\n", buff_size
);
466 char *buff
= malloc(buff_size
);
468 warn("Failed to allocate response buffer (%zu)", buff_size
);
476 bytes_read
= read(fd
, buff
+ total
, buff_size
- total
);
477 if (bytes_read
== -1) {
478 if (errno
== EINTR
) {
485 total
+= (size_t)bytes_read
;
486 if (total
== buff_size
) {
487 size_t new_buff_size
= buff_size
* 2;
488 if (OPTIONS_DEBUG(opt
, 3)) {
489 printf("Reallocating response buffer (%zu)\n", new_buff_size
);
491 char *new_buff
= realloc(buff
, new_buff_size
);
492 if (new_buff
== NULL
) {
493 warn("Failed to reallocate response buffer (%zu)", new_buff_size
);
497 buff_size
= new_buff_size
;
500 } while (bytes_read
!= 0);
509 assert(total
< buff_size
);
513 return (ssize_t
)total
;
516 #define WAITPID_WTO_SIGALRM (100) /* alternative for SIGALRM for kevent timeout */
517 #define WAITPID_WTO_SIGERR (101) /* sig for error when waiting for pid */
520 * waitpid_with_timeout returns true if the process exits successfully within
521 * timeout; otherwise, it returns false along with setting exitstatus and
522 * signal_no if the pointers are given.
525 waitpid_with_timeout(pid_t pid
, int *exitstatus
, int *signal_no
, int timeout
)
532 struct kevent64_s event
= {
533 .ident
= (uint64_t)pid
,
534 .filter
= EVFILT_PROC
,
535 .flags
= EV_ADD
| EV_ONESHOT
,
538 struct timespec tmout
= {
541 int ret
= kevent64(kq
, &event
, 1, &event
, 1, 0, &tmout
);
542 int kevent64_errno
= errno
;
545 if (ret
== 0) { /* timeout */
550 *signal_no
= WAITPID_WTO_SIGALRM
;
556 warnx("kevent64(): errno=%d (%s)\n", kevent64_errno
, strerror(kevent64_errno
));
560 if (event
.flags
== EV_ERROR
&& event
.data
!= ESRCH
) {
561 warnx("event.data (%lld) is not ESRCH when event.flags is EV_ERROR\n", event
.data
);
565 if (event
.ident
!= (uint64_t)pid
) {
566 warnx("event.ident is %lld (should be pid %d)\n", event
.ident
, pid
);
570 if (event
.filter
!= EVFILT_PROC
) {
571 warnx("event.filter (%d) is not EVFILT_PROC\n", event
.filter
);
576 while (waitpid(pid
, &status
, 0) < 0) {
577 if (errno
== EINTR
) {
580 warnx("waitpid(): errno=%d (%s)\n", errno
, strerror(errno
));
583 if (WIFEXITED(status
)) {
585 *exitstatus
= WEXITSTATUS(status
);
590 return WEXITSTATUS(status
) == 0;
592 if (WIFSIGNALED(status
)) {
597 *signal_no
= WTERMSIG(status
);
603 if (exitstatus
) *exitstatus
= 0;
604 if (signal_no
) *signal_no
= WAITPID_WTO_SIGERR
;
608 #define DSYMFORUUID_PATH "/usr/local/bin/dsymForUUID"
611 * exec_dsymForUUID spawns dsymForUUID to query dsym UUID info and responds the
612 * result plist. Upon success, this function sets response point to the buffer
613 * and returns bytes being read; otherwise, it returns -1. The caller is
614 * responsible for freeing the response buffer.
617 exec_dsymForUUID(uuid_string_t id
, char **response
)
619 int pipe_fds
[2] = {-1, -1};
620 bool file_actions_inited
= false;
621 ssize_t bytes_read
= -1;
629 posix_spawn_file_actions_t file_actions
;
630 rc
= posix_spawn_file_actions_init(&file_actions
);
634 file_actions_inited
= true;
636 rc
= posix_spawn_file_actions_addclose(&file_actions
, pipe_fds
[0]);
641 rc
= posix_spawn_file_actions_adddup2(&file_actions
, pipe_fds
[1], STDOUT_FILENO
);
646 rc
= posix_spawn_file_actions_addclose(&file_actions
, pipe_fds
[1]);
651 char *command
[] = {DSYMFORUUID_PATH
, id
, NULL
};
653 rc
= posix_spawn(&child
, command
[0], &file_actions
, NULL
, command
, NULL
);
661 bytes_read
= read_response(pipe_fds
[0], response
);
663 waitpid_with_timeout(child
, NULL
, NULL
, 3);
666 if (pipe_fds
[1] != -1) {
669 if (pipe_fds
[0] != -1) {
672 if (file_actions_inited
) {
673 posix_spawn_file_actions_destroy(&file_actions
);
680 * get_symbol_rich_executable_path_via_dsymForUUID spawns dsymForUUID to query
681 * dsym uuid info for the given uuid and returns the string of
682 * DBGSymbolRichExecutable; otherwise, it returns NULL on failures. The caller
683 * is responsible for freeing the returned string.
686 get_symbol_rich_executable_path_via_dsymForUUID(const uuid_t uuid
)
690 uuid_string_t uuid_str
;
691 xpc_object_t plist
= NULL
;
692 xpc_object_t uuid_info
= NULL
;
693 xpc_object_t exec_path
= NULL
;
694 char *expanded_exec_path
= NULL
;
696 uuid_unparse_upper(uuid
, uuid_str
);
698 size
= exec_dsymForUUID(uuid_str
, &response
);
703 if (OPTIONS_DEBUG(opt
, 3)) {
704 printf("dsymForUUID response:\n%s\n", response
);
707 plist
= xpc_create_from_plist(response
, (size_t)size
);
711 if (xpc_get_type(plist
) != XPC_TYPE_DICTIONARY
) {
715 uuid_info
= xpc_dictionary_get_value(plist
, uuid_str
);
716 if (uuid_info
== NULL
) {
719 if (xpc_get_type(uuid_info
) != XPC_TYPE_DICTIONARY
) {
723 exec_path
= xpc_dictionary_get_value(uuid_info
, "DBGSymbolRichExecutable");
724 if (exec_path
== NULL
) {
727 if (xpc_get_type(exec_path
) != XPC_TYPE_STRING
) {
731 expanded_exec_path
= expanduser(xpc_string_get_string_ptr(exec_path
));
741 return expanded_exec_path
;
745 * bind the file reference into the output core file.
746 * filename optionally prefixed with names from a ':'-separated PATH variable
749 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
)
751 const char *nm
= infr
->filename
.offset
+ (const char *)infr
;
753 const struct vm_range invr
= {
754 .addr
= infr
->vmaddr
,
755 .size
= infr
->vmsize
,
760 printvr(&invr
, "adding %s from '%s'",
761 str_hsize(hstr
, (off_t
)infr
->filesize
), nm
);
762 switch (FREF_ID_TYPE(infr
->flags
)) {
766 uuid_unparse_lower(infr
->id
, uustr
);
767 printf(" (%s)", uustr
);
769 case kFREF_ID_MTIMESPEC_LE
: {
772 char tbuf
[4 + 2 + 2 + 2 + 2 + 1 + 2 + 1]; /* touch -t */
773 memcpy(&mts
, &infr
->id
, sizeof (mts
));
774 localtime_r(&mts
.tv_sec
, &tm
);
775 strftime(tbuf
, sizeof (tbuf
), "%Y%m%d%H%M.%S", &tm
);
776 printf(" (%s)", tbuf
);
783 if (opt
->dsymforuuid
&& (FREF_ID_TYPE(infr
->flags
) == kFREF_ID_UUID
)) {
784 /* Try to use dsymForUUID to get the symbol-rich executable */
785 char *symrich_filepath
= get_symbol_rich_executable_path_via_dsymForUUID(infr
->id
);
786 if (symrich_filepath
) {
788 printf("\tTrying %s from dsymForUUID\n", symrich_filepath
);
790 ecode
= convert_fileref_with_file(symrich_filepath
, inmh
, infr
, &invr
, lc
, oi
);
791 free(symrich_filepath
);
795 warnx("Failed to convert fileref with dsymForUUID. Fall back to local paths");
799 const size_t pathsize
= path
? strlen(path
) : 0;
802 ecode
= convert_fileref_with_file(nm
, inmh
, infr
, &invr
, lc
, oi
);
804 /* search the : separated path (-L) for possible matches */
805 char *pathcopy
= strdup(path
);
806 char *searchpath
= pathcopy
;
809 while ((token
= strsep(&searchpath
, ":")) != NULL
) {
810 const size_t buflen
= strlen(token
) + 1 + strlen(nm
) + 1;
811 char *buf
= malloc(buflen
);
812 snprintf(buf
, buflen
, "%s%s%s", token
, '/' == nm
[0] ? "" : "/", nm
);
814 printf("\tTrying '%s'", buf
);
815 if (0 == access(buf
, R_OK
)) {
818 ecode
= convert_fileref_with_file(buf
, inmh
, infr
, &invr
, lc
, oi
);
823 } else if (opt
->verbose
)
825 0 == access(buf
, F_OK
) ? "Unreadable" : "Not present");
831 if (0 != ecode
&& zf
) {
833 * Failed to find the file reference. If this was a fileref that uses
834 * a file metadata tagging method (e.g. mtime), allow the user to subsitute a
835 * zfod region: assumes that it's better to have something to debug
836 * vs. nothing. UUID-tagged filerefs are Mach-O tags, and are
837 * assumed to be never substitutable.
839 switch (FREF_ID_TYPE(infr
->flags
)) {
841 case kFREF_ID_MTIMESPEC_LE
: { // weak tagging, allow zfod substitution
842 const struct file_range outfr
= {
843 .off
= oi
->oi_foffset
,
847 printf("\tWARNING: no file matched. Missing content is now zfod\n");
849 printvr(&invr
, "WARNING: missing content (%s) now zfod\n", nm
);
850 make_native_segment_command(lc
, &invr
, &outfr
, infr
->maxprot
, infr
->prot
);
862 segment_uncompflags(unsigned algnum
, compression_algorithm
*ca
)
866 *ca
= COMPRESSION_LZ4
;
869 *ca
= COMPRESSION_ZLIB
;
872 *ca
= COMPRESSION_LZMA
;
875 *ca
= COMPRESSION_LZFSE
;
878 warnx("unknown compression flavor %d", algnum
);
885 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
)
890 void *input
= (const caddr_t
)inbase
+ F_OFF(infr
);
897 printvr(invr
, "copying %s\n", str_hsize(hstr
, F_SIZE(infr
)));
900 compression_algorithm ca
;
902 if (0 != (ecode
= segment_uncompflags(flavor
, &ca
)))
905 hsize_str_t hstr1
, hstr2
;
906 printvr(invr
, "uncompressing %s to %s\n",
907 str_hsize(hstr1
, F_SIZE(infr
)), str_hsize(hstr2
, V_SIZE(invr
)));
909 const size_t buflen
= V_SIZEOF(invr
);
910 buf
= malloc(buflen
);
911 const size_t dstsize
= compression_decode_buffer(buf
, buflen
, input
, (size_t)F_SIZE(infr
), NULL
, ca
);
912 if (buflen
!= dstsize
) {
913 warnx("failed to uncompress segment");
917 cstats
->compressed
+= F_SIZE(infr
);
919 const int error
= bounded_pwrite(oi
->oi_fd
, buf
, V_SIZEOF(invr
), oi
->oi_foffset
, &oi
->oi_nocache
, NULL
);
921 warnc(error
, "failed to write data to core file");
929 const struct file_range outfr
= {
930 .off
= oi
->oi_foffset
,
931 .size
= V_SIZE(invr
),
933 make_native_segment_command(lc
, invr
, &outfr
, maxprot
, prot
);
934 oi
->oi_foffset
+= outfr
.size
;
937 cstats
->copied
+= outfr
.size
;
939 cstats
->uncompressed
+= outfr
.size
;
943 printvr(invr
, "%s remains zfod\n", str_hsize(hstr
, V_SIZE(invr
)));
945 const struct file_range outfr
= {
946 .off
= oi
->oi_foffset
,
949 make_native_segment_command(lc
, invr
, &outfr
, maxprot
, prot
);
955 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
)
957 const struct vm_range vr
= {
961 const struct file_range fr
= {
963 .size
= cc
->filesize
,
965 return convert_region(inbase
, &vr
, &fr
, cc
->prot
, cc
->maxprot
, COMP_ALG_TYPE(cc
->flags
), lc
, oi
);
969 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
)
971 const struct vm_range vr
= {
975 const struct file_range fr
= {
977 .size
= sc
->filesize
,
979 return convert_region(inbase
, &vr
, &fr
, sc
->initprot
, sc
->maxprot
, 0, lc
, oi
);
982 /* pass-through - content is all in the header */
985 convert_thread(struct thread_command
*dst
, const struct thread_command
*src
)
987 assert(LC_THREAD
== src
->cmd
);
988 memcpy(dst
, src
, src
->cmdsize
);
989 cstats
->copied
+= src
->cmdsize
;
994 gcore_conv(int infd
, const char *searchpath
, bool zf
, int fd
)
997 const void *corebase
= mmapfile(infd
, 0, &filesize
);
1000 * Check to see if the input file is "sane" as far as we're concerned.
1001 * XXX Note that this -won't- necessarily work for other ISAs than
1004 const native_mach_header_t
*inmh
= corebase
;
1005 validate_core_header(inmh
, filesize
);
1008 * The sparse file may have created many more segments, but there's no
1009 * attempt to change their numbers here. Just count all the segment
1010 * types needed to figure out the size of the output file header.
1012 * (Size assertions to be deleted once data structures stable!)
1014 __block
size_t headersize
= sizeof (native_mach_header_t
);
1015 __block
unsigned pageshift_target
= pageshift_host
;
1017 walkcore(inmh
, ^(const struct proto_coreinfo_command
*ci
) {
1018 assert(sizeof (*ci
) == ci
->cmdsize
);
1020 printf("Converting version %d core file to pre-versioned format\n", ci
->version
);
1021 if (0 < ci
->pageshift
&& ci
->pageshift
< 31)
1022 pageshift_target
= ci
->pageshift
;
1023 else if (CPU_TYPE_ARM64
== inmh
->cputype
)
1024 pageshift_target
= 14; // compatibility hack, should go soon
1025 }, ^(const struct proto_fileref_command
*__unused fc
) {
1026 const char *nm
= fc
->filename
.offset
+ (const char *)fc
;
1027 size_t nmlen
= strlen(nm
) + 1;
1028 size_t cmdsize
= sizeof (*fc
) + roundup(nmlen
, sizeof (long));
1029 assert(cmdsize
== fc
->cmdsize
);
1031 headersize
+= sizeof (native_segment_command_t
);
1032 }, ^(const struct proto_coredata_command
*__unused cc
) {
1033 assert(sizeof (*cc
) == cc
->cmdsize
);
1034 headersize
+= sizeof (native_segment_command_t
);
1035 }, ^(const native_segment_command_t
*sc
) {
1036 headersize
+= sc
->cmdsize
;
1037 }, ^(const struct thread_command
*tc
) {
1038 headersize
+= tc
->cmdsize
;
1041 void *header
= calloc(1, headersize
);
1043 errx(EX_OSERR
, "out of memory for header");
1045 native_mach_header_t
*mh
= memcpy(header
, inmh
, sizeof (*mh
));
1049 assert(0 < pageshift_target
&& pageshift_target
< 31);
1050 const vm_offset_t pagesize_target
= ((vm_offset_t
)1 << pageshift_target
);
1051 const vm_offset_t pagemask_target
= pagesize_target
- 1;
1053 const struct load_command
*inlc
= (const void *)(inmh
+ 1);
1054 struct load_command
*lc
= (void *)(mh
+ 1);
1057 struct output_info oi
= {
1059 .oi_foffset
= ((vm_offset_t
)headersize
+ pagemask_target
) & ~pagemask_target
,
1060 .oi_nocache
= false,
1063 for (unsigned i
= 0; i
< inmh
->ncmds
; i
++) {
1064 switch (inlc
->cmd
) {
1065 case proto_LC_FILEREF
:
1066 ecode
= convert_fileref(searchpath
, zf
, inmh
, (const void *)inlc
, lc
, &oi
);
1068 case proto_LC_COREDATA
:
1069 ecode
= convert_coredata(corebase
, inmh
, (const void *)inlc
, lc
, &oi
);
1071 case NATIVE_LC_SEGMENT
:
1072 ecode
= convert_segment(corebase
, inmh
, (const void *)inlc
, lc
, &oi
);
1075 ecode
= convert_thread((void *)lc
, (const void *)inlc
);
1078 if (OPTIONS_DEBUG(opt
, 1))
1079 printf("discarding load command %d\n", inlc
->cmd
);
1084 if (NATIVE_LC_SEGMENT
== lc
->cmd
|| LC_THREAD
== lc
->cmd
) {
1085 mach_header_inc_ncmds(mh
, 1);
1086 mach_header_inc_sizeofcmds(mh
, lc
->cmdsize
);
1087 lc
= (void *)next_lc(lc
);
1089 if (NULL
== (inlc
= next_lc(inlc
)))
1094 * Even if we've encountered an error, try and write out the header
1096 if (0 != bounded_pwrite(fd
, header
, headersize
, 0, &oi
.oi_nocache
, NULL
))
1098 if (0 == ecode
&& sizeof (*mh
) + mh
->sizeofcmds
!= headersize
)
1099 ecode
= EX_SOFTWARE
;
1100 validate_core_header(mh
, oi
.oi_foffset
);
1102 warnx("failed to write new core file correctly");
1103 else if (opt
->verbose
) {
1105 printf("Conversion complete: %s copied", str_hsize(hstr
, cstats
->copied
));
1106 const int64_t delta
= cstats
->uncompressed
- cstats
->compressed
;
1108 printf(", %s uncompressed", str_hsize(hstr
, delta
));
1109 const int64_t added
= cstats
->added
+ ((int)mh
->sizeofcmds
- (int)inmh
->sizeofcmds
);
1111 printf(", %s added", str_hsize(hstr
, added
));
1115 munmap((void *)corebase
, (size_t)filesize
);