xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/builtins/os_version_check.c (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
10b57cec5SDimitry Andric //===-- os_version_check.c - OS version checking  -------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file implements the function __isOSVersionAtLeast, used by
100b57cec5SDimitry Andric // Objective-C's @available
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #ifdef __APPLE__
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric #include <TargetConditionals.h>
170b57cec5SDimitry Andric #include <dispatch/dispatch.h>
180b57cec5SDimitry Andric #include <dlfcn.h>
190b57cec5SDimitry Andric #include <stdint.h>
200b57cec5SDimitry Andric #include <stdio.h>
210b57cec5SDimitry Andric #include <stdlib.h>
220b57cec5SDimitry Andric #include <string.h>
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric // These three variables hold the host's OS version.
250b57cec5SDimitry Andric static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;
260b57cec5SDimitry Andric static dispatch_once_t DispatchOnceCounter;
27e8d8bef9SDimitry Andric static dispatch_once_t CompatibilityDispatchOnceCounter;
28e8d8bef9SDimitry Andric 
29e8d8bef9SDimitry Andric // _availability_version_check darwin API support.
30e8d8bef9SDimitry Andric typedef uint32_t dyld_platform_t;
31e8d8bef9SDimitry Andric 
32e8d8bef9SDimitry Andric typedef struct {
33e8d8bef9SDimitry Andric   dyld_platform_t platform;
34e8d8bef9SDimitry Andric   uint32_t version;
35e8d8bef9SDimitry Andric } dyld_build_version_t;
36e8d8bef9SDimitry Andric 
37e8d8bef9SDimitry Andric typedef bool (*AvailabilityVersionCheckFuncTy)(uint32_t count,
38e8d8bef9SDimitry Andric                                                dyld_build_version_t versions[]);
39e8d8bef9SDimitry Andric 
40e8d8bef9SDimitry Andric static AvailabilityVersionCheckFuncTy AvailabilityVersionCheck;
410b57cec5SDimitry Andric 
420b57cec5SDimitry Andric // We can't include <CoreFoundation/CoreFoundation.h> directly from here, so
430b57cec5SDimitry Andric // just forward declare everything that we need from it.
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef,
460b57cec5SDimitry Andric     *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef;
470b57cec5SDimitry Andric 
480b57cec5SDimitry Andric #if __LLP64__
490b57cec5SDimitry Andric typedef unsigned long long CFTypeID;
500b57cec5SDimitry Andric typedef unsigned long long CFOptionFlags;
510b57cec5SDimitry Andric typedef signed long long CFIndex;
520b57cec5SDimitry Andric #else
530b57cec5SDimitry Andric typedef unsigned long CFTypeID;
540b57cec5SDimitry Andric typedef unsigned long CFOptionFlags;
550b57cec5SDimitry Andric typedef signed long CFIndex;
560b57cec5SDimitry Andric #endif
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric typedef unsigned char UInt8;
590b57cec5SDimitry Andric typedef _Bool Boolean;
600b57cec5SDimitry Andric typedef CFIndex CFPropertyListFormat;
610b57cec5SDimitry Andric typedef uint32_t CFStringEncoding;
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric // kCFStringEncodingASCII analog.
640b57cec5SDimitry Andric #define CF_STRING_ENCODING_ASCII 0x0600
650b57cec5SDimitry Andric // kCFStringEncodingUTF8 analog.
660b57cec5SDimitry Andric #define CF_STRING_ENCODING_UTF8 0x08000100
670b57cec5SDimitry Andric #define CF_PROPERTY_LIST_IMMUTABLE 0
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef,
700b57cec5SDimitry Andric                                                        const UInt8 *, CFIndex,
710b57cec5SDimitry Andric                                                        CFAllocatorRef);
720b57cec5SDimitry Andric typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)(
730b57cec5SDimitry Andric     CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *,
740b57cec5SDimitry Andric     CFErrorRef *);
750b57cec5SDimitry Andric typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)(
760b57cec5SDimitry Andric     CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *);
770b57cec5SDimitry Andric typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef,
780b57cec5SDimitry Andric                                                              const char *,
790b57cec5SDimitry Andric                                                              CFStringEncoding,
800b57cec5SDimitry Andric                                                              CFAllocatorRef);
810b57cec5SDimitry Andric typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef,
820b57cec5SDimitry Andric                                                   const void *);
830b57cec5SDimitry Andric typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef);
840b57cec5SDimitry Andric typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void);
850b57cec5SDimitry Andric typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex,
860b57cec5SDimitry Andric                                             CFStringEncoding);
870b57cec5SDimitry Andric typedef void (*CFReleaseFuncTy)(CFTypeRef);
880b57cec5SDimitry Andric 
8906c3fb27SDimitry Andric extern __attribute__((weak_import))
9006c3fb27SDimitry Andric bool _availability_version_check(uint32_t count,
9106c3fb27SDimitry Andric                                  dyld_build_version_t versions[]);
9206c3fb27SDimitry Andric 
93e8d8bef9SDimitry Andric static void _initializeAvailabilityCheck(bool LoadPlist) {
94e8d8bef9SDimitry Andric   if (AvailabilityVersionCheck && !LoadPlist) {
95e8d8bef9SDimitry Andric     // New API is supported and we're not being asked to load the plist,
96e8d8bef9SDimitry Andric     // exit early!
97e8d8bef9SDimitry Andric     return;
98e8d8bef9SDimitry Andric   }
99e8d8bef9SDimitry Andric 
100e8d8bef9SDimitry Andric   // Use the new API if it's is available.
10106c3fb27SDimitry Andric   if (_availability_version_check)
10206c3fb27SDimitry Andric     AvailabilityVersionCheck = &_availability_version_check;
103e8d8bef9SDimitry Andric 
104e8d8bef9SDimitry Andric   if (AvailabilityVersionCheck && !LoadPlist) {
105e8d8bef9SDimitry Andric     // New API is supported and we're not being asked to load the plist,
106e8d8bef9SDimitry Andric     // exit early!
107e8d8bef9SDimitry Andric     return;
108e8d8bef9SDimitry Andric   }
109e8d8bef9SDimitry Andric   // Still load the PLIST to ensure that the existing calls to
110e8d8bef9SDimitry Andric   // __isOSVersionAtLeast still work even with new compiler-rt and old OSes.
111e8d8bef9SDimitry Andric 
1120b57cec5SDimitry Andric   // Load CoreFoundation dynamically
1130b57cec5SDimitry Andric   const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");
1140b57cec5SDimitry Andric   if (!NullAllocator)
1150b57cec5SDimitry Andric     return;
1160b57cec5SDimitry Andric   const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator;
1170b57cec5SDimitry Andric   CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc =
1180b57cec5SDimitry Andric       (CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT,
1190b57cec5SDimitry Andric                                                "CFDataCreateWithBytesNoCopy");
1200b57cec5SDimitry Andric   if (!CFDataCreateWithBytesNoCopyFunc)
1210b57cec5SDimitry Andric     return;
1220b57cec5SDimitry Andric   CFPropertyListCreateWithDataFuncTy CFPropertyListCreateWithDataFunc =
1230b57cec5SDimitry Andric       (CFPropertyListCreateWithDataFuncTy)dlsym(RTLD_DEFAULT,
1240b57cec5SDimitry Andric                                                 "CFPropertyListCreateWithData");
1250b57cec5SDimitry Andric // CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it
1260b57cec5SDimitry Andric // will be NULL on earlier OS versions.
1270b57cec5SDimitry Andric #pragma clang diagnostic push
1280b57cec5SDimitry Andric #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1290b57cec5SDimitry Andric   CFPropertyListCreateFromXMLDataFuncTy CFPropertyListCreateFromXMLDataFunc =
1300b57cec5SDimitry Andric       (CFPropertyListCreateFromXMLDataFuncTy)dlsym(
1310b57cec5SDimitry Andric           RTLD_DEFAULT, "CFPropertyListCreateFromXMLData");
1320b57cec5SDimitry Andric #pragma clang diagnostic pop
1330b57cec5SDimitry Andric   // CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it
1340b57cec5SDimitry Andric   // might be NULL in future OS versions.
1350b57cec5SDimitry Andric   if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc)
1360b57cec5SDimitry Andric     return;
1370b57cec5SDimitry Andric   CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc =
1380b57cec5SDimitry Andric       (CFStringCreateWithCStringNoCopyFuncTy)dlsym(
1390b57cec5SDimitry Andric           RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy");
1400b57cec5SDimitry Andric   if (!CFStringCreateWithCStringNoCopyFunc)
1410b57cec5SDimitry Andric     return;
1420b57cec5SDimitry Andric   CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc =
1430b57cec5SDimitry Andric       (CFDictionaryGetValueFuncTy)dlsym(RTLD_DEFAULT, "CFDictionaryGetValue");
1440b57cec5SDimitry Andric   if (!CFDictionaryGetValueFunc)
1450b57cec5SDimitry Andric     return;
1460b57cec5SDimitry Andric   CFGetTypeIDFuncTy CFGetTypeIDFunc =
1470b57cec5SDimitry Andric       (CFGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFGetTypeID");
1480b57cec5SDimitry Andric   if (!CFGetTypeIDFunc)
1490b57cec5SDimitry Andric     return;
1500b57cec5SDimitry Andric   CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc =
1510b57cec5SDimitry Andric       (CFStringGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetTypeID");
1520b57cec5SDimitry Andric   if (!CFStringGetTypeIDFunc)
1530b57cec5SDimitry Andric     return;
1540b57cec5SDimitry Andric   CFStringGetCStringFuncTy CFStringGetCStringFunc =
1550b57cec5SDimitry Andric       (CFStringGetCStringFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetCString");
1560b57cec5SDimitry Andric   if (!CFStringGetCStringFunc)
1570b57cec5SDimitry Andric     return;
1580b57cec5SDimitry Andric   CFReleaseFuncTy CFReleaseFunc =
1590b57cec5SDimitry Andric       (CFReleaseFuncTy)dlsym(RTLD_DEFAULT, "CFRelease");
1600b57cec5SDimitry Andric   if (!CFReleaseFunc)
1610b57cec5SDimitry Andric     return;
1620b57cec5SDimitry Andric 
1630b57cec5SDimitry Andric   char *PListPath = "/System/Library/CoreServices/SystemVersion.plist";
1640b57cec5SDimitry Andric 
1650b57cec5SDimitry Andric #if TARGET_OS_SIMULATOR
1660b57cec5SDimitry Andric   char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT");
1670b57cec5SDimitry Andric   if (!PListPathPrefix)
1680b57cec5SDimitry Andric     return;
1690b57cec5SDimitry Andric   char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1];
1700b57cec5SDimitry Andric   strcpy(FullPath, PListPathPrefix);
1710b57cec5SDimitry Andric   strcat(FullPath, PListPath);
1720b57cec5SDimitry Andric   PListPath = FullPath;
1730b57cec5SDimitry Andric #endif
1740b57cec5SDimitry Andric   FILE *PropertyList = fopen(PListPath, "r");
1750b57cec5SDimitry Andric   if (!PropertyList)
1760b57cec5SDimitry Andric     return;
1770b57cec5SDimitry Andric 
1780b57cec5SDimitry Andric   // Dynamically allocated stuff.
1790b57cec5SDimitry Andric   CFDictionaryRef PListRef = NULL;
1800b57cec5SDimitry Andric   CFDataRef FileContentsRef = NULL;
1810b57cec5SDimitry Andric   UInt8 *PListBuf = NULL;
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric   fseek(PropertyList, 0, SEEK_END);
1840b57cec5SDimitry Andric   long PListFileSize = ftell(PropertyList);
1850b57cec5SDimitry Andric   if (PListFileSize < 0)
1860b57cec5SDimitry Andric     goto Fail;
1870b57cec5SDimitry Andric   rewind(PropertyList);
1880b57cec5SDimitry Andric 
1890b57cec5SDimitry Andric   PListBuf = malloc((size_t)PListFileSize);
1900b57cec5SDimitry Andric   if (!PListBuf)
1910b57cec5SDimitry Andric     goto Fail;
1920b57cec5SDimitry Andric 
1930b57cec5SDimitry Andric   size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList);
1940b57cec5SDimitry Andric   if (NumRead != (size_t)PListFileSize)
1950b57cec5SDimitry Andric     goto Fail;
1960b57cec5SDimitry Andric 
1970b57cec5SDimitry Andric   // Get the file buffer into CF's format. We pass in a null allocator here *
1980b57cec5SDimitry Andric   // because we free PListBuf ourselves
1990b57cec5SDimitry Andric   FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)(
2000b57cec5SDimitry Andric       NULL, PListBuf, (CFIndex)NumRead, AllocatorNull);
2010b57cec5SDimitry Andric   if (!FileContentsRef)
2020b57cec5SDimitry Andric     goto Fail;
2030b57cec5SDimitry Andric 
2040b57cec5SDimitry Andric   if (CFPropertyListCreateWithDataFunc)
2050b57cec5SDimitry Andric     PListRef = (*CFPropertyListCreateWithDataFunc)(
2060b57cec5SDimitry Andric         NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL);
2070b57cec5SDimitry Andric   else
2080b57cec5SDimitry Andric     PListRef = (*CFPropertyListCreateFromXMLDataFunc)(
2090b57cec5SDimitry Andric         NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL);
2100b57cec5SDimitry Andric   if (!PListRef)
2110b57cec5SDimitry Andric     goto Fail;
2120b57cec5SDimitry Andric 
2130b57cec5SDimitry Andric   CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(
2140b57cec5SDimitry Andric       NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull);
2150b57cec5SDimitry Andric   if (!ProductVersion)
2160b57cec5SDimitry Andric     goto Fail;
2170b57cec5SDimitry Andric   CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion);
2180b57cec5SDimitry Andric   (*CFReleaseFunc)(ProductVersion);
2190b57cec5SDimitry Andric   if (!OpaqueValue ||
2200b57cec5SDimitry Andric       (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)())
2210b57cec5SDimitry Andric     goto Fail;
2220b57cec5SDimitry Andric 
2230b57cec5SDimitry Andric   char VersionStr[32];
2240b57cec5SDimitry Andric   if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr,
2250b57cec5SDimitry Andric                                  sizeof(VersionStr), CF_STRING_ENCODING_UTF8))
2260b57cec5SDimitry Andric     goto Fail;
2270b57cec5SDimitry Andric   sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);
2280b57cec5SDimitry Andric 
2290b57cec5SDimitry Andric Fail:
2300b57cec5SDimitry Andric   if (PListRef)
2310b57cec5SDimitry Andric     (*CFReleaseFunc)(PListRef);
2320b57cec5SDimitry Andric   if (FileContentsRef)
2330b57cec5SDimitry Andric     (*CFReleaseFunc)(FileContentsRef);
2340b57cec5SDimitry Andric   free(PListBuf);
2350b57cec5SDimitry Andric   fclose(PropertyList);
2360b57cec5SDimitry Andric }
2370b57cec5SDimitry Andric 
238e8d8bef9SDimitry Andric // Find and parse the SystemVersion.plist file.
239e8d8bef9SDimitry Andric static void compatibilityInitializeAvailabilityCheck(void *Unused) {
240e8d8bef9SDimitry Andric   (void)Unused;
241e8d8bef9SDimitry Andric   _initializeAvailabilityCheck(/*LoadPlist=*/true);
242e8d8bef9SDimitry Andric }
243e8d8bef9SDimitry Andric 
244e8d8bef9SDimitry Andric static void initializeAvailabilityCheck(void *Unused) {
245e8d8bef9SDimitry Andric   (void)Unused;
246e8d8bef9SDimitry Andric   _initializeAvailabilityCheck(/*LoadPlist=*/false);
247e8d8bef9SDimitry Andric }
248e8d8bef9SDimitry Andric 
249e8d8bef9SDimitry Andric // This old API entry point is no longer used by Clang for Darwin. We still need
250e8d8bef9SDimitry Andric // to keep it around to ensure that object files that reference it are still
251e8d8bef9SDimitry Andric // usable when linked with new compiler-rt.
2520b57cec5SDimitry Andric int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
2530b57cec5SDimitry Andric   // Populate the global version variables, if they haven't already.
254e8d8bef9SDimitry Andric   dispatch_once_f(&CompatibilityDispatchOnceCounter, NULL,
255e8d8bef9SDimitry Andric                   compatibilityInitializeAvailabilityCheck);
2560b57cec5SDimitry Andric 
2570b57cec5SDimitry Andric   if (Major < GlobalMajor)
2580b57cec5SDimitry Andric     return 1;
2590b57cec5SDimitry Andric   if (Major > GlobalMajor)
2600b57cec5SDimitry Andric     return 0;
2610b57cec5SDimitry Andric   if (Minor < GlobalMinor)
2620b57cec5SDimitry Andric     return 1;
2630b57cec5SDimitry Andric   if (Minor > GlobalMinor)
2640b57cec5SDimitry Andric     return 0;
2650b57cec5SDimitry Andric   return Subminor <= GlobalSubminor;
2660b57cec5SDimitry Andric }
2670b57cec5SDimitry Andric 
268e8d8bef9SDimitry Andric static inline uint32_t ConstructVersion(uint32_t Major, uint32_t Minor,
269e8d8bef9SDimitry Andric                                         uint32_t Subminor) {
270e8d8bef9SDimitry Andric   return ((Major & 0xffff) << 16) | ((Minor & 0xff) << 8) | (Subminor & 0xff);
271e8d8bef9SDimitry Andric }
272e8d8bef9SDimitry Andric 
273e8d8bef9SDimitry Andric int32_t __isPlatformVersionAtLeast(uint32_t Platform, uint32_t Major,
274e8d8bef9SDimitry Andric                                    uint32_t Minor, uint32_t Subminor) {
275e8d8bef9SDimitry Andric   dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck);
276e8d8bef9SDimitry Andric 
277e8d8bef9SDimitry Andric   if (!AvailabilityVersionCheck) {
278e8d8bef9SDimitry Andric     return __isOSVersionAtLeast(Major, Minor, Subminor);
279e8d8bef9SDimitry Andric   }
280e8d8bef9SDimitry Andric   dyld_build_version_t Versions[] = {
281e8d8bef9SDimitry Andric       {Platform, ConstructVersion(Major, Minor, Subminor)}};
282e8d8bef9SDimitry Andric   return AvailabilityVersionCheck(1, Versions);
283e8d8bef9SDimitry Andric }
284e8d8bef9SDimitry Andric 
285e8d8bef9SDimitry Andric #elif __ANDROID__
286e8d8bef9SDimitry Andric 
287e8d8bef9SDimitry Andric #include <pthread.h>
288e8d8bef9SDimitry Andric #include <stdlib.h>
289e8d8bef9SDimitry Andric #include <string.h>
290e8d8bef9SDimitry Andric #include <sys/system_properties.h>
291e8d8bef9SDimitry Andric 
292e8d8bef9SDimitry Andric static int SdkVersion;
293e8d8bef9SDimitry Andric static int IsPreRelease;
294e8d8bef9SDimitry Andric 
295e8d8bef9SDimitry Andric static void readSystemProperties(void) {
296e8d8bef9SDimitry Andric   char buf[PROP_VALUE_MAX];
297e8d8bef9SDimitry Andric 
298e8d8bef9SDimitry Andric   if (__system_property_get("ro.build.version.sdk", buf) == 0) {
299e8d8bef9SDimitry Andric     // When the system property doesn't exist, defaults to future API level.
300e8d8bef9SDimitry Andric     SdkVersion = __ANDROID_API_FUTURE__;
301e8d8bef9SDimitry Andric   } else {
302e8d8bef9SDimitry Andric     SdkVersion = atoi(buf);
303e8d8bef9SDimitry Andric   }
304e8d8bef9SDimitry Andric 
305e8d8bef9SDimitry Andric   if (__system_property_get("ro.build.version.codename", buf) == 0) {
306e8d8bef9SDimitry Andric     IsPreRelease = 1;
307e8d8bef9SDimitry Andric   } else {
308e8d8bef9SDimitry Andric     IsPreRelease = strcmp(buf, "REL") != 0;
309e8d8bef9SDimitry Andric   }
310e8d8bef9SDimitry Andric   return;
311e8d8bef9SDimitry Andric }
312e8d8bef9SDimitry Andric 
313e8d8bef9SDimitry Andric int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
31481ad6265SDimitry Andric   (void) Minor;
31581ad6265SDimitry Andric   (void) Subminor;
316e8d8bef9SDimitry Andric   static pthread_once_t once = PTHREAD_ONCE_INIT;
317e8d8bef9SDimitry Andric   pthread_once(&once, readSystemProperties);
318e8d8bef9SDimitry Andric 
319*0fca6ea1SDimitry Andric   // Allow all on pre-release. Note that we still rely on compile-time checks.
320*0fca6ea1SDimitry Andric   return SdkVersion >= Major || IsPreRelease;
321e8d8bef9SDimitry Andric }
322e8d8bef9SDimitry Andric 
3230b57cec5SDimitry Andric #else
3240b57cec5SDimitry Andric 
3250b57cec5SDimitry Andric // Silence an empty translation unit warning.
3260b57cec5SDimitry Andric typedef int unused;
3270b57cec5SDimitry Andric 
3280b57cec5SDimitry Andric #endif
329