10b57cec5SDimitry Andric //===-- os_version_check.c - OS version checking -------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file implements the function __isOSVersionAtLeast, used by 100b57cec5SDimitry Andric // Objective-C's @available 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 130b57cec5SDimitry Andric 140b57cec5SDimitry Andric #ifdef __APPLE__ 150b57cec5SDimitry Andric 160b57cec5SDimitry Andric #include <TargetConditionals.h> 170b57cec5SDimitry Andric #include <dispatch/dispatch.h> 180b57cec5SDimitry Andric #include <dlfcn.h> 190b57cec5SDimitry Andric #include <stdint.h> 200b57cec5SDimitry Andric #include <stdio.h> 210b57cec5SDimitry Andric #include <stdlib.h> 220b57cec5SDimitry Andric #include <string.h> 230b57cec5SDimitry Andric 240b57cec5SDimitry Andric // These three variables hold the host's OS version. 250b57cec5SDimitry Andric static int32_t GlobalMajor, GlobalMinor, GlobalSubminor; 260b57cec5SDimitry Andric static dispatch_once_t DispatchOnceCounter; 27e8d8bef9SDimitry Andric static dispatch_once_t CompatibilityDispatchOnceCounter; 28e8d8bef9SDimitry Andric 29e8d8bef9SDimitry Andric // _availability_version_check darwin API support. 30e8d8bef9SDimitry Andric typedef uint32_t dyld_platform_t; 31e8d8bef9SDimitry Andric 32e8d8bef9SDimitry Andric typedef struct { 33e8d8bef9SDimitry Andric dyld_platform_t platform; 34e8d8bef9SDimitry Andric uint32_t version; 35e8d8bef9SDimitry Andric } dyld_build_version_t; 36e8d8bef9SDimitry Andric 37e8d8bef9SDimitry Andric typedef bool (*AvailabilityVersionCheckFuncTy)(uint32_t count, 38e8d8bef9SDimitry Andric dyld_build_version_t versions[]); 39e8d8bef9SDimitry Andric 40e8d8bef9SDimitry Andric static AvailabilityVersionCheckFuncTy AvailabilityVersionCheck; 410b57cec5SDimitry Andric 420b57cec5SDimitry Andric // We can't include <CoreFoundation/CoreFoundation.h> directly from here, so 430b57cec5SDimitry Andric // just forward declare everything that we need from it. 440b57cec5SDimitry Andric 450b57cec5SDimitry Andric typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef, 460b57cec5SDimitry Andric *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef; 470b57cec5SDimitry Andric 480b57cec5SDimitry Andric #if __LLP64__ 490b57cec5SDimitry Andric typedef unsigned long long CFTypeID; 500b57cec5SDimitry Andric typedef unsigned long long CFOptionFlags; 510b57cec5SDimitry Andric typedef signed long long CFIndex; 520b57cec5SDimitry Andric #else 530b57cec5SDimitry Andric typedef unsigned long CFTypeID; 540b57cec5SDimitry Andric typedef unsigned long CFOptionFlags; 550b57cec5SDimitry Andric typedef signed long CFIndex; 560b57cec5SDimitry Andric #endif 570b57cec5SDimitry Andric 580b57cec5SDimitry Andric typedef unsigned char UInt8; 590b57cec5SDimitry Andric typedef _Bool Boolean; 600b57cec5SDimitry Andric typedef CFIndex CFPropertyListFormat; 610b57cec5SDimitry Andric typedef uint32_t CFStringEncoding; 620b57cec5SDimitry Andric 630b57cec5SDimitry Andric // kCFStringEncodingASCII analog. 640b57cec5SDimitry Andric #define CF_STRING_ENCODING_ASCII 0x0600 650b57cec5SDimitry Andric // kCFStringEncodingUTF8 analog. 660b57cec5SDimitry Andric #define CF_STRING_ENCODING_UTF8 0x08000100 670b57cec5SDimitry Andric #define CF_PROPERTY_LIST_IMMUTABLE 0 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef, 700b57cec5SDimitry Andric const UInt8 *, CFIndex, 710b57cec5SDimitry Andric CFAllocatorRef); 720b57cec5SDimitry Andric typedef CFPropertyListRef (*CFPropertyListCreateWithDataFuncTy)( 730b57cec5SDimitry Andric CFAllocatorRef, CFDataRef, CFOptionFlags, CFPropertyListFormat *, 740b57cec5SDimitry Andric CFErrorRef *); 750b57cec5SDimitry Andric typedef CFPropertyListRef (*CFPropertyListCreateFromXMLDataFuncTy)( 760b57cec5SDimitry Andric CFAllocatorRef, CFDataRef, CFOptionFlags, CFStringRef *); 770b57cec5SDimitry Andric typedef CFStringRef (*CFStringCreateWithCStringNoCopyFuncTy)(CFAllocatorRef, 780b57cec5SDimitry Andric const char *, 790b57cec5SDimitry Andric CFStringEncoding, 800b57cec5SDimitry Andric CFAllocatorRef); 810b57cec5SDimitry Andric typedef const void *(*CFDictionaryGetValueFuncTy)(CFDictionaryRef, 820b57cec5SDimitry Andric const void *); 830b57cec5SDimitry Andric typedef CFTypeID (*CFGetTypeIDFuncTy)(CFTypeRef); 840b57cec5SDimitry Andric typedef CFTypeID (*CFStringGetTypeIDFuncTy)(void); 850b57cec5SDimitry Andric typedef Boolean (*CFStringGetCStringFuncTy)(CFStringRef, char *, CFIndex, 860b57cec5SDimitry Andric CFStringEncoding); 870b57cec5SDimitry Andric typedef void (*CFReleaseFuncTy)(CFTypeRef); 880b57cec5SDimitry Andric 8906c3fb27SDimitry Andric extern __attribute__((weak_import)) 9006c3fb27SDimitry Andric bool _availability_version_check(uint32_t count, 9106c3fb27SDimitry Andric dyld_build_version_t versions[]); 9206c3fb27SDimitry Andric 93e8d8bef9SDimitry Andric static void _initializeAvailabilityCheck(bool LoadPlist) { 94e8d8bef9SDimitry Andric if (AvailabilityVersionCheck && !LoadPlist) { 95e8d8bef9SDimitry Andric // New API is supported and we're not being asked to load the plist, 96e8d8bef9SDimitry Andric // exit early! 97e8d8bef9SDimitry Andric return; 98e8d8bef9SDimitry Andric } 99e8d8bef9SDimitry Andric 100e8d8bef9SDimitry Andric // Use the new API if it's is available. 10106c3fb27SDimitry Andric if (_availability_version_check) 10206c3fb27SDimitry Andric AvailabilityVersionCheck = &_availability_version_check; 103e8d8bef9SDimitry Andric 104e8d8bef9SDimitry Andric if (AvailabilityVersionCheck && !LoadPlist) { 105e8d8bef9SDimitry Andric // New API is supported and we're not being asked to load the plist, 106e8d8bef9SDimitry Andric // exit early! 107e8d8bef9SDimitry Andric return; 108e8d8bef9SDimitry Andric } 109e8d8bef9SDimitry Andric // Still load the PLIST to ensure that the existing calls to 110e8d8bef9SDimitry Andric // __isOSVersionAtLeast still work even with new compiler-rt and old OSes. 111e8d8bef9SDimitry Andric 1120b57cec5SDimitry Andric // Load CoreFoundation dynamically 1130b57cec5SDimitry Andric const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull"); 1140b57cec5SDimitry Andric if (!NullAllocator) 1150b57cec5SDimitry Andric return; 1160b57cec5SDimitry Andric const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator; 1170b57cec5SDimitry Andric CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc = 1180b57cec5SDimitry Andric (CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT, 1190b57cec5SDimitry Andric "CFDataCreateWithBytesNoCopy"); 1200b57cec5SDimitry Andric if (!CFDataCreateWithBytesNoCopyFunc) 1210b57cec5SDimitry Andric return; 1220b57cec5SDimitry Andric CFPropertyListCreateWithDataFuncTy CFPropertyListCreateWithDataFunc = 1230b57cec5SDimitry Andric (CFPropertyListCreateWithDataFuncTy)dlsym(RTLD_DEFAULT, 1240b57cec5SDimitry Andric "CFPropertyListCreateWithData"); 1250b57cec5SDimitry Andric // CFPropertyListCreateWithData was introduced only in macOS 10.6+, so it 1260b57cec5SDimitry Andric // will be NULL on earlier OS versions. 1270b57cec5SDimitry Andric #pragma clang diagnostic push 1280b57cec5SDimitry Andric #pragma clang diagnostic ignored "-Wdeprecated-declarations" 1290b57cec5SDimitry Andric CFPropertyListCreateFromXMLDataFuncTy CFPropertyListCreateFromXMLDataFunc = 1300b57cec5SDimitry Andric (CFPropertyListCreateFromXMLDataFuncTy)dlsym( 1310b57cec5SDimitry Andric RTLD_DEFAULT, "CFPropertyListCreateFromXMLData"); 1320b57cec5SDimitry Andric #pragma clang diagnostic pop 1330b57cec5SDimitry Andric // CFPropertyListCreateFromXMLDataFunc is deprecated in macOS 10.10, so it 1340b57cec5SDimitry Andric // might be NULL in future OS versions. 1350b57cec5SDimitry Andric if (!CFPropertyListCreateWithDataFunc && !CFPropertyListCreateFromXMLDataFunc) 1360b57cec5SDimitry Andric return; 1370b57cec5SDimitry Andric CFStringCreateWithCStringNoCopyFuncTy CFStringCreateWithCStringNoCopyFunc = 1380b57cec5SDimitry Andric (CFStringCreateWithCStringNoCopyFuncTy)dlsym( 1390b57cec5SDimitry Andric RTLD_DEFAULT, "CFStringCreateWithCStringNoCopy"); 1400b57cec5SDimitry Andric if (!CFStringCreateWithCStringNoCopyFunc) 1410b57cec5SDimitry Andric return; 1420b57cec5SDimitry Andric CFDictionaryGetValueFuncTy CFDictionaryGetValueFunc = 1430b57cec5SDimitry Andric (CFDictionaryGetValueFuncTy)dlsym(RTLD_DEFAULT, "CFDictionaryGetValue"); 1440b57cec5SDimitry Andric if (!CFDictionaryGetValueFunc) 1450b57cec5SDimitry Andric return; 1460b57cec5SDimitry Andric CFGetTypeIDFuncTy CFGetTypeIDFunc = 1470b57cec5SDimitry Andric (CFGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFGetTypeID"); 1480b57cec5SDimitry Andric if (!CFGetTypeIDFunc) 1490b57cec5SDimitry Andric return; 1500b57cec5SDimitry Andric CFStringGetTypeIDFuncTy CFStringGetTypeIDFunc = 1510b57cec5SDimitry Andric (CFStringGetTypeIDFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetTypeID"); 1520b57cec5SDimitry Andric if (!CFStringGetTypeIDFunc) 1530b57cec5SDimitry Andric return; 1540b57cec5SDimitry Andric CFStringGetCStringFuncTy CFStringGetCStringFunc = 1550b57cec5SDimitry Andric (CFStringGetCStringFuncTy)dlsym(RTLD_DEFAULT, "CFStringGetCString"); 1560b57cec5SDimitry Andric if (!CFStringGetCStringFunc) 1570b57cec5SDimitry Andric return; 1580b57cec5SDimitry Andric CFReleaseFuncTy CFReleaseFunc = 1590b57cec5SDimitry Andric (CFReleaseFuncTy)dlsym(RTLD_DEFAULT, "CFRelease"); 1600b57cec5SDimitry Andric if (!CFReleaseFunc) 1610b57cec5SDimitry Andric return; 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric char *PListPath = "/System/Library/CoreServices/SystemVersion.plist"; 1640b57cec5SDimitry Andric 1650b57cec5SDimitry Andric #if TARGET_OS_SIMULATOR 1660b57cec5SDimitry Andric char *PListPathPrefix = getenv("IPHONE_SIMULATOR_ROOT"); 1670b57cec5SDimitry Andric if (!PListPathPrefix) 1680b57cec5SDimitry Andric return; 1690b57cec5SDimitry Andric char FullPath[strlen(PListPathPrefix) + strlen(PListPath) + 1]; 1700b57cec5SDimitry Andric strcpy(FullPath, PListPathPrefix); 1710b57cec5SDimitry Andric strcat(FullPath, PListPath); 1720b57cec5SDimitry Andric PListPath = FullPath; 1730b57cec5SDimitry Andric #endif 1740b57cec5SDimitry Andric FILE *PropertyList = fopen(PListPath, "r"); 1750b57cec5SDimitry Andric if (!PropertyList) 1760b57cec5SDimitry Andric return; 1770b57cec5SDimitry Andric 1780b57cec5SDimitry Andric // Dynamically allocated stuff. 1790b57cec5SDimitry Andric CFDictionaryRef PListRef = NULL; 1800b57cec5SDimitry Andric CFDataRef FileContentsRef = NULL; 1810b57cec5SDimitry Andric UInt8 *PListBuf = NULL; 1820b57cec5SDimitry Andric 1830b57cec5SDimitry Andric fseek(PropertyList, 0, SEEK_END); 1840b57cec5SDimitry Andric long PListFileSize = ftell(PropertyList); 1850b57cec5SDimitry Andric if (PListFileSize < 0) 1860b57cec5SDimitry Andric goto Fail; 1870b57cec5SDimitry Andric rewind(PropertyList); 1880b57cec5SDimitry Andric 1890b57cec5SDimitry Andric PListBuf = malloc((size_t)PListFileSize); 1900b57cec5SDimitry Andric if (!PListBuf) 1910b57cec5SDimitry Andric goto Fail; 1920b57cec5SDimitry Andric 1930b57cec5SDimitry Andric size_t NumRead = fread(PListBuf, 1, (size_t)PListFileSize, PropertyList); 1940b57cec5SDimitry Andric if (NumRead != (size_t)PListFileSize) 1950b57cec5SDimitry Andric goto Fail; 1960b57cec5SDimitry Andric 1970b57cec5SDimitry Andric // Get the file buffer into CF's format. We pass in a null allocator here * 1980b57cec5SDimitry Andric // because we free PListBuf ourselves 1990b57cec5SDimitry Andric FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)( 2000b57cec5SDimitry Andric NULL, PListBuf, (CFIndex)NumRead, AllocatorNull); 2010b57cec5SDimitry Andric if (!FileContentsRef) 2020b57cec5SDimitry Andric goto Fail; 2030b57cec5SDimitry Andric 2040b57cec5SDimitry Andric if (CFPropertyListCreateWithDataFunc) 2050b57cec5SDimitry Andric PListRef = (*CFPropertyListCreateWithDataFunc)( 2060b57cec5SDimitry Andric NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL); 2070b57cec5SDimitry Andric else 2080b57cec5SDimitry Andric PListRef = (*CFPropertyListCreateFromXMLDataFunc)( 2090b57cec5SDimitry Andric NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL); 2100b57cec5SDimitry Andric if (!PListRef) 2110b57cec5SDimitry Andric goto Fail; 2120b57cec5SDimitry Andric 2130b57cec5SDimitry Andric CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)( 2140b57cec5SDimitry Andric NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull); 2150b57cec5SDimitry Andric if (!ProductVersion) 2160b57cec5SDimitry Andric goto Fail; 2170b57cec5SDimitry Andric CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion); 2180b57cec5SDimitry Andric (*CFReleaseFunc)(ProductVersion); 2190b57cec5SDimitry Andric if (!OpaqueValue || 2200b57cec5SDimitry Andric (*CFGetTypeIDFunc)(OpaqueValue) != (*CFStringGetTypeIDFunc)()) 2210b57cec5SDimitry Andric goto Fail; 2220b57cec5SDimitry Andric 2230b57cec5SDimitry Andric char VersionStr[32]; 2240b57cec5SDimitry Andric if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr, 2250b57cec5SDimitry Andric sizeof(VersionStr), CF_STRING_ENCODING_UTF8)) 2260b57cec5SDimitry Andric goto Fail; 2270b57cec5SDimitry Andric sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor); 2280b57cec5SDimitry Andric 2290b57cec5SDimitry Andric Fail: 2300b57cec5SDimitry Andric if (PListRef) 2310b57cec5SDimitry Andric (*CFReleaseFunc)(PListRef); 2320b57cec5SDimitry Andric if (FileContentsRef) 2330b57cec5SDimitry Andric (*CFReleaseFunc)(FileContentsRef); 2340b57cec5SDimitry Andric free(PListBuf); 2350b57cec5SDimitry Andric fclose(PropertyList); 2360b57cec5SDimitry Andric } 2370b57cec5SDimitry Andric 238e8d8bef9SDimitry Andric // Find and parse the SystemVersion.plist file. 239e8d8bef9SDimitry Andric static void compatibilityInitializeAvailabilityCheck(void *Unused) { 240e8d8bef9SDimitry Andric (void)Unused; 241e8d8bef9SDimitry Andric _initializeAvailabilityCheck(/*LoadPlist=*/true); 242e8d8bef9SDimitry Andric } 243e8d8bef9SDimitry Andric 244e8d8bef9SDimitry Andric static void initializeAvailabilityCheck(void *Unused) { 245e8d8bef9SDimitry Andric (void)Unused; 246e8d8bef9SDimitry Andric _initializeAvailabilityCheck(/*LoadPlist=*/false); 247e8d8bef9SDimitry Andric } 248e8d8bef9SDimitry Andric 249e8d8bef9SDimitry Andric // This old API entry point is no longer used by Clang for Darwin. We still need 250e8d8bef9SDimitry Andric // to keep it around to ensure that object files that reference it are still 251e8d8bef9SDimitry Andric // usable when linked with new compiler-rt. 2520b57cec5SDimitry Andric int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) { 2530b57cec5SDimitry Andric // Populate the global version variables, if they haven't already. 254e8d8bef9SDimitry Andric dispatch_once_f(&CompatibilityDispatchOnceCounter, NULL, 255e8d8bef9SDimitry Andric compatibilityInitializeAvailabilityCheck); 2560b57cec5SDimitry Andric 2570b57cec5SDimitry Andric if (Major < GlobalMajor) 2580b57cec5SDimitry Andric return 1; 2590b57cec5SDimitry Andric if (Major > GlobalMajor) 2600b57cec5SDimitry Andric return 0; 2610b57cec5SDimitry Andric if (Minor < GlobalMinor) 2620b57cec5SDimitry Andric return 1; 2630b57cec5SDimitry Andric if (Minor > GlobalMinor) 2640b57cec5SDimitry Andric return 0; 2650b57cec5SDimitry Andric return Subminor <= GlobalSubminor; 2660b57cec5SDimitry Andric } 2670b57cec5SDimitry Andric 268e8d8bef9SDimitry Andric static inline uint32_t ConstructVersion(uint32_t Major, uint32_t Minor, 269e8d8bef9SDimitry Andric uint32_t Subminor) { 270e8d8bef9SDimitry Andric return ((Major & 0xffff) << 16) | ((Minor & 0xff) << 8) | (Subminor & 0xff); 271e8d8bef9SDimitry Andric } 272e8d8bef9SDimitry Andric 273e8d8bef9SDimitry Andric int32_t __isPlatformVersionAtLeast(uint32_t Platform, uint32_t Major, 274e8d8bef9SDimitry Andric uint32_t Minor, uint32_t Subminor) { 275e8d8bef9SDimitry Andric dispatch_once_f(&DispatchOnceCounter, NULL, initializeAvailabilityCheck); 276e8d8bef9SDimitry Andric 277e8d8bef9SDimitry Andric if (!AvailabilityVersionCheck) { 278e8d8bef9SDimitry Andric return __isOSVersionAtLeast(Major, Minor, Subminor); 279e8d8bef9SDimitry Andric } 280e8d8bef9SDimitry Andric dyld_build_version_t Versions[] = { 281e8d8bef9SDimitry Andric {Platform, ConstructVersion(Major, Minor, Subminor)}}; 282e8d8bef9SDimitry Andric return AvailabilityVersionCheck(1, Versions); 283e8d8bef9SDimitry Andric } 284e8d8bef9SDimitry Andric 285e8d8bef9SDimitry Andric #elif __ANDROID__ 286e8d8bef9SDimitry Andric 287e8d8bef9SDimitry Andric #include <pthread.h> 288e8d8bef9SDimitry Andric #include <stdlib.h> 289e8d8bef9SDimitry Andric #include <string.h> 290e8d8bef9SDimitry Andric #include <sys/system_properties.h> 291e8d8bef9SDimitry Andric 292e8d8bef9SDimitry Andric static int SdkVersion; 293e8d8bef9SDimitry Andric static int IsPreRelease; 294e8d8bef9SDimitry Andric 295e8d8bef9SDimitry Andric static void readSystemProperties(void) { 296e8d8bef9SDimitry Andric char buf[PROP_VALUE_MAX]; 297e8d8bef9SDimitry Andric 298e8d8bef9SDimitry Andric if (__system_property_get("ro.build.version.sdk", buf) == 0) { 299e8d8bef9SDimitry Andric // When the system property doesn't exist, defaults to future API level. 300e8d8bef9SDimitry Andric SdkVersion = __ANDROID_API_FUTURE__; 301e8d8bef9SDimitry Andric } else { 302e8d8bef9SDimitry Andric SdkVersion = atoi(buf); 303e8d8bef9SDimitry Andric } 304e8d8bef9SDimitry Andric 305e8d8bef9SDimitry Andric if (__system_property_get("ro.build.version.codename", buf) == 0) { 306e8d8bef9SDimitry Andric IsPreRelease = 1; 307e8d8bef9SDimitry Andric } else { 308e8d8bef9SDimitry Andric IsPreRelease = strcmp(buf, "REL") != 0; 309e8d8bef9SDimitry Andric } 310e8d8bef9SDimitry Andric return; 311e8d8bef9SDimitry Andric } 312e8d8bef9SDimitry Andric 313e8d8bef9SDimitry Andric int32_t __isOSVersionAtLeast(int32_t Major, int32_t Minor, int32_t Subminor) { 31481ad6265SDimitry Andric (void) Minor; 31581ad6265SDimitry Andric (void) Subminor; 316e8d8bef9SDimitry Andric static pthread_once_t once = PTHREAD_ONCE_INIT; 317e8d8bef9SDimitry Andric pthread_once(&once, readSystemProperties); 318e8d8bef9SDimitry Andric 319*0fca6ea1SDimitry Andric // Allow all on pre-release. Note that we still rely on compile-time checks. 320*0fca6ea1SDimitry Andric return SdkVersion >= Major || IsPreRelease; 321e8d8bef9SDimitry Andric } 322e8d8bef9SDimitry Andric 3230b57cec5SDimitry Andric #else 3240b57cec5SDimitry Andric 3250b57cec5SDimitry Andric // Silence an empty translation unit warning. 3260b57cec5SDimitry Andric typedef int unused; 3270b57cec5SDimitry Andric 3280b57cec5SDimitry Andric #endif 329