brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.2 KiB · b10f23a Raw
355 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the function __isOSVersionAtLeast, used by10// Objective-C's @available11//12//===----------------------------------------------------------------------===//13 14#ifdef __APPLE__15 16#include <TargetConditionals.h>17#include <assert.h>18#include <dispatch/dispatch.h>19#include <dlfcn.h>20#include <stdint.h>21#include <stdio.h>22#include <stdlib.h>23#include <string.h>24 25// These three variables hold the host's OS version.26static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;27static dispatch_once_t DispatchOnceCounter;28static dispatch_once_t CompatibilityDispatchOnceCounter;29 30// _availability_version_check darwin API support.31typedef uint32_t dyld_platform_t;32 33typedef struct {34  dyld_platform_t platform;35  uint32_t version;36} dyld_build_version_t;37 38typedef bool (*AvailabilityVersionCheckFuncTy)(uint32_t count,39                                               dyld_build_version_t versions[]);40 41static AvailabilityVersionCheckFuncTy AvailabilityVersionCheck;42 43// We can't include <CoreFoundation/CoreFoundation.h> directly from here, so44// just forward declare everything that we need from it.45 46typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef,47    *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef;48 49#if __LLP64__50typedef unsigned long long CFTypeID;51typedef unsigned long long CFOptionFlags;52typedef signed long long CFIndex;53#else54typedef unsigned long CFTypeID;55typedef unsigned long CFOptionFlags;56typedef signed long CFIndex;57#endif58 59typedef unsigned char UInt8;60typedef _Bool Boolean;61typedef CFIndex CFPropertyListFormat;62typedef uint32_t CFStringEncoding;63 64// kCFStringEncodingASCII analog.65#define CF_STRING_ENCODING_ASCII 0x060066// kCFStringEncodingUTF8 analog.67#define CF_STRING_ENCODING_UTF8 0x0800010068#define CF_PROPERTY_LIST_IMMUTABLE 069 70typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef,71                                                       const UInt8 *, CFIndex,72                                                       CFAllocatorRef);73typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)(74    CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *,75    CFErrorRef *);76typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)(77    CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *);78typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef,79                                                             const char *,80                                                             CFStringEncoding,81                                                             CFAllocatorRef);82typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef,83                                                  const void *);84typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef);85typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void);86typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex,87                                            CFStringEncoding);88typedef void (*CFReleaseFuncTy)(CFTypeRef);89 90extern __attribute__((weak_import))91bool _availability_version_check(uint32_t count,92                                 dyld_build_version_t versions[]);93 94static void _initializeAvailabilityCheck(bool LoadPlist) {95  if (AvailabilityVersionCheck && !LoadPlist) {96    // New API is supported and we're not being asked to load the plist,97    // exit early!98    return;99  }100 101  // Use the new API if it's is available.102  if (_availability_version_check)103    AvailabilityVersionCheck = &_availability_version_check;104 105  if (AvailabilityVersionCheck && !LoadPlist) {106    // New API is supported and we're not being asked to load the plist,107    // exit early!108    return;109  }110  // Still load the PLIST to ensure that the existing calls to111  // __isOSVersionAtLeast still work even with new compiler-rt and old OSes.112 113  // Load CoreFoundation dynamically114  const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");115  if (!NullAllocator)116    return;117  const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator;118  CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc =119      (CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT,120                                               "CFDataCreateWithBytesNoCopy");121  if (!CFDataCreateWithBytesNoCopyFunc)122    return;123  CFPropertyListCreateWithDataFuncTy CFPropertyListCreateWithDataFunc =124      (CFPropertyListCreateWithDataFuncTy)dlsym(RTLD_DEFAULT,125                                                "CFPropertyListCreateWithData");126// CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it127// will be NULL on earlier OS versions.128#pragma clang diagnostic push129#pragma clang diagnostic ignored "-Wdeprecated-declarations"130  CFPropertyListCreateFromXMLDataFuncTy CFPropertyListCreateFromXMLDataFunc =131      (CFPropertyListCreateFromXMLDataFuncTy)dlsym(132          RTLD_DEFAULT, "CFPropertyListCreateFromXMLData");133#pragma clang diagnostic pop134  // CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it135  // might be NULL in future OS versions.136  if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc)137    return;138  CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc =139      (CFStringCreateWithCStringNoCopyFuncTy)dlsym(140          RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy");141  if (!CFStringCreateWithCStringNoCopyFunc)142    return;143  CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc =144      (CFDictionaryGetValueFuncTy)dlsym(RTLD_DEFAULT, "CFDictionaryGetValue");145  if (!CFDictionaryGetValueFunc)146    return;147  CFGetTypeIDFuncTy CFGetTypeIDFunc =148      (CFGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFGetTypeID");149  if (!CFGetTypeIDFunc)150    return;151  CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc =152      (CFStringGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetTypeID");153  if (!CFStringGetTypeIDFunc)154    return;155  CFStringGetCStringFuncTy CFStringGetCStringFunc =156      (CFStringGetCStringFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetCString");157  if (!CFStringGetCStringFunc)158    return;159  CFReleaseFuncTy CFReleaseFunc =160      (CFReleaseFuncTy)dlsym(RTLD_DEFAULT, "CFRelease");161  if (!CFReleaseFunc)162    return;163 164  char *PListPath = "/System/Library/CoreServices/SystemVersion.plist";165 166#if TARGET_OS_SIMULATOR167  char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT");168  if (!PListPathPrefix)169    return;170  char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1];171  strcpy(FullPath, PListPathPrefix);172  strcat(FullPath, PListPath);173  PListPath = FullPath;174#endif175  FILE *PropertyList = fopen(PListPath, "r");176  if (!PropertyList)177    return;178 179  // Dynamically allocated stuff.180  CFDictionaryRef PListRef = NULL;181  CFDataRef FileContentsRef = NULL;182  UInt8 *PListBuf = NULL;183 184  fseek(PropertyList, 0, SEEK_END);185  long PListFileSize = ftell(PropertyList);186  if (PListFileSize < 0)187    goto Fail;188  rewind(PropertyList);189 190  PListBuf = malloc((size_t)PListFileSize);191  if (!PListBuf)192    goto Fail;193 194  size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList);195  if (NumRead != (size_t)PListFileSize)196    goto Fail;197 198  // Get the file buffer into CF's format. We pass in a null allocator here *199  // because we free PListBuf ourselves200  FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)(201      NULL, PListBuf, (CFIndex)NumRead, AllocatorNull);202  if (!FileContentsRef)203    goto Fail;204 205  if (CFPropertyListCreateWithDataFunc)206    PListRef = (*CFPropertyListCreateWithDataFunc)(207        NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL);208  else209    PListRef = (*CFPropertyListCreateFromXMLDataFunc)(210        NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL);211  if (!PListRef)212    goto Fail;213 214  CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(215      NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull);216  if (!ProductVersion)217    goto Fail;218  CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion);219  (*CFReleaseFunc)(ProductVersion);220  if (!OpaqueValue ||221      (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)())222    goto Fail;223 224  char VersionStr[32];225  if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr,226                                 sizeof(VersionStr), CF_STRING_ENCODING_UTF8))227    goto Fail;228  sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);229 230Fail:231  if (PListRef)232    (*CFReleaseFunc)(PListRef);233  if (FileContentsRef)234    (*CFReleaseFunc)(FileContentsRef);235  free(PListBuf);236  fclose(PropertyList);237}238 239// Find and parse the SystemVersion.plist file.240static void compatibilityInitializeAvailabilityCheck(void *Unused) {241  (void)Unused;242  _initializeAvailabilityCheck(/*LoadPlist=*/true);243}244 245static void initializeAvailabilityCheck(void *Unused) {246  (void)Unused;247  _initializeAvailabilityCheck(/*LoadPlist=*/false);248}249 250// This old API entry point is no longer used by Clang for Darwin. We still need251// to keep it around to ensure that object files that reference it are still252// usable when linked with new compiler-rt.253int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {254  // Populate the global version variables, if they haven't already.255  dispatch_once_f(&CompatibilityDispatchOnceCounter, NULL,256                  compatibilityInitializeAvailabilityCheck);257 258  if (Major < GlobalMajor)259    return 1;260  if (Major > GlobalMajor)261    return 0;262  if (Minor < GlobalMinor)263    return 1;264  if (Minor > GlobalMinor)265    return 0;266  return Subminor <= GlobalSubminor;267}268 269static inline uint32_t ConstructVersion(uint32_t Major, uint32_t Minor,270                                        uint32_t Subminor) {271  return ((Major & 0xffff) << 16) | ((Minor & 0xff) << 8) | (Subminor & 0xff);272}273 274#define PLATFORM_MACOS 1275 276int32_t __isPlatformVersionAtLeast(uint32_t Platform, uint32_t Major,277                                   uint32_t Minor, uint32_t Subminor) {278  dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck);279 280  if (!AvailabilityVersionCheck) {281    return __isOSVersionAtLeast(Major, Minor, Subminor);282  }283  dyld_build_version_t Versions[] = {284      {Platform, ConstructVersion(Major, Minor, Subminor)}};285  return AvailabilityVersionCheck(1, Versions);286}287 288#if TARGET_OS_OSX289 290int32_t __isPlatformOrVariantPlatformVersionAtLeast(291    uint32_t Platform, uint32_t Major, uint32_t Minor, uint32_t Subminor,292    uint32_t Platform2, uint32_t Major2, uint32_t Minor2, uint32_t Subminor2) {293  dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck);294 295  if (!AvailabilityVersionCheck) {296    // Handle case of back-deployment for older macOS.297    if (Platform == PLATFORM_MACOS) {298      return __isOSVersionAtLeast(Major, Minor, Subminor);299    }300    assert(Platform2 == PLATFORM_MACOS && "unexpected platform");301    return __isOSVersionAtLeast(Major2, Minor2, Subminor2);302  }303  dyld_build_version_t Versions[] = {304      {Platform, ConstructVersion(Major, Minor, Subminor)},305      {Platform2, ConstructVersion(Major2, Minor2, Subminor2)}};306  return AvailabilityVersionCheck(2, Versions);307}308 309#endif310 311#elif __ANDROID__312 313#include <pthread.h>314#include <stdlib.h>315#include <string.h>316#include <sys/system_properties.h>317 318static int SdkVersion;319static int IsPreRelease;320 321static void readSystemProperties(void) {322  char buf[PROP_VALUE_MAX];323 324  if (__system_property_get("ro.build.version.sdk", buf) == 0) {325    // When the system property doesn't exist, defaults to future API level.326    SdkVersion = __ANDROID_API_FUTURE__;327  } else {328    SdkVersion = atoi(buf);329  }330 331  if (__system_property_get("ro.build.version.codename", buf) == 0) {332    IsPreRelease = 1;333  } else {334    IsPreRelease = strcmp(buf, "REL") != 0;335  }336  return;337}338 339int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) {340  (void) Minor;341  (void) Subminor;342  static pthread_once_t once = PTHREAD_ONCE_INIT;343  pthread_once(&once, readSystemProperties);344 345  // Allow all on pre-release. Note that we still rely on compile-time checks.346  return SdkVersion >= Major || IsPreRelease;347}348 349#else350 351// Silence an empty translation unit warning.352typedef int unused;353 354#endif355