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 static void _initializeAvailabilityCheck(bool LoadPlist
) {
90 if (AvailabilityVersionCheck
&& !LoadPlist
) {
91 // New API is supported and we're not being asked to load the plist,
96 // Use the new API if it's is available.
97 AvailabilityVersionCheck
= (AvailabilityVersionCheckFuncTy
)dlsym(
98 RTLD_DEFAULT
, "_availability_version_check");
100 if (AvailabilityVersionCheck
&& !LoadPlist
) {
101 // New API is supported and we're not being asked to load the plist,
105 // Still load the PLIST to ensure that the existing calls to
106 // __isOSVersionAtLeast still work even with new compiler-rt and old OSes.
108 // Load CoreFoundation dynamically
109 const void *NullAllocator
= dlsym(RTLD_DEFAULT
, "kCFAllocatorNull");
112 const CFAllocatorRef AllocatorNull
= *(const CFAllocatorRef
*)NullAllocator
;
113 CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc
=
114 (CFDataCreateWithBytesNoCopyFuncTy
)dlsym(RTLD_DEFAULT
,
115 "CFDataCreateWithBytesNoCopy");
116 if (!CFDataCreateWithBytesNoCopyFunc
)
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
)
133 CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc
=
134 (CFStringCreateWithCStringNoCopyFuncTy
)dlsym(
135 RTLD_DEFAULT
, "CFStringCreateWithCStringNoCopy");
136 if (!CFStringCreateWithCStringNoCopyFunc
)
138 CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc
=
139 (CFDictionaryGetValueFuncTy
)dlsym(RTLD_DEFAULT
, "CFDictionaryGetValue");
140 if (!CFDictionaryGetValueFunc
)
142 CFGetTypeIDFuncTy CFGetTypeIDFunc
=
143 (CFGetTypeIDFuncTy
)dlsym(RTLD_DEFAULT
, "CFGetTypeID");
144 if (!CFGetTypeIDFunc
)
146 CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc
=
147 (CFStringGetTypeIDFuncTy
)dlsym(RTLD_DEFAULT
, "CFStringGetTypeID");
148 if (!CFStringGetTypeIDFunc
)
150 CFStringGetCStringFuncTy CFStringGetCStringFunc
=
151 (CFStringGetCStringFuncTy
)dlsym(RTLD_DEFAULT
, "CFStringGetCString");
152 if (!CFStringGetCStringFunc
)
154 CFReleaseFuncTy CFReleaseFunc
=
155 (CFReleaseFuncTy
)dlsym(RTLD_DEFAULT
, "CFRelease");
159 char *PListPath
= "/System/Library/CoreServices/SystemVersion.plist";
161 #if TARGET_OS_SIMULATOR
162 char *PListPathPrefix
= getenv("IPHONE_SIMULATOR_ROOT");
163 if (!PListPathPrefix
)
165 char FullPath
[strlen(PListPathPrefix
) + strlen(PListPath
) + 1];
166 strcpy(FullPath
, PListPathPrefix
);
167 strcat(FullPath
, PListPath
);
168 PListPath
= FullPath
;
170 FILE *PropertyList
= fopen(PListPath
, "r");
174 // Dynamically allocated stuff.
175 CFDictionaryRef PListRef
= NULL
;
176 CFDataRef FileContentsRef
= NULL
;
177 UInt8
*PListBuf
= NULL
;
179 fseek(PropertyList
, 0, SEEK_END
);
180 long PListFileSize
= ftell(PropertyList
);
181 if (PListFileSize
< 0)
183 rewind(PropertyList
);
185 PListBuf
= malloc((size_t)PListFileSize
);
189 size_t NumRead
= fread(PListBuf
, 1, (size_t)PListFileSize
, PropertyList
);
190 if (NumRead
!= (size_t)PListFileSize
)
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
)
200 if (CFPropertyListCreateWithDataFunc
)
201 PListRef
= (*CFPropertyListCreateWithDataFunc
)(
202 NULL
, FileContentsRef
, CF_PROPERTY_LIST_IMMUTABLE
, NULL
, NULL
);
204 PListRef
= (*CFPropertyListCreateFromXMLDataFunc
)(
205 NULL
, FileContentsRef
, CF_PROPERTY_LIST_IMMUTABLE
, NULL
);
209 CFStringRef ProductVersion
= (*CFStringCreateWithCStringNoCopyFunc
)(
210 NULL
, "ProductVersion", CF_STRING_ENCODING_ASCII
, AllocatorNull
);
213 CFTypeRef OpaqueValue
= (*CFDictionaryGetValueFunc
)(PListRef
, ProductVersion
);
214 (*CFReleaseFunc
)(ProductVersion
);
216 (*CFGetTypeIDFunc
)(OpaqueValue
) != (*CFStringGetTypeIDFunc
)())
220 if (!(*CFStringGetCStringFunc
)((CFStringRef
)OpaqueValue
, VersionStr
,
221 sizeof(VersionStr
), CF_STRING_ENCODING_UTF8
))
223 sscanf(VersionStr
, "%d.%d.%d", &GlobalMajor
, &GlobalMinor
, &GlobalSubminor
);
227 (*CFReleaseFunc
)(PListRef
);
229 (*CFReleaseFunc
)(FileContentsRef
);
231 fclose(PropertyList
);
234 // Find and parse the SystemVersion.plist file.
235 static void compatibilityInitializeAvailabilityCheck(void *Unused
) {
237 _initializeAvailabilityCheck(/*LoadPlist=*/true);
240 static void initializeAvailabilityCheck(void *Unused
) {
242 _initializeAvailabilityCheck(/*LoadPlist=*/false);
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
);
253 if (Major
< GlobalMajor
)
255 if (Major
> GlobalMajor
)
257 if (Minor
< GlobalMinor
)
259 if (Minor
> GlobalMinor
)
261 return Subminor
<= GlobalSubminor
;
264 static inline uint32_t ConstructVersion(uint32_t Major
, uint32_t Minor
,
266 return ((Major
& 0xffff) << 16) | ((Minor
& 0xff) << 8) | (Subminor
& 0xff);
269 int32_t __isPlatformVersionAtLeast(uint32_t Platform
, uint32_t Major
,
270 uint32_t Minor
, uint32_t Subminor
) {
271 dispatch_once_f(&DispatchOnceCounter
, NULL
, initializeAvailabilityCheck
);
273 if (!AvailabilityVersionCheck
) {
274 return __isOSVersionAtLeast(Major
, Minor
, Subminor
);
276 dyld_build_version_t Versions
[] = {
277 {Platform
, ConstructVersion(Major
, Minor
, Subminor
)}};
278 return AvailabilityVersionCheck(1, Versions
);
286 #include <sys/system_properties.h>
288 static int SdkVersion
;
289 static int IsPreRelease
;
291 static void readSystemProperties(void) {
292 char buf
[PROP_VALUE_MAX
];
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__
;
298 SdkVersion
= atoi(buf
);
301 if (__system_property_get("ro.build.version.codename", buf
) == 0) {
304 IsPreRelease
= strcmp(buf
, "REL") != 0;
309 int32_t __isOSVersionAtLeast(int32_t Major
, int32_t Minor
, int32_t Subminor
) {
312 static pthread_once_t once
= PTHREAD_ONCE_INIT
;
313 pthread_once(&once
, readSystemProperties
);
315 return SdkVersion
>= Major
||
316 (IsPreRelease
&& Major
== __ANDROID_API_FUTURE__
);
321 // Silence an empty translation unit warning.