1/*
2 * Block_private.h
3 *
4 * SPI for Blocks
5 *
6 * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
7 *
8 * @APPLE_LLVM_LICENSE_HEADER@
9 *
10 */
11
12#ifndef _BLOCK_PRIVATE_H_
13#define _BLOCK_PRIVATE_H_
14
15#include <Availability.h>
16#include <AvailabilityMacros.h>
17#ifndef KERNEL
18#include <TargetConditionals.h>
19#endif
20
21#include <stdbool.h>
22#include <stdint.h>
23#ifdef KERNEL
24#include <sys/systm.h>
25#else
26#include <stdio.h>
27#endif
28
29
30#ifdef KERNEL
31#include <libkern/Block.h>
32struct Block_byref;
33#else
34#include <Block.h>
35#endif
36
37#if __has_include(<ptrauth.h>)
38#include <ptrauth.h>
39#endif
40
41#if __has_feature(ptrauth_calls) && __cplusplus < 201103L
42
43// C ptrauth or old C++ ptrauth
44
45#define _Block_set_function_pointer(field, value) \
46 ((value) \
47 ? ((field) = \
48 (__typeof__(field)) \
49 ptrauth_auth_and_resign((void*)(value), \
50 ptrauth_key_function_pointer, 0, \
51 ptrauth_key_block_function, &(field))) \
52 : ((field) = 0))
53
54#define _Block_get_function_pointer(field) \
55 ((field) \
56 ? (__typeof__(field)) \
57 ptrauth_auth_function((void*)(field), \
58 ptrauth_key_block_function, &(field)) \
59 : (__typeof__(field))0)
60
61#else
62
63// C++11 ptrauth or no ptrauth
64
65#define _Block_set_function_pointer(field, value) \
66 (field) = (value)
67
68#define _Block_get_function_pointer(field) \
69 (field)
70
71#endif
72
73
74#if __has_feature(ptrauth_calls) && __cplusplus >= 201103L
75
76// StorageSignedFunctionPointer<Key, Fn> stores a function pointer of type
77// Fn but signed with the given ptrauth key and with the address of its
78// storage as extra data.
79// Function pointers inside block objects are signed this way.
80template <typename Fn, ptrauth_key Key>
81class StorageSignedFunctionPointer {
82 uintptr_t bits;
83
84public:
85
86// Authenticate function pointer fn as a C function pointer.
87// Re-sign it with our key and the storage address as extra data.
88// DOES NOT actually write to our storage.
89 uintptr_t
90 prepareWrite(Fn fn) const
91 {
92 if (fn == nullptr) {
93 return 0;
94 } else {
95 return (uintptr_t)
96 ptrauth_auth_and_resign(fn, ptrauth_key_function_pointer, ptrauth_function_pointer_type_discriminator(Fn),
97 Key, &bits);
98 }
99 }
100
101// Authenticate otherBits at otherStorage.
102// Re-sign it with our storage address.
103// DOES NOT actually write to our storage.
104 uintptr_t
105 prepareWrite(const StorageSignedFunctionPointer& other) const
106 {
107 if (other.bits == 0) {
108 return 0;
109 } else {
110 return (uintptr_t)
111 ptrauth_auth_and_resign((void*)other.bits, Key, &other.bits,
112 Key, &bits);
113 }
114 }
115
116// Authenticate ptr as if it were stored at our storage address.
117// Re-sign it as a C function pointer.
118// DOES NOT actually read from our storage.
119 Fn
120 completeReadFn(uintptr_t ptr) const
121 {
122 if (ptr == 0) {
123 return nullptr;
124 } else {
125 return (Fn)ptrauth_auth_function((void *)ptr, Key, &bits);
126 }
127 }
128
129// Authenticate ptr as if it were at our storage address.
130// Return it as a dereferenceable pointer.
131// DOES NOT actually read from our storage.
132 void*
133 completeReadRaw(uintptr_t ptr) const
134 {
135 if (ptr == 0) {
136 return nullptr;
137 } else {
138 return ptrauth_auth_data((void*)ptr, Key, &bits);
139 }
140 }
141
142 StorageSignedFunctionPointer()
143 {
144 }
145
146 StorageSignedFunctionPointer(Fn value)
147 : bits(prepareWrite(value))
148 {
149 }
150
151 StorageSignedFunctionPointer(const StorageSignedFunctionPointer& value)
152 : bits(prepareWrite(value))
153 {
154 }
155
156 StorageSignedFunctionPointer&
157 operator =(Fn rhs)
158 {
159 bits = prepareWrite(rhs);
160 return *this;
161 }
162
163 StorageSignedFunctionPointer&
164 operator =(const StorageSignedFunctionPointer& rhs)
165 {
166 bits = prepareWrite(rhs);
167 return *this;
168 }
169
170 operator Fn() const {
171 return completeReadFn(bits);
172 }
173
174 explicit
175 operator void*() const
176 {
177 return completeReadRaw(bits);
178 }
179
180 explicit
181 operator bool() const
182 {
183 return completeReadRaw(bits) != nullptr;
184 }
185};
186
187using BlockCopyFunction = StorageSignedFunctionPointer
188 <void (*)(void *, const void *),
189 ptrauth_key_block_function>;
190
191using BlockDisposeFunction = StorageSignedFunctionPointer
192 <void (*)(const void *),
193 ptrauth_key_block_function>;
194
195using BlockInvokeFunction = StorageSignedFunctionPointer
196 <void (*)(void *, ...),
197 ptrauth_key_block_function>;
198
199using BlockByrefKeepFunction = StorageSignedFunctionPointer
200 <void (*)(struct Block_byref *, struct Block_byref *),
201 ptrauth_key_block_function>;
202
203using BlockByrefDestroyFunction = StorageSignedFunctionPointer
204 <void (*)(struct Block_byref *),
205 ptrauth_key_block_function>;
206
207// c++11 and ptrauth_calls
208#elif !__has_feature(ptrauth_calls)
209// not ptrauth_calls
210
211typedef void (*BlockCopyFunction)(void *, const void *);
212typedef void (*BlockDisposeFunction)(const void *);
213typedef void (*BlockInvokeFunction)(void *, ...);
214typedef void (*BlockByrefKeepFunction)(struct Block_byref*, struct Block_byref*);
215typedef void (*BlockByrefDestroyFunction)(struct Block_byref *);
216
217#else
218// ptrauth_calls but not c++11
219
220typedef uintptr_t BlockCopyFunction;
221typedef uintptr_t BlockDisposeFunction;
222typedef uintptr_t BlockInvokeFunction;
223typedef uintptr_t BlockByrefKeepFunction;
224typedef uintptr_t BlockByrefDestroyFunction;
225
226#endif
227
228#if !__has_feature(ptrauth_calls)
229#define BLOCK_SMALL_DESCRIPTOR_SUPPORTED 1
230#endif
231
232#if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
233#if __has_feature(ptrauth_signed_block_descriptors)
234#define _Block_get_relative_function_pointer(aBlock, field, type) \
235 ({ \
236 __typeof__(type) _fn; \
237 __asm__ ( \
238 "mov x17, %[bds] \n\t" \
239 "movk x17, %[disc], lsl 48 \n\t" \
240 "ldr x16, [%[bds]] \n\t" \
241 "autda x16, x17 \n\t" \
242 "add x16, x16, %[offset] \n\t" \
243 "ldrsw x17, [x16] \n\t" \
244 "add x16, x16, x17 \n\t" \
245 "paciza x16 \n\t" \
246 "mov %[fn], x16 \n\t" \
247 : [fn] "=r" (_fn) \
248 : [disc] "i" (_Block_descriptor_ptrauth_discriminator), \
249 [bds] "r" (&(aBlock)->descriptor), \
250 [offset] "i" (offsetof(struct Block_descriptor_small, field)) \
251 : "x16", "x17" \
252 ); \
253 _fn; \
254 })
255#else /* __has_feature(ptrauth_calls) */
256#define _Block_get_relative_function_pointer(aBlock, field, type) \
257 ({ \
258 struct Block_descriptor_small *_bds = (struct Block_descriptor_small *) \
259 (aBlock)->descriptor; \
260 (type)((uintptr_t)&_bds->field + (uintptr_t)(intptr_t)_bds->field); \
261 })
262#endif /* __has_feature(ptrauth_calls) */
263#endif /* BLOCK_SMALL_DESCRIPTOR_SUPPORTED */
264
265#define _Block_descriptor_ptrauth_discriminator 0xC0BB
266
267// Values for Block_layout->flags to describe block objects
268enum {
269 BLOCK_DEALLOCATING = (0x0001),// runtime
270 BLOCK_REFCOUNT_MASK = (0xfffe),// runtime
271 BLOCK_INLINE_LAYOUT_STRING = (1 << 21), // compiler
272
273#if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
274 BLOCK_SMALL_DESCRIPTOR = (1 << 22),// compiler
275#endif
276
277 BLOCK_IS_NOESCAPE = (1 << 23), // compiler
278 BLOCK_NEEDS_FREE = (1 << 24),// runtime
279 BLOCK_HAS_COPY_DISPOSE = (1 << 25),// compiler
280 BLOCK_HAS_CTOR = (1 << 26),// compiler: helpers have C++ code
281 BLOCK_IS_GC = (1 << 27),// runtime
282 BLOCK_IS_GLOBAL = (1 << 28),// compiler
283 BLOCK_USE_STRET = (1 << 29),// compiler: undefined if !BLOCK_HAS_SIGNATURE
284 BLOCK_HAS_SIGNATURE = (1 << 30),// compiler
285 BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler
286};
287
288#define BLOCK_DESCRIPTOR_1 1
289struct Block_descriptor_1 {
290 uintptr_t reserved;
291 uintptr_t size;
292};
293
294#define BLOCK_DESCRIPTOR_2 1
295struct Block_descriptor_2 {
296 // requires BLOCK_HAS_COPY_DISPOSE
297 BlockCopyFunction copy;
298 BlockDisposeFunction dispose;
299};
300
301#define BLOCK_DESCRIPTOR_3 1
302struct Block_descriptor_3 {
303 // requires BLOCK_HAS_SIGNATURE
304 const char *signature;
305 const char *layout; // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
306};
307
308struct Block_descriptor_small {
309 uint32_t size;
310
311 int32_t signature;
312 int32_t layout;
313
314 /* copy & dispose are optional, only access them if
315 * Block_layout->flags & BLOCK_HAS_COPY_DIPOSE */
316 int32_t copy;
317 int32_t dispose;
318};
319
320
321struct Block_layout {
322 void *isa;
323 volatile int32_t flags; // contains ref count
324 int32_t reserved;
325 BlockInvokeFunction invoke;
326 struct Block_descriptor_1 *descriptor;
327 // imported variables
328};
329
330
331// Values for Block_byref->flags to describe __block variables
332enum {
333 // Byref refcount must use the same bits as Block_layout's refcount.
334 // BLOCK_DEALLOCATING = (0x0001), // runtime
335 // BLOCK_REFCOUNT_MASK = (0xfffe), // runtime
336
337 BLOCK_BYREF_LAYOUT_MASK = (0xf << 28),// compiler
338 BLOCK_BYREF_LAYOUT_EXTENDED = (1 << 28),// compiler
339 BLOCK_BYREF_LAYOUT_NON_OBJECT = (2 << 28), // compiler
340 BLOCK_BYREF_LAYOUT_STRONG = (3 << 28),// compiler
341 BLOCK_BYREF_LAYOUT_WEAK = (4 << 28),// compiler
342 BLOCK_BYREF_LAYOUT_UNRETAINED = (5 << 28), // compiler
343
344 BLOCK_BYREF_IS_GC = (1 << 27),// runtime
345
346 BLOCK_BYREF_HAS_COPY_DISPOSE = (1 << 25),// compiler
347 BLOCK_BYREF_NEEDS_FREE = (1 << 24),// runtime
348};
349
350struct Block_byref {
351 void *isa;
352 struct Block_byref *forwarding;
353 volatile int32_t flags; // contains ref count
354 uint32_t size;
355};
356
357struct Block_byref_2 {
358 // requires BLOCK_BYREF_HAS_COPY_DISPOSE
359 BlockByrefKeepFunction byref_keep;
360 BlockByrefDestroyFunction byref_destroy;
361};
362
363struct Block_byref_3 {
364 // requires BLOCK_BYREF_LAYOUT_EXTENDED
365 const char *layout;
366};
367
368
369// Extended layout encoding.
370
371// Values for Block_descriptor_3->layout with BLOCK_HAS_EXTENDED_LAYOUT
372// and for Block_byref_3->layout with BLOCK_BYREF_LAYOUT_EXTENDED
373
374// If the layout field is less than 0x1000, then it is a compact encoding
375// of the form 0xXYZ: X strong pointers, then Y byref pointers,
376// then Z weak pointers.
377
378// If the layout field is 0x1000 or greater, it points to a
379// string of layout bytes. Each byte is of the form 0xPN.
380// Operator P is from the list below. Value N is a parameter for the operator.
381// Byte 0x00 terminates the layout; remaining block data is non-pointer bytes.
382
383enum {
384 BLOCK_LAYOUT_ESCAPE = 0, // N=0 halt, rest is non-pointer. N!=0 reserved.
385 BLOCK_LAYOUT_NON_OBJECT_BYTES = 1, // N bytes non-objects
386 BLOCK_LAYOUT_NON_OBJECT_WORDS = 2, // N words non-objects
387 BLOCK_LAYOUT_STRONG = 3,// N words strong pointers
388 BLOCK_LAYOUT_BYREF = 4,// N words byref pointers
389 BLOCK_LAYOUT_WEAK = 5,// N words weak pointers
390 BLOCK_LAYOUT_UNRETAINED = 6,// N words unretained pointers
391 BLOCK_LAYOUT_UNKNOWN_WORDS_7 = 7,// N words, reserved
392 BLOCK_LAYOUT_UNKNOWN_WORDS_8 = 8,// N words, reserved
393 BLOCK_LAYOUT_UNKNOWN_WORDS_9 = 9,// N words, reserved
394 BLOCK_LAYOUT_UNKNOWN_WORDS_A = 0xA,// N words, reserved
395 BLOCK_LAYOUT_UNUSED_B = 0xB,// unspecified, reserved
396 BLOCK_LAYOUT_UNUSED_C = 0xC,// unspecified, reserved
397 BLOCK_LAYOUT_UNUSED_D = 0xD,// unspecified, reserved
398 BLOCK_LAYOUT_UNUSED_E = 0xE,// unspecified, reserved
399 BLOCK_LAYOUT_UNUSED_F = 0xF,// unspecified, reserved
400};
401
402
403// Runtime support functions used by compiler when generating copy/dispose helpers
404
405// Values for _Block_object_assign() and _Block_object_dispose() parameters
406enum {
407 // see function implementation for a more complete description of these fields and combinations
408 BLOCK_FIELD_IS_OBJECT = 3,// id, NSObject, __attribute__((NSObject)), block, ...
409 BLOCK_FIELD_IS_BLOCK = 7,// a block variable
410 BLOCK_FIELD_IS_BYREF = 8,// the on stack structure holding the __block variable
411 BLOCK_FIELD_IS_WEAK = 16,// declared __weak, only used in byref copy helpers
412 BLOCK_BYREF_CALLER = 128,// called from __block (byref) copy/dispose support routines.
413};
414
415enum {
416 BLOCK_ALL_COPY_DISPOSE_FLAGS =
417 BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_BYREF |
418 BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER
419};
420
421
422// Function pointer accessors
423
424static inline __typeof__(void (*)(void *, ...))
425_Block_get_invoke_fn(struct Block_layout *block)
426{
427 return (void (*)(void *, ...))_Block_get_function_pointer(block->invoke);
428}
429
430static inline void
431_Block_set_invoke_fn(struct Block_layout *block, void (*fn)(void *, ...))
432{
433 _Block_set_function_pointer(block->invoke, fn);
434}
435
436static inline void *
437_Block_get_descriptor(struct Block_layout *aBlock)
438{
439#if __has_feature(ptrauth_signed_block_descriptors)
440 uintptr_t disc = ptrauth_blend_discriminator(
441 &aBlock->descriptor, _Block_descriptor_ptrauth_discriminator);
442 return ptrauth_auth_data(aBlock->descriptor, ptrauth_key_asda, disc);
443#else
444 return aBlock->descriptor;
445#endif
446}
447
448static inline __typeof__(void (*)(void *, const void *))
449_Block_get_copy_fn(struct Block_descriptor_2 *desc)
450{
451 return (void (*)(void *, const void *))_Block_get_function_pointer(desc->copy);
452}
453
454static inline void
455_Block_set_copy_fn(struct Block_descriptor_2 *desc,
456 void (*fn)(void *, const void *))
457{
458 _Block_set_function_pointer(desc->copy, fn);
459}
460
461
462static inline __typeof__(void (*)(const void *))
463_Block_get_dispose_fn(struct Block_descriptor_2 *desc)
464{
465 return (void (*)(const void *))_Block_get_function_pointer(desc->dispose);
466}
467
468static inline void
469_Block_set_dispose_fn(struct Block_descriptor_2 *desc,
470 void (*fn)(const void *))
471{
472 _Block_set_function_pointer(desc->dispose, fn);
473}
474
475#pragma clang diagnostic push
476#pragma clang diagnostic ignored "-Wcast-align"
477
478static inline __typeof__(void (*)(void *, const void *))
479_Block_get_copy_function(struct Block_layout *aBlock)
480{
481 if (!(aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) {
482 return NULL;
483 }
484
485#if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
486 if (aBlock->flags & BLOCK_SMALL_DESCRIPTOR) {
487 return _Block_get_relative_function_pointer(
488 aBlock, copy, void (*)(void *, const void *));
489 }
490#endif
491
492 void *desc = _Block_get_descriptor(aBlock);
493 struct Block_descriptor_2 *bd2 =
494 (struct Block_descriptor_2 *)((unsigned char *)desc +
495 sizeof(struct Block_descriptor_1));
496 return _Block_get_copy_fn(desc: bd2);
497}
498
499static inline __typeof__(void (*)(const void *))
500_Block_get_dispose_function(struct Block_layout *aBlock)
501{
502 if (!(aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) {
503 return NULL;
504 }
505
506#if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
507 if (aBlock->flags & BLOCK_SMALL_DESCRIPTOR) {
508 return _Block_get_relative_function_pointer(
509 aBlock, dispose, void (*)(const void *));
510 }
511#endif
512
513 void *desc = _Block_get_descriptor(aBlock);
514 struct Block_descriptor_2 *bd2 =
515 (struct Block_descriptor_2 *)((unsigned char *)desc +
516 sizeof(struct Block_descriptor_1));
517 return _Block_get_dispose_fn(desc: bd2);
518}
519
520#pragma clang diagnostic pop
521
522// Other support functions
523
524
525// runtime entry to get total size of a closure
526BLOCK_EXPORT size_t Block_size(void *aBlock);
527
528// indicates whether block was compiled with compiler that sets the ABI related metadata bits
529BLOCK_EXPORT bool _Block_has_signature(void *aBlock)
530__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
531
532// returns TRUE if return value of block is on the stack, FALSE otherwise
533BLOCK_EXPORT bool _Block_use_stret(void *aBlock)
534__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
535
536// Returns a string describing the block's parameter and return types.
537// The encoding scheme is the same as Objective-C @encode.
538// Returns NULL for blocks compiled with some compilers.
539BLOCK_EXPORT const char * _Block_signature(void *aBlock)
540__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
541
542// Returns a string describing the block's GC layout.
543// This uses the GC skip/scan encoding.
544// May return NULL.
545BLOCK_EXPORT const char * _Block_layout(void *aBlock)
546__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
547
548// Returns a string describing the block's layout.
549// This uses the "extended layout" form described above.
550// May return NULL.
551BLOCK_EXPORT const char * _Block_extended_layout(void *aBlock)
552__OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_7_0);
553
554// Callable only from the ARR weak subsystem while in exclusion zone
555BLOCK_EXPORT bool _Block_tryRetain(const void *aBlock)
556__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
557
558// Callable only from the ARR weak subsystem while in exclusion zone
559BLOCK_EXPORT bool _Block_isDeallocating(const void *aBlock)
560__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);
561
562
563// the raw data space for runtime classes for blocks
564// class+meta used for stack, malloc, and collectable based blocks
565BLOCK_EXPORT void * _NSConcreteMallocBlock[32]
566__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
567BLOCK_EXPORT void * _NSConcreteAutoBlock[32]
568__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
569BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32]
570__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
571BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32]
572__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
573// declared in Block.h
574// BLOCK_EXPORT void * _NSConcreteGlobalBlock[32];
575// BLOCK_EXPORT void * _NSConcreteStackBlock[32];
576
577
578#if !KERNEL
579struct Block_callbacks_RR {
580 size_t size; // size == sizeof(struct Block_callbacks_RR)
581 void (*retain)(const void *);
582 void (*release)(const void *);
583 void (*destructInstance)(const void *);
584};
585typedef struct Block_callbacks_RR Block_callbacks_RR;
586
587BLOCK_EXPORT void _Block_use_RR2(const Block_callbacks_RR *callbacks);
588#endif // !KERNEL
589
590#endif
591