1/*
2 *  kernelcache.c
3 *  kext_tools
4 *
5 *  Created by Nik Gervae on 2010 10 04.
6 *  Copyright 2010, 2012 Apple Computer, Inc. All rights reserved.
7 *
8 */
9
10#include "kernelcache.h"
11#include "compression.h"
12
13#if EMBEDDED_HOST
14size_t lzvn_encode(void *       dst,
15                   size_t       dst_size,
16                   const void * src,
17                   size_t       src_size,
18                   void *       work);
19size_t lzvn_decode(void *       dst,
20                   size_t       dst_size,
21                   const void * src,
22                   size_t       src_size);
23size_t lzvn_encode_work_size(void);
24#else
25#include <FastCompression.h>
26#endif
27
28#include <mach-o/arch.h>
29#include <mach-o/fat.h>
30#include <mach-o/swap.h>
31#include <sys/mman.h>
32
33#include <IOKit/kext/OSKext.h>
34#include <IOKit/kext/OSKextPrivate.h>
35
36/*******************************************************************************
37*******************************************************************************/
38ExitStatus
39writeFatFile(
40    const char                * filePath,
41    CFArrayRef                  fileSlices,
42    CFArrayRef                  fileArchs,
43    mode_t                      fileMode,
44    const struct timeval        fileTimes[2])
45{
46    ExitStatus        result               = EX_SOFTWARE;
47    char              tmpPath[PATH_MAX];
48    struct fat_header fatHeader;
49    struct fat_arch   fatArch;
50    CFDataRef         sliceData            = NULL;    // do not release
51    const uint8_t   * sliceDataPtr         = NULL;    // do not free
52    const char *      tmpPathPtr           = tmpPath; // must unlink
53    const NXArchInfo * targetArch          = NULL;    // do not free
54    mode_t             procMode            = 0;
55    uint32_t          fatOffset            = 0;
56    uint32_t          sliceLength          = 0;
57    int               fileDescriptor       = -1;      // must close
58    int                numArchs            = 0;
59    int               i                    = 0;
60
61    /* Make the temporary file */
62
63    strlcpy(tmpPath, filePath, sizeof(tmpPath));
64    if (strlcat(tmpPath, ".XXXX", sizeof(tmpPath)) >= sizeof(tmpPath)) {
65        OSKextLogStringError(/* kext */ NULL);
66        goto finish;
67    }
68
69    fileDescriptor = mkstemp(tmpPath);
70    if (-1 == fileDescriptor) {
71        OSKextLog(/* kext */ NULL,
72                kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
73                "Can't create %s - %s.",
74                tmpPath, strerror(errno));
75        goto finish;
76    }
77
78    /* Set the file's permissions */
79
80    /* Set the umask to get it, then set it back to iself. Wish there were a
81     * better way to query it.
82     */
83    procMode = umask(0);
84    umask(procMode);
85
86    if (-1 == fchmod(fileDescriptor, fileMode & ~procMode)) {
87        OSKextLog(/* kext */ NULL,
88                kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
89                "Can't set permissions on %s - %s.",
90                tmpPathPtr, strerror(errno));
91    }
92
93    /* Write out the fat headers even if there's only one arch so we know what
94     * arch a compressed prelinked kernel belongs to.
95     */
96
97    numArchs = (int)CFArrayGetCount(fileArchs);
98    fatHeader.magic = OSSwapHostToBigInt32(FAT_MAGIC);
99    fatHeader.nfat_arch = OSSwapHostToBigInt32(numArchs);
100
101    result = writeToFile(fileDescriptor, (const UInt8 *)&fatHeader,
102        sizeof(fatHeader));
103    if (result != EX_OK) {
104        goto finish;
105    }
106
107    fatOffset = sizeof(struct fat_header) +
108        (sizeof(struct fat_arch) * numArchs);
109
110    for (i = 0; i < numArchs; i++) {
111        targetArch = CFArrayGetValueAtIndex(fileArchs, i);
112        sliceData = CFArrayGetValueAtIndex(fileSlices, i);
113        sliceLength = (uint32_t)CFDataGetLength(sliceData);
114
115        fatArch.cputype = OSSwapHostToBigInt32(targetArch->cputype);
116        fatArch.cpusubtype = OSSwapHostToBigInt32(targetArch->cpusubtype);
117        fatArch.offset = OSSwapHostToBigInt32(fatOffset);
118        fatArch.size = OSSwapHostToBigInt32(sliceLength);
119        fatArch.align = OSSwapHostToBigInt32(0);
120
121        result = writeToFile(fileDescriptor,
122            (UInt8 *)&fatArch, sizeof(fatArch));
123        if (result != EX_OK) {
124            goto finish;
125        }
126
127        fatOffset += sliceLength;
128    }
129
130    /* Write out the file slices */
131
132    for (i = 0; i < numArchs; i++) {
133        sliceData = CFArrayGetValueAtIndex(fileSlices, i);
134        sliceDataPtr = CFDataGetBytePtr(sliceData);
135        sliceLength = (uint32_t)CFDataGetLength(sliceData);
136
137        result = writeToFile(fileDescriptor, sliceDataPtr, sliceLength);
138        if (result != EX_OK) {
139            goto finish;
140        }
141    }
142
143    OSKextLog(/* kext */ NULL,
144        kOSKextLogDebugLevel | kOSKextLogFileAccessFlag,
145        "Renaming temp file to %s.",
146        filePath);
147
148    /* Move the file to its final path */
149
150    if (rename(tmpPathPtr, filePath) != 0) {
151        OSKextLog(/* kext */ NULL,
152                kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
153                "Can't rename temporary file %s to %s - %s.",
154                tmpPathPtr, filePath, strerror(errno));
155        result = EX_OSERR;
156        goto finish;
157    }
158    tmpPathPtr = NULL;
159
160    /* Update the file's mod time if necessary */
161
162    if (utimes(filePath, fileTimes)) {
163        OSKextLog(/* kext */ NULL,
164            kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
165            "Can't update mod time of %s - %s.", filePath, strerror(errno));
166    }
167
168finish:
169
170    if (fileDescriptor >= 0) (void)close(fileDescriptor);
171    if (tmpPathPtr) unlink(tmpPathPtr);
172
173    return result;
174}
175
176/*******************************************************************************
177*******************************************************************************/
178void *
179mapAndSwapFatHeaderPage(
180    int fileDescriptor)
181{
182    void              * result          = NULL;
183    void              * headerPage      = NULL;  // must unmapFatHeaderPage()
184    struct fat_header * fatHeader       = NULL;  // do not free
185    struct fat_arch   * fatArch         = NULL;  // do not free
186
187    /* Map the first page to read the fat headers. */
188
189    headerPage = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
190        MAP_FILE | MAP_PRIVATE, fileDescriptor, 0);
191    if (MAP_FAILED == headerPage) {
192        OSKextLog(/* kext */ NULL,
193                kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
194                "Failed to map file header page.");
195        goto finish;
196    }
197
198    /* Make sure that the fat header, if any, is swapped to the host's byte
199     * order.
200     */
201
202    fatHeader = (struct fat_header *) headerPage;
203    fatArch = (struct fat_arch *) (&fatHeader[1]);
204
205    if (fatHeader->magic == FAT_CIGAM) {
206        swap_fat_header(fatHeader, NXHostByteOrder());
207        swap_fat_arch(fatArch, fatHeader->nfat_arch, NXHostByteOrder());
208    }
209
210    result = headerPage;
211    headerPage = NULL;
212
213finish:
214    if (headerPage) unmapFatHeaderPage(headerPage);
215
216    return result;
217}
218
219/*******************************************************************************
220*******************************************************************************/
221void
222unmapFatHeaderPage(
223    void *headerPage)
224{
225    munmap(headerPage, PAGE_SIZE);
226}
227
228/*******************************************************************************
229*******************************************************************************/
230struct fat_arch *
231getFirstFatArch(
232    u_char *headerPage)
233{
234    struct fat_header * fatHeader       = NULL;
235    struct fat_arch   * fatArch         = NULL;
236
237    fatHeader = (struct fat_header *) headerPage;
238    if (fatHeader->magic != FAT_MAGIC || !fatHeader->nfat_arch) {
239            goto finish;
240        }
241
242    fatArch = (struct fat_arch *) (&fatHeader[1]);
243
244finish:
245    return fatArch;
246}
247
248/*******************************************************************************
249*******************************************************************************/
250struct fat_arch *
251getNextFatArch(
252    u_char *headerPage,
253    struct fat_arch *prevArch)
254{
255    struct fat_header * fatHeader       = NULL;
256    struct fat_arch   * firstArch       = NULL;
257    struct fat_arch   * nextArch        = NULL;
258    unsigned int numArchs;
259
260    fatHeader = (struct fat_header *) headerPage;
261    if (fatHeader->magic != FAT_MAGIC) {
262        goto finish;
263    }
264
265    firstArch = (struct fat_arch *) (&fatHeader[1]);
266    nextArch = &prevArch[1];
267    numArchs = (unsigned int)(nextArch - firstArch);
268
269    if (numArchs >= fatHeader->nfat_arch) {
270        nextArch = NULL;
271        goto finish;
272    }
273
274finish:
275    return nextArch;
276}
277
278/*******************************************************************************
279*******************************************************************************/
280struct fat_arch *
281getFatArchForArchInfo(
282    u_char *headerPage,
283    const NXArchInfo *archInfo)
284{
285    struct fat_header * fatHeader       = NULL;
286    struct fat_arch   * fatArch         = NULL;
287
288    fatHeader = (struct fat_header *)headerPage;
289
290    if (fatHeader->magic != FAT_MAGIC) {
291        goto finish;
292    }
293
294    fatArch = (struct fat_arch *)(&fatHeader[1]);
295    fatArch = NXFindBestFatArch(archInfo->cputype, archInfo->cpusubtype,
296        fatArch, fatHeader->nfat_arch);
297
298finish:
299    return fatArch;
300}
301
302/*******************************************************************************
303*******************************************************************************/
304const NXArchInfo *
305getThinHeaderPageArch(
306    const void *headerPage)
307{
308    const NXArchInfo          * result          = NULL;
309    struct mach_header        * machHdr         = NULL;
310    struct mach_header_64     * machHdr64       = NULL;
311    Boolean                     is32Bit         = true;
312
313    machHdr = (struct mach_header *) headerPage;
314    machHdr64 = (struct mach_header_64 *) headerPage;
315
316    switch (machHdr->magic) {
317    case MH_MAGIC:
318        break;
319    case MH_MAGIC_64:
320        is32Bit = false;
321        break;
322    case MH_CIGAM:
323        swap_mach_header(machHdr, NXHostByteOrder());
324        break;
325    case MH_CIGAM_64:
326        swap_mach_header_64(machHdr64, NXHostByteOrder());
327        is32Bit = false;
328        break;
329    default:
330        goto finish;
331    }
332
333    if (is32Bit) {
334        machHdr64 = NULL;
335        result = NXGetArchInfoFromCpuType(machHdr->cputype,
336            machHdr->cpusubtype);
337    } else {
338        machHdr = NULL;
339        result = NXGetArchInfoFromCpuType(machHdr64->cputype,
340            machHdr64->cpusubtype);
341    }
342
343finish:
344    return result;
345}
346
347/*******************************************************************************
348*******************************************************************************/
349ExitStatus
350readFatFileArchsWithPath(
351    const char        * filePath,
352    CFMutableArrayRef * archsOut)
353{
354    ExitStatus          result          = EX_SOFTWARE;
355    void              * headerPage      = NULL;         // must unmapFatHeaderPage()
356    int                 fileDescriptor  = 0;            // must close()
357
358    /* Open the file. */
359
360    fileDescriptor = open(filePath, O_RDONLY);
361    if (fileDescriptor < 0) {
362        goto finish;
363    }
364
365    /* Map the fat headers in */
366
367    headerPage = mapAndSwapFatHeaderPage(fileDescriptor);
368    if (!headerPage) {
369        goto finish;
370    }
371
372    result = readFatFileArchsWithHeader(headerPage, archsOut);
373finish:
374    if (fileDescriptor >= 0) close(fileDescriptor);
375    if (headerPage) unmapFatHeaderPage(headerPage);
376
377    return result;
378}
379
380/*******************************************************************************
381*******************************************************************************/
382Boolean archInfoEqualityCallback(const void *v1, const void *v2)
383{
384    const NXArchInfo *a1 = (const NXArchInfo *)v1;
385    const NXArchInfo *a2 = (const NXArchInfo *)v2;
386
387    return ((a1->cputype == a2->cputype) && (a1->cpusubtype == a2->cpusubtype));
388}
389
390/*******************************************************************************
391*******************************************************************************/
392ExitStatus
393readFatFileArchsWithHeader(
394    u_char            * headerPage,
395    CFMutableArrayRef * archsOut)
396{
397    ExitStatus          result          = EX_SOFTWARE;
398    CFMutableArrayRef   fileArchs       = NULL;         // must release
399    struct fat_arch   * fatArch         = NULL;         // do not free
400    const NXArchInfo  * archInfo        = NULL;         // do not free
401    CFArrayCallBacks    callbacks       = { 0, NULL, NULL, NULL, archInfoEqualityCallback };
402
403    /* Create an array to hold the fat archs */
404
405    if (!createCFMutableArray(&fileArchs, &callbacks))
406    {
407        OSKextLogMemError();
408        result = EX_OSERR;
409        goto finish;
410    }
411
412    /* Read the archs */
413
414    fatArch = getFirstFatArch(headerPage);
415    if (fatArch) {
416        while (fatArch) {
417            archInfo = NXGetArchInfoFromCpuType(fatArch->cputype,
418                fatArch->cpusubtype);
419            CFArrayAppendValue(fileArchs, archInfo);
420
421            fatArch = getNextFatArch(headerPage, fatArch);
422        }
423    } else {
424        archInfo = getThinHeaderPageArch(headerPage);
425        if (archInfo) {
426            CFArrayAppendValue(fileArchs, archInfo);
427        } else {
428            // We can't determine the arch information, so don't return any
429            archsOut = NULL;
430        }
431    }
432
433    if (archsOut) *archsOut = (CFMutableArrayRef) CFRetain(fileArchs);
434    result = EX_OK;
435
436finish:
437    SAFE_RELEASE(fileArchs);
438
439    return result;
440}
441
442/*******************************************************************************
443*******************************************************************************/
444ExitStatus
445readMachOSlices(
446    const char        * filePath,
447    CFMutableArrayRef * slicesOut,
448    CFMutableArrayRef * archsOut,
449    mode_t            * modeOut,
450    struct timeval      machOTimesOut[2])
451{
452    struct stat         statBuf;
453    ExitStatus          result          = EX_SOFTWARE;
454    CFMutableArrayRef   fileSlices      = NULL;         // release
455    CFMutableArrayRef   fileArchs       = NULL;         // release
456    CFDataRef           sliceData       = NULL;         // release
457    u_char            * fileBuf         = NULL;         // must free
458    void              * headerPage      = NULL;         // must unmapFatHeaderPage()
459    struct fat_arch   * fatArch         = NULL;         // do not free
460    int                 fileDescriptor  = 0;            // must close()
461
462    /* Create an array to hold the fat slices */
463
464    if (!createCFMutableArray(&fileSlices, &kCFTypeArrayCallBacks))
465    {
466        OSKextLogMemError();
467        result = EX_OSERR;
468        goto finish;
469    }
470
471    /* Open the file. */
472
473    fileDescriptor = open(filePath, O_RDONLY);
474    if (fileDescriptor < 0) {
475        goto finish;
476    }
477
478    if (fstat(fileDescriptor, &statBuf)) {
479        goto finish;
480    }
481
482    /* Map the fat headers in */
483
484    headerPage = mapAndSwapFatHeaderPage(fileDescriptor);
485    if (!headerPage) {
486        goto finish;
487    }
488
489    /* If the file is fat, read the slices into separate objects.  If not,
490     * read the whole file into one large slice.
491     */
492
493    fatArch = getFirstFatArch(headerPage);
494    if (fatArch) {
495        while (fatArch) {
496            sliceData = readMachOSlice(fileDescriptor,
497                fatArch->offset, fatArch->size);
498            if (!sliceData) goto finish;
499
500            CFArrayAppendValue(fileSlices, sliceData);
501            fatArch = getNextFatArch(headerPage, fatArch);
502            CFRelease(sliceData); // drop ref from readMachOSlice()
503            sliceData = NULL;
504        }
505    } else {
506        sliceData = readMachOSlice(fileDescriptor, 0, (size_t)statBuf.st_size);
507        if (!sliceData) goto finish;
508
509        CFArrayAppendValue(fileSlices, sliceData);
510    }
511
512    if (archsOut) {
513        result = readFatFileArchsWithHeader(headerPage, &fileArchs);
514        if (result != EX_OK) {
515            goto finish;
516        }
517
518        if (!fileArchs) archsOut = NULL;
519    }
520
521    result = EX_OK;
522    if (slicesOut) *slicesOut = (CFMutableArrayRef) CFRetain(fileSlices);
523    if (archsOut) *archsOut = (CFMutableArrayRef) CFRetain(fileArchs);
524    if (modeOut) *modeOut = statBuf.st_mode;
525    if (machOTimesOut) {
526        TIMESPEC_TO_TIMEVAL(&machOTimesOut[0], &statBuf.st_atimespec);
527        TIMESPEC_TO_TIMEVAL(&machOTimesOut[1], &statBuf.st_mtimespec);
528    }
529
530finish:
531    SAFE_RELEASE(fileSlices);
532    SAFE_RELEASE(fileArchs);
533    SAFE_RELEASE(sliceData);
534    SAFE_FREE(fileBuf);
535    if (fileDescriptor >= 0) close(fileDescriptor);
536    if (headerPage) unmapFatHeaderPage(headerPage);
537
538    return result;
539}
540
541/*******************************************************************************
542*******************************************************************************/
543CFDataRef
544readMachOSliceForArch(
545    const char        * filePath,
546    const NXArchInfo  * archInfo,
547    Boolean             checkArch)
548{
549    CFDataRef           result          = NULL; // must release
550    CFDataRef           fileData        = NULL; // must release
551    void              * headerPage      = NULL; // must unmapFatHeaderPage()
552    struct fat_header * fatHeader       = NULL; // do not free
553    struct fat_arch   * fatArch         = NULL; // do not free
554    int                 fileDescriptor  = 0;    // must close()
555    off_t               fileSliceOffset = 0;
556    size_t              fileSliceSize   = 0;
557    struct stat statBuf;
558
559    /* Open the file */
560
561    fileDescriptor = open(filePath, O_RDONLY);
562    if (fileDescriptor < 0) {
563        goto finish;
564    }
565
566    /* Map the fat headers in */
567
568    headerPage = mapAndSwapFatHeaderPage(fileDescriptor);
569    if (!headerPage) {
570        goto finish;
571    }
572
573    /* Find the slice for the target architecture */
574
575    fatHeader = (struct fat_header *)headerPage;
576
577    if (archInfo && fatHeader->magic == FAT_MAGIC) {
578        fatArch = NXFindBestFatArch(archInfo->cputype, archInfo->cpusubtype,
579            (struct fat_arch *)(&fatHeader[1]), fatHeader->nfat_arch);
580        if (!fatArch) {
581            OSKextLog(/* kext */ NULL,
582                    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
583                    "Fat file does not contain requested architecture %s.",
584                    archInfo->name);
585            goto finish;
586        }
587
588        fileSliceOffset = fatArch->offset;
589        fileSliceSize = fatArch->size;
590    } else {
591        if (fstat(fileDescriptor, &statBuf)) {
592            goto finish;
593        }
594
595        fileSliceOffset = 0;
596        fileSliceSize = (size_t)statBuf.st_size;
597    }
598
599    /* Read the file */
600
601    fileData = readMachOSlice(fileDescriptor, fileSliceOffset,
602        fileSliceSize);
603    if (!fileData) {
604        goto finish;
605    }
606
607    /* Verify that the file is of the right architecture */
608
609    if (checkArch) {
610        if (verifyMachOIsArch(CFDataGetBytePtr(fileData),
611                fileSliceSize, archInfo))
612        {
613            goto finish;
614        }
615    }
616
617    result = CFRetain(fileData);
618
619finish:
620    SAFE_RELEASE(fileData);
621    if (fileDescriptor >= 0) close(fileDescriptor);
622    if (headerPage) unmapFatHeaderPage(headerPage);
623
624    return result;
625}
626
627/*******************************************************************************
628*******************************************************************************/
629CFDataRef
630readMachOSlice(
631    int         fileDescriptor,
632    off_t       fileOffset,
633    size_t      fileSliceSize)
634{
635    CFDataRef   fileData        = NULL;  // do not release
636    u_char    * fileBuf         = NULL;  // must free
637    off_t       seekedBytes     = 0;
638    ssize_t     readBytes       = 0;
639    size_t      totalReadBytes  = 0;
640
641    /* Allocate a buffer for the file */
642
643    fileBuf = malloc(fileSliceSize);
644    if (!fileBuf) {
645        OSKextLogMemError();
646        goto finish;
647    }
648
649    /* Seek to the specified file offset */
650
651    seekedBytes = lseek(fileDescriptor, fileOffset, SEEK_SET);
652    if (seekedBytes != fileOffset) {
653        OSKextLog(/* kext */ NULL,
654                kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
655                "Failed to seek in file.");  // xxx - which file is that?
656        goto finish;
657    }
658
659    /* Read the file's bytes into the buffer */
660
661    while (totalReadBytes < fileSliceSize) {
662        readBytes = read(fileDescriptor, fileBuf + totalReadBytes,
663                fileSliceSize - totalReadBytes);
664        if (readBytes < 0) {
665            OSKextLog(/* kext */ NULL,
666                    kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
667                    "Failed to read file.");
668            goto finish;
669        }
670
671        totalReadBytes += (size_t) readBytes;
672    }
673
674    /* Wrap the file slice in a CFData object */
675
676    fileData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
677            (UInt8 *) fileBuf, fileSliceSize, kCFAllocatorMalloc);
678    if (!fileData) {
679        goto finish;
680    }
681    fileBuf = NULL;
682
683finish:
684    SAFE_FREE(fileBuf);
685
686    return fileData;
687}
688
689/*******************************************************************************
690*******************************************************************************/
691int
692verifyMachOIsArch(
693    const UInt8      * fileBuf,
694    size_t             size,
695    const NXArchInfo * archInfo)
696{
697    int result = -1;
698    cpu_type_t cputype = 0;
699    struct mach_header *checkHeader = (struct mach_header *)fileBuf;
700
701    if (!archInfo) {
702        result = 0;
703        goto finish;
704    }
705
706    /* Get the cputype from the mach header */
707    if (size < sizeof(uint32_t)) {
708        goto finish;
709    }
710
711    if (checkHeader->magic == MH_MAGIC_64 || checkHeader->magic == MH_CIGAM_64) {
712        struct mach_header_64 *machHeader =
713            (struct mach_header_64 *) fileBuf;
714
715        if (size < sizeof(*machHeader)) {
716            goto finish;
717        }
718
719        cputype = machHeader->cputype;
720        if (checkHeader->magic == MH_CIGAM_64) cputype = OSSwapInt32(cputype);
721    } else if (checkHeader->magic == MH_MAGIC || checkHeader->magic == MH_CIGAM) {
722        struct mach_header *machHeader =
723            (struct mach_header *) fileBuf;
724
725        if (size < sizeof(*machHeader)) {
726            goto finish;
727        }
728
729        cputype = machHeader->cputype;
730        if (checkHeader->magic == MH_CIGAM) cputype = OSSwapInt32(cputype);
731    }
732
733    /* Make sure the file's cputype matches the host's.
734     */
735    if (result == 0 && cputype != archInfo->cputype) {
736        OSKextLog(/* kext */ NULL,
737                kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
738                "File is not of expected architecture %s.", archInfo->name);
739        goto finish;
740    }
741
742    result = 0;
743finish:
744    return result;
745}
746
747/*********************************************************************
748 *********************************************************************/
749CFDataRef
750uncompressPrelinkedSlice(
751    CFDataRef      prelinkImage)
752{
753    CFDataRef                     result              = NULL;
754    CFMutableDataRef              uncompressedImage   = NULL;  // must release
755    const PrelinkedKernelHeader * prelinkHeader       = NULL;  // do not free
756    unsigned char               * buf                 = NULL;  // do not free
757    vm_size_t                     bufsize             = 0;
758    vm_size_t                     uncompsize          = 0;
759    uint32_t                      adler32             = 0;
760
761    prelinkHeader = (PrelinkedKernelHeader *) CFDataGetBytePtr(prelinkImage);
762
763    /* Verify the header information.
764     */
765    if (prelinkHeader->signature != OSSwapHostToBigInt32('comp')) {
766        OSKextLog(/* kext */ NULL,
767                kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
768                "Compressed prelinked kernel has invalid signature: 0x%x.",
769                prelinkHeader->signature);
770        goto finish;
771    }
772
773    if ( !(prelinkHeader->compressType == OSSwapHostToBigInt32(COMP_TYPE_LZSS) ||
774           prelinkHeader->compressType == OSSwapHostToBigInt32(COMP_TYPE_FASTLIB)) ) {
775        OSKextLog(/* kext */ NULL,
776                  kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
777                  "Compressed prelinked kernel has invalid compressType: 0x%x.",
778                  prelinkHeader->compressType);
779        goto finish;
780    }
781
782    /* Create a buffer to hold the uncompressed kernel.
783     */
784    bufsize = OSSwapBigToHostInt32(prelinkHeader->uncompressedSize);
785    uncompressedImage = CFDataCreateMutable(kCFAllocatorDefault, bufsize);
786    if (!uncompressedImage) {
787        goto finish;
788    }
789
790    /* We have to call CFDataSetLength explicitly to get CFData to allocate
791     * its internal buffer.
792     */
793    CFDataSetLength(uncompressedImage, bufsize);
794    buf = CFDataGetMutableBytePtr(uncompressedImage);
795    if (!buf) {
796        OSKextLogMemError();
797        goto finish;
798    }
799
800    /* Uncompress the kernel.
801     */
802    if (prelinkHeader->compressType == OSSwapHostToBigInt32(COMP_TYPE_LZSS)) {
803        uncompsize = decompress_lzss(buf, (u_int32_t)bufsize,
804                                     ((u_int8_t *)(CFDataGetBytePtr(prelinkImage))) + sizeof(*prelinkHeader),
805                                     (u_int32_t)(CFDataGetLength(prelinkImage) - sizeof(*prelinkHeader)));
806    }
807    else if (prelinkHeader->compressType == OSSwapHostToBigInt32(COMP_TYPE_FASTLIB)) {
808        uncompsize = lzvn_decode(buf,
809                                 bufsize,
810                                 ((u_int8_t *)(CFDataGetBytePtr(prelinkImage))) + sizeof(*prelinkHeader),
811                                 (CFDataGetLength(prelinkImage) - sizeof(*prelinkHeader)));
812    }
813    else {
814        goto finish;
815    }
816
817    if (uncompsize != bufsize) {
818        OSKextLog(/* kext */ NULL,
819                kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
820                "Compressed prelinked kernel uncompressed to an unexpected size: %u.",
821                (unsigned)uncompsize);
822        goto finish;
823    }
824
825    /* Verify the adler32.
826     */
827    adler32 = local_adler32((u_int8_t *) buf, (int)bufsize);
828    if (prelinkHeader->adler32 != OSSwapHostToBigInt32(adler32)) {
829        OSKextLog(/* kext */ NULL,
830                kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
831                "Checksum error for compressed prelinked kernel.");
832        goto finish;
833    }
834    result = CFRetain(uncompressedImage);
835
836finish:
837    SAFE_RELEASE(uncompressedImage);
838    return result;
839}
840
841/*********************************************************************
842 *********************************************************************/
843CFDataRef
844compressPrelinkedSlice(
845                       uint32_t            compressionType,
846                       CFDataRef           prelinkImage,
847                       Boolean             hasRelocs)
848{
849    CFDataRef               result          = NULL;
850    CFMutableDataRef        compressedImage = NULL;  // must release
851    PrelinkedKernelHeader * kernelHeader    = NULL;  // do not free
852    const PrelinkedKernelHeader * kernelHeaderIn = NULL; // do not free
853    unsigned char         * buf             = NULL;  // do not free
854    unsigned char         * bufend          = NULL;  // do not free
855    u_long                  offset          = 0;
856    vm_size_t               bufsize         = 0;
857    vm_size_t               compsize        = 0;
858    uint32_t                adler32         = 0;
859
860    /* Check that the kernel is not already compressed */
861
862    kernelHeaderIn = (const PrelinkedKernelHeader *)
863        CFDataGetBytePtr(prelinkImage);
864    if (kernelHeaderIn->signature == OSSwapHostToBigInt('comp')) {
865        OSKextLog(/* kext */ NULL,
866                kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
867                "Prelinked kernel is already compressed.");
868        goto finish;
869    }
870
871    /* Create a buffer to hold the compressed kernel */
872
873    offset = sizeof(*kernelHeader);
874    bufsize = CFDataGetLength(prelinkImage) + offset;
875    compressedImage = CFDataCreateMutable(kCFAllocatorDefault, bufsize);
876    if (!compressedImage) {
877        goto finish;
878    }
879
880    /* We have to call CFDataSetLength explicitly to get CFData to allocate
881     * its internal buffer.
882     */
883    CFDataSetLength(compressedImage, bufsize);
884    buf = CFDataGetMutableBytePtr(compressedImage);
885    if (!buf) {
886        OSKextLogMemError();
887        goto finish;
888    }
889
890    kernelHeader = (PrelinkedKernelHeader *) buf;
891    bzero(kernelHeader, sizeof(*kernelHeader));
892    kernelHeader->prelinkVersion = OSSwapHostToBigInt32(hasRelocs ? 1 : 0);
893
894    /* Fill in the compression information */
895
896    kernelHeader->signature = OSSwapHostToBigInt32('comp');
897    adler32 = local_adler32((u_int8_t *)CFDataGetBytePtr(prelinkImage),
898            (int)CFDataGetLength(prelinkImage));
899    kernelHeader->adler32 = OSSwapHostToBigInt32(adler32);
900    kernelHeader->uncompressedSize =
901        OSSwapHostToBigInt32(CFDataGetLength(prelinkImage));
902
903    /* Compress the kernel */
904    if (compressionType == COMP_TYPE_FASTLIB) {
905        size_t outSize = 0;
906        void * work_space = malloc(lzvn_encode_work_size());
907
908        if (work_space != NULL) {
909            kernelHeader->compressType = OSSwapHostToBigInt32(COMP_TYPE_FASTLIB);
910            outSize = lzvn_encode(buf + offset,
911            bufsize,
912            (u_int8_t *)CFDataGetBytePtr(prelinkImage),
913            CFDataGetLength(prelinkImage),
914            work_space);
915            free(work_space);
916            if (outSize != 0) {
917                bufend = buf + offset + outSize;
918            }
919        }
920    }
921    else if (compressionType == COMP_TYPE_LZSS) {
922        kernelHeader->compressType = OSSwapHostToBigInt32(COMP_TYPE_LZSS);
923        bufend = compress_lzss(buf + offset, (u_int32_t)bufsize,
924                               (u_int8_t *)CFDataGetBytePtr(prelinkImage),
925                               (u_int32_t)CFDataGetLength(prelinkImage));
926    }
927    else {
928        OSKextLog(/* kext */ NULL,
929                  kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
930                  "Unrecognized compression algorithm.");
931        goto finish;
932    }
933
934    if (!bufend) {
935        OSKextLog(/* kext */ NULL,
936                kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
937                "Failed to compress prelinked kernel.");
938        goto finish;
939    }
940
941    compsize = bufend - (buf + offset);
942    kernelHeader->compressedSize = OSSwapHostToBigInt32(compsize);
943    CFDataSetLength(compressedImage, bufend - buf);
944
945    result = CFRetain(compressedImage);
946
947finish:
948    SAFE_RELEASE(compressedImage);
949    return result;
950}
951
952/*******************************************************************************
953*******************************************************************************/
954ExitStatus
955writePrelinkedSymbols(
956    CFURLRef    symbolDirURL,
957    CFArrayRef  prelinkSymbols,
958    CFArrayRef  prelinkArchs)
959{
960    SaveFileContext saveFileContext;
961    ExitStatus          result          = EX_SOFTWARE;
962    CFDictionaryRef     sliceSymbols    = NULL; // do not release
963    CFURLRef            saveDirURL      = NULL; // must release
964    const NXArchInfo  * archInfo        = NULL; // do not free
965    CFIndex             numArchs        = 0;
966    CFIndex             i               = 0;
967
968    saveFileContext.overwrite = true;
969    saveFileContext.fatal = false;
970    numArchs = CFArrayGetCount(prelinkArchs);
971
972    for (i = 0; i < numArchs; ++i) {
973        archInfo = CFArrayGetValueAtIndex(prelinkArchs, i);
974        sliceSymbols = CFArrayGetValueAtIndex(prelinkSymbols, i);
975
976        SAFE_RELEASE_NULL(saveDirURL);
977
978        /* We don't need arch-specific directories if there's only one arch */
979        if (numArchs == 1) {
980            saveDirURL = CFRetain(symbolDirURL);
981        } else {
982            saveDirURL = CFURLCreateFromFileSystemRepresentationRelativeToBase(
983                kCFAllocatorDefault, (const UInt8 *) archInfo->name,
984                strlen(archInfo->name), /* isDirectory */ true, symbolDirURL);
985            if (!saveDirURL) {
986                goto finish;
987            }
988        }
989
990        result = makeDirectoryWithURL(saveDirURL);
991        if (result != EX_OK) {
992            goto finish;
993        }
994
995        saveFileContext.saveDirURL = saveDirURL;
996
997        CFDictionaryApplyFunction(sliceSymbols, &saveFile,
998            &saveFileContext);
999        if (saveFileContext.fatal) {
1000            goto finish;
1001        }
1002    }
1003    result = EX_OK;
1004
1005finish:
1006    SAFE_RELEASE(saveDirURL);
1007
1008    return result;
1009}
1010
1011/*******************************************************************************
1012*******************************************************************************/
1013ExitStatus
1014makeDirectoryWithURL(
1015    CFURLRef dirURL)
1016{
1017    char dirPath[MAXPATHLEN];
1018    struct stat statBuf;
1019    ExitStatus result = EX_SOFTWARE;
1020
1021    if (!CFURLHasDirectoryPath(dirURL)) {
1022        goto finish;
1023    }
1024
1025    if (!CFURLGetFileSystemRepresentation(dirURL, /* resolveToBase */ true,
1026            (UInt8 *)dirPath, sizeof(dirPath)))
1027    {
1028        goto finish;
1029    }
1030
1031    result = stat(dirPath, &statBuf);
1032
1033    /* If the directory exists, return success */
1034    if (result == 0 && statBuf.st_mode & S_IFDIR) {
1035        result = EX_OK;
1036        goto finish;
1037    }
1038
1039    /* Die if the stat failed for any reason other than an invalid path */
1040    if (result == 0 || errno != ENOENT) {
1041        goto finish;
1042    }
1043
1044    result = mkdir(dirPath, 0755);
1045    if (result != 0) {
1046        goto finish;
1047    }
1048
1049    result = EX_OK;
1050
1051finish:
1052    return result;
1053}
1054
1055#if __i386__ || EMBEDDED_HOST // no lzvn for embedded host tools yet
1056
1057Boolean supportsFastLibCompression(void)
1058{
1059    return(false);
1060}
1061
1062size_t lzvn_encode(void *       dst,
1063                   size_t       dst_size,
1064                   const void * src,
1065                   size_t       src_size,
1066                   void *       work)
1067{
1068#pragma unused(dst)
1069#pragma unused(dst_size)
1070#pragma unused(src)
1071#pragma unused(src_size)
1072#pragma unused(work)
1073    return 0;
1074}
1075
1076size_t lzvn_decode(void *       dst,
1077                   size_t       dst_size,
1078                   const void * src,
1079                   size_t       src_size)
1080{
1081#pragma unused(dst)
1082#pragma unused(dst_size)
1083#pragma unused(src)
1084#pragma unused(src_size)
1085    return 0;
1086}
1087
1088size_t lzvn_encode_work_size(void)
1089{
1090    return 0;
1091}
1092
1093#else
1094
1095Boolean supportsFastLibCompression(void)
1096{
1097    return(true);
1098}
1099
1100#endif // __i386__ || EMBEDDED_HOST
1101