/* ===-- os_version_check.c - OS version checking -------------------------=== * * The LLVM Compiler Infrastructure * * This file is dual licensed under the MIT and the University of Illinois Open * Source Licenses. See LICENSE.TXT for details. * * ===----------------------------------------------------------------------=== * * This file implements the function __isOSVersionAtLeast, used by * Objective-C's @available * * ===----------------------------------------------------------------------=== */ #ifdef __APPLE__ #include #include #include #include #include #include #include #include /* These three variables hold the host's OS version. */ static int32_t GlobalMajor, GlobalMinor, GlobalSubminor; static dispatch_once_t DispatchOnceCounter; typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef, const UInt8 *, CFIndex, CFAllocatorRef); typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)( CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *, CFErrorRef *); typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)( CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *); typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef, const char *, CFStringEncoding, CFAllocatorRef); typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef, const void *); typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef); typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void); typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex, CFStringEncoding); typedef void (*CFReleaseFuncTy)(CFTypeRef); /* Find and parse the SystemVersion.plist file. */ static void parseSystemVersionPList(void *Unused) { (void)Unused; /* Load CoreFoundation dynamically */ const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull"); if (!NullAllocator) return; const CFAllocatorRef kCFAllocatorNull = *(const CFAllocatorRef *)NullAllocator; CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc = (CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT, "CFDataCreateWithBytesNoCopy"); if (!CFDataCreateWithBytesNoCopyFunc) return; CFPropertyListCreateWithDataFuncTy CFPropertyListCreateWithDataFunc = (CFPropertyListCreateWithDataFuncTy)dlsym( RTLD_DEFAULT, "CFPropertyListCreateWithData"); /* CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it * will be NULL on earlier OS versions. */ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" CFPropertyListCreateFromXMLDataFuncTy CFPropertyListCreateFromXMLDataFunc = (CFPropertyListCreateFromXMLDataFuncTy)dlsym( RTLD_DEFAULT, "CFPropertyListCreateFromXMLData"); #pragma clang diagnostic pop /* CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it * might be NULL in future OS versions. */ if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc) return; CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc = (CFStringCreateWithCStringNoCopyFuncTy)dlsym( RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy"); if (!CFStringCreateWithCStringNoCopyFunc) return; CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc = (CFDictionaryGetValueFuncTy)dlsym(RTLD_DEFAULT, "CFDictionaryGetValue"); if (!CFDictionaryGetValueFunc) return; CFGetTypeIDFuncTy CFGetTypeIDFunc = (CFGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFGetTypeID"); if (!CFGetTypeIDFunc) return; CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc = (CFStringGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetTypeID"); if (!CFStringGetTypeIDFunc) return; CFStringGetCStringFuncTy CFStringGetCStringFunc = (CFStringGetCStringFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetCString"); if (!CFStringGetCStringFunc) return; CFReleaseFuncTy CFReleaseFunc = (CFReleaseFuncTy)dlsym(RTLD_DEFAULT, "CFRelease"); if (!CFReleaseFunc) return; char *PListPath = "/System/Library/CoreServices/SystemVersion.plist"; #if TARGET_OS_SIMULATOR char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT"); if (!PListPathPrefix) return; char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1]; strcpy(FullPath, PListPathPrefix); strcat(FullPath, PListPath); PListPath = FullPath; #endif FILE *PropertyList = fopen(PListPath, "r"); if (!PropertyList) return; /* Dynamically allocated stuff. */ CFDictionaryRef PListRef = NULL; CFDataRef FileContentsRef = NULL; UInt8 *PListBuf = NULL; fseek(PropertyList, 0, SEEK_END); long PListFileSize = ftell(PropertyList); if (PListFileSize < 0) goto Fail; rewind(PropertyList); PListBuf = malloc((size_t)PListFileSize); if (!PListBuf) goto Fail; size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList); if (NumRead != (size_t)PListFileSize) goto Fail; /* Get the file buffer into CF's format. We pass in a null allocator here * * because we free PListBuf ourselves */ FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)( NULL, PListBuf, (CFIndex)NumRead, kCFAllocatorNull); if (!FileContentsRef) goto Fail; if (CFPropertyListCreateWithDataFunc) PListRef = (*CFPropertyListCreateWithDataFunc)( NULL, FileContentsRef, kCFPropertyListImmutable, NULL, NULL); else PListRef = (*CFPropertyListCreateFromXMLDataFunc)( NULL, FileContentsRef, kCFPropertyListImmutable, NULL); if (!PListRef) goto Fail; CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)( NULL, "ProductVersion", kCFStringEncodingASCII, kCFAllocatorNull); if (!ProductVersion) goto Fail; CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion); (*CFReleaseFunc)(ProductVersion); if (!OpaqueValue || (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)()) goto Fail; char VersionStr[32]; if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr, sizeof(VersionStr), kCFStringEncodingUTF8)) goto Fail; sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor); Fail: if (PListRef) (*CFReleaseFunc)(PListRef); if (FileContentsRef) (*CFReleaseFunc)(FileContentsRef); free(PListBuf); fclose(PropertyList); } int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) { /* Populate the global version variables, if they haven't already. */ dispatch_once_f(&DispatchOnceCounter, NULL, parseSystemVersionPList); if (Major < GlobalMajor) return 1; if (Major > GlobalMajor) return 0; if (Minor < GlobalMinor) return 1; if (Minor > GlobalMinor) return 0; return Subminor <= GlobalSubminor; } #else /* Silence an empty translation unit warning. */ typedef int unused; #endif