xref: /xnu-8020.140.41/libkern/libclosure/runtime.cpp (revision 27b03b360a988dfd3dfdf34262bb0042026747cc)
1 /*
2  * runtime.c
3  * libclosure
4  *
5  * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
6  *
7  * @APPLE_LLVM_LICENSE_HEADER@
8  */
9 
10 
11 #ifndef KERNEL
12 
13 #include "Block_private.h"
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <dlfcn.h>
17 #include <os/assumes.h>
18 #include <TargetConditionals.h>
19 
20 #else /* !KERNEL */
21 #define TARGET_OS_WIN32 0
22 
23 #include <libkern/Block_private.h>
24 __BEGIN_DECLS
25 #include <kern/kalloc.h>
26 __END_DECLS
27 
28 static inline void *
malloc(size_t size)29 malloc(size_t size)
30 {
31 	if (size == 0) {
32 		return NULL;
33 	}
34 	return kheap_alloc(KHEAP_DEFAULT, size,
35 	           Z_VM_TAG_BT(Z_WAITOK_ZERO, VM_KERN_MEMORY_LIBKERN));
36 }
37 
38 static inline void
free(void * addr,size_t size)39 free(void *addr, size_t size)
40 {
41 	kheap_free(KHEAP_DEFAULT, addr, size);
42 }
43 
44 #endif /* KERNEL */
45 
46 #include <machine/atomic.h>
47 #include <string.h>
48 #include <stdint.h>
49 #ifndef os_assumes
50 #define os_assumes(_x) (_x)
51 #endif
52 #ifndef os_assert
53 #define os_assert(_x) assert(_x)
54 #endif
55 
56 #if TARGET_OS_WIN32
57 #define _CRT_SECURE_NO_WARNINGS 1
58 #include <windows.h>
59 static __inline bool
OSAtomicCompareAndSwapLong(long oldl,long newl,long volatile * dst)60 OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst)
61 {
62 	// fixme barrier is overkill -- see objc-os.h
63 	long original = InterlockedCompareExchange(dst, newl, oldl);
64 	return original == oldl;
65 }
66 
67 static __inline bool
OSAtomicCompareAndSwapInt(int oldi,int newi,int volatile * dst)68 OSAtomicCompareAndSwapInt(int oldi, int newi, int volatile *dst)
69 {
70 	// fixme barrier is overkill -- see objc-os.h
71 	int original = InterlockedCompareExchange(dst, newi, oldi);
72 	return original == oldi;
73 }
74 #else
75 #define OSAtomicCompareAndSwapLong(_Old, _New, _Ptr) os_atomic_cmpxchg(_Ptr, _Old, _New, relaxed)
76 #define OSAtomicCompareAndSwapInt(_Old, _New, _Ptr) os_atomic_cmpxchg(_Ptr, _Old, _New, relaxed)
77 #endif
78 
79 
80 /*******************************************************************************
81  *  Internal Utilities
82  ********************************************************************************/
83 
84 static int32_t
latching_incr_int(volatile int32_t * where)85 latching_incr_int(volatile int32_t *where)
86 {
87 	while (1) {
88 		int32_t old_value = *where;
89 		if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
90 			return BLOCK_REFCOUNT_MASK;
91 		}
92 		if (OSAtomicCompareAndSwapInt(old_value, old_value + 2, where)) {
93 			return old_value + 2;
94 		}
95 	}
96 }
97 
98 static bool
latching_incr_int_not_deallocating(volatile int32_t * where)99 latching_incr_int_not_deallocating(volatile int32_t *where)
100 {
101 	while (1) {
102 		int32_t old_value = *where;
103 		if (old_value & BLOCK_DEALLOCATING) {
104 			// if deallocating we can't do this
105 			return false;
106 		}
107 		if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
108 			// if latched, we're leaking this block, and we succeed
109 			return true;
110 		}
111 		if (OSAtomicCompareAndSwapInt(old_value, old_value + 2, where)) {
112 			// otherwise, we must store a new retained value without the deallocating bit set
113 			return true;
114 		}
115 	}
116 }
117 
118 
119 // return should_deallocate?
120 static bool
latching_decr_int_should_deallocate(volatile int32_t * where)121 latching_decr_int_should_deallocate(volatile int32_t *where)
122 {
123 	while (1) {
124 		int32_t old_value = *where;
125 		if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
126 			return false; // latched high
127 		}
128 		if ((old_value & BLOCK_REFCOUNT_MASK) == 0) {
129 			return false; // underflow, latch low
130 		}
131 		int32_t new_value = old_value - 2;
132 		bool result = false;
133 		if ((old_value & (BLOCK_REFCOUNT_MASK | BLOCK_DEALLOCATING)) == 2) {
134 			new_value = old_value - 1;
135 			result = true;
136 		}
137 		if (OSAtomicCompareAndSwapInt(old_value, new_value, where)) {
138 			return result;
139 		}
140 	}
141 }
142 
143 
144 /**************************************************************************
145  *  Framework callback functions and their default implementations.
146  ***************************************************************************/
147 #if !TARGET_OS_WIN32
148 #pragma mark Framework Callback Routines
149 #endif
150 #if KERNEL
151 static inline void
_Block_retain_object(const void * ptr __unused)152 _Block_retain_object(const void *ptr __unused)
153 {
154 }
155 
156 static inline void
_Block_release_object(const void * ptr __unused)157 _Block_release_object(const void *ptr __unused)
158 {
159 }
160 
161 static inline void
_Block_destructInstance(const void * aBlock __unused)162 _Block_destructInstance(const void *aBlock __unused)
163 {
164 }
165 
166 #else
167 
168 static void
_Block_retain_object_default(const void * ptr __unused)169 _Block_retain_object_default(const void *ptr __unused)
170 {
171 }
172 
173 static void
_Block_release_object_default(const void * ptr __unused)174 _Block_release_object_default(const void *ptr __unused)
175 {
176 }
177 
178 static void
_Block_destructInstance_default(const void * aBlock __unused)179 _Block_destructInstance_default(const void *aBlock __unused)
180 {
181 }
182 
183 static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default;
184 static void (*_Block_release_object)(const void *ptr) = _Block_release_object_default;
185 static void (*_Block_destructInstance) (const void *aBlock) = _Block_destructInstance_default;
186 
187 
188 /**************************************************************************
189  *  Callback registration from ObjC runtime and CoreFoundation
190  ***************************************************************************/
191 
192 void
_Block_use_RR2(const Block_callbacks_RR * callbacks)193 _Block_use_RR2(const Block_callbacks_RR *callbacks)
194 {
195 	_Block_retain_object = callbacks->retain;
196 	_Block_release_object = callbacks->release;
197 	_Block_destructInstance = callbacks->destructInstance;
198 }
199 #endif // !KERNEL
200 
201 /****************************************************************************
202  *  Accessors for block descriptor fields
203  *****************************************************************************/
204 
205 #if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
206 template <class T>
207 static T *
unwrap_relative_pointer(int32_t & offset)208 unwrap_relative_pointer(int32_t &offset)
209 {
210 	if (offset == 0) {
211 		return nullptr;
212 	}
213 
214 	uintptr_t base = (uintptr_t)&offset;
215 	uintptr_t extendedOffset = (uintptr_t)(intptr_t)offset;
216 	uintptr_t pointer = base + extendedOffset;
217 	return (T *)pointer;
218 }
219 #endif
220 
221 #if 0
222 static struct Block_descriptor_2 *
223 _Block_descriptor_2(struct Block_layout *aBlock)
224 {
225 	uint8_t *desc = (uint8_t *)_Block_get_descriptor(aBlock);
226 	desc += sizeof(struct Block_descriptor_1);
227 	return __IGNORE_WCASTALIGN((struct Block_descriptor_2 *)desc);
228 }
229 #endif
230 
231 static struct Block_descriptor_3 *
_Block_descriptor_3(struct Block_layout * aBlock)232 _Block_descriptor_3(struct Block_layout *aBlock)
233 {
234 	uint8_t *desc = (uint8_t *)_Block_get_descriptor(aBlock);
235 	desc += sizeof(struct Block_descriptor_1);
236 	if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
237 		desc += sizeof(struct Block_descriptor_2);
238 	}
239 	return __IGNORE_WCASTALIGN((struct Block_descriptor_3 *)desc);
240 }
241 
242 static void
_Block_call_copy_helper(void * result,struct Block_layout * aBlock)243 _Block_call_copy_helper(void *result, struct Block_layout *aBlock)
244 {
245 	if (auto *pFn = _Block_get_copy_function(aBlock)) {
246 		pFn(result, aBlock);
247 	}
248 }
249 
250 static void
_Block_call_dispose_helper(struct Block_layout * aBlock)251 _Block_call_dispose_helper(struct Block_layout *aBlock)
252 {
253 	if (auto *pFn = _Block_get_dispose_function(aBlock)) {
254 		pFn(aBlock);
255 	}
256 }
257 
258 /*******************************************************************************
259  *  Internal Support routines for copying
260  ********************************************************************************/
261 
262 #if !TARGET_OS_WIN32
263 #pragma mark Copy/Release support
264 #endif
265 
266 // Copy, or bump refcount, of a block.  If really copying, call the copy helper if present.
267 void *
_Block_copy(const void * arg)268 _Block_copy(const void *arg)
269 {
270 	struct Block_layout *aBlock;
271 
272 	if (!arg) {
273 		return NULL;
274 	}
275 
276 	// The following would be better done as a switch statement
277 	aBlock = (struct Block_layout *)arg;
278 	if (aBlock->flags & BLOCK_NEEDS_FREE) {
279 		// latches on high
280 		latching_incr_int(&aBlock->flags);
281 		return aBlock;
282 	} else if (aBlock->flags & BLOCK_IS_GLOBAL) {
283 		return aBlock;
284 	} else {
285 		// Its a stack block.  Make a copy.
286 		size_t size = Block_size(aBlock);
287 		struct Block_layout *result = (struct Block_layout *)malloc(size);
288 		if (!result) {
289 			return NULL;
290 		}
291 		memmove(result, aBlock, size); // bitcopy first
292 #if __has_feature(ptrauth_calls)
293 		// Resign the invoke pointer as it uses address authentication.
294 		result->invoke = aBlock->invoke;
295 
296 #if __has_feature(ptrauth_signed_block_descriptors)
297 		uintptr_t oldDesc =
298 		    ptrauth_blend_discriminator(
299 			&aBlock->descriptor, _Block_descriptor_ptrauth_discriminator);
300 		uintptr_t newDesc =
301 		    ptrauth_blend_discriminator(
302 			&result->descriptor, _Block_descriptor_ptrauth_discriminator);
303 
304 		result->descriptor =
305 		    ptrauth_auth_and_resign(aBlock->descriptor, ptrauth_key_asda, oldDesc,
306 		    ptrauth_key_asda, newDesc);
307 #endif
308 #endif
309 
310 		// reset refcount
311 		result->flags &= ~(BLOCK_REFCOUNT_MASK | BLOCK_DEALLOCATING); // XXX not needed
312 		result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
313 		_Block_call_copy_helper(result, aBlock);
314 		// Set isa last so memory analysis tools see a fully-initialized object.
315 		result->isa = _NSConcreteMallocBlock;
316 		return result;
317 	}
318 }
319 
320 
321 // Runtime entry points for maintaining the sharing knowledge of byref data blocks.
322 
323 // A closure has been copied and its fixup routine is asking us to fix up the reference to the shared byref data
324 // Closures that aren't copied must still work, so everyone always accesses variables after dereferencing the forwarding ptr.
325 // We ask if the byref pointer that we know about has already been copied to the heap, and if so, increment and return it.
326 // Otherwise we need to copy it and update the stack forwarding pointer
327 static struct Block_byref *
_Block_byref_copy(const void * arg)328 _Block_byref_copy(const void *arg)
329 {
330 	struct Block_byref *src = (struct Block_byref *)arg;
331 
332 	if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
333 		// src points to stack
334 		struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
335 		copy->isa = NULL;
336 		// byref value 4 is logical refcount of 2: one for caller, one for stack
337 		copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
338 		copy->forwarding = copy; // patch heap copy to point to itself
339 		src->forwarding = copy; // patch stack to point to heap copy
340 		copy->size = src->size;
341 
342 		if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
343 			// Trust copy helper to copy everything of interest
344 			// If more than one field shows up in a byref block this is wrong XXX
345 			struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src + 1);
346 			struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy + 1);
347 			copy2->byref_keep = src2->byref_keep;
348 			copy2->byref_destroy = src2->byref_destroy;
349 
350 			if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
351 				struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2 + 1);
352 				struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2 + 1);
353 				copy3->layout = src3->layout;
354 			}
355 
356 			(*src2->byref_keep)(copy, src);
357 		} else {
358 			// Bitwise copy.
359 			// This copy includes Block_byref_3, if any.
360 			memmove(copy + 1, src + 1, src->size - sizeof(*src));
361 		}
362 	}
363 	// already copied to heap
364 	else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
365 		latching_incr_int(&src->forwarding->flags);
366 	}
367 
368 	return src->forwarding;
369 }
370 
371 static void
_Block_byref_release(const void * arg)372 _Block_byref_release(const void *arg)
373 {
374 	struct Block_byref *byref = (struct Block_byref *)arg;
375 
376 	// dereference the forwarding pointer since the compiler isn't doing this anymore (ever?)
377 	byref = byref->forwarding;
378 
379 	if (byref->flags & BLOCK_BYREF_NEEDS_FREE) {
380 		__assert_only int32_t refcount = byref->flags & BLOCK_REFCOUNT_MASK;
381 		os_assert(refcount);
382 		if (latching_decr_int_should_deallocate(&byref->flags)) {
383 			if (byref->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
384 				struct Block_byref_2 *byref2 = (struct Block_byref_2 *)(byref + 1);
385 				(*byref2->byref_destroy)(byref);
386 			}
387 			free(byref, byref->size);
388 		}
389 	}
390 }
391 
392 
393 /************************************************************
394  *
395  * API supporting SPI
396  * _Block_copy, _Block_release, and (old) _Block_destroy
397  *
398  ***********************************************************/
399 
400 #if !TARGET_OS_WIN32
401 #pragma mark SPI/API
402 #endif
403 
404 
405 // API entry point to release a copied Block
406 void
_Block_release(const void * arg)407 _Block_release(const void *arg)
408 {
409 	struct Block_layout *aBlock = (struct Block_layout *)arg;
410 	if (!aBlock) {
411 		return;
412 	}
413 	if (aBlock->flags & BLOCK_IS_GLOBAL) {
414 		return;
415 	}
416 	if (!(aBlock->flags & BLOCK_NEEDS_FREE)) {
417 		return;
418 	}
419 
420 	if (latching_decr_int_should_deallocate(&aBlock->flags)) {
421 		_Block_call_dispose_helper(aBlock);
422 		_Block_destructInstance(aBlock);
423 		free(aBlock, Block_size(aBlock));
424 	}
425 }
426 
427 bool
_Block_tryRetain(const void * arg)428 _Block_tryRetain(const void *arg)
429 {
430 	struct Block_layout *aBlock = (struct Block_layout *)arg;
431 	return latching_incr_int_not_deallocating(&aBlock->flags);
432 }
433 
434 bool
_Block_isDeallocating(const void * arg)435 _Block_isDeallocating(const void *arg)
436 {
437 	struct Block_layout *aBlock = (struct Block_layout *)arg;
438 	return (aBlock->flags & BLOCK_DEALLOCATING) != 0;
439 }
440 
441 
442 /************************************************************
443  *
444  * SPI used by other layers
445  *
446  ***********************************************************/
447 
448 size_t
Block_size(void * aBlock)449 Block_size(void *aBlock)
450 {
451 	auto *layout = (Block_layout *)aBlock;
452 	void *desc = _Block_get_descriptor(layout);
453 #if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
454 	if (layout->flags & BLOCK_SMALL_DESCRIPTOR) {
455 		return ((Block_descriptor_small *)desc)->size;
456 	}
457 #endif
458 	return ((Block_descriptor_1 *)desc)->size;
459 }
460 
461 bool
_Block_use_stret(void * aBlock)462 _Block_use_stret(void *aBlock)
463 {
464 	struct Block_layout *layout = (struct Block_layout *)aBlock;
465 
466 	int requiredFlags = BLOCK_HAS_SIGNATURE | BLOCK_USE_STRET;
467 	return (layout->flags & requiredFlags) == requiredFlags;
468 }
469 
470 // Checks for a valid signature, not merely the BLOCK_HAS_SIGNATURE bit.
471 bool
_Block_has_signature(void * aBlock)472 _Block_has_signature(void *aBlock)
473 {
474 	return _Block_signature(aBlock) ? true : false;
475 }
476 
477 const char *
_Block_signature(void * aBlock)478 _Block_signature(void *aBlock)
479 {
480 	struct Block_layout *layout = (struct Block_layout *)aBlock;
481 	if (!(layout->flags & BLOCK_HAS_SIGNATURE)) {
482 		return nullptr;
483 	}
484 
485 #if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
486 	if (layout->flags & BLOCK_SMALL_DESCRIPTOR) {
487 		auto *bds = (Block_descriptor_small *)_Block_get_descriptor(layout);
488 		return unwrap_relative_pointer<const char>(bds->signature);
489 	}
490 #endif
491 
492 	struct Block_descriptor_3 *desc3 = _Block_descriptor_3(layout);
493 	return desc3->signature;
494 }
495 
496 const char *
_Block_layout(void * aBlock)497 _Block_layout(void *aBlock)
498 {
499 	// Don't return extended layout to callers expecting old GC layout
500 	Block_layout *layout = (Block_layout *)aBlock;
501 	if ((layout->flags & BLOCK_HAS_EXTENDED_LAYOUT) ||
502 	    !(layout->flags & BLOCK_HAS_SIGNATURE)) {
503 		return nullptr;
504 	}
505 
506 #if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
507 	if (layout->flags & BLOCK_SMALL_DESCRIPTOR) {
508 		auto *bds = (Block_descriptor_small *)_Block_get_descriptor(layout);
509 		return unwrap_relative_pointer<const char>(bds->layout);
510 	}
511 #endif
512 
513 	Block_descriptor_3 *desc = _Block_descriptor_3(layout);
514 	return desc->layout;
515 }
516 
517 const char *
_Block_extended_layout(void * aBlock)518 _Block_extended_layout(void *aBlock)
519 {
520 	// Don't return old GC layout to callers expecting extended layout
521 	Block_layout *layout = (Block_layout *)aBlock;
522 	if (!(layout->flags & BLOCK_HAS_EXTENDED_LAYOUT) ||
523 	    !(layout->flags & BLOCK_HAS_SIGNATURE)) {
524 		return nullptr;
525 	}
526 
527 	const char *extLayout;
528 #if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
529 	if (layout->flags & BLOCK_SMALL_DESCRIPTOR) {
530 		auto *bds = (Block_descriptor_small *)_Block_get_descriptor(layout);
531 		if (layout->flags & BLOCK_INLINE_LAYOUT_STRING) {
532 			extLayout = (const char *)(uintptr_t)bds->layout;
533 		} else {
534 			extLayout = unwrap_relative_pointer<const char>(bds->layout);
535 		}
536 	} else
537 #endif
538 	{
539 		Block_descriptor_3 *desc3 = _Block_descriptor_3(layout);
540 		extLayout = desc3->layout;
541 	}
542 
543 	// Return empty string (all non-object bytes) instead of NULL
544 	// so callers can distinguish "empty layout" from "no layout".
545 	if (!extLayout) {
546 		extLayout = "";
547 	}
548 	return extLayout;
549 }
550 
551 #if !TARGET_OS_WIN32
552 #pragma mark Compiler SPI entry points
553 #endif
554 
555 
556 /*******************************************************
557  *
558  *  Entry points used by the compiler - the real API!
559  *
560  *
561  *  A Block can reference four different kinds of things that require help when the Block is copied to the heap.
562  *  1) C++ stack based objects
563  *  2) References to Objective-C objects
564  *  3) Other Blocks
565  *  4) __block variables
566  *
567  *  In these cases helper functions are synthesized by the compiler for use in Block_copy and Block_release, called the copy and dispose helpers.  The copy helper emits a call to the C++ const copy constructor for C++ stack based objects and for the rest calls into the runtime support function _Block_object_assign.  The dispose helper has a call to the C++ destructor for case 1 and a call into _Block_object_dispose for the rest.
568  *
569  *  The flags parameter of _Block_object_assign and _Block_object_dispose is set to
570  * BLOCK_FIELD_IS_OBJECT (3), for the case of an Objective-C Object,
571  * BLOCK_FIELD_IS_BLOCK (7), for the case of another Block, and
572  * BLOCK_FIELD_IS_BYREF (8), for the case of a __block variable.
573  *  If the __block variable is marked weak the compiler also or's in BLOCK_FIELD_IS_WEAK (16)
574  *
575  *  So the Block copy/dispose helpers should only ever generate the four flag values of 3, 7, 8, and 24.
576  *
577  *  When  a __block variable is either a C++ object, an Objective-C object, or another Block then the compiler also generates copy/dispose helper functions.  Similarly to the Block copy helper, the "__block" copy helper (formerly and still a.k.a. "byref" copy helper) will do a C++ copy constructor (not a const one though!) and the dispose helper will do the destructor.  And similarly the helpers will call into the same two support functions with the same values for objects and Blocks with the additional BLOCK_BYREF_CALLER (128) bit of information supplied.
578  *
579  *  So the __block copy/dispose helpers will generate flag values of 3 or 7 for objects and Blocks respectively, with BLOCK_FIELD_IS_WEAK (16) or'ed as appropriate and always 128 or'd in, for the following set of possibilities:
580  *   __block id                   128+3       (0x83)
581  *   __block (^Block)             128+7       (0x87)
582  *   __weak __block id            128+3+16    (0x93)
583  *   __weak __block (^Block)      128+7+16    (0x97)
584  *
585  *
586  ********************************************************/
587 
588 //
589 // When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point
590 // to do the assignment.
591 //
592 void
_Block_object_assign(void * destArg,const void * object,const int flags)593 _Block_object_assign(void *destArg, const void *object, const int flags)
594 {
595 	const void **dest = (const void **)destArg;
596 	switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
597 	case BLOCK_FIELD_IS_OBJECT:
598 		/*******
599 		 *  id object = ...;
600 		 *  [^{ object; } copy];
601 		 ********/
602 
603 		_Block_retain_object(object);
604 		*dest = object;
605 		break;
606 
607 	case BLOCK_FIELD_IS_BLOCK:
608 		/*******
609 		 *  void (^object)(void) = ...;
610 		 *  [^{ object; } copy];
611 		 ********/
612 
613 		*dest = _Block_copy(object);
614 		break;
615 
616 	case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
617 	case BLOCK_FIELD_IS_BYREF:
618 		/*******
619 		 *  // copy the onstack __block container to the heap
620 		 *  // Note this __weak is old GC-weak/MRC-unretained.
621 		 *  // ARC-style __weak is handled by the copy helper directly.
622 		 *  __block ... x;
623 		 *  __weak __block ... x;
624 		 *  [^{ x; } copy];
625 		 ********/
626 
627 		*dest = _Block_byref_copy(object);
628 		break;
629 
630 	case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
631 	case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
632 		/*******
633 		 *  // copy the actual field held in the __block container
634 		 *  // Note this is MRC unretained __block only.
635 		 *  // ARC retained __block is handled by the copy helper directly.
636 		 *  __block id object;
637 		 *  __block void (^object)(void);
638 		 *  [^{ object; } copy];
639 		 ********/
640 
641 		*dest = object;
642 		break;
643 
644 	case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
645 	case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK  | BLOCK_FIELD_IS_WEAK:
646 		/*******
647 		 *  // copy the actual field held in the __block container
648 		 *  // Note this __weak is old GC-weak/MRC-unretained.
649 		 *  // ARC-style __weak is handled by the copy helper directly.
650 		 *  __weak __block id object;
651 		 *  __weak __block void (^object)(void);
652 		 *  [^{ object; } copy];
653 		 ********/
654 
655 		*dest = object;
656 		break;
657 
658 	default:
659 		break;
660 	}
661 }
662 
663 // When Blocks or Block_byrefs hold objects their destroy helper routines call this entry point
664 // to help dispose of the contents
665 void
_Block_object_dispose(const void * object,const int flags)666 _Block_object_dispose(const void *object, const int flags)
667 {
668 	switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
669 	case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
670 	case BLOCK_FIELD_IS_BYREF:
671 		// get rid of the __block data structure held in a Block
672 		_Block_byref_release(object);
673 		break;
674 	case BLOCK_FIELD_IS_BLOCK:
675 		_Block_release(object);
676 		break;
677 	case BLOCK_FIELD_IS_OBJECT:
678 		_Block_release_object(object);
679 		break;
680 	case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
681 	case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
682 	case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
683 	case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK  | BLOCK_FIELD_IS_WEAK:
684 		break;
685 	default:
686 		break;
687 	}
688 }
689 
690 
691 // Workaround for <rdar://26015603> dylib with no __DATA segment fails to rebase
692 __attribute__((used))
693 static int let_there_be_data = 42;
694