1 //===-- os_version_check.c - OS version checking -------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements the function __isOSVersionAtLeast, used by
10 // Objective-C's @available
12 //===----------------------------------------------------------------------===//
16 #include <TargetConditionals.h>
17 #include <dispatch/dispatch.h>
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
;
29 // _availability_version_check darwin API support.
30 typedef uint32_t dyld_platform_t
;
33 dyld_platform_t platform
;
35 } dyld_build_version_t
;
37 typedef bool (*AvailabilityVersionCheckFuncTy
)(uint32_t count
,
38 dyld_build_version_t versions
[]);
40 static AvailabilityVersionCheckFuncTy AvailabilityVersionCheck
;
42 // We can't include <CoreFoundation/CoreFoundation.h> directly from here, so
43 // just forward declare everything that we need from it.
45 typedef const void *CFDataRef
, *CFAllocatorRef
, *CFPropertyListRef
,
46 *CFStringRef
, *CFDictionaryRef
, *CFTypeRef
, *CFErrorRef
;
49 typedef unsigned long long CFTypeID
;
50 typedef unsigned long long CFOptionFlags
;
51 typedef signed long long CFIndex
;
53 typedef unsigned long CFTypeID
;
54 typedef unsigned long CFOptionFlags
;
55 typedef signed long CFIndex
;
58 typedef unsigned char UInt8
;
59 typedef _Bool Boolean
;
60 typedef CFIndex CFPropertyListFormat
;
61 typedef uint32_t CFStringEncoding
;
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
69 typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy
)(CFAllocatorRef
,
70 const UInt8
*, CFIndex
,
72 typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy
)(
73 CFAllocatorRef
, CFDataRef
, CFOptionFlags
, CFPropertyListFormat
*,
75 typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy
)(
76 CFAllocatorRef
, CFDataRef
, CFOptionFlags
, CFStringRef
*);
77 typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy
)(CFAllocatorRef
,
81 typedef const void *(*CFDictionaryGetValueFuncTy
)(CFDictionaryRef
,
83 typedef CFTypeID (*CFGetTypeIDFuncTy
)(CFTypeRef
);
84 typedef CFTypeID (*CFStringGetTypeIDFuncTy
)(void);
85 typedef Boolean (*CFStringGetCStringFuncTy
)(CFStringRef
, char *, CFIndex
,
87 typedef void (*CFReleaseFuncTy
)(CFTypeRef
);
89 extern __attribute__((weak_import
))
90 bool _availability_version_check(uint32_t count
,
91 dyld_build_version_t versions
[]);
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,
100 // Use the new API if it's is available.
101 if (_availability_version_check
)
102 AvailabilityVersionCheck
= &_availability_version_check
;
104 if (AvailabilityVersionCheck
&& !LoadPlist
) {
105 // New API is supported and we're not being asked to load the plist,
109 // Still load the PLIST to ensure that the existing calls to
110 // __isOSVersionAtLeast still work even with new compiler-rt and old OSes.
112 // Load CoreFoundation dynamically
113 const void *NullAllocator
= dlsym(RTLD_DEFAULT
, "kCFAllocatorNull");
116 const CFAllocatorRef AllocatorNull
= *(const CFAllocatorRef
*)NullAllocator
;
117 CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc
=
118 (CFDataCreateWithBytesNoCopyFuncTy
)dlsym(RTLD_DEFAULT
,
119 "CFDataCreateWithBytesNoCopy");
120 if (!CFDataCreateWithBytesNoCopyFunc
)
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
)
137 CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc
=
138 (CFStringCreateWithCStringNoCopyFuncTy
)dlsym(
139 RTLD_DEFAULT
, "CFStringCreateWithCStringNoCopy");
140 if (!CFStringCreateWithCStringNoCopyFunc
)
142 CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc
=
143 (CFDictionaryGetValueFuncTy
)dlsym(RTLD_DEFAULT
, "CFDictionaryGetValue");
144 if (!CFDictionaryGetValueFunc
)
146 CFGetTypeIDFuncTy CFGetTypeIDFunc
=
147 (CFGetTypeIDFuncTy
)dlsym(RTLD_DEFAULT
, "CFGetTypeID");
148 if (!CFGetTypeIDFunc
)
150 CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc
=
151 (CFStringGetTypeIDFuncTy
)dlsym(RTLD_DEFAULT
, "CFStringGetTypeID");
152 if (!CFStringGetTypeIDFunc
)
154 CFStringGetCStringFuncTy CFStringGetCStringFunc
=
155 (CFStringGetCStringFuncTy
)dlsym(RTLD_DEFAULT
, "CFStringGetCString");
156 if (!CFStringGetCStringFunc
)
158 CFReleaseFuncTy CFReleaseFunc
=
159 (CFReleaseFuncTy
)dlsym(RTLD_DEFAULT
, "CFRelease");
163 char *PListPath
= "/System/Library/CoreServices/SystemVersion.plist";
165 #if TARGET_OS_SIMULATOR
166 char *PListPathPrefix
= getenv("IPHONE_SIMULATOR_ROOT");
167 if (!PListPathPrefix
)
169 char FullPath
[strlen(PListPathPrefix
) + strlen(PListPath
) + 1];
170 strcpy(FullPath
, PListPathPrefix
);
171 strcat(FullPath
, PListPath
);
172 PListPath
= FullPath
;
174 FILE *PropertyList
= fopen(PListPath
, "r");
178 // Dynamically allocated stuff.
179 CFDictionaryRef PListRef
= NULL
;
180 CFDataRef FileContentsRef
= NULL
;
181 UInt8
*PListBuf
= NULL
;
183 fseek(PropertyList
, 0, SEEK_END
);
184 long PListFileSize
= ftell(PropertyList
);
185 if (PListFileSize
< 0)
187 rewind(PropertyList
);
189 PListBuf
= malloc((size_t)PListFileSize
);
193 size_t NumRead
= fread(PListBuf
, 1, (size_t)PListFileSize
, PropertyList
);
194 if (NumRead
!= (size_t)PListFileSize
)
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
)
204 if (CFPropertyListCreateWithDataFunc
)
205 PListRef
= (*CFPropertyListCreateWithDataFunc
)(
206 NULL
, FileContentsRef
, CF_PROPERTY_LIST_IMMUTABLE
, NULL
, NULL
);
208 PListRef
= (*CFPropertyListCreateFromXMLDataFunc
)(
209 NULL
, FileContentsRef
, CF_PROPERTY_LIST_IMMUTABLE
, NULL
);
213 CFStringRef ProductVersion
= (*CFStringCreateWithCStringNoCopyFunc
)(
214 NULL
, "ProductVersion", CF_STRING_ENCODING_ASCII
, AllocatorNull
);
217 CFTypeRef OpaqueValue
= (*CFDictionaryGetValueFunc
)(PListRef
, ProductVersion
);
218 (*CFReleaseFunc
)(ProductVersion
);
220 (*CFGetTypeIDFunc
)(OpaqueValue
) != (*CFStringGetTypeIDFunc
)())
224 if (!(*CFStringGetCStringFunc
)((CFStringRef
)OpaqueValue
, VersionStr
,
225 sizeof(VersionStr
), CF_STRING_ENCODING_UTF8
))
227 sscanf(VersionStr
, "%d.%d.%d", &GlobalMajor
, &GlobalMinor
, &GlobalSubminor
);
231 (*CFReleaseFunc
)(PListRef
);
233 (*CFReleaseFunc
)(FileContentsRef
);
235 fclose(PropertyList
);
238 // Find and parse the SystemVersion.plist file.
239 static void compatibilityInitializeAvailabilityCheck(void *Unused
) {
241 _initializeAvailabilityCheck(/*LoadPlist=*/true);
244 static void initializeAvailabilityCheck(void *Unused
) {
246 _initializeAvailabilityCheck(/*LoadPlist=*/false);
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
);
257 if (Major
< GlobalMajor
)
259 if (Major
> GlobalMajor
)
261 if (Minor
< GlobalMinor
)
263 if (Minor
> GlobalMinor
)
265 return Subminor
<= GlobalSubminor
;
268 static inline uint32_t ConstructVersion(uint32_t Major
, uint32_t Minor
,
270 return ((Major
& 0xffff) << 16) | ((Minor
& 0xff) << 8) | (Subminor
& 0xff);
273 int32_t __isPlatformVersionAtLeast(uint32_t Platform
, uint32_t Major
,
274 uint32_t Minor
, uint32_t Subminor
) {
275 dispatch_once_f(&DispatchOnceCounter
, NULL
, initializeAvailabilityCheck
);
277 if (!AvailabilityVersionCheck
) {
278 return __isOSVersionAtLeast(Major
, Minor
, Subminor
);
280 dyld_build_version_t Versions
[] = {
281 {Platform
, ConstructVersion(Major
, Minor
, Subminor
)}};
282 return AvailabilityVersionCheck(1, Versions
);
290 #include <sys/system_properties.h>
292 static int SdkVersion
;
293 static int IsPreRelease
;
295 static void readSystemProperties(void) {
296 char buf
[PROP_VALUE_MAX
];
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__
;
302 SdkVersion
= atoi(buf
);
305 if (__system_property_get("ro.build.version.codename", buf
) == 0) {
308 IsPreRelease
= strcmp(buf
, "REL") != 0;
313 int32_t __isOSVersionAtLeast(int32_t Major
, int32_t Minor
, int32_t Subminor
) {
316 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
317 pthread_once(&once
, readSystemProperties
);
319 return SdkVersion
>= Major
||
320 (IsPreRelease
&& Major
== __ANDROID_API_FUTURE__
);
325 // Silence an empty translation unit warning.