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