1 /* ===-- os_version_check.c - OS version checking -------------------------===
2 *
3 * The LLVM Compiler Infrastructure
4 *
5 * This file is dual licensed under the MIT and the University of Illinois Open
6 * Source Licenses. See LICENSE.TXT for details.
7 *
8 * ===----------------------------------------------------------------------===
9 *
10 * This file implements the function __isOSVersionAtLeast, used by
11 * Objective-C's @available
12 *
13 * ===----------------------------------------------------------------------===
14 */
15
16 #ifdef __APPLE__
17
18 #include <TargetConditionals.h>
19 #include <dispatch/dispatch.h>
20 #include <dlfcn.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 /* These three variables hold the host's OS version. */
27 static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;
28 static dispatch_once_t DispatchOnceCounter;
29
30 /* We can't include <CoreFoundation/CoreFoundation.h> directly from here, so
31 * just forward declare everything that we need from it. */
32
33 typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef,
34 *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef;
35
36 #if __LLP64__
37 typedef unsigned long long CFTypeID;
38 typedef unsigned long long CFOptionFlags;
39 typedef signed long long CFIndex;
40 #else
41 typedef unsigned long CFTypeID;
42 typedef unsigned long CFOptionFlags;
43 typedef signed long CFIndex;
44 #endif
45
46 typedef unsigned char UInt8;
47 typedef _Bool Boolean;
48 typedef CFIndex CFPropertyListFormat;
49 typedef uint32_t CFStringEncoding;
50
51 /* kCFStringEncodingASCII analog. */
52 #define CF_STRING_ENCODING_ASCII 0x0600
53 /* kCFStringEncodingUTF8 analog. */
54 #define CF_STRING_ENCODING_UTF8 0x08000100
55 #define CF_PROPERTY_LIST_IMMUTABLE 0
56
57 typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef,
58 const UInt8 *, CFIndex,
59 CFAllocatorRef);
60 typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)(
61 CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *,
62 CFErrorRef *);
63 typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)(
64 CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *);
65 typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef,
66 const char *,
67 CFStringEncoding,
68 CFAllocatorRef);
69 typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef,
70 const void *);
71 typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef);
72 typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void);
73 typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex,
74 CFStringEncoding);
75 typedef void (*CFReleaseFuncTy)(CFTypeRef);
76
77 /* Find and parse the SystemVersion.plist file. */
parseSystemVersionPList(void * Unused)78 static void parseSystemVersionPList(void *Unused) {
79 (void)Unused;
80 /* Load CoreFoundation dynamically */
81 const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");
82 if (!NullAllocator)
83 return;
84 const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator;
85 CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc =
86 (CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT,
87 "CFDataCreateWithBytesNoCopy");
88 if (!CFDataCreateWithBytesNoCopyFunc)
89 return;
90 CFPropertyListCreateWithDataFuncTy CFPropertyListCreateWithDataFunc =
91 (CFPropertyListCreateWithDataFuncTy)dlsym(
92 RTLD_DEFAULT, "CFPropertyListCreateWithData");
93 /* CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it
94 * will be NULL on earlier OS versions. */
95 #pragma clang diagnostic push
96 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
97 CFPropertyListCreateFromXMLDataFuncTy CFPropertyListCreateFromXMLDataFunc =
98 (CFPropertyListCreateFromXMLDataFuncTy)dlsym(
99 RTLD_DEFAULT, "CFPropertyListCreateFromXMLData");
100 #pragma clang diagnostic pop
101 /* CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it
102 * might be NULL in future OS versions. */
103 if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc)
104 return;
105 CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc =
106 (CFStringCreateWithCStringNoCopyFuncTy)dlsym(
107 RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy");
108 if (!CFStringCreateWithCStringNoCopyFunc)
109 return;
110 CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc =
111 (CFDictionaryGetValueFuncTy)dlsym(RTLD_DEFAULT, "CFDictionaryGetValue");
112 if (!CFDictionaryGetValueFunc)
113 return;
114 CFGetTypeIDFuncTy CFGetTypeIDFunc =
115 (CFGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFGetTypeID");
116 if (!CFGetTypeIDFunc)
117 return;
118 CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc =
119 (CFStringGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetTypeID");
120 if (!CFStringGetTypeIDFunc)
121 return;
122 CFStringGetCStringFuncTy CFStringGetCStringFunc =
123 (CFStringGetCStringFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetCString");
124 if (!CFStringGetCStringFunc)
125 return;
126 CFReleaseFuncTy CFReleaseFunc =
127 (CFReleaseFuncTy)dlsym(RTLD_DEFAULT, "CFRelease");
128 if (!CFReleaseFunc)
129 return;
130
131 char *PListPath = "/System/Library/CoreServices/SystemVersion.plist";
132
133 #if TARGET_OS_SIMULATOR
134 char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT");
135 if (!PListPathPrefix)
136 return;
137 char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1];
138 strcpy(FullPath, PListPathPrefix);
139 strcat(FullPath, PListPath);
140 PListPath = FullPath;
141 #endif
142 FILE *PropertyList = fopen(PListPath, "r");
143 if (!PropertyList)
144 return;
145
146 /* Dynamically allocated stuff. */
147 CFDictionaryRef PListRef = NULL;
148 CFDataRef FileContentsRef = NULL;
149 UInt8 *PListBuf = NULL;
150
151 fseek(PropertyList, 0, SEEK_END);
152 long PListFileSize = ftell(PropertyList);
153 if (PListFileSize < 0)
154 goto Fail;
155 rewind(PropertyList);
156
157 PListBuf = malloc((size_t)PListFileSize);
158 if (!PListBuf)
159 goto Fail;
160
161 size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList);
162 if (NumRead != (size_t)PListFileSize)
163 goto Fail;
164
165 /* Get the file buffer into CF's format. We pass in a null allocator here *
166 * because we free PListBuf ourselves */
167 FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)(
168 NULL, PListBuf, (CFIndex)NumRead, AllocatorNull);
169 if (!FileContentsRef)
170 goto Fail;
171
172 if (CFPropertyListCreateWithDataFunc)
173 PListRef = (*CFPropertyListCreateWithDataFunc)(
174 NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL);
175 else
176 PListRef = (*CFPropertyListCreateFromXMLDataFunc)(
177 NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL);
178 if (!PListRef)
179 goto Fail;
180
181 CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(
182 NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull);
183 if (!ProductVersion)
184 goto Fail;
185 CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion);
186 (*CFReleaseFunc)(ProductVersion);
187 if (!OpaqueValue ||
188 (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)())
189 goto Fail;
190
191 char VersionStr[32];
192 if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr,
193 sizeof(VersionStr), CF_STRING_ENCODING_UTF8))
194 goto Fail;
195 sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);
196
197 Fail:
198 if (PListRef)
199 (*CFReleaseFunc)(PListRef);
200 if (FileContentsRef)
201 (*CFReleaseFunc)(FileContentsRef);
202 free(PListBuf);
203 fclose(PropertyList);
204 }
205
__isOSVersionAtLeast(int32_t Major,int32_t Minor,int32_t Subminor)206 int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
207 /* Populate the global version variables, if they haven't already. */
208 dispatch_once_f(&DispatchOnceCounter, NULL, parseSystemVersionPList);
209
210 if (Major < GlobalMajor)
211 return 1;
212 if (Major > GlobalMajor)
213 return 0;
214 if (Minor < GlobalMinor)
215 return 1;
216 if (Minor > GlobalMinor)
217 return 0;
218 return Subminor <= GlobalSubminor;
219 }
220
221 #else
222
223 /* Silence an empty translation unit warning. */
224 typedef int unused;
225
226 #endif
227