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