os_version_check.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /* ===-- os_version_check.c - OS version checking -------------------------===
  2. *
  3. * The LLVM Compiler Infrastructure
  4. *
  5. * This file is dual licensed under the MIT and the University of Illinois Open
  6. * Source Licenses. See LICENSE.TXT for details.
  7. *
  8. * ===----------------------------------------------------------------------===
  9. *
  10. * This file implements the function __isOSVersionAtLeast, used by
  11. * Objective-C's @available
  12. *
  13. * ===----------------------------------------------------------------------===
  14. */
  15. #ifdef __APPLE__
  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. /* These three variables hold the host's OS version. */
  24. static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;
  25. static dispatch_once_t DispatchOnceCounter;
  26. // _availability_version_check darwin API support.
  27. typedef uint32_t dyld_platform_t;
  28. typedef struct {
  29. dyld_platform_t platform;
  30. uint32_t version;
  31. } dyld_build_version_t;
  32. typedef bool (*AvailabilityVersionCheckFuncTy)(uint32_t count,
  33. dyld_build_version_t versions[]);
  34. static AvailabilityVersionCheckFuncTy AvailabilityVersionCheck;
  35. /* We can't include <CoreFoundation/CoreFoundation.h> directly from here, so
  36. * just forward declare everything that we need from it. */
  37. typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef,
  38. *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef;
  39. #if __LLP64__
  40. typedef unsigned long long CFTypeID;
  41. typedef unsigned long long CFOptionFlags;
  42. typedef signed long long CFIndex;
  43. #else
  44. typedef unsigned long CFTypeID;
  45. typedef unsigned long CFOptionFlags;
  46. typedef signed long CFIndex;
  47. #endif
  48. typedef unsigned char UInt8;
  49. typedef _Bool Boolean;
  50. typedef CFIndex CFPropertyListFormat;
  51. typedef uint32_t CFStringEncoding;
  52. /* kCFStringEncodingASCII analog. */
  53. #define CF_STRING_ENCODING_ASCII 0x0600
  54. /* kCFStringEncodingUTF8 analog. */
  55. #define CF_STRING_ENCODING_UTF8 0x08000100
  56. #define CF_PROPERTY_LIST_IMMUTABLE 0
  57. typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef,
  58. const UInt8 *, CFIndex,
  59. CFAllocatorRef);
  60. typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)(
  61. CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *,
  62. CFErrorRef *);
  63. typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)(
  64. CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *);
  65. typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef,
  66. const char *,
  67. CFStringEncoding,
  68. CFAllocatorRef);
  69. typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef,
  70. const void *);
  71. typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef);
  72. typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void);
  73. typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex,
  74. CFStringEncoding);
  75. typedef void (*CFReleaseFuncTy)(CFTypeRef);
  76. /* Find and parse the SystemVersion.plist file. */
  77. static void initializeAvailabilityCheck(void *Unused) {
  78. (void)Unused;
  79. // Use the new API if it's is available. Still load the PLIST to ensure that the
  80. // existing calls to __isOSVersionAtLeast still work even with new
  81. // compiler-rt and new OSes.
  82. AvailabilityVersionCheck = (AvailabilityVersionCheckFuncTy)dlsym(
  83. RTLD_DEFAULT, "_availability_version_check");
  84. /* Load CoreFoundation dynamically */
  85. const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");
  86. if (!NullAllocator)
  87. return;
  88. const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator;
  89. CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc =
  90. (CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT,
  91. "CFDataCreateWithBytesNoCopy");
  92. if (!CFDataCreateWithBytesNoCopyFunc)
  93. return;
  94. CFPropertyListCreateWithDataFuncTy CFPropertyListCreateWithDataFunc =
  95. (CFPropertyListCreateWithDataFuncTy)dlsym(
  96. RTLD_DEFAULT, "CFPropertyListCreateWithData");
  97. /* CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it
  98. * will be NULL on earlier OS versions. */
  99. #pragma clang diagnostic push
  100. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  101. CFPropertyListCreateFromXMLDataFuncTy CFPropertyListCreateFromXMLDataFunc =
  102. (CFPropertyListCreateFromXMLDataFuncTy)dlsym(
  103. RTLD_DEFAULT, "CFPropertyListCreateFromXMLData");
  104. #pragma clang diagnostic pop
  105. /* CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it
  106. * might be NULL in future OS versions. */
  107. if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc)
  108. return;
  109. CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc =
  110. (CFStringCreateWithCStringNoCopyFuncTy)dlsym(
  111. RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy");
  112. if (!CFStringCreateWithCStringNoCopyFunc)
  113. return;
  114. CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc =
  115. (CFDictionaryGetValueFuncTy)dlsym(RTLD_DEFAULT, "CFDictionaryGetValue");
  116. if (!CFDictionaryGetValueFunc)
  117. return;
  118. CFGetTypeIDFuncTy CFGetTypeIDFunc =
  119. (CFGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFGetTypeID");
  120. if (!CFGetTypeIDFunc)
  121. return;
  122. CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc =
  123. (CFStringGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetTypeID");
  124. if (!CFStringGetTypeIDFunc)
  125. return;
  126. CFStringGetCStringFuncTy CFStringGetCStringFunc =
  127. (CFStringGetCStringFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetCString");
  128. if (!CFStringGetCStringFunc)
  129. return;
  130. CFReleaseFuncTy CFReleaseFunc =
  131. (CFReleaseFuncTy)dlsym(RTLD_DEFAULT, "CFRelease");
  132. if (!CFReleaseFunc)
  133. return;
  134. char *PListPath = "/System/Library/CoreServices/SystemVersion.plist";
  135. #if TARGET_OS_SIMULATOR
  136. char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT");
  137. if (!PListPathPrefix)
  138. return;
  139. char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1];
  140. strcpy(FullPath, PListPathPrefix);
  141. strcat(FullPath, PListPath);
  142. PListPath = FullPath;
  143. #endif
  144. FILE *PropertyList = fopen(PListPath, "r");
  145. if (!PropertyList)
  146. return;
  147. /* Dynamically allocated stuff. */
  148. CFDictionaryRef PListRef = NULL;
  149. CFDataRef FileContentsRef = NULL;
  150. UInt8 *PListBuf = NULL;
  151. fseek(PropertyList, 0, SEEK_END);
  152. long PListFileSize = ftell(PropertyList);
  153. if (PListFileSize < 0)
  154. goto Fail;
  155. rewind(PropertyList);
  156. PListBuf = malloc((size_t)PListFileSize);
  157. if (!PListBuf)
  158. goto Fail;
  159. size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList);
  160. if (NumRead != (size_t)PListFileSize)
  161. goto Fail;
  162. /* Get the file buffer into CF's format. We pass in a null allocator here *
  163. * because we free PListBuf ourselves */
  164. FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)(
  165. NULL, PListBuf, (CFIndex)NumRead, AllocatorNull);
  166. if (!FileContentsRef)
  167. goto Fail;
  168. if (CFPropertyListCreateWithDataFunc)
  169. PListRef = (*CFPropertyListCreateWithDataFunc)(
  170. NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL);
  171. else
  172. PListRef = (*CFPropertyListCreateFromXMLDataFunc)(
  173. NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL);
  174. if (!PListRef)
  175. goto Fail;
  176. CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(
  177. NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull);
  178. if (!ProductVersion)
  179. goto Fail;
  180. CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion);
  181. (*CFReleaseFunc)(ProductVersion);
  182. if (!OpaqueValue ||
  183. (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)())
  184. goto Fail;
  185. char VersionStr[32];
  186. if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr,
  187. sizeof(VersionStr), CF_STRING_ENCODING_UTF8))
  188. goto Fail;
  189. sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);
  190. Fail:
  191. if (PListRef)
  192. (*CFReleaseFunc)(PListRef);
  193. if (FileContentsRef)
  194. (*CFReleaseFunc)(FileContentsRef);
  195. free(PListBuf);
  196. fclose(PropertyList);
  197. }
  198. // This old API entry point is no longer used by Clang. We still need to keep it
  199. // around to ensure that object files that reference it are still usable when
  200. // linked with new compiler-rt.
  201. int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {
  202. /* Populate the global version variables, if they haven't already. */
  203. dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck);
  204. if (Major < GlobalMajor)
  205. return 1;
  206. if (Major > GlobalMajor)
  207. return 0;
  208. if (Minor < GlobalMinor)
  209. return 1;
  210. if (Minor > GlobalMinor)
  211. return 0;
  212. return Subminor <= GlobalSubminor;
  213. }
  214. static inline uint32_t ConstructVersion(uint32_t Major, uint32_t Minor,
  215. uint32_t Subminor) {
  216. return ((Major & 0xffff) << 16) | ((Minor & 0xff) << 8) | (Subminor & 0xff);
  217. }
  218. int32_t __isPlatformVersionAtLeast(uint32_t Platform, uint32_t Major,
  219. uint32_t Minor, uint32_t Subminor) {
  220. dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck);
  221. if (!AvailabilityVersionCheck) {
  222. return __isOSVersionAtLeast(Major, Minor, Subminor);
  223. }
  224. dyld_build_version_t Versions[] = {
  225. {Platform, ConstructVersion(Major, Minor, Subminor)}};
  226. return AvailabilityVersionCheck(1, Versions);
  227. }
  228. #else
  229. /* Silence an empty translation unit warning. */
  230. typedef int unused;
  231. #endif