xref: /xnu-8796.101.5/libkern/libkern/Block_private.h (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
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>
32 struct 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.
80 template <typename Fn, ptrauth_key Key>
81 class StorageSignedFunctionPointer {
82 	uintptr_t bits;
83 
84 public:
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
prepareWrite(Fn fn)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
prepareWrite(const StorageSignedFunctionPointer & other)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
completeReadFn(uintptr_t ptr)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*
completeReadRaw(uintptr_t ptr)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 
StorageSignedFunctionPointer()142 	StorageSignedFunctionPointer()
143 	{
144 	}
145 
StorageSignedFunctionPointer(Fn value)146 	StorageSignedFunctionPointer(Fn value)
147 		: bits(prepareWrite(value))
148 	{
149 	}
150 
StorageSignedFunctionPointer(const StorageSignedFunctionPointer & value)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 
Fn()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 
187 using BlockCopyFunction = StorageSignedFunctionPointer
188     <void (*)(void *, const void *),
189     ptrauth_key_block_function>;
190 
191 using BlockDisposeFunction = StorageSignedFunctionPointer
192     <void (*)(const void *),
193     ptrauth_key_block_function>;
194 
195 using BlockInvokeFunction = StorageSignedFunctionPointer
196     <void (*)(void *, ...),
197     ptrauth_key_block_function>;
198 
199 using BlockByrefKeepFunction = StorageSignedFunctionPointer
200     <void (*)(struct Block_byref *, struct Block_byref *),
201     ptrauth_key_block_function>;
202 
203 using 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 
211 typedef void (*BlockCopyFunction)(void *, const void *);
212 typedef void (*BlockDisposeFunction)(const void *);
213 typedef void (*BlockInvokeFunction)(void *, ...);
214 typedef void (*BlockByrefKeepFunction)(struct Block_byref*, struct Block_byref*);
215 typedef void (*BlockByrefDestroyFunction)(struct Block_byref *);
216 
217 #else
218 // ptrauth_calls but not c++11
219 
220 typedef uintptr_t BlockCopyFunction;
221 typedef uintptr_t BlockDisposeFunction;
222 typedef uintptr_t BlockInvokeFunction;
223 typedef uintptr_t BlockByrefKeepFunction;
224 typedef 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
268 enum {
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
289 struct Block_descriptor_1 {
290 	uintptr_t reserved;
291 	uintptr_t size;
292 };
293 
294 #define BLOCK_DESCRIPTOR_2 1
295 struct Block_descriptor_2 {
296 	// requires BLOCK_HAS_COPY_DISPOSE
297 	BlockCopyFunction copy;
298 	BlockDisposeFunction dispose;
299 };
300 
301 #define BLOCK_DESCRIPTOR_3 1
302 struct 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 
308 struct 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 
321 struct 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
332 enum {
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 
350 struct Block_byref {
351 	void *isa;
352 	struct Block_byref *forwarding;
353 	volatile int32_t flags; // contains ref count
354 	uint32_t size;
355 };
356 
357 struct Block_byref_2 {
358 	// requires BLOCK_BYREF_HAS_COPY_DISPOSE
359 	BlockByrefKeepFunction byref_keep;
360 	BlockByrefDestroyFunction byref_destroy;
361 };
362 
363 struct 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 
383 enum {
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
406 enum {
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 
415 enum {
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 
424 static inline __typeof__(void (*)(void *, ...))
_Block_get_invoke_fn(struct Block_layout * block)425 _Block_get_invoke_fn(struct Block_layout *block)
426 {
427 	return (void (*)(void *, ...))_Block_get_function_pointer(block->invoke);
428 }
429 
430 static inline void
_Block_set_invoke_fn(struct Block_layout * block,void (* fn)(void *,...))431 _Block_set_invoke_fn(struct Block_layout *block, void (*fn)(void *, ...))
432 {
433 	_Block_set_function_pointer(block->invoke, fn);
434 }
435 
436 static inline void *
_Block_get_descriptor(struct Block_layout * aBlock)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 
448 static inline __typeof__(void (*)(void *, const void *))
_Block_get_copy_fn(struct Block_descriptor_2 * desc)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 
454 static inline void
_Block_set_copy_fn(struct Block_descriptor_2 * desc,void (* fn)(void *,const 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 
462 static inline __typeof__(void (*)(const void *))
_Block_get_dispose_fn(struct Block_descriptor_2 * desc)463 _Block_get_dispose_fn(struct Block_descriptor_2 *desc)
464 {
465 	return (void (*)(const void *))_Block_get_function_pointer(desc->dispose);
466 }
467 
468 static inline void
_Block_set_dispose_fn(struct Block_descriptor_2 * desc,void (* fn)(const 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 
478 static inline __typeof__(void (*)(void *, const void *))
_Block_get_copy_function(struct Block_layout * aBlock)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(bd2);
497 }
498 
499 static inline __typeof__(void (*)(const void *))
_Block_get_dispose_function(struct Block_layout * aBlock)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(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
526 BLOCK_EXPORT size_t Block_size(void *aBlock);
527 
528 // indicates whether block was compiled with compiler that sets the ABI related metadata bits
529 BLOCK_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
533 BLOCK_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.
539 BLOCK_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.
545 BLOCK_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.
551 BLOCK_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
555 BLOCK_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
559 BLOCK_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
565 BLOCK_EXPORT void * _NSConcreteMallocBlock[32]
566 __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
567 BLOCK_EXPORT void * _NSConcreteAutoBlock[32]
568 __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
569 BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32]
570 __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
571 BLOCK_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
579 struct 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 };
585 typedef struct Block_callbacks_RR Block_callbacks_RR;
586 
587 BLOCK_EXPORT void _Block_use_RR2(const Block_callbacks_RR *callbacks);
588 #endif // !KERNEL
589 
590 #endif
591