1//===-- os_version_check.c - OS version checking -------------------------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements the function __isOSVersionAtLeast, used by
10// Objective-C's @available
11//
12//===----------------------------------------------------------------------===//
13
14#ifdef __APPLE__
15
16#include <TargetConditionals.h>
17#include <assert.h>
18#include <dispatch/dispatch.h>
19#include <dlfcn.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25// These three variables hold the host's OS version.
26static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;
27static dispatch_once_t DispatchOnceCounter;
28static dispatch_once_t CompatibilityDispatchOnceCounter;
29
30// _availability_version_check darwin API support.
31typedef uint32_t dyld_platform_t;
32
33typedef struct {
34 dyld_platform_t platform;
35 uint32_t version;
36} dyld_build_version_t;
37
38typedef bool (*AvailabilityVersionCheckFuncTy)(uint32_t count,
39 dyld_build_version_t versions[]);
40
41static AvailabilityVersionCheckFuncTy AvailabilityVersionCheck;
42
43// We can't include <CoreFoundation/CoreFoundation.h> directly from here, so
44// just forward declare everything that we need from it.
45
46typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef,
47 *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef;
48
49#if __LLP64__
50typedef unsigned long long CFTypeID;
51typedef unsigned long long CFOptionFlags;
52typedef signed long long CFIndex;
53#else
54typedef unsigned long CFTypeID;
55typedef unsigned long CFOptionFlags;
56typedef signed long CFIndex;
57#endif
58
59typedef unsigned char UInt8;
60typedef _Bool Boolean;
61typedef CFIndex CFPropertyListFormat;
62typedef uint32_t CFStringEncoding;
63
64// kCFStringEncodingASCII analog.
65#define CF_STRING_ENCODING_ASCII 0x0600
66// kCFStringEncodingUTF8 analog.
67#define CF_STRING_ENCODING_UTF8 0x08000100
68#define CF_PROPERTY_LIST_IMMUTABLE 0
69
70typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef,
71 const UInt8 *, CFIndex,
72 CFAllocatorRef);
73typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)(
74 CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *,
75 CFErrorRef *);
76typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)(
77 CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *);
78typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef,
79 const char *,
80 CFStringEncoding,
81 CFAllocatorRef);
82typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef,
83 const void *);
84typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef);
85typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void);
86typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex,
87 CFStringEncoding);
88typedef void (*CFReleaseFuncTy)(CFTypeRef);
89
90extern __attribute__((weak_import))
91bool _availability_version_check(uint32_t count,
92 dyld_build_version_t versions[]);
93
94static void _initializeAvailabilityCheck(bool LoadPlist) {
95 if (AvailabilityVersionCheck && !LoadPlist) {
96 // New API is supported and we're not being asked to load the plist,
97 // exit early!
98 return;
99 }
100
101 // Use the new API if it's is available.
102 if (_availability_version_check)
103 AvailabilityVersionCheck = &_availability_version_check;
104
105 if (AvailabilityVersionCheck && !LoadPlist) {
106 // New API is supported and we're not being asked to load the plist,
107 // exit early!
108 return;
109 }
110 // Still load the PLIST to ensure that the existing calls to
111 // __isOSVersionAtLeast still work even with new compiler-rt and old OSes.
112
113 // Load CoreFoundation dynamically
114 const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");
115 if (!NullAllocator)
116 return;
117 const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator;
118 CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc =
119 (CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT,
120 "CFDataCreateWithBytesNoCopy");
121 if (!CFDataCreateWithBytesNoCopyFunc)
122 return;
123 CFPropertyListCreateWithDataFuncTy CFPropertyListCreateWithDataFunc =
124 (CFPropertyListCreateWithDataFuncTy)dlsym(RTLD_DEFAULT,
125 "CFPropertyListCreateWithData");
126// CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it
127// will be NULL on earlier OS versions.
128#pragma clang diagnostic push
129#pragma clang diagnostic ignored "-Wdeprecated-declarations"
130 CFPropertyListCreateFromXMLDataFuncTy CFPropertyListCreateFromXMLDataFunc =
131 (CFPropertyListCreateFromXMLDataFuncTy)dlsym(
132 RTLD_DEFAULT, "CFPropertyListCreateFromXMLData");
133#pragma clang diagnostic pop
134 // CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it
135 // might be NULL in future OS versions.
136 if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc)
137 return;
138 CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc =
139 (CFStringCreateWithCStringNoCopyFuncTy)dlsym(
140 RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy");
141 if (!CFStringCreateWithCStringNoCopyFunc)
142 return;
143 CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc =
144 (CFDictionaryGetValueFuncTy)dlsym(RTLD_DEFAULT, "CFDictionaryGetValue");
145 if (!CFDictionaryGetValueFunc)
146 return;
147 CFGetTypeIDFuncTy CFGetTypeIDFunc =
148 (CFGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFGetTypeID");
149 if (!CFGetTypeIDFunc)
150 return;
151 CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc =
152 (CFStringGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetTypeID");
153 if (!CFStringGetTypeIDFunc)
154 return;
155 CFStringGetCStringFuncTy CFStringGetCStringFunc =
156 (CFStringGetCStringFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetCString");
157 if (!CFStringGetCStringFunc)
158 return;
159 CFReleaseFuncTy CFReleaseFunc =
160 (CFReleaseFuncTy)dlsym(RTLD_DEFAULT, "CFRelease");
161 if (!CFReleaseFunc)
162 return;
163
164 char *PListPath = "/System/Library/CoreServices/SystemVersion.plist";
165
166#if TARGET_OS_SIMULATOR
167 char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT");
168 if (!PListPathPrefix)
169 return;
170 char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1];
171 strcpy(FullPath, PListPathPrefix);
172 strcat(FullPath, PListPath);
173 PListPath = FullPath;
174#endif
175 FILE *PropertyList = fopen(PListPath, "r");
176 if (!PropertyList)
177 return;
178
179 // Dynamically allocated stuff.
180 CFDictionaryRef PListRef = NULL;
181 CFDataRef FileContentsRef = NULL;
182 UInt8 *PListBuf = NULL;
183
184 fseek(PropertyList, 0, SEEK_END);
185 long PListFileSize = ftell(PropertyList);
186 if (PListFileSize < 0)
187 goto Fail;
188 rewind(PropertyList);
189
190 PListBuf = malloc((size_t)PListFileSize);
191 if (!PListBuf)
192 goto Fail;
193
194 size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList);
195 if (NumRead != (size_t)PListFileSize)
196 goto Fail;
197
198 // Get the file buffer into CF's format. We pass in a null allocator here *
199 // because we free PListBuf ourselves
200 FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)(
201 NULL, PListBuf, (CFIndex)NumRead, AllocatorNull);
202 if (!FileContentsRef)
203 goto Fail;
204
205 if (CFPropertyListCreateWithDataFunc)
206 PListRef = (*CFPropertyListCreateWithDataFunc)(
207 NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL);
208 else
209 PListRef = (*CFPropertyListCreateFromXMLDataFunc)(
210 NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL);
211 if (!PListRef)
212 goto Fail;
213
214 CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(
215 NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull);
216 if (!ProductVersion)
217 goto Fail;
218 CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion);
219 (*CFReleaseFunc)(ProductVersion);
220 if (!OpaqueValue ||
221 (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)())
222 goto Fail;
223
224 char VersionStr[32];
225 if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr,
226 sizeof(VersionStr), CF_STRING_ENCODING_UTF8))
227 goto Fail;
228 sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);
229
230Fail:
231 if (PListRef)
232 (*CFReleaseFunc)(PListRef);
233 if (FileContentsRef)
234 (*CFReleaseFunc)(FileContentsRef);
235 free(PListBuf);
236 fclose(PropertyList);
237}
238
239// Find and parse the SystemVersion.plist file.
240static void compatibilityInitializeAvailabilityCheck(void *Unused) {
241 (void)Unused;
242 _initializeAvailabilityCheck(/*LoadPlist=*/true);
243}
244
245static void initializeAvailabilityCheck(void *Unused) {
246 (void)Unused;
247 _initializeAvailabilityCheck(/*LoadPlist=*/false);
248}
249
250// This old API entry point is no longer used by Clang for Darwin. We still need
251// to keep it around to ensure that object files that reference it are still
252// usable when linked with new compiler-rt.
253int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
254 // Populate the global version variables, if they haven't already.
255 dispatch_once_f(&CompatibilityDispatchOnceCounter, NULL,
256 compatibilityInitializeAvailabilityCheck);
257
258 if (Major < GlobalMajor)
259 return 1;
260 if (Major > GlobalMajor)
261 return 0;
262 if (Minor < GlobalMinor)
263 return 1;
264 if (Minor > GlobalMinor)
265 return 0;
266 return Subminor <= GlobalSubminor;
267}
268
269static inline uint32_t ConstructVersion(uint32_t Major, uint32_t Minor,
270 uint32_t Subminor) {
271 return ((Major & 0xffff) << 16) | ((Minor & 0xff) << 8) | (Subminor & 0xff);
272}
273
274#define PLATFORM_MACOS 1
275
276int32_t __isPlatformVersionAtLeast(uint32_t Platform, uint32_t Major,
277 uint32_t Minor, uint32_t Subminor) {
278 dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck);
279
280 if (!AvailabilityVersionCheck) {
281 return __isOSVersionAtLeast(Major, Minor, Subminor);
282 }
283 dyld_build_version_t Versions[] = {
284 {Platform, ConstructVersion(Major, Minor, Subminor)}};
285 return AvailabilityVersionCheck(1, Versions);
286}
287
288#if TARGET_OS_OSX
289
290int32_t __isPlatformOrVariantPlatformVersionAtLeast(
291 uint32_t Platform, uint32_t Major, uint32_t Minor, uint32_t Subminor,
292 uint32_t Platform2, uint32_t Major2, uint32_t Minor2, uint32_t Subminor2) {
293 dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck);
294
295 if (!AvailabilityVersionCheck) {
296 // Handle case of back-deployment for older macOS.
297 if (Platform == PLATFORM_MACOS) {
298 return __isOSVersionAtLeast(Major, Minor, Subminor);
299 }
300 assert(Platform2 == PLATFORM_MACOS && "unexpected platform");
301 return __isOSVersionAtLeast(Major2, Minor2, Subminor2);
302 }
303 dyld_build_version_t Versions[] = {
304 {Platform, ConstructVersion(Major, Minor, Subminor)},
305 {Platform2, ConstructVersion(Major2, Minor2, Subminor2)}};
306 return AvailabilityVersionCheck(2, Versions);
307}
308
309#endif
310
311#elif __ANDROID__
312
313#include <pthread.h>
314#include <stdlib.h>
315#include <string.h>
316#include <sys/system_properties.h>
317
318static int SdkVersion;
319static int IsPreRelease;
320
321static void readSystemProperties(void) {
322 char buf[PROP_VALUE_MAX];
323
324 if (__system_property_get("ro.build.version.sdk", buf) == 0) {
325 // When the system property doesn't exist, defaults to future API level.
326 SdkVersion = __ANDROID_API_FUTURE__;
327 } else {
328 SdkVersion = atoi(buf);
329 }
330
331 if (__system_property_get("ro.build.version.codename", buf) == 0) {
332 IsPreRelease = 1;
333 } else {
334 IsPreRelease = strcmp(buf, "REL") != 0;
335 }
336 return;
337}
338
339int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
340 (void) Minor;
341 (void) Subminor;
342 static pthread_once_t once = PTHREAD_ONCE_INIT;
343 pthread_once(&once, readSystemProperties);
344
345 // Allow all on pre-release. Note that we still rely on compile-time checks.
346 return SdkVersion >= Major || IsPreRelease;
347}
348
349#else
350
351// Silence an empty translation unit warning.
352typedef int unused;
353
354#endif
355

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of compiler-rt/lib/builtins/os_version_check.c