1 //===-- os_version_check.c - OS version checking -------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file implements the function __isOSVersionAtLeast, used by
10 // Objective-C's @available
12 //===----------------------------------------------------------------------===//
16 #include <TargetConditionals.h>
17 #include <dispatch/dispatch.h>
24 // These three variables hold the host's OS version.
25 static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;
26 static dispatch_once_t DispatchOnceCounter;
28 // We can't include <CoreFoundation/CoreFoundation.h> directly from here, so
29 // just forward declare everything that we need from it.
31 typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef,
32 *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef;
35 typedef unsigned long long CFTypeID;
36 typedef unsigned long long CFOptionFlags;
37 typedef signed long long CFIndex;
39 typedef unsigned long CFTypeID;
40 typedef unsigned long CFOptionFlags;
41 typedef signed long CFIndex;
44 typedef unsigned char UInt8;
45 typedef _Bool Boolean;
46 typedef CFIndex CFPropertyListFormat;
47 typedef uint32_t CFStringEncoding;
49 // kCFStringEncodingASCII analog.
50 #define CF_STRING_ENCODING_ASCII 0x0600
51 // kCFStringEncodingUTF8 analog.
52 #define CF_STRING_ENCODING_UTF8 0x08000100
53 #define CF_PROPERTY_LIST_IMMUTABLE 0
55 typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef,
56 const UInt8 *, CFIndex,
58 typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)(
59 CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *,
61 typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)(
62 CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *);
63 typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef,
67 typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef,
69 typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef);
70 typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void);
71 typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex,
73 typedef void (*CFReleaseFuncTy)(CFTypeRef);
75 // Find and parse the SystemVersion.plist file.
76 static void parseSystemVersionPList(void *Unused) {
78 // Load CoreFoundation dynamically
79 const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");
82 const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator;
83 CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc =
84 (CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT,
85 "CFDataCreateWithBytesNoCopy");
86 if (!CFDataCreateWithBytesNoCopyFunc)
88 CFPropertyListCreateWithDataFuncTy CFPropertyListCreateWithDataFunc =
89 (CFPropertyListCreateWithDataFuncTy)dlsym(RTLD_DEFAULT,
90 "CFPropertyListCreateWithData");
91 // CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it
92 // will be NULL on earlier OS versions.
93 #pragma clang diagnostic push
94 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
95 CFPropertyListCreateFromXMLDataFuncTy CFPropertyListCreateFromXMLDataFunc =
96 (CFPropertyListCreateFromXMLDataFuncTy)dlsym(
97 RTLD_DEFAULT, "CFPropertyListCreateFromXMLData");
98 #pragma clang diagnostic pop
99 // CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it
100 // might be NULL in future OS versions.
101 if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc)
103 CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc =
104 (CFStringCreateWithCStringNoCopyFuncTy)dlsym(
105 RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy");
106 if (!CFStringCreateWithCStringNoCopyFunc)
108 CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc =
109 (CFDictionaryGetValueFuncTy)dlsym(RTLD_DEFAULT, "CFDictionaryGetValue");
110 if (!CFDictionaryGetValueFunc)
112 CFGetTypeIDFuncTy CFGetTypeIDFunc =
113 (CFGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFGetTypeID");
114 if (!CFGetTypeIDFunc)
116 CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc =
117 (CFStringGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetTypeID");
118 if (!CFStringGetTypeIDFunc)
120 CFStringGetCStringFuncTy CFStringGetCStringFunc =
121 (CFStringGetCStringFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetCString");
122 if (!CFStringGetCStringFunc)
124 CFReleaseFuncTy CFReleaseFunc =
125 (CFReleaseFuncTy)dlsym(RTLD_DEFAULT, "CFRelease");
129 char *PListPath = "/System/Library/CoreServices/SystemVersion.plist";
131 #if TARGET_OS_SIMULATOR
132 char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT");
133 if (!PListPathPrefix)
135 char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1];
136 strcpy(FullPath, PListPathPrefix);
137 strcat(FullPath, PListPath);
138 PListPath = FullPath;
140 FILE *PropertyList = fopen(PListPath, "r");
144 // Dynamically allocated stuff.
145 CFDictionaryRef PListRef = NULL;
146 CFDataRef FileContentsRef = NULL;
147 UInt8 *PListBuf = NULL;
149 fseek(PropertyList, 0, SEEK_END);
150 long PListFileSize = ftell(PropertyList);
151 if (PListFileSize < 0)
153 rewind(PropertyList);
155 PListBuf = malloc((size_t)PListFileSize);
159 size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList);
160 if (NumRead != (size_t)PListFileSize)
163 // Get the file buffer into CF's format. We pass in a null allocator here *
164 // because we free PListBuf ourselves
165 FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)(
166 NULL, PListBuf, (CFIndex)NumRead, AllocatorNull);
167 if (!FileContentsRef)
170 if (CFPropertyListCreateWithDataFunc)
171 PListRef = (*CFPropertyListCreateWithDataFunc)(
172 NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL);
174 PListRef = (*CFPropertyListCreateFromXMLDataFunc)(
175 NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL);
179 CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(
180 NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull);
183 CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion);
184 (*CFReleaseFunc)(ProductVersion);
186 (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)())
190 if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr,
191 sizeof(VersionStr), CF_STRING_ENCODING_UTF8))
193 sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);
197 (*CFReleaseFunc)(PListRef);
199 (*CFReleaseFunc)(FileContentsRef);
201 fclose(PropertyList);
204 int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
205 // Populate the global version variables, if they haven't already.
206 dispatch_once_f(&DispatchOnceCounter, NULL, parseSystemVersionPList);
208 if (Major < GlobalMajor)
210 if (Major > GlobalMajor)
212 if (Minor < GlobalMinor)
214 if (Minor > GlobalMinor)
216 return Subminor <= GlobalSubminor;
221 // Silence an empty translation unit warning.