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 #include <xpc/private.h>
33 #include <sys/event.h>
36 #if defined(CONFIG_GCORE_MAP) || defined(CONFIG_GCORE_CONV) || defined(CONFIG_GCORE_FREF)
39 mmapfile(int fd
, off_t off
, off_t
*filesize
)
42 if (-1 == fstat(fd
, &st
))
43 errc(EX_OSERR
, errno
, "can't stat input file");
45 const size_t size
= (size_t)(st
.st_size
- off
);
46 if ((off_t
)size
!= (st
.st_size
- off
))
47 errc(EX_OSERR
, EOVERFLOW
, "input file too large?");
49 const void *addr
= mmap(0, size
, PROT_READ
, MAP_PRIVATE
, fd
, off
);
50 if ((void *)-1 == addr
)
51 errc(EX_OSERR
, errno
, "can't mmap input file");
52 *filesize
= st
.st_size
;
58 const native_mach_header_t
*mh
,
59 void (^coreinfo
)(const struct proto_coreinfo_command
*),
60 void (^frefdata
)(const struct proto_fileref_command
*),
61 void (^coredata
)(const struct proto_coredata_command
*),
62 void (^segdata
)(const native_segment_command_t
*),
63 void (^thrdata
)(const struct thread_command
*))
65 const struct load_command
*lc
= (const void *)(mh
+ 1);
66 for (unsigned i
= 0; i
< mh
->ncmds
; i
++) {
68 case proto_LC_COREINFO
:
70 coreinfo((const void *)lc
);
72 case proto_LC_FILEREF
:
74 frefdata((const void *)lc
);
76 case proto_LC_COREDATA
:
78 coredata((const void *)lc
);
80 case NATIVE_LC_SEGMENT
:
82 segdata((const void *)lc
);
86 thrdata((const void *)lc
);
91 if (NULL
== (lc
= next_lc(lc
)))
98 #ifdef CONFIG_GCORE_FREF
104 const void *corebase
= mmapfile(fd
, 0, &filesize
);
108 STAILQ_ENTRY(flist
) f_linkage
;
110 unsigned long f_nmhash
;
112 STAILQ_HEAD(flisthead
, flist
) __flh
, *flh
= &__flh
;
115 walkcore(corebase
, NULL
, ^(const struct proto_fileref_command
*fc
) {
116 const char *nm
= fc
->filename
.offset
+ (const char *)fc
;
117 const unsigned long nmhash
= simple_namehash(nm
);
119 STAILQ_FOREACH(f
, flh
, f_linkage
) {
120 if (nmhash
== f
->f_nmhash
&& 0 == strcmp(f
->f_nm
, nm
))
121 return; /* skip duplicates */
123 struct flist
*nf
= calloc(1, sizeof (*nf
));
125 nf
->f_nmhash
= nmhash
;
126 STAILQ_INSERT_TAIL(flh
, nf
, f_linkage
);
127 }, NULL
, NULL
, NULL
);
129 struct flist
*f
, *tf
;
130 STAILQ_FOREACH_SAFE(f
, flh
, f_linkage
, tf
) {
131 printf("%s\n", f
->f_nm
);
136 munmap((void *)corebase
, (size_t)filesize
);
140 #endif /* CONFIG_GCORE_FREF */
142 #ifdef CONFIG_GCORE_MAP
145 * A pale imitation of vmmap, but for core files
151 const void *corebase
= mmapfile(fd
, 0, &filesize
);
153 __block
int coreversion
= 0;
155 walkcore(corebase
, ^(const struct proto_coreinfo_command
*ci
) {
156 coreversion
= ci
->version
;
157 }, NULL
, NULL
, NULL
, NULL
);
159 if (0 == coreversion
) {
160 const char titlfmt
[] = "%16s-%-16s [%7s] %3s/%3s\n";
161 const char *segcfmt
= "%016llx-%016llx [%7s] %3s/%3s\n";
163 printf(titlfmt
, "start ", " end", "vsize", "prt", "max");
164 walkcore(corebase
, NULL
, NULL
, NULL
, ^(const native_segment_command_t
*sc
) {
166 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
));
169 const char titlfmt
[] = "%-23s %16s-%-16s [%7s] %3s/%3s %6s %4s %-14s\n";
170 const char *freffmt
= "%-23s %016llx-%016llx [%7s] %3s/%3s %6s %4s %-14s @%lld\n";
171 const char *datafmt
= "%-23s %016llx-%016llx [%7s] %3s/%3s %6s %4s %-14s\n";
173 printf(titlfmt
, "region type", "start ", " end", "vsize", "prt", "max", "shrmod", "purge", "region detail");
174 walkcore(corebase
, NULL
, ^(const struct proto_fileref_command
*fc
) {
175 const char *nm
= fc
->filename
.offset
+ (const char *)fc
;
178 printf(freffmt
, str_tag(tstr
, fc
->tag
, fc
->share_mode
, fc
->prot
, fc
->extp
),
179 fc
->vmaddr
, fc
->vmaddr
+ fc
->vmsize
,
180 str_hsize(vstr
, fc
->vmsize
), str_prot(fc
->prot
),
181 str_prot(fc
->maxprot
), str_shared(fc
->share_mode
),
182 str_purgable(fc
->purgable
, fc
->share_mode
), nm
, fc
->fileoff
);
183 }, ^(const struct proto_coredata_command
*cc
) {
186 printf(datafmt
, str_tag(tstr
, cc
->tag
, cc
->share_mode
, cc
->prot
, cc
->extp
),
187 cc
->vmaddr
, cc
->vmaddr
+ cc
->vmsize
,
188 str_hsize(vstr
, cc
->vmsize
), str_prot(cc
->prot
),
189 str_prot(cc
->maxprot
), str_shared(cc
->share_mode
),
190 str_purgable(cc
->purgable
, cc
->share_mode
),
191 cc
->vmsize
&& 0 == cc
->filesize
? "(zfod)" : "");
192 }, ^(const native_segment_command_t
*sc
) {
194 printf(datafmt
, "", (mach_vm_offset_t
)sc
->vmaddr
,
195 (mach_vm_offset_t
)sc
->vmaddr
+ sc
->vmsize
,
196 str_hsize(vstr
, sc
->vmsize
), str_prot(sc
->initprot
),
197 str_prot(sc
->maxprot
), "", "",
198 sc
->vmsize
&& 0 == sc
->filesize
? "(zfod)" : "");
202 munmap((void *)corebase
, (size_t)filesize
);
208 #ifdef CONFIG_GCORE_CONV
211 * Convert an input core file into an "old" format core file
212 * (a) convert all fileref segments into regular segments
213 * (b) uncompress anything we find compressed.
214 * This should be equivalent to a copy for an "old" format core file.
218 machocmp(const native_mach_header_t
*tmh
, const native_mach_header_t
*mh
, const struct proto_fileref_command
*fr
)
220 if (tmh
->magic
== mh
->magic
) {
221 const struct load_command
*lc
= (const void *)(tmh
+ 1);
222 for (unsigned i
= 0; i
< tmh
->ncmds
; i
++) {
223 if (LC_UUID
== lc
->cmd
&& lc
->cmdsize
>= sizeof (struct uuid_command
)) {
224 const struct uuid_command
*uc
= (const void *)lc
;
225 return uuid_compare(uc
->uuid
, fr
->id
);
227 if (NULL
== (lc
= next_lc(lc
)))
235 fat_machocmp(const struct fat_header
*fh
, const native_mach_header_t
*mh
, const struct proto_fileref_command
*fr
, off_t
*reloff
)
237 const uint32_t (^get32
)(uint32_t);
239 if (FAT_MAGIC
== fh
->magic
) {
240 get32
= ^(uint32_t val
) {
244 get32
= ^(uint32_t val
) {
246 for (unsigned i
= 0; i
< sizeof (uint32_t); i
++)
247 ((uint8_t *)&result
)[i
] = ((uint8_t *)&val
)[3-i
];
252 assert(FAT_MAGIC
== get32(fh
->magic
));
253 assert(kFREF_ID_UUID
== FREF_ID_TYPE(fr
->flags
) && !uuid_is_null(fr
->id
));
255 const struct fat_arch
*fa
= (const struct fat_arch
*)(fh
+ 1);
256 uint32_t narch
= get32(fh
->nfat_arch
);
257 for (unsigned n
= 0; n
< narch
; n
++, fa
++) {
258 const native_mach_header_t
*tmh
= (const void *)(((const char *)fh
) + get32(fa
->offset
));
259 if (tmh
->magic
== mh
->magic
&& 0 == machocmp(tmh
, mh
, fr
)) {
260 *reloff
= get32(fa
->offset
);
273 static struct convstats
{
277 int64_t uncompressed
;
278 } cstat
, *cstats
= &cstat
;
281 * A fileref segment references a read-only file that contains pages from
282 * the image. The file may be a Mach binary or dylib identified with a uuid.
285 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
)
287 assert(invr
->addr
== infr
->vmaddr
&& invr
->size
== infr
->vmsize
);
290 const int rfd
= open(filename
, O_RDONLY
);
291 if (-1 == rfd
|| -1 == fstat(rfd
, &st
)) {
292 warnc(errno
, "%s: open", filename
);
295 const size_t rlen
= (size_t)st
.st_size
;
296 void *raddr
= mmap(NULL
, rlen
, PROT_READ
, MAP_PRIVATE
, rfd
, 0);
297 if ((void *)-1 == raddr
) {
298 warnc(errno
, "%s: mmap", filename
);
304 off_t fatoff
= 0; /* for FAT objects */
305 int ecode
= EX_DATAERR
;
307 switch (FREF_ID_TYPE(infr
->flags
)) {
308 case kFREF_ID_UUID
: {
309 /* file should be a mach binary: check that uuid matches */
310 const uint32_t magic
= *(uint32_t *)raddr
;
314 if (0 == fat_machocmp(raddr
, inmh
, infr
, &fatoff
))
317 case NATIVE_MH_MAGIC
:
318 if (0 == machocmp(raddr
, inmh
, infr
))
323 * Maybe this is the shared cache?
326 if (get_uuid_from_shared_cache_mapping(raddr
, rlen
, uu
) && uuid_compare(uu
, infr
->id
) == 0)
333 case kFREF_ID_MTIMESPEC_LE
:
334 /* file should have the same mtime */
335 if (0 == memcmp(&st
.st_mtimespec
, infr
->id
, sizeof (infr
->id
)))
339 /* file has no uniquifier, copy it anyway */
345 warnx("%s doesn't match corefile content", filename
);
349 const off_t fileoff
= fatoff
+ infr
->fileoff
;
350 const void *start
= (const char *)raddr
+ fileoff
;
351 const size_t len
= (size_t)infr
->filesize
;
355 if (fileoff
+ (off_t
)infr
->filesize
> (off_t
)rlen
) {
357 * the file content needed (as described on machine with
358 * larger pagesize) extends beyond the end of the mapped
359 * file using our smaller pagesize. Zero pad it.
361 const size_t pagesize_host
= 1ul << pageshift_host
;
362 void *endaddr
= (caddr_t
)raddr
+ roundup(rlen
, pagesize_host
);
363 zlen
= (size_t)(fileoff
+ infr
->filesize
- rlen
);
364 zaddr
= mmap(endaddr
, zlen
, PROT_READ
, MAP_FIXED
| MAP_PRIVATE
| MAP_ANON
, -1, 0);
365 if ((void *)-1 == zaddr
) {
367 warnc(errno
, "cannot zero-pad %s mapping for %s", str_hsize(hstr
, zlen
),filename
);
373 if (-1 == madvise((void *)start
, len
, MADV_SEQUENTIAL
))
374 warnc(errno
, "%s: madvise", filename
);
376 const int error
= bounded_pwrite(oi
->oi_fd
, start
, len
, oi
->oi_foffset
, &oi
->oi_nocache
, NULL
);
379 if (-1 == munmap(zaddr
, zlen
))
380 warnc(errno
, "%s: munmap zero pad", filename
);
382 if (-1 == munmap(raddr
, rlen
))
383 warnc(errno
, "%s: munmap", filename
);
385 warnc(error
, "while copying %s to core file", filename
);
389 const struct file_range fr
= {
390 .off
= oi
->oi_foffset
,
391 .size
= infr
->filesize
,
393 make_native_segment_command(lc
, invr
, &fr
, infr
->maxprot
, infr
->prot
);
394 oi
->oi_foffset
+= fr
.size
;
395 cstats
->added
+= infr
->filesize
;
400 * expanduser tries to expand the leading '~' (if there is any) in the given
401 * path and returns a copy of the expanded path; it returns NULL on failures.
402 * The caller is responsible for freeing the returned string.
405 expanduser(const char *path
)
410 if (path
[0] != '~') {
412 * For consistency, still dup the string so that the caller always
413 * needs to free the string.
418 char *expanded
= NULL
;
420 if (OPTIONS_DEBUG(opt
, 1)) {
421 printf("Expanding %s\n", path
);
423 int rc
= glob(path
, GLOB_TILDE
, NULL
, &globbuf
);
425 if (OPTIONS_DEBUG(opt
, 3)) {
426 printf("expanduser - gl_pathc: %zu\n", globbuf
.gl_pathc
);
427 for (size_t i
= 0; i
< globbuf
.gl_pathc
; i
++) {
428 printf("expanduser - gl_pathv[%zu]: %s\n", i
, globbuf
.gl_pathv
[i
]);
431 if (globbuf
.gl_pathc
== 1) {
432 expanded
= strdup(globbuf
.gl_pathv
[0]);
433 if (OPTIONS_DEBUG(opt
, 1)) {
434 printf("Expanded path: %s\n", expanded
);
443 #define RESPONSE_BUFF_SIZE (2048)
446 * read_response dynamically allocates buffer for reading bytes from the given
447 * fd. Upon success, this function sets response to point to the buffer and
448 * returns bytes being read; otherwise, it returns -1. The caller is
449 * responsible for freeing the response buffer.
452 read_response(int fd
, char **response
)
454 if (response
== NULL
|| *response
) {
455 warnx("Invalid response buffer pointer");
459 ssize_t bytes_read
= 0;
460 size_t buff_size
= RESPONSE_BUFF_SIZE
;
462 if (OPTIONS_DEBUG(opt
, 3)) {
463 printf("Allocating response buffer (%zu)\n", buff_size
);
465 char *buff
= malloc(buff_size
);
467 warn("Failed to allocate response buffer (%zu)", buff_size
);
475 bytes_read
= read(fd
, buff
+ total
, buff_size
- total
);
476 if (bytes_read
== -1) {
477 if (errno
== EINTR
) {
484 total
+= (size_t)bytes_read
;
485 if (total
== buff_size
) {
486 size_t new_buff_size
= buff_size
* 2;
487 if (OPTIONS_DEBUG(opt
, 3)) {
488 printf("Reallocating response buffer (%zu)\n", new_buff_size
);
490 char *new_buff
= realloc(buff
, new_buff_size
);
491 if (new_buff
== NULL
) {
492 warn("Failed to reallocate response buffer (%zu)", new_buff_size
);
496 buff_size
= new_buff_size
;
499 } while (bytes_read
!= 0);
508 assert(total
< buff_size
);
512 return (ssize_t
)total
;
515 #define WAITPID_WTO_SIGALRM (100) /* alternative for SIGALRM for kevent timeout */
516 #define WAITPID_WTO_SIGERR (101) /* sig for error when waiting for pid */
519 * waitpid_with_timeout returns true if the process exits successfully within
520 * timeout; otherwise, it returns false along with setting exitstatus and
521 * signal_no if the pointers are given.
524 waitpid_with_timeout(pid_t pid
, int *exitstatus
, int *signal_no
, int timeout
)
531 struct kevent64_s event
= {
532 .ident
= (uint64_t)pid
,
533 .filter
= EVFILT_PROC
,
534 .flags
= EV_ADD
| EV_ONESHOT
,
537 struct timespec tmout
= {
540 int ret
= kevent64(kq
, &event
, 1, &event
, 1, 0, &tmout
);
541 int kevent64_errno
= errno
;
544 if (ret
== 0) { /* timeout */
549 *signal_no
= WAITPID_WTO_SIGALRM
;
555 warnx("kevent64(): errno=%d (%s)\n", kevent64_errno
, strerror(kevent64_errno
));
559 if (event
.flags
== EV_ERROR
&& event
.data
!= ESRCH
) {
560 warnx("event.data (%lld) is not ESRCH when event.flags is EV_ERROR\n", event
.data
);
564 if (event
.ident
!= (uint64_t)pid
) {
565 warnx("event.ident is %lld (should be pid %d)\n", event
.ident
, pid
);
569 if (event
.filter
!= EVFILT_PROC
) {
570 warnx("event.filter (%d) is not EVFILT_PROC\n", event
.filter
);
575 while (waitpid(pid
, &status
, 0) < 0) {
576 if (errno
== EINTR
) {
579 warnx("waitpid(): errno=%d (%s)\n", errno
, strerror(errno
));
582 if (WIFEXITED(status
)) {
584 *exitstatus
= WEXITSTATUS(status
);
589 return WEXITSTATUS(status
) == 0;
591 if (WIFSIGNALED(status
)) {
596 *signal_no
= WTERMSIG(status
);
602 if (exitstatus
) *exitstatus
= 0;
603 if (signal_no
) *signal_no
= WAITPID_WTO_SIGERR
;
607 #define DSYMFORUUID_PATH "/usr/local/bin/dsymForUUID"
610 * exec_dsymForUUID spawns dsymForUUID to query dsym UUID info and responds the
611 * result plist. Upon success, this function sets response point to the buffer
612 * and returns bytes being read; otherwise, it returns -1. The caller is
613 * responsible for freeing the response buffer.
616 exec_dsymForUUID(uuid_string_t id
, char **response
)
618 int pipe_fds
[2] = {-1, -1};
619 bool file_actions_inited
= false;
620 ssize_t bytes_read
= -1;
628 posix_spawn_file_actions_t file_actions
;
629 rc
= posix_spawn_file_actions_init(&file_actions
);
633 file_actions_inited
= true;
635 rc
= posix_spawn_file_actions_addclose(&file_actions
, pipe_fds
[0]);
640 rc
= posix_spawn_file_actions_adddup2(&file_actions
, pipe_fds
[1], STDOUT_FILENO
);
645 rc
= posix_spawn_file_actions_addclose(&file_actions
, pipe_fds
[1]);
650 char *command
[] = {DSYMFORUUID_PATH
, id
, NULL
};
652 rc
= posix_spawn(&child
, command
[0], &file_actions
, NULL
, command
, NULL
);
660 bytes_read
= read_response(pipe_fds
[0], response
);
662 waitpid_with_timeout(child
, NULL
, NULL
, 3);
665 if (pipe_fds
[1] != -1) {
668 if (pipe_fds
[0] != -1) {
671 if (file_actions_inited
) {
672 posix_spawn_file_actions_destroy(&file_actions
);
679 * get_symbol_rich_executable_path_via_dsymForUUID spawns dsymForUUID to query
680 * dsym uuid info for the given uuid and returns the string of
681 * DBGSymbolRichExecutable; otherwise, it returns NULL on failures. The caller
682 * is responsible for freeing the returned string.
685 get_symbol_rich_executable_path_via_dsymForUUID(const uuid_t uuid
)
689 uuid_string_t uuid_str
;
690 xpc_object_t plist
= NULL
;
691 xpc_object_t uuid_info
= NULL
;
692 xpc_object_t exec_path
= NULL
;
693 char *expanded_exec_path
= NULL
;
695 uuid_unparse_upper(uuid
, uuid_str
);
697 size
= exec_dsymForUUID(uuid_str
, &response
);
702 if (OPTIONS_DEBUG(opt
, 3)) {
703 printf("dsymForUUID response:\n%s\n", response
);
706 plist
= xpc_create_from_plist(response
, (size_t)size
);
710 if (xpc_get_type(plist
) != XPC_TYPE_DICTIONARY
) {
714 uuid_info
= xpc_dictionary_get_value(plist
, uuid_str
);
715 if (uuid_info
== NULL
) {
718 if (xpc_get_type(uuid_info
) != XPC_TYPE_DICTIONARY
) {
722 exec_path
= xpc_dictionary_get_value(uuid_info
, "DBGSymbolRichExecutable");
723 if (exec_path
== NULL
) {
726 if (xpc_get_type(exec_path
) != XPC_TYPE_STRING
) {
730 expanded_exec_path
= expanduser(xpc_string_get_string_ptr(exec_path
));
740 return expanded_exec_path
;
744 * bind the file reference into the output core file.
745 * filename optionally prefixed with names from a ':'-separated PATH variable
748 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
)
750 const char *nm
= infr
->filename
.offset
+ (const char *)infr
;
752 const struct vm_range invr
= {
753 .addr
= infr
->vmaddr
,
754 .size
= infr
->vmsize
,
759 printvr(&invr
, "adding %s from '%s'",
760 str_hsize(hstr
, (off_t
)infr
->filesize
), nm
);
761 switch (FREF_ID_TYPE(infr
->flags
)) {
765 uuid_unparse_lower(infr
->id
, uustr
);
766 printf(" (%s)", uustr
);
768 case kFREF_ID_MTIMESPEC_LE
: {
771 char tbuf
[4 + 2 + 2 + 2 + 2 + 1 + 2 + 1]; /* touch -t */
772 memcpy(&mts
, &infr
->id
, sizeof (mts
));
773 localtime_r(&mts
.tv_sec
, &tm
);
774 strftime(tbuf
, sizeof (tbuf
), "%Y%m%d%H%M.%S", &tm
);
775 printf(" (%s)", tbuf
);
782 if (opt
->dsymforuuid
&& (FREF_ID_TYPE(infr
->flags
) == kFREF_ID_UUID
)) {
783 /* Try to use dsymForUUID to get the symbol-rich executable */
784 char *symrich_filepath
= get_symbol_rich_executable_path_via_dsymForUUID(infr
->id
);
785 if (symrich_filepath
) {
787 printf("\tTrying %s from dsymForUUID\n", symrich_filepath
);
789 ecode
= convert_fileref_with_file(symrich_filepath
, inmh
, infr
, &invr
, lc
, oi
);
790 free(symrich_filepath
);
794 warnx("Failed to convert fileref with dsymForUUID. Fall back to local paths");
798 const size_t pathsize
= path
? strlen(path
) : 0;
801 ecode
= convert_fileref_with_file(nm
, inmh
, infr
, &invr
, lc
, oi
);
803 /* search the : separated path (-L) for possible matches */
804 char *pathcopy
= strdup(path
);
805 char *searchpath
= pathcopy
;
808 while ((token
= strsep(&searchpath
, ":")) != NULL
) {
809 const size_t buflen
= strlen(token
) + 1 + strlen(nm
) + 1;
810 char *buf
= malloc(buflen
);
811 snprintf(buf
, buflen
, "%s%s%s", token
, '/' == nm
[0] ? "" : "/", nm
);
813 printf("\tTrying '%s'", buf
);
814 if (0 == access(buf
, R_OK
)) {
817 ecode
= convert_fileref_with_file(buf
, inmh
, infr
, &invr
, lc
, oi
);
822 } else if (opt
->verbose
)
824 0 == access(buf
, F_OK
) ? "Unreadable" : "Not present");
830 if (0 != ecode
&& zf
) {
832 * Failed to find the file reference. If this was a fileref that uses
833 * a file metadata tagging method (e.g. mtime), allow the user to subsitute a
834 * zfod region: assumes that it's better to have something to debug
835 * vs. nothing. UUID-tagged filerefs are Mach-O tags, and are
836 * assumed to be never substitutable.
838 switch (FREF_ID_TYPE(infr
->flags
)) {
840 case kFREF_ID_MTIMESPEC_LE
: { // weak tagging, allow zfod substitution
841 const struct file_range outfr
= {
842 .off
= oi
->oi_foffset
,
846 printf("\tWARNING: no file matched. Missing content is now zfod\n");
848 printvr(&invr
, "WARNING: missing content (%s) now zfod\n", nm
);
849 make_native_segment_command(lc
, &invr
, &outfr
, infr
->maxprot
, infr
->prot
);
861 segment_uncompflags(unsigned algnum
, compression_algorithm
*ca
)
865 *ca
= COMPRESSION_LZ4
;
868 *ca
= COMPRESSION_ZLIB
;
871 *ca
= COMPRESSION_LZMA
;
874 *ca
= COMPRESSION_LZFSE
;
877 warnx("unknown compression flavor %d", algnum
);
884 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
)
889 void *input
= (const caddr_t
)inbase
+ F_OFF(infr
);
896 printvr(invr
, "copying %s\n", str_hsize(hstr
, F_SIZE(infr
)));
899 compression_algorithm ca
;
901 if (0 != (ecode
= segment_uncompflags(flavor
, &ca
)))
904 hsize_str_t hstr1
, hstr2
;
905 printvr(invr
, "uncompressing %s to %s\n",
906 str_hsize(hstr1
, F_SIZE(infr
)), str_hsize(hstr2
, V_SIZE(invr
)));
908 const size_t buflen
= V_SIZEOF(invr
);
909 buf
= malloc(buflen
);
910 const size_t dstsize
= compression_decode_buffer(buf
, buflen
, input
, (size_t)F_SIZE(infr
), NULL
, ca
);
911 if (buflen
!= dstsize
) {
912 warnx("failed to uncompress segment");
916 cstats
->compressed
+= F_SIZE(infr
);
918 const int error
= bounded_pwrite(oi
->oi_fd
, buf
, V_SIZEOF(invr
), oi
->oi_foffset
, &oi
->oi_nocache
, NULL
);
920 warnc(error
, "failed to write data to core file");
928 const struct file_range outfr
= {
929 .off
= oi
->oi_foffset
,
930 .size
= V_SIZE(invr
),
932 make_native_segment_command(lc
, invr
, &outfr
, maxprot
, prot
);
933 oi
->oi_foffset
+= outfr
.size
;
936 cstats
->copied
+= outfr
.size
;
938 cstats
->uncompressed
+= outfr
.size
;
942 printvr(invr
, "%s remains zfod\n", str_hsize(hstr
, V_SIZE(invr
)));
944 const struct file_range outfr
= {
945 .off
= oi
->oi_foffset
,
948 make_native_segment_command(lc
, invr
, &outfr
, maxprot
, prot
);
954 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
)
956 const struct vm_range vr
= {
960 const struct file_range fr
= {
962 .size
= cc
->filesize
,
964 return convert_region(inbase
, &vr
, &fr
, cc
->prot
, cc
->maxprot
, COMP_ALG_TYPE(cc
->flags
), lc
, oi
);
968 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
)
970 const struct vm_range vr
= {
974 const struct file_range fr
= {
976 .size
= sc
->filesize
,
978 return convert_region(inbase
, &vr
, &fr
, sc
->initprot
, sc
->maxprot
, 0, lc
, oi
);
981 /* pass-through - content is all in the header */
984 convert_thread(struct thread_command
*dst
, const struct thread_command
*src
)
986 assert(LC_THREAD
== src
->cmd
);
987 memcpy(dst
, src
, src
->cmdsize
);
988 cstats
->copied
+= src
->cmdsize
;
993 gcore_conv(int infd
, const char *searchpath
, bool zf
, int fd
)
996 const void *corebase
= mmapfile(infd
, 0, &filesize
);
999 * Check to see if the input file is "sane" as far as we're concerned.
1000 * XXX Note that this -won't- necessarily work for other ISAs than
1003 const native_mach_header_t
*inmh
= corebase
;
1004 validate_core_header(inmh
, filesize
);
1007 * The sparse file may have created many more segments, but there's no
1008 * attempt to change their numbers here. Just count all the segment
1009 * types needed to figure out the size of the output file header.
1011 * (Size assertions to be deleted once data structures stable!)
1013 __block
size_t headersize
= sizeof (native_mach_header_t
);
1014 __block
unsigned pageshift_target
= pageshift_host
;
1016 walkcore(inmh
, ^(const struct proto_coreinfo_command
*ci
) {
1017 assert(sizeof (*ci
) == ci
->cmdsize
);
1019 printf("Converting version %d core file to pre-versioned format\n", ci
->version
);
1020 if (0 < ci
->pageshift
&& ci
->pageshift
< 31)
1021 pageshift_target
= ci
->pageshift
;
1022 else if (CPU_TYPE_ARM64
== inmh
->cputype
)
1023 pageshift_target
= 14; // compatibility hack, should go soon
1024 }, ^(const struct proto_fileref_command
*__unused fc
) {
1025 const char *nm
= fc
->filename
.offset
+ (const char *)fc
;
1026 size_t nmlen
= strlen(nm
) + 1;
1027 size_t cmdsize
= sizeof (*fc
) + roundup(nmlen
, sizeof (long));
1028 assert(cmdsize
== fc
->cmdsize
);
1030 headersize
+= sizeof (native_segment_command_t
);
1031 }, ^(const struct proto_coredata_command
*__unused cc
) {
1032 assert(sizeof (*cc
) == cc
->cmdsize
);
1033 headersize
+= sizeof (native_segment_command_t
);
1034 }, ^(const native_segment_command_t
*sc
) {
1035 headersize
+= sc
->cmdsize
;
1036 }, ^(const struct thread_command
*tc
) {
1037 headersize
+= tc
->cmdsize
;
1040 void *header
= calloc(1, headersize
);
1042 errx(EX_OSERR
, "out of memory for header");
1044 native_mach_header_t
*mh
= memcpy(header
, inmh
, sizeof (*mh
));
1048 assert(0 < pageshift_target
&& pageshift_target
< 31);
1049 const vm_offset_t pagesize_target
= ((vm_offset_t
)1 << pageshift_target
);
1050 const vm_offset_t pagemask_target
= pagesize_target
- 1;
1052 const struct load_command
*inlc
= (const void *)(inmh
+ 1);
1053 struct load_command
*lc
= (void *)(mh
+ 1);
1056 struct output_info oi
= {
1058 .oi_foffset
= ((vm_offset_t
)headersize
+ pagemask_target
) & ~pagemask_target
,
1059 .oi_nocache
= false,
1062 for (unsigned i
= 0; i
< inmh
->ncmds
; i
++) {
1063 switch (inlc
->cmd
) {
1064 case proto_LC_FILEREF
:
1065 ecode
= convert_fileref(searchpath
, zf
, inmh
, (const void *)inlc
, lc
, &oi
);
1067 case proto_LC_COREDATA
:
1068 ecode
= convert_coredata(corebase
, inmh
, (const void *)inlc
, lc
, &oi
);
1070 case NATIVE_LC_SEGMENT
:
1071 ecode
= convert_segment(corebase
, inmh
, (const void *)inlc
, lc
, &oi
);
1074 ecode
= convert_thread((void *)lc
, (const void *)inlc
);
1077 if (OPTIONS_DEBUG(opt
, 1))
1078 printf("discarding load command %d\n", inlc
->cmd
);
1083 if (NATIVE_LC_SEGMENT
== lc
->cmd
|| LC_THREAD
== lc
->cmd
) {
1084 mach_header_inc_ncmds(mh
, 1);
1085 mach_header_inc_sizeofcmds(mh
, lc
->cmdsize
);
1086 lc
= (void *)next_lc(lc
);
1088 if (NULL
== (inlc
= next_lc(inlc
)))
1093 * Even if we've encountered an error, try and write out the header
1095 if (0 != bounded_pwrite(fd
, header
, headersize
, 0, &oi
.oi_nocache
, NULL
))
1097 if (0 == ecode
&& sizeof (*mh
) + mh
->sizeofcmds
!= headersize
)
1098 ecode
= EX_SOFTWARE
;
1099 validate_core_header(mh
, oi
.oi_foffset
);
1101 warnx("failed to write new core file correctly");
1102 else if (opt
->verbose
) {
1104 printf("Conversion complete: %s copied", str_hsize(hstr
, cstats
->copied
));
1105 const int64_t delta
= cstats
->uncompressed
- cstats
->compressed
;
1107 printf(", %s uncompressed", str_hsize(hstr
, delta
));
1108 const int64_t added
= cstats
->added
+ ((int)mh
->sizeofcmds
- (int)inmh
->sizeofcmds
);
1110 printf(", %s added", str_hsize(hstr
, added
));
1114 munmap((void *)corebase
, (size_t)filesize
);