]> git.saurik.com Git - apple/system_cmds.git/blob - gcore.tproj/convert.c
system_cmds-805.250.2.tar.gz
[apple/system_cmds.git] / gcore.tproj / convert.c
1 /*
2 * Copyright (c) 2016 Apple Inc. All rights reserved.
3 */
4
5 #include "convert.h"
6 #include "corefile.h"
7 #include "vanilla.h"
8 #include "threads.h"
9 #include "vm.h"
10 #include "dyld_shared_cache.h"
11 #include "utils.h"
12
13 #include <sys/types.h>
14 #include <sys/mman.h>
15 #include <sys/stat.h>
16 #include <sys/queue.h>
17 #include <sys/param.h>
18 #include <mach-o/fat.h>
19 #include <uuid/uuid.h>
20 #include <fcntl.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <err.h>
26 #include <sysexits.h>
27 #include <time.h>
28
29 #if defined(CONFIG_GCORE_MAP) || defined(CONFIG_GCORE_CONV) || defined(CONFIG_GCORE_FREF)
30
31 static const void *
32 mmapfile(int fd, off_t off, off_t *filesize)
33 {
34 struct stat st;
35 if (-1 == fstat(fd, &st))
36 errc(EX_OSERR, errno, "can't stat input file");
37
38 const size_t size = (size_t)(st.st_size - off);
39 if ((off_t)size != (st.st_size - off))
40 errc(EX_OSERR, EOVERFLOW, "input file too large?");
41
42 const void *addr = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, off);
43 if ((void *)-1 == addr)
44 errc(EX_OSERR, errno, "can't mmap input file");
45 *filesize = st.st_size;
46 return addr;
47 }
48
49 static void
50 walkcore(
51 const native_mach_header_t *mh,
52 void (^coreinfo)(const struct proto_coreinfo_command *),
53 void (^frefdata)(const struct proto_fileref_command *),
54 void (^coredata)(const struct proto_coredata_command *),
55 void (^segdata)(const native_segment_command_t *),
56 void (^thrdata)(const struct thread_command *))
57 {
58 const struct load_command *lc = (const void *)(mh + 1);
59 for (unsigned i = 0; i < mh->ncmds; i++) {
60 switch (lc->cmd) {
61 case proto_LC_COREINFO:
62 if (coreinfo)
63 coreinfo((const void *)lc);
64 break;
65 case proto_LC_FILEREF:
66 if (frefdata)
67 frefdata((const void *)lc);
68 break;
69 case proto_LC_COREDATA:
70 if (coredata)
71 coredata((const void *)lc);
72 break;
73 case NATIVE_LC_SEGMENT:
74 if (segdata)
75 segdata((const void *)lc);
76 break;
77 case LC_THREAD:
78 if (thrdata)
79 thrdata((const void *)lc);
80 break;
81 default:
82 break;
83 }
84 if (NULL == (lc = next_lc(lc)))
85 break;
86 }
87 }
88
89 #endif
90
91 #ifdef CONFIG_GCORE_FREF
92
93 int
94 gcore_fref(int fd)
95 {
96 off_t filesize;
97 const void *corebase = mmapfile(fd, 0, &filesize);
98
99 close(fd);
100 struct flist {
101 STAILQ_ENTRY(flist) f_linkage;
102 const char *f_nm;
103 unsigned long f_nmhash;
104 };
105 STAILQ_HEAD(flisthead, flist) __flh, *flh = &__flh;
106 STAILQ_INIT(flh);
107
108 walkcore(corebase, NULL, ^(const struct proto_fileref_command *fc) {
109 const char *nm = fc->filename.offset + (const char *)fc;
110 const unsigned long nmhash = simple_namehash(nm);
111 struct flist *f;
112 STAILQ_FOREACH(f, flh, f_linkage) {
113 if (nmhash == f->f_nmhash && 0 == strcmp(f->f_nm, nm))
114 return; /* skip duplicates */
115 }
116 struct flist *nf = calloc(1, sizeof (*nf));
117 nf->f_nm = nm;
118 nf->f_nmhash = nmhash;
119 STAILQ_INSERT_TAIL(flh, nf, f_linkage);
120 }, NULL, NULL, NULL);
121
122 struct flist *f, *tf;
123 STAILQ_FOREACH_SAFE(f, flh, f_linkage, tf) {
124 printf("%s\n", f->f_nm);
125 free(f);
126 f = NULL;
127 }
128
129 munmap((void *)corebase, (size_t)filesize);
130 return 0;
131 }
132
133 #endif /* CONFIG_GCORE_FREF */
134
135 #ifdef CONFIG_GCORE_MAP
136
137 /*
138 * A pale imitation of vmmap, but for core files
139 */
140 int
141 gcore_map(int fd)
142 {
143 off_t filesize;
144 const void *corebase = mmapfile(fd, 0, &filesize);
145
146 __block int coreversion = 0;
147
148 walkcore(corebase, ^(const struct proto_coreinfo_command *ci) {
149 coreversion = ci->version;
150 }, NULL, NULL, NULL, NULL);
151
152 if (0 == coreversion) {
153 const char titlfmt[] = "%16s-%-16s [%7s] %3s/%3s\n";
154 const char *segcfmt = "%016llx-%016llx [%7s] %3s/%3s\n";
155
156 printf(titlfmt, "start ", " end", "vsize", "prt", "max");
157 walkcore(corebase, NULL, NULL, NULL, ^(const native_segment_command_t *sc) {
158 hsize_str_t vstr;
159 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));
160 }, NULL);
161 } else {
162 const char titlfmt[] = "%-23s %16s-%-16s [%7s] %3s/%3s %6s %4s %-14s\n";
163 const char *freffmt = "%-23s %016llx-%016llx [%7s] %3s/%3s %6s %4s %-14s @%lld\n";
164 const char *datafmt = "%-23s %016llx-%016llx [%7s] %3s/%3s %6s %4s %-14s\n";
165
166 printf(titlfmt, "region type", "start ", " end", "vsize", "prt", "max", "shrmod", "purge", "region detail");
167 walkcore(corebase, NULL, ^(const struct proto_fileref_command *fc) {
168 const char *nm = fc->filename.offset + (const char *)fc;
169 tag_str_t tstr;
170 hsize_str_t vstr;
171 printf(freffmt, str_tag(tstr, fc->tag, fc->share_mode, fc->prot, fc->extp),
172 fc->vmaddr, fc->vmaddr + fc->vmsize,
173 str_hsize(vstr, fc->vmsize), str_prot(fc->prot),
174 str_prot(fc->maxprot), str_shared(fc->share_mode),
175 str_purgable(fc->purgable, fc->share_mode), nm, fc->fileoff);
176 }, ^(const struct proto_coredata_command *cc) {
177 tag_str_t tstr;
178 hsize_str_t vstr;
179 printf(datafmt, str_tag(tstr, cc->tag, cc->share_mode, cc->prot, cc->extp),
180 cc->vmaddr, cc->vmaddr + cc->vmsize,
181 str_hsize(vstr, cc->vmsize), str_prot(cc->prot),
182 str_prot(cc->maxprot), str_shared(cc->share_mode),
183 str_purgable(cc->purgable, cc->share_mode),
184 cc->vmsize && 0 == cc->filesize ? "(zfod)" : "");
185 }, ^(const native_segment_command_t *sc) {
186 hsize_str_t vstr;
187 printf(datafmt, "", (mach_vm_offset_t)sc->vmaddr,
188 (mach_vm_offset_t)sc->vmaddr + sc->vmsize,
189 str_hsize(vstr, sc->vmsize), str_prot(sc->initprot),
190 str_prot(sc->maxprot), "", "",
191 sc->vmsize && 0 == sc->filesize ? "(zfod)" : "");
192 }, NULL);
193 }
194 close(fd);
195 munmap((void *)corebase, (size_t)filesize);
196 return 0;
197 }
198
199 #endif
200
201 #ifdef CONFIG_GCORE_CONV
202
203 /*
204 * Convert an input core file into an "old" format core file
205 * (a) convert all fileref segments into regular segments
206 * (b) uncompress anything we find compressed.
207 * This should be equivalent to a copy for an "old" format core file.
208 */
209
210 static int
211 machocmp(const native_mach_header_t *tmh, const native_mach_header_t *mh, const struct proto_fileref_command *fr)
212 {
213 if (tmh->magic == mh->magic) {
214 const struct load_command *lc = (const void *)(tmh + 1);
215 for (unsigned i = 0; i < tmh->ncmds; i++) {
216 if (LC_UUID == lc->cmd && lc->cmdsize >= sizeof (struct uuid_command)) {
217 const struct uuid_command *uc = (const void *)lc;
218 return uuid_compare(uc->uuid, fr->id);
219 }
220 if (NULL == (lc = next_lc(lc)))
221 break;
222 }
223 }
224 return -1;
225 }
226
227 static int
228 fat_machocmp(const struct fat_header *fh, const native_mach_header_t *mh, const struct proto_fileref_command *fr, off_t *reloff)
229 {
230 const uint32_t (^get32)(uint32_t);
231
232 if (FAT_MAGIC == fh->magic) {
233 get32 = ^(uint32_t val) {
234 return val;
235 };
236 } else {
237 get32 = ^(uint32_t val) {
238 uint32_t result = 0;
239 for (unsigned i = 0; i < sizeof (uint32_t); i++)
240 ((uint8_t *)&result)[i] = ((uint8_t *)&val)[3-i];
241 return result;
242 };
243 }
244
245 assert(FAT_MAGIC == get32(fh->magic));
246 assert(kFREF_ID_UUID == FREF_ID_TYPE(fr->flags) && !uuid_is_null(fr->id));
247
248 const struct fat_arch *fa = (const struct fat_arch *)(fh + 1);
249 uint32_t narch = get32(fh->nfat_arch);
250 for (unsigned n = 0; n < narch; n++, fa++) {
251 const native_mach_header_t *tmh = (const void *)(((const char *)fh) + get32(fa->offset));
252 if (tmh->magic == mh->magic && 0 == machocmp(tmh, mh, fr)) {
253 *reloff = get32(fa->offset);
254 return 0;
255 }
256 }
257 return -1;
258 }
259
260 struct output_info {
261 int oi_fd;
262 off_t oi_foffset;
263 bool oi_nocache;
264 };
265
266 static struct convstats {
267 int64_t copied;
268 int64_t added;
269 int64_t compressed;
270 int64_t uncompressed;
271 } cstat, *cstats = &cstat;
272
273 /*
274 * A fileref segment references a read-only file that contains pages from
275 * the image. The file may be a Mach binary or dylib identified with a uuid.
276 */
277 static int
278 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)
279 {
280 assert(invr->addr == infr->vmaddr && invr->size == infr->vmsize);
281
282 struct stat st;
283 const int rfd = open(filename, O_RDONLY);
284 if (-1 == rfd || -1 == fstat(rfd, &st)) {
285 warnc(errno, "%s: open", filename);
286 return EX_IOERR;
287 }
288 const size_t rlen = (size_t)st.st_size;
289 void *raddr = mmap(NULL, rlen, PROT_READ, MAP_PRIVATE, rfd, 0);
290 if ((void *)-1 == raddr) {
291 warnc(errno, "%s: mmap", filename);
292 close(rfd);
293 return EX_IOERR;
294 }
295 close(rfd);
296
297 off_t fatoff = 0; /* for FAT objects */
298 int ecode = EX_DATAERR;
299
300 switch (FREF_ID_TYPE(infr->flags)) {
301 case kFREF_ID_UUID: {
302 /* file should be a mach binary: check that uuid matches */
303 const uint32_t magic = *(uint32_t *)raddr;
304 switch (magic) {
305 case FAT_MAGIC:
306 case FAT_CIGAM:
307 if (0 == fat_machocmp(raddr, inmh, infr, &fatoff))
308 ecode = 0;
309 break;
310 case NATIVE_MH_MAGIC:
311 if (0 == machocmp(raddr, inmh, infr))
312 ecode = 0;
313 break;
314 default: {
315 /*
316 * Maybe this is the shared cache?
317 */
318 uuid_t uu;
319 if (get_uuid_from_shared_cache_mapping(raddr, rlen, uu) && uuid_compare(uu, infr->id) == 0)
320 ecode = 0;
321 break;
322 }
323 }
324 break;
325 }
326 case kFREF_ID_MTIMESPEC_LE:
327 /* file should have the same mtime */
328 if (0 == memcmp(&st.st_mtimespec, infr->id, sizeof (infr->id)))
329 ecode = 0;
330 break;
331 case kFREF_ID_NONE:
332 /* file has no uniquifier, copy it anyway */
333 break;
334 }
335
336 if (0 != ecode) {
337 munmap(raddr, rlen);
338 warnx("%s doesn't match corefile content", filename);
339 return ecode;
340 }
341
342 const off_t fileoff = fatoff + infr->fileoff;
343 const void *start = (const char *)raddr + fileoff;
344 const size_t len = (size_t)infr->filesize;
345 void *zaddr = NULL;
346 size_t zlen = 0;
347
348 if (fileoff + (off_t)infr->filesize > (off_t)rlen) {
349 /*
350 * the file content needed (as described on machine with
351 * larger pagesize) extends beyond the end of the mapped
352 * file using our smaller pagesize. Zero pad it.
353 */
354 const size_t pagesize_host = 1ul << pageshift_host;
355 void *endaddr = (caddr_t)raddr + roundup(rlen, pagesize_host);
356 zlen = (size_t)(fileoff + infr->filesize - rlen);
357 zaddr = mmap(endaddr, zlen, PROT_READ, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0);
358 if ((void *)-1 == zaddr) {
359 hsize_str_t hstr;
360 warnc(errno, "cannot zero-pad %s mapping for %s", str_hsize(hstr, zlen),filename);
361 munmap(raddr, rlen);
362 return EX_IOERR;
363 }
364 }
365
366 if (-1 == madvise((void *)start, len, MADV_SEQUENTIAL))
367 warnc(errno, "%s: madvise", filename);
368
369 const int error = bounded_pwrite(oi->oi_fd, start, len, oi->oi_foffset, &oi->oi_nocache, NULL);
370
371 if (zlen) {
372 if (-1 == munmap(zaddr, zlen))
373 warnc(errno, "%s: munmap zero pad", filename);
374 }
375 if (-1 == munmap(raddr, rlen))
376 warnc(errno, "%s: munmap", filename);
377 if (error) {
378 warnc(error, "while copying %s to core file", filename);
379 return EX_IOERR;
380 }
381
382 const struct file_range fr = {
383 .off = oi->oi_foffset,
384 .size = infr->filesize,
385 };
386 make_native_segment_command(lc, invr, &fr, infr->maxprot, infr->prot);
387 oi->oi_foffset += fr.size;
388 cstats->added += infr->filesize;
389 return 0;
390 }
391
392 /*
393 * bind the file reference into the output core file.
394 * filename optionally prefixed with names from a ':'-separated PATH variable
395 */
396 static int
397 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)
398 {
399 const char *nm = infr->filename.offset + (const char *)infr;
400 uuid_string_t uustr;
401 const struct vm_range invr = {
402 .addr = infr->vmaddr,
403 .size = infr->vmsize,
404 };
405
406 if (opt->verbose) {
407 hsize_str_t hstr;
408 printvr(&invr, "adding %s from '%s'",
409 str_hsize(hstr, (off_t)infr->filesize), nm);
410 switch (FREF_ID_TYPE(infr->flags)) {
411 case kFREF_ID_NONE:
412 break;
413 case kFREF_ID_UUID:
414 uuid_unparse_lower(infr->id, uustr);
415 printf(" (%s)", uustr);
416 break;
417 case kFREF_ID_MTIMESPEC_LE: {
418 struct timespec mts;
419 struct tm tm;
420 char tbuf[4 + 2 + 2 + 2 + 2 + 1 + 2 + 1]; /* touch -t */
421 memcpy(&mts, &infr->id, sizeof (mts));
422 localtime_r(&mts.tv_sec, &tm);
423 strftime(tbuf, sizeof (tbuf), "%Y%m%d%H%M.%S", &tm);
424 printf(" (%s)", tbuf);
425 } break;
426 }
427 printf("\n");
428 }
429
430 const size_t pathsize = path ? strlen(path) : 0;
431 int ecode = EX_DATAERR;
432 if (0 == pathsize)
433 ecode = convert_fileref_with_file(nm, inmh, infr, &invr, lc, oi);
434 else {
435 /* search the : separated path (-L) for possible matches */
436 char *pathcopy = strdup(path);
437 char *searchpath = pathcopy;
438 const char *token;
439
440 while ((token = strsep(&searchpath, ":")) != NULL) {
441 const size_t buflen = strlen(token) + 1 + strlen(nm) + 1;
442 char *buf = malloc(buflen);
443 snprintf(buf, buflen, "%s%s%s", token, '/' == nm[0] ? "" : "/", nm);
444 if (opt->verbose)
445 printf("\tTrying '%s'", buf);
446 if (0 == access(buf, R_OK)) {
447 if (opt->verbose)
448 printf("\n");
449 ecode = convert_fileref_with_file(buf, inmh, infr, &invr, lc, oi);
450 if (0 == ecode) {
451 free(buf);
452 break;
453 }
454 } else if (opt->verbose)
455 printf(": %s.\n",
456 0 == access(buf, F_OK) ? "Unreadable" : "Not present");
457 free(buf);
458 }
459 free(pathcopy);
460 }
461
462 if (0 != ecode && zf) {
463 /*
464 * Failed to find the file reference. If this was a fileref that uses
465 * a file metadata tagging method (e.g. mtime), allow the user to subsitute a
466 * zfod region: assumes that it's better to have something to debug
467 * vs. nothing. UUID-tagged filerefs are Mach-O tags, and are
468 * assumed to be never substitutable.
469 */
470 switch (FREF_ID_TYPE(infr->flags)) {
471 case kFREF_ID_NONE:
472 case kFREF_ID_MTIMESPEC_LE: { // weak tagging, allow zfod substitution
473 const struct file_range outfr = {
474 .off = oi->oi_foffset,
475 .size = 0,
476 };
477 if (opt->verbose)
478 printf("\tWARNING: no file matched. Missing content is now zfod\n");
479 else
480 printvr(&invr, "WARNING: missing content (%s) now zfod\n", nm);
481 make_native_segment_command(lc, &invr, &outfr, infr->maxprot, infr->prot);
482 ecode = 0;
483 } break;
484 default:
485 break;
486 }
487 }
488
489 return (ecode);
490 }
491
492 static int
493 segment_uncompflags(unsigned algnum, compression_algorithm *ca)
494 {
495 switch (algnum) {
496 case kCOMP_LZ4:
497 *ca = COMPRESSION_LZ4;
498 break;
499 case kCOMP_ZLIB:
500 *ca = COMPRESSION_ZLIB;
501 break;
502 case kCOMP_LZMA:
503 *ca = COMPRESSION_LZMA;
504 break;
505 case kCOMP_LZFSE:
506 *ca = COMPRESSION_LZFSE;
507 break;
508 default:
509 warnx("unknown compression flavor %d", algnum);
510 return EX_DATAERR;
511 }
512 return 0;
513 }
514
515 static int
516 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)
517 {
518 int ecode = 0;
519
520 if (F_SIZE(infr)) {
521 void *input = (const caddr_t)inbase + F_OFF(infr);
522 void *buf;
523
524 if (0 == flavor) {
525 buf = input;
526 if (opt->verbose) {
527 hsize_str_t hstr;
528 printvr(invr, "copying %s\n", str_hsize(hstr, F_SIZE(infr)));
529 }
530 } else {
531 compression_algorithm ca;
532
533 if (0 != (ecode = segment_uncompflags(flavor, &ca)))
534 return ecode;
535 if (opt->verbose) {
536 hsize_str_t hstr1, hstr2;
537 printvr(invr, "uncompressing %s to %s\n",
538 str_hsize(hstr1, F_SIZE(infr)), str_hsize(hstr2, V_SIZE(invr)));
539 }
540 const size_t buflen = V_SIZEOF(invr);
541 buf = malloc(buflen);
542 const size_t dstsize = compression_decode_buffer(buf, buflen, input, (size_t)F_SIZE(infr), NULL, ca);
543 if (buflen != dstsize) {
544 warnx("failed to uncompress segment");
545 free(buf);
546 return EX_DATAERR;
547 }
548 cstats->compressed += F_SIZE(infr);
549 }
550 const int error = bounded_pwrite(oi->oi_fd, buf, V_SIZEOF(invr), oi->oi_foffset, &oi->oi_nocache, NULL);
551 if (error) {
552 warnc(error, "failed to write data to core file");
553 ecode = EX_IOERR;
554 }
555 if (buf != input)
556 free(buf);
557 if (ecode)
558 return ecode;
559
560 const struct file_range outfr = {
561 .off = oi->oi_foffset,
562 .size = V_SIZE(invr),
563 };
564 make_native_segment_command(lc, invr, &outfr, maxprot, prot);
565 oi->oi_foffset += outfr.size;
566
567 if (0 == flavor)
568 cstats->copied += outfr.size;
569 else
570 cstats->uncompressed += outfr.size;
571 } else {
572 if (opt->verbose) {
573 hsize_str_t hstr;
574 printvr(invr, "%s remains zfod\n", str_hsize(hstr, V_SIZE(invr)));
575 }
576 const struct file_range outfr = {
577 .off = oi->oi_foffset,
578 .size = 0,
579 };
580 make_native_segment_command(lc, invr, &outfr, maxprot, prot);
581 }
582 return ecode;
583 }
584
585 static int
586 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)
587 {
588 const struct vm_range vr = {
589 .addr = cc->vmaddr,
590 .size = cc->vmsize,
591 };
592 const struct file_range fr = {
593 .off = cc->fileoff,
594 .size = cc->filesize,
595 };
596 return convert_region(inbase, &vr, &fr, cc->prot, cc->maxprot, COMP_ALG_TYPE(cc->flags), lc, oi);
597 }
598
599 static int
600 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)
601 {
602 const struct vm_range vr = {
603 .addr = sc->vmaddr,
604 .size = sc->vmsize,
605 };
606 const struct file_range fr = {
607 .off = sc->fileoff,
608 .size = sc->filesize,
609 };
610 return convert_region(inbase, &vr, &fr, sc->initprot, sc->maxprot, 0, lc, oi);
611 }
612
613 /* pass-through - content is all in the header */
614
615 static int
616 convert_thread(struct thread_command *dst, const struct thread_command *src)
617 {
618 assert(LC_THREAD == src->cmd);
619 memcpy(dst, src, src->cmdsize);
620 cstats->copied += src->cmdsize;
621 return 0;
622 }
623
624 int
625 gcore_conv(int infd, const char *searchpath, bool zf, int fd)
626 {
627 off_t filesize;
628 const void *corebase = mmapfile(infd, 0, &filesize);
629 close(infd);
630 /*
631 * Check to see if the input file is "sane" as far as we're concerned.
632 * XXX Note that this -won't- necessarily work for other ISAs than
633 * our own!
634 */
635 const native_mach_header_t *inmh = corebase;
636 validate_core_header(inmh, filesize);
637
638 /*
639 * The sparse file may have created many more segments, but there's no
640 * attempt to change their numbers here. Just count all the segment
641 * types needed to figure out the size of the output file header.
642 *
643 * (Size assertions to be deleted once data structures stable!)
644 */
645 __block size_t headersize = sizeof (native_mach_header_t);
646 __block unsigned pageshift_target = pageshift_host;
647
648 walkcore(inmh, ^(const struct proto_coreinfo_command *ci) {
649 assert(sizeof (*ci) == ci->cmdsize);
650 if (opt->verbose)
651 printf("Converting version %d core file to pre-versioned format\n", ci->version);
652 if (0 < ci->pageshift && ci->pageshift < 31)
653 pageshift_target = ci->pageshift;
654 else if (CPU_TYPE_ARM64 == inmh->cputype)
655 pageshift_target = 14; // compatibility hack, should go soon
656 }, ^(const struct proto_fileref_command *__unused fc) {
657 const char *nm = fc->filename.offset + (const char *)fc;
658 size_t nmlen = strlen(nm) + 1;
659 size_t cmdsize = sizeof (*fc) + roundup(nmlen, sizeof (long));
660 assert(cmdsize == fc->cmdsize);
661
662 headersize += sizeof (native_segment_command_t);
663 }, ^(const struct proto_coredata_command *__unused cc) {
664 assert(sizeof (*cc) == cc->cmdsize);
665 headersize += sizeof (native_segment_command_t);
666 }, ^(const native_segment_command_t *sc) {
667 headersize += sc->cmdsize;
668 }, ^(const struct thread_command *tc) {
669 headersize += tc->cmdsize;
670 });
671
672 void *header = calloc(1, headersize);
673 if (NULL == header)
674 errx(EX_OSERR, "out of memory for header");
675
676 native_mach_header_t *mh = memcpy(header, inmh, sizeof (*mh));
677 mh->ncmds = 0;
678 mh->sizeofcmds = 0;
679
680 assert(0 < pageshift_target && pageshift_target < 31);
681 const vm_offset_t pagesize_target = ((vm_offset_t)1 << pageshift_target);
682 const vm_offset_t pagemask_target = pagesize_target - 1;
683
684 const struct load_command *inlc = (const void *)(inmh + 1);
685 struct load_command *lc = (void *)(mh + 1);
686 int ecode = 0;
687
688 struct output_info oi = {
689 .oi_fd = fd,
690 .oi_foffset = ((vm_offset_t)headersize + pagemask_target) & ~pagemask_target,
691 .oi_nocache = false,
692 };
693
694 for (unsigned i = 0; i < inmh->ncmds; i++) {
695 switch (inlc->cmd) {
696 case proto_LC_FILEREF:
697 ecode = convert_fileref(searchpath, zf, inmh, (const void *)inlc, lc, &oi);
698 break;
699 case proto_LC_COREDATA:
700 ecode = convert_coredata(corebase, inmh, (const void *)inlc, lc, &oi);
701 break;
702 case NATIVE_LC_SEGMENT:
703 ecode = convert_segment(corebase, inmh, (const void *)inlc, lc, &oi);
704 break;
705 case LC_THREAD:
706 ecode = convert_thread((void *)lc, (const void *)inlc);
707 break;
708 default:
709 if (OPTIONS_DEBUG(opt, 1))
710 printf("discarding load command %d\n", inlc->cmd);
711 break;
712 }
713 if (0 != ecode)
714 break;
715 if (NATIVE_LC_SEGMENT == lc->cmd || LC_THREAD == lc->cmd) {
716 mach_header_inc_ncmds(mh, 1);
717 mach_header_inc_sizeofcmds(mh, lc->cmdsize);
718 lc = (void *)next_lc(lc);
719 }
720 if (NULL == (inlc = next_lc(inlc)))
721 break;
722 }
723
724 /*
725 * Even if we've encountered an error, try and write out the header
726 */
727 if (0 != bounded_pwrite(fd, header, headersize, 0, &oi.oi_nocache, NULL))
728 ecode = EX_IOERR;
729 if (0 == ecode && sizeof (*mh) + mh->sizeofcmds != headersize)
730 ecode = EX_SOFTWARE;
731 validate_core_header(mh, oi.oi_foffset);
732 if (ecode)
733 warnx("failed to write new core file correctly");
734 else if (opt->verbose) {
735 hsize_str_t hstr;
736 printf("Conversion complete: %s copied", str_hsize(hstr, cstats->copied));
737 const int64_t delta = cstats->uncompressed - cstats->compressed;
738 if (delta > 0)
739 printf(", %s uncompressed", str_hsize(hstr, delta));
740 const int64_t added = cstats->added + ((int)mh->sizeofcmds - (int)inmh->sizeofcmds);
741 if (added > 0)
742 printf(", %s added", str_hsize(hstr, added));
743 printf("\n");
744 }
745 free(header);
746 munmap((void *)corebase, (size_t)filesize);
747 return ecode;
748 }
749 #endif