os_version_check.c 11 KB

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