1 /* amd64-darwin.macho-upxmain.c -- loader hack for Mach-o AMD64
2 
3    This file is part of the UPX executable compressor.
4 
5    Copyright (C) 1996-2020 Markus Franz Xaver Johannes Oberhumer
6    Copyright (C) 1996-2020 Laszlo Molnar
7    Copyright (C) 2000-2020 John F. Reiser
8    All Rights Reserved.
9 
10    UPX and the UCL library are free software; you can redistribute them
11    and/or modify them under the terms of the GNU General Public License as
12    published by the Free Software Foundation; either version 2 of
13    the License, or (at your option) any later version.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; see the file COPYING.
22    If not, write to the Free Software Foundation, Inc.,
23    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 
25    Markus F.X.J. Oberhumer              Laszlo Molnar
26    <markus@oberhumer.com>               <ezerotven+github@gmail.com>
27 
28    John F. Reiser
29    <jreiser@users.sourceforge.net>
30  */
31 
32 #define __WORDSIZE 64
33 #include "include/darwin.h"
34 
35 typedef unsigned char * Addr;
36 
37 #define SIMULATE_ON_LINUX_EABI4 0
38 #ifndef DEBUG  /*{*/
39 #define DEBUG 0
40 #endif  /*}*/
41 
42 /*************************************************************************
43 // configuration section
44 **************************************************************************/
45 
46 // In order to make it much easier to move this code at runtime and execute
47 // it at an address different from it load address:  there must be no
48 // static data, and no string constants.
49 
50 #if !DEBUG  /*{*/
51 #define DPRINTF(a) /* empty: no debug drivel */
52 #define DEBUG_STRCON(name, value) /* empty */
53 #else  /*}{ DEBUG */
54 extern int write(int, void const *, size_t);
55 #if 0
56 #include "stdarg.h"
57 #else
58 #define va_arg      __builtin_va_arg
59 #define va_end      __builtin_va_end
60 #define va_list     __builtin_va_list
61 #define va_start    __builtin_va_start
62 #endif
63 
64 #if defined(__i386__) || defined(__x86_64__) /*{*/
65 #define PIC_STRING(value, var) \
66     __asm__ __volatile__ ( \
67         "call 0f; .asciz \"" value "\"; \
68       0: pop %0;" : "=r"(var) : \
69     )
70 #elif defined(__arm__)  /*}{*/
71 #define PIC_STRING(value, var) \
72     __asm__ __volatile__ ( \
73         "mov %0,pc; b 0f; \
74         .asciz \"" value "\"; .balign 4; \
75       0: " : "=r"(var) \
76     )
77 #elif defined(__mips__)  /*}{*/
78 #define PIC_STRING(value, var) \
79     __asm__ __volatile__ ( \
80         ".set noreorder; bal 0f; move %0,$31; .set reorder; \
81         .asciz \"" value "\"; .balign 4; \
82       0: " \
83         : "=r"(var) : : "ra" \
84     )
85 #endif  /*}*/
86 
87 
88 #define DEBUG_STRCON(name, strcon) \
89     static char const *name(void) { \
90         register char const *rv; PIC_STRING(strcon, rv); \
91         return rv; \
92     }
93 
94 
95 #ifdef __arm__  /*{*/
96 extern unsigned div10(unsigned);
97 #else  /*}{*/
98 static unsigned
div10(unsigned x)99 div10(unsigned x)
100 {
101     return x / 10u;
102 }
103 #endif  /*}*/
104 
105 static int
unsimal(unsigned x,char * ptr,int n)106 unsimal(unsigned x, char *ptr, int n)
107 {
108     if (10<=x) {
109         unsigned const q = div10(x);
110         x -= 10 * q;
111         n = unsimal(q, ptr, n);
112     }
113     ptr[n] = '0' + x;
114     return 1+ n;
115 }
116 
117 static int
decimal(int x,char * ptr,int n)118 decimal(int x, char *ptr, int n)
119 {
120     if (x < 0) {
121         x = -x;
122         ptr[n++] = '-';
123     }
124     return unsimal(x, ptr, n);
125 }
126 
127 DEBUG_STRCON(STR_hex, "0123456789abcdef");
128 
129 static int
heximal(unsigned long x,char * ptr,int n)130 heximal(unsigned long x, char *ptr, int n)
131 {
132     if (16<=x) {
133         n = heximal(x>>4, ptr, n);
134         x &= 0xf;
135     }
136     ptr[n] = STR_hex()[x];
137     return 1+ n;
138 }
139 
140 
141 #define DPRINTF(a) my_printf a
142 
143 static int
my_printf(char const * fmt,...)144 my_printf(char const *fmt, ...)
145 {
146     char c;
147     int n= 0;
148     char *ptr;
149     char buf[20];
150     va_list va; va_start(va, fmt);
151     ptr= &buf[0];
152     while (0!=(c= *fmt++)) if ('%'!=c) goto literal;
153     else switch (c= *fmt++) {
154     default: {
155 literal:
156         n+= write(2, fmt-1, 1);
157     } break;
158     case 0: goto done;  /* early */
159     case 'u': {
160         n+= write(2, buf, unsimal(va_arg(va, unsigned), buf, 0));
161     } break;
162     case 'd': {
163         n+= write(2, buf, decimal(va_arg(va, int), buf, 0));
164     } break;
165     case 'p': {
166         buf[0] = '0';
167         buf[1] = 'x';
168         n+= write(2, buf, heximal((unsigned long)va_arg(va, void *), buf, 2));
169     } break;
170     case 'x': {
171         buf[0] = '0';
172         buf[1] = 'x';
173         n+= write(2, buf, heximal(va_arg(va, int), buf, 2));
174     } break;
175     }
176 done:
177     va_end(va);
178     return n;
179 }
180 #endif  /*}*/
181 
182 
183 /*************************************************************************
184 // "file" util
185 **************************************************************************/
186 
187 typedef struct {
188     size_t size;  // must be first to match size[0] uncompressed size
189     Addr buf;
190 } Extent;
191 
192 DEBUG_STRCON(STR_xread, "xread %%p(%%x %%p) %%p %%x\\n")
193 DEBUG_STRCON(STR_xreadfail, "xreadfail %%p(%%x %%p) %%p %%x\\n")
194 
195 static void
xread(Extent * x,Addr buf,size_t count)196 xread(Extent *x, Addr buf, size_t count)
197 {
198     Addr p=x->buf, q=buf;
199     size_t j;
200     DPRINTF((STR_xread(), x, x->size, x->buf, buf, count));
201     if (x->size < count) {
202         DPRINTF((STR_xreadfail(), x, x->size, x->buf, buf, count));
203         exit(127);
204     }
205     for (j = count; 0!=j--; ++p, ++q) {
206         *q = *p;
207     }
208     x->buf  += count;
209     x->size -= count;
210 }
211 
212 
213 /*************************************************************************
214 // util
215 **************************************************************************/
216 
217 #if 1  //{  save space
218 #define ERR_LAB error: exit(127);
219 #define err_exit(a) goto error
220 #else  //}{  save debugging time
221 #define ERR_LAB /*empty*/
222 DEBUG_STRCON(STR_exit, "err_exit %%x\\n");
223 
224 static void
err_exit(int a)225 err_exit(int a)
226 {
227     DPRINTF((STR_exit(), a));
228     (void)a;  // debugging convenience
229     exit(127);
230 }
231 #endif  //}
232 
233 
234 /*************************************************************************
235 // UPX & NRV stuff
236 **************************************************************************/
237 
238 struct l_info { // 12-byte trailer for loader (after macho headers)
239     unsigned l_checksum;
240     unsigned l_magic;  // UPX_MAGIC_LE32
241     unsigned short l_lsize;
242     unsigned char l_version;
243     unsigned char l_format;
244 };
245 struct p_info { // 12-byte packed program header
246     unsigned p_progid;
247     unsigned p_filesize;
248     unsigned p_blocksize;
249 };
250 
251 struct b_info { // 12-byte header before each compressed block
252     unsigned sz_unc;  // uncompressed_size
253     unsigned sz_cpr;  //   compressed_size
254     unsigned char b_method;  // compression algorithm
255     unsigned char b_ftid;  // filter id
256     unsigned char b_cto8;  // filter parameter
257     unsigned char b_unused;
258 };
259 
260 typedef void f_unfilter(
261     nrv_byte *,  // also addvalue
262     nrv_uint,
263     unsigned cto8, // junk in high 24 bits
264     unsigned ftid
265 );
266 typedef int f_expand(
267     const nrv_byte *, nrv_uint,
268           nrv_byte *, nrv_uint *, unsigned );
269 
270 DEBUG_STRCON(STR_unpackExtent,
271         "unpackExtent in=%%p(%%x %%p)  out=%%p(%%x %%p)  %%p %%p\\n");
272 DEBUG_STRCON(STR_err5, "sz_cpr=%%x  sz_unc=%%x  xo->size=%%x\\n");
273 
274 static void
unpackExtent(Extent * const xi,Extent * const xo,f_expand * const f_decompress,f_unfilter * f_unf)275 unpackExtent(
276     Extent *const xi,  // input
277     Extent *const xo,  // output
278     f_expand *const f_decompress,
279     f_unfilter *f_unf
280 )
281 {
282     DPRINTF((STR_unpackExtent(),
283         xi, xi->size, xi->buf, xo, xo->size, xo->buf, f_decompress, f_unf));
284     while (xo->size) {
285         struct b_info h;
286         //   Note: if h.sz_unc == h.sz_cpr then the block was not
287         //   compressible and is stored in its uncompressed form.
288 
289         // Read and check block sizes.
290         xread(xi, (Addr)&h, sizeof(h));
291         if (h.sz_unc == 0) {                     // uncompressed size 0 -> EOF
292             if (h.sz_cpr != UPX_MAGIC_LE32)      // h.sz_cpr must be h->magic
293                 err_exit(2);
294             if (xi->size != 0)                 // all bytes must be written
295                 err_exit(3);
296             break;
297         }
298         if (h.sz_cpr <= 0) {
299             err_exit(4);
300 ERR_LAB
301         }
302         if (h.sz_cpr > h.sz_unc
303         ||  h.sz_unc > xo->size ) {
304             DPRINTF((STR_err5(), h.sz_cpr, h.sz_unc, xo->size));
305             err_exit(5);
306         }
307         // Now we have:
308         //   assert(h.sz_cpr <= h.sz_unc);
309         //   assert(h.sz_unc > 0 && h.sz_unc <= blocksize);
310         //   assert(h.sz_cpr > 0 && h.sz_cpr <= blocksize);
311 
312         if (h.sz_cpr < h.sz_unc) { // Decompress block
313             nrv_uint out_len = h.sz_unc;  // EOF for lzma
314             int const j = (*f_decompress)(xi->buf, h.sz_cpr,
315                 xo->buf, &out_len, h.b_method);
316             if (j != 0 || out_len != (nrv_uint)h.sz_unc)
317                 err_exit(7);
318             if (h.b_ftid!=0 && f_unf) {  // have filter
319                 (*f_unf)(xo->buf, out_len, h.b_cto8, h.b_ftid);
320             }
321             xi->buf  += h.sz_cpr;
322             xi->size -= h.sz_cpr;
323         }
324         else { // copy literal block
325             xread(xi, xo->buf, h.sz_cpr);
326         }
327         xo->buf  += h.sz_unc;
328         xo->size -= h.sz_unc;
329     }
330 }
331 
332 static void
upx_bzero(Addr p,size_t len)333 upx_bzero(Addr p, size_t len)
334 {
335     if (len) do {
336         *p++= 0;
337     } while (--len);
338 }
339 #define bzero upx_bzero
340 
341 
342 // The PF_* and PROT_* bits are {1,2,4}; the conversion table fits in 32 bits.
343 #define REP8(x) \
344     ((x)|((x)<<4)|((x)<<8)|((x)<<12)|((x)<<16)|((x)<<20)|((x)<<24)|((x)<<28))
345 #define EXP8(y) \
346     ((1&(y)) ? 0xf0f0f0f0 : (2&(y)) ? 0xff00ff00 : (4&(y)) ? 0xffff0000 : 0)
347 #define PF_TO_PROT(pf) \
348     ((PROT_READ|PROT_WRITE|PROT_EXEC) & ( \
349         ( (REP8(PROT_EXEC ) & EXP8(PF_X)) \
350          |(REP8(PROT_READ ) & EXP8(PF_R)) \
351          |(REP8(PROT_WRITE) & EXP8(PF_W)) \
352         ) >> ((pf & (PF_R|PF_W|PF_X))<<2) ))
353 
354 typedef struct {
355     unsigned magic;
356     unsigned nfat_arch;
357 } Fat_header;
358 typedef struct {
359     unsigned cputype;
360     unsigned cpusubtype;
361     unsigned offset;
362     unsigned size;
363     unsigned align;  /* shift count (log base 2) */
364 } Fat_arch;
365     enum e8 {
366         FAT_MAGIC = 0xcafebabe,
367         FAT_CIGAM = 0xbebafeca
368     };
369     enum e9 {
370         CPU_TYPE_I386      =          7,
371         CPU_TYPE_AMD64     = 0x01000007,
372         CPU_TYPE_ARM       =         12,
373         CPU_TYPE_POWERPC   = 0x00000012,
374         CPU_TYPE_POWERPC64 = 0x01000012
375     };
376 
377 typedef struct {
378     unsigned magic;
379     unsigned cputype;
380     unsigned cpysubtype;
381     unsigned filetype;
382     unsigned ncmds;
383     unsigned sizeofcmds;
384     unsigned flags;
385     unsigned reserved;
386 } Mach_header64;
387         enum e0 {
388             MH_MAGIC   =   0xfeedface,
389             MH_MAGIC64 = 1+0xfeedface
390         };
391         enum e2 {
392             MH_EXECUTE = 2
393         };
394         enum e3 {
395             MH_NOUNDEFS = 1
396         };
397 
398 typedef struct {
399     unsigned cmd;
400     unsigned cmdsize;
401 } Mach_load_command;
402         enum e4 {
403             LC_REQ_DYLD      = 0x80000000,  // OR'ed ==> must not ignore
404             LC_SEGMENT       = 0x1,
405             LC_SEGMENT_64    = 0x19,
406             LC_THREAD        = 0x4,
407             LC_UNIXTHREAD    = 0x5,
408             LC_LOAD_DYLINKER = 0xe,
409             LC_MAIN          = (0x28|LC_REQ_DYLD)
410         };
411 
412 typedef struct {
413     unsigned cmd;
414     unsigned cmdsize;
415     char segname[16];
416     uint64_t vmaddr;
417     uint64_t vmsize;
418     uint64_t fileoff;
419     uint64_t filesize;
420     unsigned maxprot;
421     unsigned initprot;
422     unsigned nsects;
423     unsigned flags;
424 } Mach_segment_command;
425         enum e5 {
426             VM_PROT_READ = 1,
427             VM_PROT_WRITE = 2,
428             VM_PROT_EXECUTE = 4
429         };
430 
431 typedef struct {
432     char sectname[16];
433     char segname[16];
434     uint64_t addr;   /* memory address */
435     uint64_t size;   /* size in bytes */
436     unsigned offset; /* file offset */
437     unsigned align;  /* power of 2 */
438     unsigned reloff; /* file offset of relocation entries */
439     unsigned nreloc; /* number of relocation entries */
440     unsigned flags;  /* section type and attributes */
441     unsigned reserved1;  /* for offset or index */
442     unsigned reserved2;  /* for count or sizeof */
443 } Mach_section_command;
444 
445 typedef struct {
446     uint32_t cmd;  // LC_MAIN;  MH_EXECUTE only
447     uint32_t cmdsize;  // 24
448     uint64_t entryoff;  // file offset of main() [expected in __TEXT]
449     uint64_t stacksize;  // non-default initial stack size
450 } Mach_main_command;
451 
452 typedef struct {
453     uint64_t rax, rbx, rcx, rdx;
454     uint64_t rdi, rsi, rbp, rsp;
455     uint64_t  r8,  r9, r10, r11;
456     uint64_t r12, r13, r14, r15;
457     uint64_t rip, rflags;
458     uint64_t cs, fs, gs;
459 } Mach_AMD64_thread_state;
460 
461 typedef struct {
462     unsigned cmd;            /* LC_THREAD or  LC_UNIXTHREAD */
463     unsigned cmdsize;        /* total size of this command */
464     unsigned flavor;
465     unsigned count;          /* sizeof(following_thread_state)/4 */
466     Mach_AMD64_thread_state state;
467 } Mach_thread_command;
468         enum e6 {
469             AMD64_THREAD_STATE = 4  // x86_THREAD_STATE64
470         };
471         enum e7 {
472             AMD64_THREAD_STATE_COUNT = sizeof(Mach_AMD64_thread_state)/4
473         };
474 
475 typedef union {
476     unsigned offset;  /* from start of load command to string */
477 } Mach_lc_str;
478 
479 #define MAP_FIXED     0x10
480 #define MAP_PRIVATE   0x02
481 #define MAP_ANON    0x1000
482 //#define MAP_ANON  0x20  // x86 DEBUG ONLY
483 #define PROT_READ      1
484 #define PROT_WRITE     2
485 #define PROT_EXEC      4
486 #define MAP_ANON_FD    -1
487 #define MAP_FAILED ((void *) -1)
488 
489 extern void *mmap(void *, size_t, unsigned, unsigned, int, off_t_upx_stub);
490 ssize_t pread(int, void *, size_t, off_t_upx_stub);
491 extern void bswap(void *, unsigned);
492 
493 DEBUG_STRCON(STR_mmap,
494     "mmap  addr=%%p  len=%%p  prot=%%x  flags=%%x  fd=%%d  off=%%p\\n");
495 DEBUG_STRCON(STR_do_xmap,
496     "do_xmap  fdi=%%x  mhdr=%%p  xi=%%p(%%x %%p) f_unf=%%p\\n")
497 
498 static uint64_t  // entry address
do_xmap(Mach_header64 const * const mhdr,off_t_upx_stub const fat_offset,Extent * const xi,int const fdi,Mach_header64 ** mhdrpp,f_expand * const f_decompress,f_unfilter * const f_unf)499 do_xmap(
500     Mach_header64 const *const mhdr,
501     off_t_upx_stub const fat_offset,
502     Extent *const xi,
503     int const fdi,
504     Mach_header64 **mhdrpp,
505     f_expand *const f_decompress,
506     f_unfilter *const f_unf
507 )
508 {
509     Mach_segment_command const *sc = (Mach_segment_command const *)(1+ mhdr);
510     Mach_segment_command const *segTEXT = 0;
511     uint64_t entry = 0;
512     unsigned long base = 0;
513     unsigned j;
514 
515     DPRINTF((STR_do_xmap(),
516         fdi, mhdr, xi, (xi? xi->size: 0), (xi? xi->buf: 0), f_unf));
517 
518     for ( j=0; j < mhdr->ncmds; ++j,
519         (sc = (Mach_segment_command const *)(sc->cmdsize + (unsigned char const *)sc))
520     ) if (LC_SEGMENT_64==sc->cmd && sc->vmsize==0) {
521             // Typical __DWARF info segment for 'rust'
522             struct b_info h;
523             xread(xi, (unsigned char *)&h, sizeof(h));
524             DPRINTF("    0==.vmsize; skipping %%x\\n", h.sz_cpr);
525             xi->buf += h.sz_cpr;
526     else if (LC_SEGMENT_64==sc->cmd && sc->vmsize!=0) {
527         Extent xo;
528         size_t mlen = xo.size = sc->filesize;
529         Addr  addr = xo.buf  = base + (Addr)sc->vmaddr;
530         Addr haddr =     sc->vmsize +             addr;
531         size_t frag = (int)(uint64_t)addr &~ PAGE_MASK;
532         addr -= frag;
533         mlen += frag;
534 
535         if (0!=mlen) { // In particular, omitted for __PAGEZERO
536             // Decompressor can overrun the destination by 3 bytes.  [x86 only]
537             size_t const mlen3 = mlen + (xi ? 3 : 0);
538             unsigned const prot = VM_PROT_READ | VM_PROT_WRITE;
539             unsigned const flags = (addr ? MAP_FIXED : 0) | MAP_PRIVATE |
540                         ((xi || 0==sc->filesize) ? MAP_ANON : 0);
541             int const fdm = ((0==sc->filesize) ? MAP_ANON_FD : fdi);
542             off_t_upx_stub const offset = sc->fileoff + fat_offset;
543 
544             DPRINTF((STR_mmap(),       addr, mlen3, prot, flags, fdm, offset));
545             Addr mapa = (Addr)mmap(addr, mlen3, prot, flags, fdm, offset);
546             if (MAP_FAILED == mapa) {
547                 err_exit(8);
548             }
549             if (0 == addr) { // dyld auto-relocate
550                 base = (unsigned long)mapa;  // relocation constant
551             }
552             addr = mapa;
553         }
554         if (xi && 0!=sc->filesize) {
555             if (0==sc->fileoff /*&& 0!=mhdrpp*/) {
556                 segTEXT = sc;
557                 *mhdrpp = (Mach_header64 *)(void *)addr;
558             }
559             unpackExtent(xi, &xo, f_decompress, f_unf);
560         }
561         /*bzero(addr, frag);*/  // fragment at lo end
562         frag = (-mlen) &~ PAGE_MASK;  // distance to next page boundary
563         bzero(mlen+addr, frag);  // fragment at hi end
564         if (0!=mlen && 0!=mprotect(addr, mlen, sc->initprot)) {
565             err_exit(10);
566 ERR_LAB
567         }
568         addr += mlen + frag;  /* page boundary on hi end */
569         if (
570 #if SIMULATE_ON_LINUX_EABI4  /*{*/
571             0!=addr &&
572 #endif  /*}*/
573                         addr < haddr) { // need pages for .bss
574             if (0!=addr && addr != mmap(addr, haddr - addr, sc->initprot,
575                     MAP_FIXED | MAP_PRIVATE | MAP_ANON, MAP_ANON_FD, 0 ) ) {
576                 err_exit(9);
577             }
578         }
579         else if (xi) { // cleanup if decompressor overrun crosses page boundary
580             mlen = ~PAGE_MASK & (3+ mlen);
581             if (mlen<=3) { // page fragment was overrun buffer only
582                 munmap((char *)addr, mlen);
583             }
584         }
585     }
586     else if (LC_UNIXTHREAD==sc->cmd || LC_THREAD==sc->cmd) {
587         Mach_thread_command const *const thrc = (Mach_thread_command const *)sc;
588         if (AMD64_THREAD_STATE      ==thrc->flavor
589         &&  AMD64_THREAD_STATE_COUNT==thrc->count ) {
590             entry = thrc->state.rip + base;  // JMP
591         }
592     }
593     else if (LC_MAIN==sc->cmd) {
594         entry = ((Mach_main_command const *)sc)->entryoff;
595         if (segTEXT->fileoff <= entry && entry < segTEXT->filesize) {
596             entry += segTEXT->vmaddr;  // CALL
597         }
598         // XXX FIXME TODO: if entry not in segTEXT?
599         // XXX FIXME TODO: LC_MAIN is a CALL; LC_*THREAD is a JMP
600     }
601     return entry;
602 }
603 
604 static off_t_upx_stub
605 fat_find(Fat_header *fh) // *fh suffers bswap()
606 {
607     Fat_arch *fa = (Fat_arch *)(1+ fh);
608     bswap(fh, sizeof(*fh) + (fh->nfat_arch>>24)*sizeof(*fa));
609     unsigned j;
610     for (j= 0; j < fh->nfat_arch; ++j, ++fa) {
611         if (CPU_TYPE_AMD64==fa->cputype) {
612             return fa->offset;  // should not be 0 because of header
613         }
614     }
615     return 0;
616 }
617 
618 /*************************************************************************
619 // upx_main - called by our entry code
620 //
621 **************************************************************************/
622 
623 DEBUG_STRCON(STR_upx_main,
624     "upx_main szc=%%x  f_dec=%%p  f_unf=%%p  "
625     "  xo=%%p(%%x %%p)  xi=%%p(%%x %%p)  mhdrpp=%%p\\n")
626 
627 uint64_t // entry address
628 upx_main(
629     struct l_info const *const li,
630     size_t volatile sz_compressed,  // total length
631     Mach_header64 *const mhdr,  // temp char[sz_mhdr] for decompressing
632     size_t const sz_mhdr,
633     f_expand *const f_decompress,
634     f_unfilter *const f_unf,
635     Mach_header64 **const mhdrpp  // Out: *mhdrpp= &real Mach_header64
636 )
637 {
638     uint64_t entry;
639     off_t_upx_stub fat_offset = 0;
640     Extent xi, xo, xi0;
641     xi.buf  = CONST_CAST(Addr, 1+ (struct p_info const *)(1+ li));  // &b_info
642     xi.size = sz_compressed - (sizeof(struct l_info) + sizeof(struct p_info));
643     xo.buf  = (Addr)mhdr;
644     xo.size = ((struct b_info const *)(void const *)xi.buf)->sz_unc;
645     xi0 = xi;
646 
647     DPRINTF((STR_upx_main(),
648         sz_compressed, f_decompress, f_unf, &xo, xo.size, xo.buf,
649         &xi, xi.size, xi.buf, mhdrpp));
650 
651     // Uncompress Macho headers
652     unpackExtent(&xi, &xo, f_decompress, 0);  // never filtered?
653 
654     entry = do_xmap(mhdr, fat_offset, &xi0, MAP_ANON_FD, mhdrpp, f_decompress, f_unf);
655 
656   { // Map dyld dynamic loader
657     Mach_load_command const *lc = (Mach_load_command const *)(1+ mhdr);
658     unsigned j;
659 
660     for (j=0; j < mhdr->ncmds; ++j,
661         (lc = (Mach_load_command const *)(lc->cmdsize + (unsigned char const *)lc))
662     ) if (LC_LOAD_DYLINKER==lc->cmd) {
663         char const *const dyld_name = ((Mach_lc_str const *)(1+ lc))->offset +
664             (char const *)lc;
665         int const fdi = open(dyld_name, O_RDONLY, 0);
666         if (0 > fdi) {
667             err_exit(18);
668         }
669         for (;;) { // possibly 2 times for 'fat' binary
670             if (sz_mhdr!=pread(fdi, (void *)mhdr, sz_mhdr, fat_offset)) {
671 ERR_LAB
672                 err_exit(19);
673             }
674             switch (mhdr->magic) {
675             case MH_MAGIC: break;  // i686 on x86_64 ?
676             case MH_MAGIC64: break;
677 
678             case FAT_CIGAM:
679             case FAT_MAGIC: {
680                 // stupid Apple: waste code and a page fault on EVERY execve
681                 fat_offset = fat_find((Fat_header *)mhdr);
682                 if (fat_offset) {
683                     continue;  // the 'for' loop
684                 }
685                 err_exit(20);  // no other choice
686             } break;
687             } // switch
688             break;
689         }
690         entry = do_xmap(mhdr, fat_offset, 0, fdi, 0, 0, 0);
691         close(fdi);
692         break;
693     }
694   }
695 
696     return entry;
697 }
698 
699 /* vim:set ts=4 sw=4 et: */
700