123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- /* ===-- 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 <TargetConditionals.h>
- #include <dispatch/dispatch.h>
- #include <dlfcn.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- /* These three variables hold the host's OS version. */
- static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;
- static dispatch_once_t DispatchOnceCounter;
- // _availability_version_check darwin API support.
- typedef uint32_t dyld_platform_t;
- typedef struct {
- dyld_platform_t platform;
- uint32_t version;
- } dyld_build_version_t;
- typedef bool (*AvailabilityVersionCheckFuncTy)(uint32_t count,
- dyld_build_version_t versions[]);
- static AvailabilityVersionCheckFuncTy AvailabilityVersionCheck;
- /* We can't include <CoreFoundation/CoreFoundation.h> directly from here, so
- * just forward declare everything that we need from it. */
- typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef,
- *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef;
- #if __LLP64__
- typedef unsigned long long CFTypeID;
- typedef unsigned long long CFOptionFlags;
- typedef signed long long CFIndex;
- #else
- typedef unsigned long CFTypeID;
- typedef unsigned long CFOptionFlags;
- typedef signed long CFIndex;
- #endif
- typedef unsigned char UInt8;
- typedef _Bool Boolean;
- typedef CFIndex CFPropertyListFormat;
- typedef uint32_t CFStringEncoding;
- /* kCFStringEncodingASCII analog. */
- #define CF_STRING_ENCODING_ASCII 0x0600
- /* kCFStringEncodingUTF8 analog. */
- #define CF_STRING_ENCODING_UTF8 0x08000100
- #define CF_PROPERTY_LIST_IMMUTABLE 0
- 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 initializeAvailabilityCheck(void *Unused) {
- (void)Unused;
- // Use the new API if it's is available. Still load the PLIST to ensure that the
- // existing calls to __isOSVersionAtLeast still work even with new
- // compiler-rt and new OSes.
- AvailabilityVersionCheck = (AvailabilityVersionCheckFuncTy)dlsym(
- RTLD_DEFAULT, "_availability_version_check");
- /* Load CoreFoundation dynamically */
- const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");
- if (!NullAllocator)
- return;
- const CFAllocatorRef AllocatorNull = *(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, AllocatorNull);
- if (!FileContentsRef)
- goto Fail;
- if (CFPropertyListCreateWithDataFunc)
- PListRef = (*CFPropertyListCreateWithDataFunc)(
- NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL);
- else
- PListRef = (*CFPropertyListCreateFromXMLDataFunc)(
- NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL);
- if (!PListRef)
- goto Fail;
- CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(
- NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull);
- 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), CF_STRING_ENCODING_UTF8))
- goto Fail;
- sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);
- Fail:
- if (PListRef)
- (*CFReleaseFunc)(PListRef);
- if (FileContentsRef)
- (*CFReleaseFunc)(FileContentsRef);
- free(PListBuf);
- fclose(PropertyList);
- }
- // This old API entry point is no longer used by Clang. We still need to keep it
- // around to ensure that object files that reference it are still usable when
- // linked with new compiler-rt.
- 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, initializeAvailabilityCheck);
- if (Major < GlobalMajor)
- return 1;
- if (Major > GlobalMajor)
- return 0;
- if (Minor < GlobalMinor)
- return 1;
- if (Minor > GlobalMinor)
- return 0;
- return Subminor <= GlobalSubminor;
- }
- static inline uint32_t ConstructVersion(uint32_t Major, uint32_t Minor,
- uint32_t Subminor) {
- return ((Major & 0xffff) << 16) | ((Minor & 0xff) << 8) | (Subminor & 0xff);
- }
- int32_t __isPlatformVersionAtLeast(uint32_t Platform, uint32_t Major,
- uint32_t Minor, uint32_t Subminor) {
- dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck);
- if (!AvailabilityVersionCheck) {
- return __isOSVersionAtLeast(Major, Minor, Subminor);
- }
- dyld_build_version_t Versions[] = {
- {Platform, ConstructVersion(Major, Minor, Subminor)}};
- return AvailabilityVersionCheck(1, Versions);
- }
- #else
- /* Silence an empty translation unit warning. */
- typedef int unused;
- #endif
|