blob: 2a9a2324ce0afb019f86ce9ab5441a9e4cc6e258 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_SONAME_H_
#define SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_SONAME_H_
#include <cassert>
#include <cstdint>
#include <string_view>
#include <type_traits>
#if __cpp_impl_three_way_comparison >= 201907L
#include <compare>
#endif
#include "abi-ptr.h"
#include "abi-span.h"
#include "gnu-hash.h"
namespace elfldltl {
// This provides an optimized type for holding a DT_SONAME / DT_NEEDED string.
// It always hashes the string to make equality comparisons faster.
template <class Elf = Elf<>, class AbiTraits = LocalAbiTraits>
class Soname {
public:
constexpr Soname() = default;
constexpr Soname(const Soname&) = default;
template <typename Ptr = AbiPtr<const char, Elf, AbiTraits>,
typename = std::enable_if_t<std::is_constructible_v<Ptr, const char*>>>
constexpr explicit Soname(std::string_view name)
: name_(name.data()), size_(static_cast<uint32_t>(name.size())), hash_(GnuHashString(name)) {
assert(size_ == name.size());
}
constexpr Soname& operator=(const Soname&) noexcept = default;
template <typename Ptr = AbiPtr<const char, Elf, AbiTraits>,
typename = std::enable_if_t<std::is_constructible_v<Ptr, const char*>>>
constexpr Soname& operator=(std::string_view name) noexcept {
*this = Soname{name};
return *this;
}
template <typename Ptr = AbiPtr<const char, Elf, AbiTraits>, typename = decltype(Ptr{}.get())>
constexpr std::string_view str() const {
return {name_.get(), size_};
}
// This can only be used if the std::string_view used in construction is
// known to point to a NUL-terminated string, such as a string literal or a
// DT_STRTAB entry.
template <typename Ptr = AbiPtr<const char, Elf, AbiTraits>, typename = decltype(Ptr{}.get())>
constexpr const char* c_str() const {
assert(name_.get()[size_] == '\0');
return name_.get();
}
constexpr uint32_t hash() const { return hash_; }
template <typename Ptr = AbiPtr<const char, Elf, AbiTraits>, typename = decltype(Ptr{}.get())>
constexpr bool operator==(const Soname& other) const {
return other.hash_ == hash_ && other.str() == str();
}
template <typename Ptr = AbiPtr<const char, Elf, AbiTraits>, typename = decltype(Ptr{}.get())>
constexpr bool operator!=(const Soname& other) const {
return other.hash_ != hash_ || other.str() != str();
}
#if __cpp_impl_three_way_comparison >= 201907L
template <typename Ptr = AbiPtr<const char, Elf, AbiTraits>, typename = decltype(Ptr{}.get())>
constexpr auto operator<=>(const Soname& other) const {
return str() <=> other.str();
}
#else // No operator<=>.
template <typename Ptr = AbiPtr<const char, Elf, AbiTraits>, typename = decltype(Ptr{}.get())>
constexpr bool operator<(const Soname& other) const {
return str() < other.str();
}
template <typename Ptr = AbiPtr<const char, Elf, AbiTraits>, typename = decltype(Ptr{}.get())>
constexpr bool operator<=(const Soname& other) const {
return str() <= other.str();
}
template <typename Ptr = AbiPtr<const char, Elf, AbiTraits>, typename = decltype(Ptr{}.get())>
constexpr bool operator>(const Soname& other) const {
return str() > other.str();
}
template <typename Ptr = AbiPtr<const char, Elf, AbiTraits>, typename = decltype(Ptr{}.get())>
constexpr bool operator>=(const Soname& other) const {
return str() >= other.str();
}
#endif // operator<=>
private:
// This stores a pointer and 32-bit length directly rather than just using
// std::string_view so that the whole object is still only two 64-bit words.
// Crucially, both x86-64 and AArch64 ABIs pass and return trivial two-word
// objects in registers but anything larger in memory, so this keeps passing
// Soname as cheap as passing std::string_view. This limits lengths to 4GiB,
// which is far more than the practical limit.
AbiPtr<const char, Elf, AbiTraits> name_;
typename Elf::Word size_ = 0;
typename Elf::Word hash_ = 0;
public:
// <lib/ld/remote-abi-transcriber.h> introspection API. These aliases must
// be public, but can't be defined lexically before the private: section that
// declares the members; so this special public: section is at the end.
using AbiLocal = Soname<Elf, LocalAbiTraits>;
template <template <class...> class Template>
using AbiBases = Template<>;
template <template <auto...> class Template>
using AbiMembers = Template<&Soname::name_, &Soname::size_, &Soname::hash_>;
};
} // namespace elfldltl
#endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_SONAME_H_