//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Utils/KCFIHash.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" using namespace llvm; using namespace support; // xxHash64 is a deprecated pre-xxh3 hash, retained here only as the default // KCFI type-ID hash for ABI compatibility. static uint64_t rotl64(uint64_t X, size_t R) { return (X << R) | (X >> (64 - R)); } constexpr uint64_t PRIME64_1 = 11400714785074694791ULL; constexpr uint64_t PRIME64_2 = 14029467366897019727ULL; constexpr uint64_t PRIME64_3 = 1609587929392839161ULL; constexpr uint64_t PRIME64_4 = 9650029242287828579ULL; constexpr uint64_t PRIME64_5 = 2870177450012600261ULL; static uint64_t round(uint64_t Acc, uint64_t Input) { Acc += Input * PRIME64_2; Acc = rotl64(Acc, 31); Acc *= PRIME64_1; return Acc; } static uint64_t mergeRound(uint64_t Acc, uint64_t Val) { Val = round(0, Val); Acc ^= Val; Acc = Acc * PRIME64_1 + PRIME64_4; return Acc; } static uint64_t avalanche(uint64_t H) { H ^= H >> 33; H *= PRIME64_2; H ^= H >> 29; H *= PRIME64_3; H ^= H >> 32; return H; } static uint64_t xxHash64(const uint8_t *P, size_t Len) { const uint8_t *const BEnd = P + Len; uint64_t H64; if (Len >= 32) { const uint8_t *const Limit = BEnd - 32; uint64_t V1 = PRIME64_1 + PRIME64_2; uint64_t V2 = PRIME64_2; uint64_t V3 = 0; uint64_t V4 = -PRIME64_1; do { V1 = round(V1, endian::read64le(P)); P += 8; V2 = round(V2, endian::read64le(P)); P += 8; V3 = round(V3, endian::read64le(P)); P += 8; V4 = round(V4, endian::read64le(P)); P += 8; } while (P <= Limit); H64 = rotl64(V1, 1) + rotl64(V2, 7) + rotl64(V3, 12) + rotl64(V4, 18); H64 = mergeRound(H64, V1); H64 = mergeRound(H64, V2); H64 = mergeRound(H64, V3); H64 = mergeRound(H64, V4); } else { H64 = PRIME64_5; } H64 += (uint64_t)Len; while (reinterpret_cast(P) + 8 <= reinterpret_cast(BEnd)) { H64 ^= round(0, endian::read64le(P)); H64 = rotl64(H64, 27) * PRIME64_1 + PRIME64_4; P += 8; } if (reinterpret_cast(P) + 4 <= reinterpret_cast(BEnd)) { H64 ^= (uint64_t)endian::read32le(P) * PRIME64_1; H64 = rotl64(H64, 23) * PRIME64_2 + PRIME64_3; P += 4; } while (P < BEnd) { H64 ^= (*P) * PRIME64_5; H64 = rotl64(H64, 11) * PRIME64_1; ++P; } return avalanche(H64); } KCFIHashAlgorithm llvm::parseKCFIHashAlgorithm(StringRef Name) { if (Name == "FNV-1a") return KCFIHashAlgorithm::FNV1a; // Default to xxHash64 for backward compatibility return KCFIHashAlgorithm::xxHash64; } StringRef llvm::stringifyKCFIHashAlgorithm(KCFIHashAlgorithm Algorithm) { switch (Algorithm) { case KCFIHashAlgorithm::xxHash64: return "xxHash64"; case KCFIHashAlgorithm::FNV1a: return "FNV-1a"; } llvm_unreachable("Unknown KCFI hash algorithm"); } uint32_t llvm::getKCFITypeID(StringRef MangledTypeName, KCFIHashAlgorithm Algorithm) { switch (Algorithm) { case KCFIHashAlgorithm::xxHash64: // Use lower 32 bits of xxHash64 return static_cast( xxHash64(reinterpret_cast(MangledTypeName.data()), MangledTypeName.size())); case KCFIHashAlgorithm::FNV1a: // FNV-1a hash (32-bit) uint32_t Hash = 2166136261u; // FNV offset basis for (unsigned char C : MangledTypeName) { Hash ^= C; Hash *= 16777619u; // FNV prime } return Hash; } llvm_unreachable("Unknown KCFI hash algorithm"); }