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