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 28 // We can't include <CoreFoundation/CoreFoundation.h> directly from here, so 29 // just forward declare everything that we need from it. 30 31 typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef, 32 *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef; 33 34 #if __LLP64__ 35 typedef unsigned long long CFTypeID; 36 typedef unsigned long long CFOptionFlags; 37 typedef signed long long CFIndex; 38 #else 39 typedef unsigned long CFTypeID; 40 typedef unsigned long CFOptionFlags; 41 typedef signed long CFIndex; 42 #endif 43 44 typedef unsigned char UInt8; 45 typedef _Bool Boolean; 46 typedef CFIndex CFPropertyListFormat; 47 typedef uint32_t CFStringEncoding; 48 49 // kCFStringEncodingASCII analog. 50 #define CF_STRING_ENCODING_ASCII 0x0600 51 // kCFStringEncodingUTF8 analog. 52 #define CF_STRING_ENCODING_UTF8 0x08000100 53 #define CF_PROPERTY_LIST_IMMUTABLE 0 54 55 typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef, 56 const UInt8 *, CFIndex, 57 CFAllocatorRef); 58 typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)( 59 CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *, 60 CFErrorRef *); 61 typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)( 62 CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *); 63 typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef, 64 const char *, 65 CFStringEncoding, 66 CFAllocatorRef); 67 typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef, 68 const void *); 69 typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef); 70 typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void); 71 typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex, 72 CFStringEncoding); 73 typedef void (*CFReleaseFuncTy)(CFTypeRef); 74 75 // Find and parse the SystemVersion.plist file. 76 static void parseSystemVersionPList(void *Unused) { 77 (void)Unused; 78 // Load CoreFoundation dynamically 79 const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull"); 80 if (!NullAllocator) 81 return; 82 const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator; 83 CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc = 84 (CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT, 85 "CFDataCreateWithBytesNoCopy"); 86 if (!CFDataCreateWithBytesNoCopyFunc) 87 return; 88 CFPropertyListCreateWithDataFuncTy CFPropertyListCreateWithDataFunc = 89 (CFPropertyListCreateWithDataFuncTy)dlsym(RTLD_DEFAULT, 90 "CFPropertyListCreateWithData"); 91 // CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it 92 // will be NULL on earlier OS versions. 93 #pragma clang diagnostic push 94 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 95 CFPropertyListCreateFromXMLDataFuncTy CFPropertyListCreateFromXMLDataFunc = 96 (CFPropertyListCreateFromXMLDataFuncTy)dlsym( 97 RTLD_DEFAULT, "CFPropertyListCreateFromXMLData"); 98 #pragma clang diagnostic pop 99 // CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it 100 // might be NULL in future OS versions. 101 if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc) 102 return; 103 CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc = 104 (CFStringCreateWithCStringNoCopyFuncTy)dlsym( 105 RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy"); 106 if (!CFStringCreateWithCStringNoCopyFunc) 107 return; 108 CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc = 109 (CFDictionaryGetValueFuncTy)dlsym(RTLD_DEFAULT, "CFDictionaryGetValue"); 110 if (!CFDictionaryGetValueFunc) 111 return; 112 CFGetTypeIDFuncTy CFGetTypeIDFunc = 113 (CFGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFGetTypeID"); 114 if (!CFGetTypeIDFunc) 115 return; 116 CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc = 117 (CFStringGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetTypeID"); 118 if (!CFStringGetTypeIDFunc) 119 return; 120 CFStringGetCStringFuncTy CFStringGetCStringFunc = 121 (CFStringGetCStringFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetCString"); 122 if (!CFStringGetCStringFunc) 123 return; 124 CFReleaseFuncTy CFReleaseFunc = 125 (CFReleaseFuncTy)dlsym(RTLD_DEFAULT, "CFRelease"); 126 if (!CFReleaseFunc) 127 return; 128 129 char *PListPath = "/System/Library/CoreServices/SystemVersion.plist"; 130 131 #if TARGET_OS_SIMULATOR 132 char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT"); 133 if (!PListPathPrefix) 134 return; 135 char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1]; 136 strcpy(FullPath, PListPathPrefix); 137 strcat(FullPath, PListPath); 138 PListPath = FullPath; 139 #endif 140 FILE *PropertyList = fopen(PListPath, "r"); 141 if (!PropertyList) 142 return; 143 144 // Dynamically allocated stuff. 145 CFDictionaryRef PListRef = NULL; 146 CFDataRef FileContentsRef = NULL; 147 UInt8 *PListBuf = NULL; 148 149 fseek(PropertyList, 0, SEEK_END); 150 long PListFileSize = ftell(PropertyList); 151 if (PListFileSize < 0) 152 goto Fail; 153 rewind(PropertyList); 154 155 PListBuf = malloc((size_t)PListFileSize); 156 if (!PListBuf) 157 goto Fail; 158 159 size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList); 160 if (NumRead != (size_t)PListFileSize) 161 goto Fail; 162 163 // Get the file buffer into CF's format. We pass in a null allocator here * 164 // because we free PListBuf ourselves 165 FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)( 166 NULL, PListBuf, (CFIndex)NumRead, AllocatorNull); 167 if (!FileContentsRef) 168 goto Fail; 169 170 if (CFPropertyListCreateWithDataFunc) 171 PListRef = (*CFPropertyListCreateWithDataFunc)( 172 NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL); 173 else 174 PListRef = (*CFPropertyListCreateFromXMLDataFunc)( 175 NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL); 176 if (!PListRef) 177 goto Fail; 178 179 CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)( 180 NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull); 181 if (!ProductVersion) 182 goto Fail; 183 CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion); 184 (*CFReleaseFunc)(ProductVersion); 185 if (!OpaqueValue || 186 (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)()) 187 goto Fail; 188 189 char VersionStr[32]; 190 if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr, 191 sizeof(VersionStr), CF_STRING_ENCODING_UTF8)) 192 goto Fail; 193 sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor); 194 195 Fail: 196 if (PListRef) 197 (*CFReleaseFunc)(PListRef); 198 if (FileContentsRef) 199 (*CFReleaseFunc)(FileContentsRef); 200 free(PListBuf); 201 fclose(PropertyList); 202 } 203 204 int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) { 205 // Populate the global version variables, if they haven't already. 206 dispatch_once_f(&DispatchOnceCounter, NULL, parseSystemVersionPList); 207 208 if (Major < GlobalMajor) 209 return 1; 210 if (Major > GlobalMajor) 211 return 0; 212 if (Minor < GlobalMinor) 213 return 1; 214 if (Minor > GlobalMinor) 215 return 0; 216 return Subminor <= GlobalSubminor; 217 } 218 219 #else 220 221 // Silence an empty translation unit warning. 222 typedef int unused; 223 224 #endif 225