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