13cab2bb3Spatrick //===-- os_version_check.c - OS version checking -------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file implements the function __isOSVersionAtLeast, used by
103cab2bb3Spatrick // Objective-C's @available
113cab2bb3Spatrick //
123cab2bb3Spatrick //===----------------------------------------------------------------------===//
133cab2bb3Spatrick
143cab2bb3Spatrick #ifdef __APPLE__
153cab2bb3Spatrick
163cab2bb3Spatrick #include <TargetConditionals.h>
173cab2bb3Spatrick #include <dispatch/dispatch.h>
183cab2bb3Spatrick #include <dlfcn.h>
193cab2bb3Spatrick #include <stdint.h>
203cab2bb3Spatrick #include <stdio.h>
213cab2bb3Spatrick #include <stdlib.h>
223cab2bb3Spatrick #include <string.h>
233cab2bb3Spatrick
243cab2bb3Spatrick // These three variables hold the host's OS version.
253cab2bb3Spatrick static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;
263cab2bb3Spatrick static dispatch_once_t DispatchOnceCounter;
27d89ec533Spatrick static dispatch_once_t CompatibilityDispatchOnceCounter;
28d89ec533Spatrick
29d89ec533Spatrick // _availability_version_check darwin API support.
30d89ec533Spatrick typedef uint32_t dyld_platform_t;
31d89ec533Spatrick
32d89ec533Spatrick typedef struct {
33d89ec533Spatrick dyld_platform_t platform;
34d89ec533Spatrick uint32_t version;
35d89ec533Spatrick } dyld_build_version_t;
36d89ec533Spatrick
37d89ec533Spatrick typedef bool (*AvailabilityVersionCheckFuncTy)(uint32_t count,
38d89ec533Spatrick dyld_build_version_t versions[]);
39d89ec533Spatrick
40d89ec533Spatrick static AvailabilityVersionCheckFuncTy AvailabilityVersionCheck;
413cab2bb3Spatrick
423cab2bb3Spatrick // We can't include <CoreFoundation/CoreFoundation.h> directly from here, so
433cab2bb3Spatrick // just forward declare everything that we need from it.
443cab2bb3Spatrick
453cab2bb3Spatrick typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef,
463cab2bb3Spatrick *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef;
473cab2bb3Spatrick
483cab2bb3Spatrick #if __LLP64__
493cab2bb3Spatrick typedef unsigned long long CFTypeID;
503cab2bb3Spatrick typedef unsigned long long CFOptionFlags;
513cab2bb3Spatrick typedef signed long long CFIndex;
523cab2bb3Spatrick #else
533cab2bb3Spatrick typedef unsigned long CFTypeID;
543cab2bb3Spatrick typedef unsigned long CFOptionFlags;
553cab2bb3Spatrick typedef signed long CFIndex;
563cab2bb3Spatrick #endif
573cab2bb3Spatrick
583cab2bb3Spatrick typedef unsigned char UInt8;
593cab2bb3Spatrick typedef _Bool Boolean;
603cab2bb3Spatrick typedef CFIndex CFPropertyListFormat;
613cab2bb3Spatrick typedef uint32_t CFStringEncoding;
623cab2bb3Spatrick
633cab2bb3Spatrick // kCFStringEncodingASCII analog.
643cab2bb3Spatrick #define CF_STRING_ENCODING_ASCII 0x0600
653cab2bb3Spatrick // kCFStringEncodingUTF8 analog.
663cab2bb3Spatrick #define CF_STRING_ENCODING_UTF8 0x08000100
673cab2bb3Spatrick #define CF_PROPERTY_LIST_IMMUTABLE 0
683cab2bb3Spatrick
693cab2bb3Spatrick typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef,
703cab2bb3Spatrick const UInt8 *, CFIndex,
713cab2bb3Spatrick CFAllocatorRef);
723cab2bb3Spatrick typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)(
733cab2bb3Spatrick CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *,
743cab2bb3Spatrick CFErrorRef *);
753cab2bb3Spatrick typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)(
763cab2bb3Spatrick CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *);
773cab2bb3Spatrick typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef,
783cab2bb3Spatrick const char *,
793cab2bb3Spatrick CFStringEncoding,
803cab2bb3Spatrick CFAllocatorRef);
813cab2bb3Spatrick typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef,
823cab2bb3Spatrick const void *);
833cab2bb3Spatrick typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef);
843cab2bb3Spatrick typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void);
853cab2bb3Spatrick typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex,
863cab2bb3Spatrick CFStringEncoding);
873cab2bb3Spatrick typedef void (*CFReleaseFuncTy)(CFTypeRef);
883cab2bb3Spatrick
_initializeAvailabilityCheck(bool LoadPlist)89d89ec533Spatrick static void _initializeAvailabilityCheck(bool LoadPlist) {
90d89ec533Spatrick if (AvailabilityVersionCheck && !LoadPlist) {
91d89ec533Spatrick // New API is supported and we're not being asked to load the plist,
92d89ec533Spatrick // exit early!
93d89ec533Spatrick return;
94d89ec533Spatrick }
95d89ec533Spatrick
96d89ec533Spatrick // Use the new API if it's is available.
97d89ec533Spatrick AvailabilityVersionCheck = (AvailabilityVersionCheckFuncTy)dlsym(
98d89ec533Spatrick RTLD_DEFAULT, "_availability_version_check");
99d89ec533Spatrick
100d89ec533Spatrick if (AvailabilityVersionCheck && !LoadPlist) {
101d89ec533Spatrick // New API is supported and we're not being asked to load the plist,
102d89ec533Spatrick // exit early!
103d89ec533Spatrick return;
104d89ec533Spatrick }
105d89ec533Spatrick // Still load the PLIST to ensure that the existing calls to
106d89ec533Spatrick // __isOSVersionAtLeast still work even with new compiler-rt and old OSes.
107d89ec533Spatrick
1083cab2bb3Spatrick // Load CoreFoundation dynamically
1093cab2bb3Spatrick const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");
1103cab2bb3Spatrick if (!NullAllocator)
1113cab2bb3Spatrick return;
1123cab2bb3Spatrick const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator;
1133cab2bb3Spatrick CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc =
1143cab2bb3Spatrick (CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT,
1153cab2bb3Spatrick "CFDataCreateWithBytesNoCopy");
1163cab2bb3Spatrick if (!CFDataCreateWithBytesNoCopyFunc)
1173cab2bb3Spatrick return;
1183cab2bb3Spatrick CFPropertyListCreateWithDataFuncTy CFPropertyListCreateWithDataFunc =
1193cab2bb3Spatrick (CFPropertyListCreateWithDataFuncTy)dlsym(RTLD_DEFAULT,
1203cab2bb3Spatrick "CFPropertyListCreateWithData");
1213cab2bb3Spatrick // CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it
1223cab2bb3Spatrick // will be NULL on earlier OS versions.
1233cab2bb3Spatrick #pragma clang diagnostic push
1243cab2bb3Spatrick #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1253cab2bb3Spatrick CFPropertyListCreateFromXMLDataFuncTy CFPropertyListCreateFromXMLDataFunc =
1263cab2bb3Spatrick (CFPropertyListCreateFromXMLDataFuncTy)dlsym(
1273cab2bb3Spatrick RTLD_DEFAULT, "CFPropertyListCreateFromXMLData");
1283cab2bb3Spatrick #pragma clang diagnostic pop
1293cab2bb3Spatrick // CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it
1303cab2bb3Spatrick // might be NULL in future OS versions.
1313cab2bb3Spatrick if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc)
1323cab2bb3Spatrick return;
1333cab2bb3Spatrick CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc =
1343cab2bb3Spatrick (CFStringCreateWithCStringNoCopyFuncTy)dlsym(
1353cab2bb3Spatrick RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy");
1363cab2bb3Spatrick if (!CFStringCreateWithCStringNoCopyFunc)
1373cab2bb3Spatrick return;
1383cab2bb3Spatrick CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc =
1393cab2bb3Spatrick (CFDictionaryGetValueFuncTy)dlsym(RTLD_DEFAULT, "CFDictionaryGetValue");
1403cab2bb3Spatrick if (!CFDictionaryGetValueFunc)
1413cab2bb3Spatrick return;
1423cab2bb3Spatrick CFGetTypeIDFuncTy CFGetTypeIDFunc =
1433cab2bb3Spatrick (CFGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFGetTypeID");
1443cab2bb3Spatrick if (!CFGetTypeIDFunc)
1453cab2bb3Spatrick return;
1463cab2bb3Spatrick CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc =
1473cab2bb3Spatrick (CFStringGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetTypeID");
1483cab2bb3Spatrick if (!CFStringGetTypeIDFunc)
1493cab2bb3Spatrick return;
1503cab2bb3Spatrick CFStringGetCStringFuncTy CFStringGetCStringFunc =
1513cab2bb3Spatrick (CFStringGetCStringFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetCString");
1523cab2bb3Spatrick if (!CFStringGetCStringFunc)
1533cab2bb3Spatrick return;
1543cab2bb3Spatrick CFReleaseFuncTy CFReleaseFunc =
1553cab2bb3Spatrick (CFReleaseFuncTy)dlsym(RTLD_DEFAULT, "CFRelease");
1563cab2bb3Spatrick if (!CFReleaseFunc)
1573cab2bb3Spatrick return;
1583cab2bb3Spatrick
1593cab2bb3Spatrick char *PListPath = "/System/Library/CoreServices/SystemVersion.plist";
1603cab2bb3Spatrick
1613cab2bb3Spatrick #if TARGET_OS_SIMULATOR
1623cab2bb3Spatrick char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT");
1633cab2bb3Spatrick if (!PListPathPrefix)
1643cab2bb3Spatrick return;
1653cab2bb3Spatrick char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1];
1663cab2bb3Spatrick strcpy(FullPath, PListPathPrefix);
1673cab2bb3Spatrick strcat(FullPath, PListPath);
1683cab2bb3Spatrick PListPath = FullPath;
1693cab2bb3Spatrick #endif
1703cab2bb3Spatrick FILE *PropertyList = fopen(PListPath, "r");
1713cab2bb3Spatrick if (!PropertyList)
1723cab2bb3Spatrick return;
1733cab2bb3Spatrick
1743cab2bb3Spatrick // Dynamically allocated stuff.
1753cab2bb3Spatrick CFDictionaryRef PListRef = NULL;
1763cab2bb3Spatrick CFDataRef FileContentsRef = NULL;
1773cab2bb3Spatrick UInt8 *PListBuf = NULL;
1783cab2bb3Spatrick
1793cab2bb3Spatrick fseek(PropertyList, 0, SEEK_END);
1803cab2bb3Spatrick long PListFileSize = ftell(PropertyList);
1813cab2bb3Spatrick if (PListFileSize < 0)
1823cab2bb3Spatrick goto Fail;
1833cab2bb3Spatrick rewind(PropertyList);
1843cab2bb3Spatrick
1853cab2bb3Spatrick PListBuf = malloc((size_t)PListFileSize);
1863cab2bb3Spatrick if (!PListBuf)
1873cab2bb3Spatrick goto Fail;
1883cab2bb3Spatrick
1893cab2bb3Spatrick size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList);
1903cab2bb3Spatrick if (NumRead != (size_t)PListFileSize)
1913cab2bb3Spatrick goto Fail;
1923cab2bb3Spatrick
1933cab2bb3Spatrick // Get the file buffer into CF's format. We pass in a null allocator here *
1943cab2bb3Spatrick // because we free PListBuf ourselves
1953cab2bb3Spatrick FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)(
1963cab2bb3Spatrick NULL, PListBuf, (CFIndex)NumRead, AllocatorNull);
1973cab2bb3Spatrick if (!FileContentsRef)
1983cab2bb3Spatrick goto Fail;
1993cab2bb3Spatrick
2003cab2bb3Spatrick if (CFPropertyListCreateWithDataFunc)
2013cab2bb3Spatrick PListRef = (*CFPropertyListCreateWithDataFunc)(
2023cab2bb3Spatrick NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL);
2033cab2bb3Spatrick else
2043cab2bb3Spatrick PListRef = (*CFPropertyListCreateFromXMLDataFunc)(
2053cab2bb3Spatrick NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL);
2063cab2bb3Spatrick if (!PListRef)
2073cab2bb3Spatrick goto Fail;
2083cab2bb3Spatrick
2093cab2bb3Spatrick CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(
2103cab2bb3Spatrick NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull);
2113cab2bb3Spatrick if (!ProductVersion)
2123cab2bb3Spatrick goto Fail;
2133cab2bb3Spatrick CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion);
2143cab2bb3Spatrick (*CFReleaseFunc)(ProductVersion);
2153cab2bb3Spatrick if (!OpaqueValue ||
2163cab2bb3Spatrick (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)())
2173cab2bb3Spatrick goto Fail;
2183cab2bb3Spatrick
2193cab2bb3Spatrick char VersionStr[32];
2203cab2bb3Spatrick if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr,
2213cab2bb3Spatrick sizeof(VersionStr), CF_STRING_ENCODING_UTF8))
2223cab2bb3Spatrick goto Fail;
2233cab2bb3Spatrick sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);
2243cab2bb3Spatrick
2253cab2bb3Spatrick Fail:
2263cab2bb3Spatrick if (PListRef)
2273cab2bb3Spatrick (*CFReleaseFunc)(PListRef);
2283cab2bb3Spatrick if (FileContentsRef)
2293cab2bb3Spatrick (*CFReleaseFunc)(FileContentsRef);
2303cab2bb3Spatrick free(PListBuf);
2313cab2bb3Spatrick fclose(PropertyList);
2323cab2bb3Spatrick }
2333cab2bb3Spatrick
234d89ec533Spatrick // Find and parse the SystemVersion.plist file.
compatibilityInitializeAvailabilityCheck(void * Unused)235d89ec533Spatrick static void compatibilityInitializeAvailabilityCheck(void *Unused) {
236d89ec533Spatrick (void)Unused;
237d89ec533Spatrick _initializeAvailabilityCheck(/*LoadPlist=*/true);
238d89ec533Spatrick }
239d89ec533Spatrick
initializeAvailabilityCheck(void * Unused)240d89ec533Spatrick static void initializeAvailabilityCheck(void *Unused) {
241d89ec533Spatrick (void)Unused;
242d89ec533Spatrick _initializeAvailabilityCheck(/*LoadPlist=*/false);
243d89ec533Spatrick }
244d89ec533Spatrick
245d89ec533Spatrick // This old API entry point is no longer used by Clang for Darwin. We still need
246d89ec533Spatrick // to keep it around to ensure that object files that reference it are still
247d89ec533Spatrick // usable when linked with new compiler-rt.
__isOSVersionAtLeast(int32_t Major,int32_t Minor,int32_t Subminor)2483cab2bb3Spatrick int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
2493cab2bb3Spatrick // Populate the global version variables, if they haven't already.
250d89ec533Spatrick dispatch_once_f(&CompatibilityDispatchOnceCounter, NULL,
251d89ec533Spatrick compatibilityInitializeAvailabilityCheck);
2523cab2bb3Spatrick
2533cab2bb3Spatrick if (Major < GlobalMajor)
2543cab2bb3Spatrick return 1;
2553cab2bb3Spatrick if (Major > GlobalMajor)
2563cab2bb3Spatrick return 0;
2573cab2bb3Spatrick if (Minor < GlobalMinor)
2583cab2bb3Spatrick return 1;
2593cab2bb3Spatrick if (Minor > GlobalMinor)
2603cab2bb3Spatrick return 0;
2613cab2bb3Spatrick return Subminor <= GlobalSubminor;
2623cab2bb3Spatrick }
2633cab2bb3Spatrick
ConstructVersion(uint32_t Major,uint32_t Minor,uint32_t Subminor)264d89ec533Spatrick static inline uint32_t ConstructVersion(uint32_t Major, uint32_t Minor,
265d89ec533Spatrick uint32_t Subminor) {
266d89ec533Spatrick return ((Major & 0xffff) << 16) | ((Minor & 0xff) << 8) | (Subminor & 0xff);
267d89ec533Spatrick }
268d89ec533Spatrick
__isPlatformVersionAtLeast(uint32_t Platform,uint32_t Major,uint32_t Minor,uint32_t Subminor)269d89ec533Spatrick int32_t __isPlatformVersionAtLeast(uint32_t Platform, uint32_t Major,
270d89ec533Spatrick uint32_t Minor, uint32_t Subminor) {
271d89ec533Spatrick dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck);
272d89ec533Spatrick
273d89ec533Spatrick if (!AvailabilityVersionCheck) {
274d89ec533Spatrick return __isOSVersionAtLeast(Major, Minor, Subminor);
275d89ec533Spatrick }
276d89ec533Spatrick dyld_build_version_t Versions[] = {
277d89ec533Spatrick {Platform, ConstructVersion(Major, Minor, Subminor)}};
278d89ec533Spatrick return AvailabilityVersionCheck(1, Versions);
279d89ec533Spatrick }
280d89ec533Spatrick
281d89ec533Spatrick #elif __ANDROID__
282d89ec533Spatrick
283d89ec533Spatrick #include <pthread.h>
284d89ec533Spatrick #include <stdlib.h>
285d89ec533Spatrick #include <string.h>
286d89ec533Spatrick #include <sys/system_properties.h>
287d89ec533Spatrick
288d89ec533Spatrick static int SdkVersion;
289d89ec533Spatrick static int IsPreRelease;
290d89ec533Spatrick
readSystemProperties(void)291d89ec533Spatrick static void readSystemProperties(void) {
292d89ec533Spatrick char buf[PROP_VALUE_MAX];
293d89ec533Spatrick
294d89ec533Spatrick if (__system_property_get("ro.build.version.sdk", buf) == 0) {
295d89ec533Spatrick // When the system property doesn't exist, defaults to future API level.
296d89ec533Spatrick SdkVersion = __ANDROID_API_FUTURE__;
297d89ec533Spatrick } else {
298d89ec533Spatrick SdkVersion = atoi(buf);
299d89ec533Spatrick }
300d89ec533Spatrick
301d89ec533Spatrick if (__system_property_get("ro.build.version.codename", buf) == 0) {
302d89ec533Spatrick IsPreRelease = 1;
303d89ec533Spatrick } else {
304d89ec533Spatrick IsPreRelease = strcmp(buf, "REL") != 0;
305d89ec533Spatrick }
306d89ec533Spatrick return;
307d89ec533Spatrick }
308d89ec533Spatrick
__isOSVersionAtLeast(int32_t Major,int32_t Minor,int32_t Subminor)309d89ec533Spatrick int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
310*810390e3Srobert (void) Minor;
311*810390e3Srobert (void) Subminor;
312d89ec533Spatrick static pthread_once_t once = PTHREAD_ONCE_INIT;
313d89ec533Spatrick pthread_once(&once, readSystemProperties);
314d89ec533Spatrick
315d89ec533Spatrick return SdkVersion >= Major ||
316d89ec533Spatrick (IsPreRelease && Major == __ANDROID_API_FUTURE__);
317d89ec533Spatrick }
318d89ec533Spatrick
3193cab2bb3Spatrick #else
3203cab2bb3Spatrick
3213cab2bb3Spatrick // Silence an empty translation unit warning.
3223cab2bb3Spatrick typedef int unused;
3233cab2bb3Spatrick
3243cab2bb3Spatrick #endif
325