os_version_check.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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. #ifdef __APPLE__
  14. #include <TargetConditionals.h>
  15. #include <dispatch/dispatch.h>
  16. #include <dlfcn.h>
  17. #include <stdint.h>
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. // These three variables hold the host's OS version.
  22. static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;
  23. static dispatch_once_t DispatchOnceCounter;
  24. static dispatch_once_t CompatibilityDispatchOnceCounter;
  25. // _availability_version_check darwin API support.
  26. typedef uint32_t dyld_platform_t;
  27. typedef struct {
  28. dyld_platform_t platform;
  29. uint32_t version;
  30. } dyld_build_version_t;
  31. typedef bool (*AvailabilityVersionCheckFuncTy)(uint32_t count,
  32. dyld_build_version_t versions[]);
  33. static AvailabilityVersionCheckFuncTy AvailabilityVersionCheck;
  34. // We can't include <CoreFoundation/CoreFoundation.h> directly from here, so
  35. // just forward declare everything that we need from it.
  36. typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef,
  37. *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef;
  38. #if __LLP64__
  39. typedef unsigned long long CFTypeID;
  40. typedef unsigned long long CFOptionFlags;
  41. typedef signed long long CFIndex;
  42. #else
  43. typedef unsigned long CFTypeID;
  44. typedef unsigned long CFOptionFlags;
  45. typedef signed long CFIndex;
  46. #endif
  47. typedef unsigned char UInt8;
  48. typedef _Bool Boolean;
  49. typedef CFIndex CFPropertyListFormat;
  50. typedef uint32_t CFStringEncoding;
  51. // kCFStringEncodingASCII analog.
  52. #define CF_STRING_ENCODING_ASCII 0x0600
  53. // kCFStringEncodingUTF8 analog.
  54. #define CF_STRING_ENCODING_UTF8 0x08000100
  55. #define CF_PROPERTY_LIST_IMMUTABLE 0
  56. typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef,
  57. const UInt8 *, CFIndex,
  58. CFAllocatorRef);
  59. typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)(
  60. CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *,
  61. CFErrorRef *);
  62. typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)(
  63. CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *);
  64. typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef,
  65. const char *,
  66. CFStringEncoding,
  67. CFAllocatorRef);
  68. typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef,
  69. const void *);
  70. typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef);
  71. typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void);
  72. typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex,
  73. CFStringEncoding);
  74. typedef void (*CFReleaseFuncTy)(CFTypeRef);
  75. extern __attribute__((weak_import))
  76. bool _availability_version_check(uint32_t count,
  77. dyld_build_version_t versions[]);
  78. static void _initializeAvailabilityCheck(bool LoadPlist) {
  79. if (AvailabilityVersionCheck && !LoadPlist) {
  80. // New API is supported and we're not being asked to load the plist,
  81. // exit early!
  82. return;
  83. }
  84. // Use the new API if it's is available.
  85. if (_availability_version_check)
  86. AvailabilityVersionCheck = &_availability_version_check;
  87. if (AvailabilityVersionCheck && !LoadPlist) {
  88. // New API is supported and we're not being asked to load the plist,
  89. // exit early!
  90. return;
  91. }
  92. // Still load the PLIST to ensure that the existing calls to
  93. // __isOSVersionAtLeast still work even with new compiler-rt and old OSes.
  94. // Load CoreFoundation dynamically
  95. const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");
  96. if (!NullAllocator)
  97. return;
  98. const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator;
  99. CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc =
  100. (CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT,
  101. "CFDataCreateWithBytesNoCopy");
  102. if (!CFDataCreateWithBytesNoCopyFunc)
  103. return;
  104. CFPropertyListCreateWithDataFuncTy CFPropertyListCreateWithDataFunc =
  105. (CFPropertyListCreateWithDataFuncTy)dlsym(RTLD_DEFAULT,
  106. "CFPropertyListCreateWithData");
  107. // CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it
  108. // will be NULL on earlier OS versions.
  109. #pragma clang diagnostic push
  110. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  111. CFPropertyListCreateFromXMLDataFuncTy CFPropertyListCreateFromXMLDataFunc =
  112. (CFPropertyListCreateFromXMLDataFuncTy)dlsym(
  113. RTLD_DEFAULT, "CFPropertyListCreateFromXMLData");
  114. #pragma clang diagnostic pop
  115. // CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it
  116. // might be NULL in future OS versions.
  117. if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc)
  118. return;
  119. CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc =
  120. (CFStringCreateWithCStringNoCopyFuncTy)dlsym(
  121. RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy");
  122. if (!CFStringCreateWithCStringNoCopyFunc)
  123. return;
  124. CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc =
  125. (CFDictionaryGetValueFuncTy)dlsym(RTLD_DEFAULT, "CFDictionaryGetValue");
  126. if (!CFDictionaryGetValueFunc)
  127. return;
  128. CFGetTypeIDFuncTy CFGetTypeIDFunc =
  129. (CFGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFGetTypeID");
  130. if (!CFGetTypeIDFunc)
  131. return;
  132. CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc =
  133. (CFStringGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetTypeID");
  134. if (!CFStringGetTypeIDFunc)
  135. return;
  136. CFStringGetCStringFuncTy CFStringGetCStringFunc =
  137. (CFStringGetCStringFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetCString");
  138. if (!CFStringGetCStringFunc)
  139. return;
  140. CFReleaseFuncTy CFReleaseFunc =
  141. (CFReleaseFuncTy)dlsym(RTLD_DEFAULT, "CFRelease");
  142. if (!CFReleaseFunc)
  143. return;
  144. char *PListPath = "/System/Library/CoreServices/SystemVersion.plist";
  145. #if TARGET_OS_SIMULATOR
  146. char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT");
  147. if (!PListPathPrefix)
  148. return;
  149. char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1];
  150. strcpy(FullPath, PListPathPrefix);
  151. strcat(FullPath, PListPath);
  152. PListPath = FullPath;
  153. #endif
  154. FILE *PropertyList = fopen(PListPath, "r");
  155. if (!PropertyList)
  156. return;
  157. // Dynamically allocated stuff.
  158. CFDictionaryRef PListRef = NULL;
  159. CFDataRef FileContentsRef = NULL;
  160. UInt8 *PListBuf = NULL;
  161. fseek(PropertyList, 0, SEEK_END);
  162. long PListFileSize = ftell(PropertyList);
  163. if (PListFileSize < 0)
  164. goto Fail;
  165. rewind(PropertyList);
  166. PListBuf = malloc((size_t)PListFileSize);
  167. if (!PListBuf)
  168. goto Fail;
  169. size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList);
  170. if (NumRead != (size_t)PListFileSize)
  171. goto Fail;
  172. // Get the file buffer into CF's format. We pass in a null allocator here *
  173. // because we free PListBuf ourselves
  174. FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)(
  175. NULL, PListBuf, (CFIndex)NumRead, AllocatorNull);
  176. if (!FileContentsRef)
  177. goto Fail;
  178. if (CFPropertyListCreateWithDataFunc)
  179. PListRef = (*CFPropertyListCreateWithDataFunc)(
  180. NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL);
  181. else
  182. PListRef = (*CFPropertyListCreateFromXMLDataFunc)(
  183. NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL);
  184. if (!PListRef)
  185. goto Fail;
  186. CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(
  187. NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull);
  188. if (!ProductVersion)
  189. goto Fail;
  190. CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion);
  191. (*CFReleaseFunc)(ProductVersion);
  192. if (!OpaqueValue ||
  193. (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)())
  194. goto Fail;
  195. char VersionStr[32];
  196. if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr,
  197. sizeof(VersionStr), CF_STRING_ENCODING_UTF8))
  198. goto Fail;
  199. sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);
  200. Fail:
  201. if (PListRef)
  202. (*CFReleaseFunc)(PListRef);
  203. if (FileContentsRef)
  204. (*CFReleaseFunc)(FileContentsRef);
  205. free(PListBuf);
  206. fclose(PropertyList);
  207. }
  208. // Find and parse the SystemVersion.plist file.
  209. static void compatibilityInitializeAvailabilityCheck(void *Unused) {
  210. (void)Unused;
  211. _initializeAvailabilityCheck(/*LoadPlist=*/true);
  212. }
  213. static void initializeAvailabilityCheck(void *Unused) {
  214. (void)Unused;
  215. _initializeAvailabilityCheck(/*LoadPlist=*/false);
  216. }
  217. // This old API entry point is no longer used by Clang for Darwin. We still need
  218. // to keep it around to ensure that object files that reference it are still
  219. // usable when linked with new compiler-rt.
  220. int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
  221. // Populate the global version variables, if they haven't already.
  222. dispatch_once_f(&CompatibilityDispatchOnceCounter, NULL,
  223. compatibilityInitializeAvailabilityCheck);
  224. if (Major < GlobalMajor)
  225. return 1;
  226. if (Major > GlobalMajor)
  227. return 0;
  228. if (Minor < GlobalMinor)
  229. return 1;
  230. if (Minor > GlobalMinor)
  231. return 0;
  232. return Subminor <= GlobalSubminor;
  233. }
  234. static inline uint32_t ConstructVersion(uint32_t Major, uint32_t Minor,
  235. uint32_t Subminor) {
  236. return ((Major & 0xffff) << 16) | ((Minor & 0xff) << 8) | (Subminor & 0xff);
  237. }
  238. int32_t __isPlatformVersionAtLeast(uint32_t Platform, uint32_t Major,
  239. uint32_t Minor, uint32_t Subminor) {
  240. dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck);
  241. if (!AvailabilityVersionCheck) {
  242. return __isOSVersionAtLeast(Major, Minor, Subminor);
  243. }
  244. dyld_build_version_t Versions[] = {
  245. {Platform, ConstructVersion(Major, Minor, Subminor)}};
  246. return AvailabilityVersionCheck(1, Versions);
  247. }
  248. #elif __ANDROID__
  249. #include <pthread.h>
  250. #include <stdlib.h>
  251. #include <string.h>
  252. #include <sys/system_properties.h>
  253. static int SdkVersion;
  254. static int IsPreRelease;
  255. static void readSystemProperties(void) {
  256. char buf[PROP_VALUE_MAX];
  257. if (__system_property_get("ro.build.version.sdk", buf) == 0) {
  258. // When the system property doesn't exist, defaults to future API level.
  259. SdkVersion = __ANDROID_API_FUTURE__;
  260. } else {
  261. SdkVersion = atoi(buf);
  262. }
  263. if (__system_property_get("ro.build.version.codename", buf) == 0) {
  264. IsPreRelease = 1;
  265. } else {
  266. IsPreRelease = strcmp(buf, "REL") != 0;
  267. }
  268. return;
  269. }
  270. int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
  271. (void) Minor;
  272. (void) Subminor;
  273. static pthread_once_t once = PTHREAD_ONCE_INIT;
  274. pthread_once(&once, readSystemProperties);
  275. // Allow all on pre-release. Note that we still rely on compile-time checks.
  276. return SdkVersion >= Major || IsPreRelease;
  277. }
  278. #else
  279. // Silence an empty translation unit warning.
  280. typedef int unused;
  281. #endif