1 //===-- ubsan_value.cpp ---------------------------------------------------===// 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 // Representation of a runtime value, as marshaled from the generated code to 10 // the ubsan runtime. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "ubsan_platform.h" 15 #if CAN_SANITIZE_UB 16 #include "ubsan_value.h" 17 #include "sanitizer_common/sanitizer_common.h" 18 #include "sanitizer_common/sanitizer_libc.h" 19 #include "sanitizer_common/sanitizer_mutex.h" 20 21 // TODO(dliew): Prefer '__APPLE__' here over 'SANITIZER_MAC', as the latter is 22 // unclear. rdar://58124919 tracks using a more obviously portable guard. 23 #if defined(__APPLE__) 24 #include <dlfcn.h> 25 #endif 26 27 using namespace __ubsan; 28 29 typedef const char *(*ObjCGetClassNameTy)(void *); 30 31 const char *__ubsan::getObjCClassName(ValueHandle Pointer) { 32 #if defined(__APPLE__) 33 // We need to query the ObjC runtime for some information, but do not want 34 // to introduce a static dependency from the ubsan runtime onto ObjC. Try to 35 // grab a handle to the ObjC runtime used by the process. 36 static bool AttemptedDlopen = false; 37 static void *ObjCHandle = nullptr; 38 static void *ObjCObjectGetClassName = nullptr; 39 40 // Prevent threads from racing to dlopen(). 41 static __sanitizer::StaticSpinMutex Lock; 42 { 43 __sanitizer::SpinMutexLock Guard(&Lock); 44 45 if (!AttemptedDlopen) { 46 ObjCHandle = dlopen( 47 "/usr/lib/libobjc.A.dylib", 48 RTLD_LAZY // Only bind symbols when used. 49 | RTLD_LOCAL // Only make symbols available via the handle. 50 | RTLD_NOLOAD // Do not load the dylib, just grab a handle if the 51 // image is already loaded. 52 | RTLD_FIRST // Only search the image pointed-to by the handle. 53 ); 54 AttemptedDlopen = true; 55 if (!ObjCHandle) 56 return nullptr; 57 ObjCObjectGetClassName = dlsym(ObjCHandle, "object_getClassName"); 58 } 59 } 60 61 if (!ObjCObjectGetClassName) 62 return nullptr; 63 64 return ObjCGetClassNameTy(ObjCObjectGetClassName)((void *)Pointer); 65 #else 66 return nullptr; 67 #endif 68 } 69 70 SIntMax Value::getSIntValue() const { 71 CHECK(getType().isSignedIntegerTy()); 72 if (isInlineInt()) { 73 // Val was zero-extended to ValueHandle. Sign-extend from original width 74 // to SIntMax. 75 const unsigned ExtraBits = 76 sizeof(SIntMax) * 8 - getType().getIntegerBitWidth(); 77 return SIntMax(UIntMax(Val) << ExtraBits) >> ExtraBits; 78 } 79 if (getType().getIntegerBitWidth() == 64) 80 return *reinterpret_cast<s64*>(Val); 81 #if HAVE_INT128_T 82 if (getType().getIntegerBitWidth() == 128) 83 return *reinterpret_cast<s128*>(Val); 84 #else 85 if (getType().getIntegerBitWidth() == 128) 86 UNREACHABLE("libclang_rt.ubsan was built without __int128 support"); 87 #endif 88 UNREACHABLE("unexpected bit width"); 89 } 90 91 UIntMax Value::getUIntValue() const { 92 CHECK(getType().isUnsignedIntegerTy()); 93 if (isInlineInt()) 94 return Val; 95 if (getType().getIntegerBitWidth() == 64) 96 return *reinterpret_cast<u64*>(Val); 97 #if HAVE_INT128_T 98 if (getType().getIntegerBitWidth() == 128) 99 return *reinterpret_cast<u128*>(Val); 100 #else 101 if (getType().getIntegerBitWidth() == 128) 102 UNREACHABLE("libclang_rt.ubsan was built without __int128 support"); 103 #endif 104 UNREACHABLE("unexpected bit width"); 105 } 106 107 UIntMax Value::getPositiveIntValue() const { 108 if (getType().isUnsignedIntegerTy()) 109 return getUIntValue(); 110 SIntMax Val = getSIntValue(); 111 CHECK(Val >= 0); 112 return Val; 113 } 114 115 /// Get the floating-point value of this object, extended to a long double. 116 /// These are always passed by address (our calling convention doesn't allow 117 /// them to be passed in floating-point registers, so this has little cost). 118 FloatMax Value::getFloatValue() const { 119 CHECK(getType().isFloatTy()); 120 if (isInlineFloat()) { 121 switch (getType().getFloatBitWidth()) { 122 #if 0 123 // FIXME: OpenCL / NEON 'half' type. LLVM can't lower the conversion 124 // from '__fp16' to 'long double'. 125 case 16: { 126 __fp16 Value; 127 internal_memcpy(&Value, &Val, 4); 128 return Value; 129 } 130 #endif 131 case 32: { 132 float Value; 133 #if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 134 // For big endian the float value is in the last 4 bytes. 135 // On some targets we may only have 4 bytes so we count backwards from 136 // the end of Val to account for both the 32-bit and 64-bit cases. 137 internal_memcpy(&Value, ((const char*)(&Val + 1)) - 4, 4); 138 #else 139 internal_memcpy(&Value, &Val, 4); 140 #endif 141 return Value; 142 } 143 case 64: { 144 double Value; 145 internal_memcpy(&Value, &Val, 8); 146 return Value; 147 } 148 } 149 } else { 150 switch (getType().getFloatBitWidth()) { 151 case 64: return *reinterpret_cast<double*>(Val); 152 case 80: return *reinterpret_cast<long double*>(Val); 153 case 96: return *reinterpret_cast<long double*>(Val); 154 case 128: return *reinterpret_cast<long double*>(Val); 155 } 156 } 157 UNREACHABLE("unexpected floating point bit width"); 158 } 159 160 #endif // CAN_SANITIZE_UB 161