xref: /openbsd-src/gnu/llvm/compiler-rt/lib/builtins/os_version_check.c (revision 810390e339a5425391477d5d41c78d7cab2424ac)
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