xref: /freebsd/contrib/llvm-project/compiler-rt/lib/builtins/os_version_check.c (revision 3f0efe05432b1633991114ca4ca330102a561959)
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