1/* 2 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23#include <fcntl.h> 24#include <stdio.h> 25#include <string.h> 26#include <stdarg.h> 27#include <stdlib.h> 28#include <sysexits.h> 29#include <unistd.h> 30 31#include <sys/mman.h> 32#include <sys/param.h> 33#include <sys/stat.h> 34#include <sys/types.h> 35 36#include <mach/mach.h> 37#include <mach/mach_error.h> 38 39#include <mach-o/arch.h> 40 41#include <System/libkern/mkext.h> 42#include <CoreFoundation/CFBundlePriv.h> 43#include <IOKit/kext/OSKextPrivate.h> 44 45#include "kext_tools_util.h" 46#include "compression.h" 47 48 49/******************************************************************************* 50*******************************************************************************/ 51typedef struct { 52 CFMutableDataRef mkext; 53 uint32_t kextIndex; 54 uint32_t compressOffset; 55 const NXArchInfo * arch; 56 Boolean fatal; 57 Boolean compress; 58} Mkext1Context; 59 60void addToMkext1( 61 const void * vKey, 62 const void * vValue, 63 void * vContext); 64 65Boolean addDataToMkext( 66 CFDataRef data, 67 Mkext1Context * context, 68 char * kextPath, 69 Boolean isInfoDict); 70 71/******************************************************************************* 72*******************************************************************************/ 73CFDataRef createMkext1ForArch(const NXArchInfo * arch, CFArrayRef archiveKexts, 74 boolean_t compress) 75{ 76 CFMutableDataRef result = NULL; 77 CFMutableDictionaryRef kextsByIdentifier = NULL; 78 Mkext1Context context; 79 mkext1_header * mkextHeader = NULL; // do not free 80 const uint8_t * adler_point = 0; 81 CFIndex count, i; 82 83 result = CFDataCreateMutable(kCFAllocatorDefault, /* capaacity */ 0); 84 if (!result || !createCFMutableDictionary(&kextsByIdentifier)) { 85 OSKextLogMemError(); 86 goto finish; 87 } 88 89 /* mkext1 can only contain 1 kext for a given bundle identifier, so we 90 * have to pick out the most recent versions. 91 */ 92 count = CFArrayGetCount(archiveKexts); 93 for (i = 0; i < count; i++) { 94 OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex(archiveKexts, i); 95 CFStringRef bundleIdentifier = OSKextGetIdentifier(theKext); 96 OSKextRef savedKext = (OSKextRef)CFDictionaryGetValue(kextsByIdentifier, 97 bundleIdentifier); 98 OSKextVersion thisVersion, savedVersion; 99 100 101 if (!OSKextSupportsArchitecture(theKext, arch)) { 102 continue; 103 } 104 105 if (!savedKext) { 106 CFDictionarySetValue(kextsByIdentifier, bundleIdentifier, theKext); 107 continue; 108 } 109 110 thisVersion = OSKextGetVersion(theKext); 111 savedVersion = OSKextGetVersion(savedKext); 112 113 if (thisVersion > savedVersion) { 114 CFDictionarySetValue(kextsByIdentifier, bundleIdentifier, theKext); 115 } 116 } 117 118 /* Add room for the mkext header and kext descriptors. 119 */ 120 CFDataSetLength(result, sizeof(mkext1_header) + 121 CFDictionaryGetCount(kextsByIdentifier) * sizeof(mkext_kext)); 122 123 context.mkext = result; 124 context.kextIndex = 0; 125 context.compressOffset = (uint32_t)CFDataGetLength(result); 126 context.arch = arch; 127 context.fatal = false; 128 context.compress = compress; 129 CFDictionaryApplyFunction(kextsByIdentifier, addToMkext1, &context); 130 if (context.fatal) { 131 SAFE_RELEASE_NULL(result); 132 goto finish; 133 } 134 135 mkextHeader = (mkext1_header *)CFDataGetBytePtr(result); 136 mkextHeader->magic = OSSwapHostToBigInt32(MKEXT_MAGIC); 137 mkextHeader->signature = OSSwapHostToBigInt32(MKEXT_SIGN); 138 mkextHeader->version = OSSwapHostToBigInt32(0x01008000); // 'vers' 1.0.0 139 mkextHeader->numkexts = 140 OSSwapHostToBigInt32(CFDictionaryGetCount(kextsByIdentifier)); 141 mkextHeader->cputype = OSSwapHostToBigInt32(arch->cputype); 142 mkextHeader->cpusubtype = OSSwapHostToBigInt32(arch->cpusubtype); 143 mkextHeader->length = OSSwapHostToBigInt32(CFDataGetLength(result)); 144 145 adler_point = (UInt8 *)&mkextHeader->version; 146 mkextHeader->adler32 = OSSwapHostToBigInt32(local_adler32( 147 (UInt8 *)&mkextHeader->version, 148 (int)(CFDataGetLength(result) - (adler_point - (uint8_t *)mkextHeader)))); 149 150 OSKextLog(/* kext */ NULL, kOSKextLogProgressLevel | kOSKextLogArchiveFlag, 151 "Created mkext for %s containing %lu kexts.", 152 arch->name, 153 CFDictionaryGetCount(kextsByIdentifier)); 154 155finish: 156 SAFE_RELEASE(kextsByIdentifier); 157 return result; 158} 159 160/******************************************************************************* 161*******************************************************************************/ 162void addToMkext1( 163 const void * vKey __unused, 164 const void * vValue, 165 void * vContext) 166{ 167 OSKextRef aKext = (OSKextRef)vValue; 168 Mkext1Context * context = (Mkext1Context *)vContext; 169 170 CFBundleRef kextBundle = NULL; // must release 171 CFURLRef infoDictURL = NULL; // must release 172 CFDataRef rawInfoDict = NULL; // must release 173 CFDataRef executable = NULL; // must release 174 char kextPath[PATH_MAX]; 175 176 if (context->fatal) { 177 goto finish; 178 } 179 180 if (!CFURLGetFileSystemRepresentation(OSKextGetURL(aKext), 181 /* resolveToBase */ false, (UInt8 *)kextPath, sizeof(kextPath))) { 182 183 strlcpy(kextPath, "(unknown)", sizeof(kextPath)); 184 } 185 186 OSKextLog(aKext, 187 kOSKextLogProgressLevel | kOSKextLogArchiveFlag, 188 "Adding %s to mkext.", kextPath); 189 190 OSKextLog(aKext, 191 kOSKextLogStepLevel | kOSKextLogArchiveFlag | kOSKextLogFileAccessFlag, 192 "Opening CFBundle for %s.", kextPath); 193 kextBundle = CFBundleCreate(kCFAllocatorDefault, OSKextGetURL(aKext)); 194 if (!kextBundle) { 195 OSKextLog(aKext, 196 kOSKextLogStepLevel | kOSKextLogArchiveFlag | kOSKextLogFileAccessFlag, 197 "Can't open bundle for %s.", kextPath); 198 context->fatal = true; 199 goto finish; 200 } 201 202 infoDictURL = _CFBundleCopyInfoPlistURL(kextBundle); 203 if (!infoDictURL) { 204 OSKextLog(aKext, 205 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 206 "Can't get URL for info dict of %s.", kextPath); 207 context->fatal = true; 208 goto finish; 209 } 210 211 /* create and fill infoDictPath 212 */ 213 char infoDictPath[PATH_MAX]; 214 215 if (!CFURLGetFileSystemRepresentation(infoDictURL, 216 true, 217 (uint8_t *)infoDictPath, 218 sizeof(infoDictPath))) { 219 OSKextLogStringError(/* kext */ NULL); 220 context->fatal = true; 221 goto finish; 222 } 223 224 if (!createCFDataFromFile(&rawInfoDict, 225 infoDictPath)) { 226 OSKextLog(/* kext */ NULL, 227 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 228 "%s: Can't read info dictoionary file '%s'", 229 __func__, infoDictPath); 230 context->fatal = true; 231 goto finish; 232 } 233 234 if (!addDataToMkext(rawInfoDict, context, kextPath, /* isInfoDict */ true)) { 235 context->fatal = true; 236 goto finish; 237 } 238 239 executable = OSKextCopyExecutableForArchitecture(aKext, context->arch); 240 if (executable) { 241 if (!addDataToMkext(executable, context, kextPath, /* isInfoDict */ false)) { 242 context->fatal = true; 243 goto finish; 244 } 245 } else if (OSKextDeclaresExecutable(aKext)) { 246 OSKextLog(aKext, 247 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 248 "Can't get executable for %s (architecture %s).", kextPath, 249 context->arch->name); 250 context->fatal = true; 251 goto finish; 252 } 253 254 context->kextIndex++; 255 256finish: 257 if (kextBundle) { 258 OSKextLog(aKext, 259 kOSKextLogStepLevel | kOSKextLogArchiveFlag | kOSKextLogFileAccessFlag, 260 "Releaseing CFBundle for %s.", kextPath); 261 SAFE_RELEASE(kextBundle); 262 } 263 SAFE_RELEASE(infoDictURL); 264 SAFE_RELEASE(rawInfoDict); 265 SAFE_RELEASE(executable); 266 return; 267} 268 269/******************************************************************************* 270*******************************************************************************/ 271Boolean addDataToMkext( 272 CFDataRef data, 273 Mkext1Context * context, 274 char * kextPath, 275 Boolean isInfoDict) 276{ 277 Boolean result = false; 278 CFIndex newMkextLength; 279 uint32_t origCompressOffset; 280 const UInt8 * mkextStart = NULL; // must calc after changing length! 281 UInt8 * addedDataStart = NULL; // must calc after changing length! 282 UInt8 * addedDataEnd = NULL; // do not free 283 uint32_t addedDataFullLength; 284 uint32_t compressedLength; 285 286 uint8_t * checkBuffer = NULL; // must free 287 288 mkext1_header * mkextHeader = NULL; // do not free 289 mkext_kext * mkextKextEntry = NULL; // do not free 290 mkext_file * mkextFileEntry = NULL; // do not free 291 292 /* Add enough to the mkext buffer to append the whole uncompressed file. 293 * If the file can't be compressed, we'll just copy it in; if it can 294 * be compressed, we'll set the mkext buffer length to fit exactly. 295 */ 296 addedDataFullLength = (uint32_t)CFDataGetLength(data); 297 newMkextLength = CFDataGetLength(context->mkext) + addedDataFullLength; 298 CFDataSetLength(context->mkext, newMkextLength); 299 if (CFDataGetLength(context->mkext) != newMkextLength) { 300 OSKextLog(/* kext */ NULL, 301 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 302 "Can't resize mkext buffer."); 303 goto finish; 304 } 305 mkextStart = CFDataGetBytePtr(context->mkext); 306 307 origCompressOffset = context->compressOffset; 308 addedDataStart = (UInt8 *)mkextStart + origCompressOffset; 309 310 if (context->compress) { 311 addedDataEnd = compress_lzss((uint8_t *)addedDataStart, addedDataFullLength, 312 (uint8_t *)CFDataGetBytePtr(data), addedDataFullLength); 313 if (!addedDataEnd) { 314 OSKextLog(/* kext */ NULL, 315 kOSKextLogWarningLevel | kOSKextLogArchiveFlag, 316 "%s did not compress; copying file (%d bytes).", 317 isInfoDict ? "info dictionary" : "executable", 318 addedDataFullLength); 319 } 320 } 321 322 if (!addedDataEnd) { 323 memcpy(addedDataStart, (const void *)CFDataGetBytePtr(data), 324 addedDataFullLength); 325 addedDataEnd = addedDataStart + addedDataFullLength; 326 compressedLength = 0; 327 context->compressOffset += addedDataFullLength; 328 } else { 329 size_t checkLength; 330 331 compressedLength = (uint32_t)(addedDataEnd - addedDataStart); 332 context->compressOffset += compressedLength; 333 334 checkBuffer = (uint8_t *)malloc(addedDataFullLength); 335 if (!checkBuffer) { 336 OSKextLogMemError(); 337 goto finish; 338 } 339 340 checkLength = decompress_lzss(checkBuffer, addedDataFullLength, 341 addedDataStart, compressedLength); 342 if (checkLength != addedDataFullLength) { 343 OSKextLog(/* kext */ NULL, 344 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 345 "%s - %s decompressed size %d differs from original size %d", 346 kextPath, 347 isInfoDict ? "info dictionary" : "executable", 348 (int)checkLength, (int)addedDataFullLength); 349 goto finish; 350 } 351 if (0 != memcmp(checkBuffer, CFDataGetBytePtr(data), checkLength)) { 352 OSKextLog(/* kext */ NULL, 353 kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 354 "%s - %s decompressed data differs from input", 355 kextPath, 356 isInfoDict ? "info dictionary" : "executable"); 357 goto finish; 358 } 359 360 OSKextLog(/* kext */ NULL, 361 kOSKextLogDetailLevel | kOSKextLogArchiveFlag, 362 "Compressed %s from %u to %u bytes (%.2f%%).", 363 isInfoDict ? "info dict" : "executable", 364 addedDataFullLength, compressedLength, 365 (100.0 * (float)compressedLength/(float)addedDataFullLength)); 366 } 367 368 /* Truncate the mkext to exactly fit the new total size. 369 */ 370 CFDataSetLength(context->mkext, addedDataEnd - mkextStart); 371 372 mkextHeader = (mkext1_header *)CFDataGetBytePtr(context->mkext); 373 mkextKextEntry = &(mkextHeader->kext[context->kextIndex]); 374 if (isInfoDict) { 375 mkextFileEntry = &(mkextKextEntry->plist); 376 } else { 377 mkextFileEntry = &(mkextKextEntry->module); 378 } 379 mkextFileEntry->offset = OSSwapHostToBigInt32(origCompressOffset); 380 mkextFileEntry->realsize = OSSwapHostToBigInt32(addedDataFullLength); 381 mkextFileEntry->compsize = OSSwapHostToBigInt32(compressedLength); 382 mkextFileEntry->modifiedsecs = 0; // we never use this anyway 383 384 result = true; 385 386finish: 387 SAFE_FREE(checkBuffer); 388 return result; 389} 390