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