| // Copyright Microsoft and CHERIoT Contributors. | |
| // SPDX-License-Identifier: MIT | |
| /** | |
| * @file Pointer utilities | |
| */ | |
| #pragma once | |
| #include <cheri.hh> | |
| #include <concepts> | |
| namespace ds::pointer | |
| { | |
| /** | |
| * Offset a pointer by a number of bytes. The return type must be | |
| * explicitly specified by the caller. The type of the displacement offset | |
| * (Offset) is templated so that we can accept both signed and unsigned | |
| * offsets. | |
| */ | |
| template<typename T, typename U> | |
| static inline __always_inline T *offset(U *base, std::integral auto offset) | |
| { | |
| CHERI::Capability c{base}; | |
| c.address() += offset; | |
| return c.template cast<void>().template cast<T>(); | |
| } | |
| /** | |
| * Compute the unsigned difference in bytes between two pointers' target | |
| * addresses. To be standards-compliant, cursor must be part of the same | |
| * allocation as base and at a higher address. | |
| */ | |
| static inline __always_inline size_t diff(const void *base, | |
| const void *cursor) | |
| { | |
| return static_cast<size_t>(reinterpret_cast<const char *>(cursor) - | |
| reinterpret_cast<const char *>(base)); | |
| } | |
| namespace proxy | |
| { | |
| /** | |
| * Proxies<P,T> if P is a proxy object for T*-s. | |
| */ | |
| template<typename P, typename T> | |
| concept Proxies = std::same_as<T, typename P::Type> && | |
| requires(P &proxy, P &proxy2, T *ptr) | |
| { | |
| /* Probe for operator=(T*) */ | |
| { | |
| proxy = ptr | |
| } -> std::same_as<P &>; | |
| /* Probe for operator T*() */ | |
| { | |
| ptr == proxy | |
| } -> std::same_as<bool>; | |
| /* TODO: How to probe for operator-> ? */ | |
| /* Probe for operator==(T*) */ | |
| { | |
| proxy == ptr | |
| } -> std::same_as<bool>; | |
| /* Probe for operator==(P&) */ | |
| { | |
| proxy == proxy2 | |
| } -> std::same_as<bool>; | |
| /* Probe for operator<=>(T*) */ | |
| { | |
| proxy <=> ptr | |
| } -> std::same_as<std::strong_ordering>; | |
| /* Probe for operator<=>(P) */ | |
| { | |
| proxy <=> proxy2 | |
| } -> std::same_as<std::strong_ordering>; | |
| }; | |
| /** | |
| * Pointer references are pointer proxies, shockingly enough. | |
| */ | |
| template<typename T> | |
| class Pointer | |
| { | |
| T *&ref; | |
| public: | |
| using Type = T; | |
| __always_inline Pointer(T *&r) : ref(r) {} | |
| __always_inline operator T *() | |
| { | |
| return ref; | |
| } | |
| __always_inline T *operator->() | |
| { | |
| return *this; | |
| } | |
| __always_inline Pointer<T> &operator=(T *t) | |
| { | |
| ref = t; | |
| return *this; | |
| } | |
| __always_inline Pointer<T> &operator=(Pointer const &p) | |
| { | |
| ref = p.ref; | |
| return *this; | |
| } | |
| __always_inline bool operator==(Pointer &p) | |
| { | |
| return this->ref == p.ref; | |
| } | |
| __always_inline auto operator<=>(Pointer &p) | |
| { | |
| return this->ref <=> p.ref; | |
| } | |
| }; | |
| static_assert(Proxies<Pointer<void>, void>); | |
| /** | |
| * Equipped with a context for bounds, an address reference can be a | |
| * proxy for a pointer. | |
| */ | |
| template<typename T> | |
| class PtrAddr | |
| { | |
| CHERI::Capability<void> ctx; | |
| ptraddr_t &ref; | |
| public: | |
| using Type = T; | |
| __always_inline PtrAddr(void *c, ptraddr_t &r) : ctx(c), ref(r) {} | |
| __always_inline operator T *() | |
| { | |
| auto c = ctx; | |
| c.address() = ref; | |
| return c.cast<T>().get(); | |
| } | |
| __always_inline T *operator->() | |
| { | |
| return *this; | |
| } | |
| __always_inline PtrAddr &operator=(T *p) | |
| { | |
| ref = CHERI::Capability{p}.address(); | |
| return *this; | |
| } | |
| __always_inline PtrAddr &operator=(PtrAddr const &p) | |
| { | |
| ref = p.ref; | |
| return *this; | |
| } | |
| /* | |
| * Since the context is used only for bounds, don't bother | |
| * implicitly converting both proxies up to T* | |
| */ | |
| __always_inline bool operator==(PtrAddr &p) | |
| { | |
| return ref == p.ref; | |
| } | |
| __always_inline auto operator<=>(PtrAddr &p) | |
| { | |
| return ref <=> p.ref; | |
| } | |
| }; | |
| static_assert(Proxies<PtrAddr<void>, void>); | |
| /** | |
| * Deduction gude for the common enough case where the context | |
| * type and the represented type are equal. | |
| */ | |
| template<typename T> | |
| PtrAddr(T *, ptraddr_t) -> PtrAddr<T>; | |
| /** | |
| * Like the above, but with a constant offset on the interpretation of | |
| * its addresss fields. This is useful for building points-to-container | |
| * data structures (rather than points-to-member as with the above two). | |
| * The container_of and address-taking operations that move back and | |
| * forth between container and link member should fuse away with the | |
| * offsetting operations herein. You may prefer this if your common or | |
| * fast-paths involve lots of container_of operations. | |
| */ | |
| template<ptrdiff_t Offset, typename T> | |
| class OffsetPtrAddr | |
| { | |
| CHERI::Capability<void> ctx; | |
| ptraddr_t &ref; | |
| public: | |
| using Type = T; | |
| __always_inline OffsetPtrAddr(void *c, ptraddr_t &r) | |
| : ctx(c), ref(r) | |
| { | |
| } | |
| __always_inline operator T *() | |
| { | |
| auto c = ctx; | |
| c.address() = ref + Offset; | |
| return c.cast<T>().get(); | |
| } | |
| __always_inline T *operator->() | |
| { | |
| return *this; | |
| } | |
| __always_inline OffsetPtrAddr &operator=(T *p) | |
| { | |
| ref = CHERI::Capability{p}.address() - Offset; | |
| return *this; | |
| } | |
| __always_inline OffsetPtrAddr &operator=(OffsetPtrAddr const &p) | |
| { | |
| ref = p.ref; | |
| return *this; | |
| } | |
| /* | |
| * Since the context is used only for bounds, don't bother | |
| * implicitly converting both proxies up to T*. This also probably | |
| * saves the optimizer the effort of cancelling the Offset | |
| * arithmetic on either side of the comparison. | |
| */ | |
| __always_inline bool operator==(OffsetPtrAddr &p) | |
| { | |
| return ref == p.ref; | |
| } | |
| __always_inline auto operator<=>(OffsetPtrAddr &p) | |
| { | |
| return ref <=> p.ref; | |
| } | |
| }; | |
| static_assert(Proxies<OffsetPtrAddr<8, void>, void>); | |
| } // namespace proxy | |
| } // namespace ds::pointer |