| //===----------------------------------------------------------------------===// |
| // |
| // 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<uintptr_t>(P) + 8 <= |
| reinterpret_cast<uintptr_t>(BEnd)) { |
| H64 ^= round(0, endian::read64le(P)); |
| H64 = rotl64(H64, 27) * PRIME64_1 + PRIME64_4; |
| P += 8; |
| } |
| |
| if (reinterpret_cast<uintptr_t>(P) + 4 <= reinterpret_cast<uintptr_t>(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<uint32_t>( |
| xxHash64(reinterpret_cast<const uint8_t *>(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"); |
| } |