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. 25 static int32_t GlobalMajor, GlobalMinor, GlobalSubminor; 26 static dispatch_once_t DispatchOnceCounter; 27 static dispatch_once_t CompatibilityDispatchOnceCounter; 28 29 // _availability_version_check darwin API support. 30 typedef uint32_t dyld_platform_t; 31 32 typedef struct { 33 dyld_platform_t platform; 34 uint32_t version; 35 } dyld_build_version_t; 36 37 typedef bool (*AvailabilityVersionCheckFuncTy)(uint32_t count, 38 dyld_build_version_t versions[]); 39 40 static 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 45 typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef, 46 *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef; 47 48 #if __LLP64__ 49 typedef unsigned long long CFTypeID; 50 typedef unsigned long long CFOptionFlags; 51 typedef signed long long CFIndex; 52 #else 53 typedef unsigned long CFTypeID; 54 typedef unsigned long CFOptionFlags; 55 typedef signed long CFIndex; 56 #endif 57 58 typedef unsigned char UInt8; 59 typedef _Bool Boolean; 60 typedef CFIndex CFPropertyListFormat; 61 typedef 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 69 typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef, 70 const UInt8 *, CFIndex, 71 CFAllocatorRef); 72 typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)( 73 CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *, 74 CFErrorRef *); 75 typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)( 76 CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *); 77 typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef, 78 const char *, 79 CFStringEncoding, 80 CFAllocatorRef); 81 typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef, 82 const void *); 83 typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef); 84 typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void); 85 typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex, 86 CFStringEncoding); 87 typedef void (*CFReleaseFuncTy)(CFTypeRef); 88 89 extern __attribute__((weak_import)) 90 bool _availability_version_check(uint32_t count, 91 dyld_build_version_t versions[]); 92 93 static 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 229 Fail: 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. 239 static void compatibilityInitializeAvailabilityCheck(void *Unused) { 240 (void)Unused; 241 _initializeAvailabilityCheck(/*LoadPlist=*/true); 242 } 243 244 static 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. 252 int32_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 268 static 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 273 int32_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 292 static int SdkVersion; 293 static int IsPreRelease; 294 295 static 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 313 int32_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 return SdkVersion >= Major || 320 (IsPreRelease && Major == __ANDROID_API_FUTURE__); 321 } 322 323 #else 324 325 // Silence an empty translation unit warning. 326 typedef int unused; 327 328 #endif 329