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