CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
pointer.h
Go to the documentation of this file.
1// Copyright Microsoft and CHERIoT Contributors.
2// SPDX-License-Identifier: MIT
3
4/**
5 * @file
6 * Pointer utilities
7 */
8
9#pragma once
10
11#include <cheri.hh>
12#include <concepts>
13
14namespace ds::pointer
15{
16
17 /**
18 * Offset a pointer by a number of bytes. The return type must be
19 * explicitly specified by the caller. The type of the displacement offset
20 * (Offset) is templated so that we can accept both signed and unsigned
21 * offsets.
22 */
23 template<typename T, typename U>
24 static inline __always_inline T *offset(U *base, std::integral auto offset)
25 {
26 CHERI::Capability c{base};
27 c.address() += offset;
28 return c.template cast<void>().template cast<T>();
29 }
30
31 /**
32 * Compute the unsigned difference in bytes between two pointers' target
33 * addresses. To be standards-compliant, cursor must be part of the same
34 * allocation as base and at a higher address.
35 */
36 static inline __always_inline size_t diff(const void *base,
37 const void *cursor)
38 {
39 return static_cast<size_t>(reinterpret_cast<const char *>(cursor) -
40 reinterpret_cast<const char *>(base));
41 }
42
43 namespace proxy
44 {
45
46 /**
47 * Proxies<P,T> if P is a proxy object for T*-s.
48 */
49 template<typename P, typename T>
50 concept Proxies = std::same_as<T, typename P::Type> &&
51 requires(P &proxy, P &proxy2, T *ptr) {
52 /* Probe for operator=(T*) */
53 { proxy = ptr } -> std::same_as<P &>;
54
55 /* Probe for operator T*() */
56 { ptr == proxy } -> std::same_as<bool>;
57
58 /* TODO: How to probe for operator-> ? */
59
60 /* Probe for operator==(T*) */
61 { proxy == ptr } -> std::same_as<bool>;
62
63 /* Probe for operator==(P&) */
64 { proxy == proxy2 } -> std::same_as<bool>;
65
66 /* Probe for operator<=>(T*) */
67 {
68 proxy <=> ptr
69 } -> std::same_as<std::strong_ordering>;
70
71 /* Probe for operator<=>(P) */
72 {
73 proxy <=> proxy2
74 } -> std::same_as<std::strong_ordering>;
75 };
76
77 /**
78 * Pointer references are pointer proxies, shockingly enough.
79 */
80 template<typename T>
81 class Pointer
82 {
83 T *&ref;
84
85 public:
86 using Type = T;
87
88 __always_inline Pointer(T *&r) : ref(r) {}
89
90 __always_inline operator T *()
91 {
92 return ref;
93 }
94
95 __always_inline T *operator->()
96 {
97 return *this;
98 }
99
100 __always_inline Pointer<T> &operator=(T *t)
101 {
102 ref = t;
103 return *this;
104 }
105
106 __always_inline Pointer<T> &operator=(Pointer const &p)
107 {
108 ref = static_cast<T *>(p.ref);
109 return *this;
110 }
111
112 __always_inline bool operator==(Pointer &p)
113 {
114 return this->ref == p.ref;
115 }
116
117 __always_inline auto operator<=>(Pointer &p)
118 {
119 return this->ref <=> p.ref;
120 }
121 };
122 static_assert(Proxies<Pointer<void>, void>);
123
124 /**
125 * Equipped with a context for bounds, an address reference can be a
126 * proxy for a pointer.
127 */
128 template<typename T>
129 class PtrAddr
130 {
132 ptraddr_t &ref;
133
134 public:
135 using Type = T;
136
137 __always_inline PtrAddr(void *c, ptraddr_t &r) : ctx(c), ref(r) {}
138
139 __always_inline operator T *()
140 {
141 auto c = ctx;
142 c.address() = ref;
143 return c.cast<T>().get();
144 }
145
146 __always_inline T *operator->()
147 {
148 return *this;
149 }
150
151 __always_inline PtrAddr &operator=(T *p)
152 {
153 ref = CHERI::Capability{p}.address();
154 return *this;
155 }
156
157 __always_inline PtrAddr &operator=(PtrAddr const &p)
158 {
159 ref = p.ref;
160 return *this;
161 }
162
163 /*
164 * Since the context is used only for bounds, don't bother
165 * implicitly converting both proxies up to T*
166 */
167
168 __always_inline bool operator==(PtrAddr &p)
169 {
170 return ref == p.ref;
171 }
172
173 __always_inline auto operator<=>(PtrAddr &p)
174 {
175 return ref <=> p.ref;
176 }
177 };
178 static_assert(Proxies<PtrAddr<void>, void>);
179
180 /**
181 * Deduction gude for the common enough case where the context
182 * type and the represented type are equal.
183 */
184 template<typename T>
185 PtrAddr(T *, ptraddr_t) -> PtrAddr<T>;
186
187 /**
188 * Like the above, but with a constant offset on the interpretation of
189 * its addresss fields. This is useful for building points-to-container
190 * data structures (rather than points-to-member as with the above two).
191 * The container_of and address-taking operations that move back and
192 * forth between container and link member should fuse away with the
193 * offsetting operations herein. You may prefer this if your common or
194 * fast-paths involve lots of container_of operations.
195 */
196 template<ptrdiff_t Offset, typename T>
197 class OffsetPtrAddr
198 {
200 ptraddr_t &ref;
201
202 public:
203 using Type = T;
204
205 __always_inline OffsetPtrAddr(void *c, ptraddr_t &r)
206 : ctx(c), ref(r)
207 {
208 }
209
210 __always_inline operator T *()
211 {
212 auto c = ctx;
213 c.address() = ref + Offset;
214 return c.cast<T>().get();
215 }
216
217 __always_inline T *operator->()
218 {
219 return *this;
220 }
221
222 __always_inline OffsetPtrAddr &operator=(T *p)
223 {
224 ref = CHERI::Capability{p}.address() - Offset;
225 return *this;
226 }
227
228 __always_inline OffsetPtrAddr &operator=(OffsetPtrAddr const &p)
229 {
230 ref = p.ref;
231 return *this;
232 }
233
234 /*
235 * Since the context is used only for bounds, don't bother
236 * implicitly converting both proxies up to T*. This also probably
237 * saves the optimizer the effort of cancelling the Offset
238 * arithmetic on either side of the comparison.
239 */
240
241 __always_inline bool operator==(OffsetPtrAddr &p)
242 {
243 return ref == p.ref;
244 }
245
246 __always_inline auto operator<=>(OffsetPtrAddr &p)
247 {
248 return ref <=> p.ref;
249 }
250 };
251 static_assert(Proxies<OffsetPtrAddr<8, void>, void>);
252
253 } // namespace proxy
254
255} // namespace ds::pointer
C++ helpers for operating on capabilities.
Helper class for accessing capability properties on pointers.
Definition cheri.hh:482
AddressProxy address()
Access the address of the capability.
Definition cheri.hh:851
Equipped with a context for bounds, an address reference can be a proxy for a pointer.
Definition pointer.h:130
Proxies<P,T> if P is a proxy object for T*-s.
Definition pointer.h:50
static size_t diff(const void *base, const void *cursor)
Compute the unsigned difference in bytes between two pointers' target addresses.
Definition pointer.h:36
static T * offset(U *base, std::integral auto offset)
Offset a pointer by a number of bytes.
Definition pointer.h:24