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

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