]> git.saurik.com Git - apple/objc4.git/blame - runtime/objc-block-trampolines.mm
objc4-818.2.tar.gz
[apple/objc4.git] / runtime / objc-block-trampolines.mm
CommitLineData
8972963c
A
1/*
2 * Copyright (c) 2010 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/***********************************************************************
24 * objc-block-trampolines.m
25 * Author: b.bum
26 *
27 **********************************************************************/
28
29/***********************************************************************
30 * Imports.
31 **********************************************************************/
32#include "objc-private.h"
33#include "runtime.h"
34
35#include <Block.h>
36#include <Block_private.h>
37#include <mach/mach.h>
66799735 38#include <objc/objc-block-trampolines.h>
8972963c 39
66799735
A
40// fixme C++ compilers don't implemement memory_order_consume efficiently.
41// Use memory_order_relaxed and cross our fingers.
42#define MEMORY_ORDER_CONSUME std::memory_order_relaxed
8972963c 43
66799735
A
44// 8 bytes of text and data per trampoline on all architectures.
45#define SLOT_SIZE 8
46
47// The trampolines are defined in assembly files in libobjc-trampolines.dylib.
48// We can't link to libobjc-trampolines.dylib directly because
49// for security reasons it isn't in the dyld shared cache.
50
51// Trampoline addresses are lazily looked up.
52// All of them are hidden behind a single atomic pointer for lock-free init.
53
54#ifdef __PTRAUTH_INTRINSICS__
55# define TrampolinePtrauth __ptrauth(ptrauth_key_function_pointer, 1, 0x3af1)
56#else
57# define TrampolinePtrauth
58#endif
59
34d5b5e8
A
60// A page of trampolines is as big as the maximum supported page size
61// everywhere except i386. i386 only exists for the watch simulator
62// now, and we know it really only has 4kB pages. Also see comments
63// below about PAGE_SIZE and PAGE_MAX_SIZE.
64#ifdef __i386__
65#define TRAMPOLINE_PAGE_SIZE PAGE_MIN_SIZE
66#else
67#define TRAMPOLINE_PAGE_SIZE PAGE_MAX_SIZE
68#endif
69
66799735
A
70class TrampolinePointerWrapper {
71 struct TrampolinePointers {
72 class TrampolineAddress {
73 const void * TrampolinePtrauth storage;
74
75 public:
76 TrampolineAddress(void *dylib, const char *name) {
77#define PREFIX "_objc_blockTrampoline"
78 char symbol[strlen(PREFIX) + strlen(name) + 1];
79 strcpy(symbol, PREFIX);
80 strcat(symbol, name);
81 // dlsym() from a text segment returns a signed pointer
82 // Authenticate it manually and let the compiler re-sign it.
83 storage = ptrauth_auth_data(dlsym(dylib, symbol),
84 ptrauth_key_function_pointer, 0);
85 if (!storage) {
86 _objc_fatal("couldn't dlsym %s", symbol);
87 }
88 }
89
90 uintptr_t address() {
91 return (uintptr_t)(void*)storage;
92 }
93 };
94
95 TrampolineAddress impl; // trampoline header code
96 TrampolineAddress start; // first trampoline
97#if DEBUG
98 // These symbols are only used in assertions.
99 // fixme might be able to move the assertions to libobjc-trampolines itself
100 TrampolineAddress last; // start of the last trampoline
101 // We don't use the address after the last trampoline because that
102 // address might be in a different section, and then dlsym() would not
103 // sign it as a function pointer.
104# if SUPPORT_STRET
105 TrampolineAddress impl_stret;
106 TrampolineAddress start_stret;
107 TrampolineAddress last_stret;
108# endif
109#endif
110
111 uintptr_t textSegment;
112 uintptr_t textSegmentSize;
113
114 void check() {
115#if DEBUG
34d5b5e8
A
116 ASSERT(impl.address() == textSegment + TRAMPOLINE_PAGE_SIZE);
117 ASSERT(impl.address() % PAGE_SIZE == 0); // not TRAMPOLINE_PAGE_SIZE
118 ASSERT(impl.address() + TRAMPOLINE_PAGE_SIZE ==
66799735 119 last.address() + SLOT_SIZE);
1807f628
A
120 ASSERT(last.address()+8 < textSegment + textSegmentSize);
121 ASSERT((last.address() - start.address()) % SLOT_SIZE == 0);
66799735 122# if SUPPORT_STRET
34d5b5e8
A
123 ASSERT(impl_stret.address() == textSegment + 2*TRAMPOLINE_PAGE_SIZE);
124 ASSERT(impl_stret.address() % PAGE_SIZE == 0); // not TRAMPOLINE_PAGE_SIZE
125 ASSERT(impl_stret.address() + TRAMPOLINE_PAGE_SIZE ==
66799735 126 last_stret.address() + SLOT_SIZE);
34d5b5e8 127 ASSERT(start.address() - impl.address() ==
66799735 128 start_stret.address() - impl_stret.address());
34d5b5e8 129 ASSERT(last_stret.address() + SLOT_SIZE <
66799735 130 textSegment + textSegmentSize);
34d5b5e8 131 ASSERT((last_stret.address() - start_stret.address())
66799735
A
132 % SLOT_SIZE == 0);
133# endif
134#endif
135 }
136
137
138 TrampolinePointers(void *dylib)
139 : impl(dylib, "Impl")
140 , start(dylib, "Start")
141#if DEBUG
142 , last(dylib, "Last")
143# if SUPPORT_STRET
144 , impl_stret(dylib, "Impl_stret")
145 , start_stret(dylib, "Start_stret")
146 , last_stret(dylib, "Last_stret")
147# endif
8070259c 148#endif
66799735
A
149 {
150 const auto *mh =
151 dyld_image_header_containing_address((void *)impl.address());
152 unsigned long size = 0;
153 textSegment = (uintptr_t)
154 getsegmentdata((headerType *)mh, "__TEXT", &size);
155 textSegmentSize = size;
156
157 check();
158 }
159 };
160
161 std::atomic<TrampolinePointers *> trampolines{nil};
162
163 TrampolinePointers *get() {
164 return trampolines.load(MEMORY_ORDER_CONSUME);
165 }
166
167public:
168 void Initialize() {
169 if (get()) return;
170
171 // This code may be called concurrently.
172 // In the worst case we perform extra dyld operations.
173 void *dylib = dlopen("/usr/lib/libobjc-trampolines.dylib",
174 RTLD_NOW | RTLD_LOCAL | RTLD_FIRST);
175 if (!dylib) {
13ba007e
A
176 _objc_fatal("couldn't dlopen libobjc-trampolines.dylib: %s",
177 dlerror());
66799735
A
178 }
179
180 auto t = new TrampolinePointers(dylib);
181 TrampolinePointers *old = nil;
182 if (! trampolines.compare_exchange_strong(old, t, memory_order_release))
183 {
184 delete t; // Lost an initialization race.
185 }
186 }
187
188 uintptr_t textSegment() { return get()->textSegment; }
189 uintptr_t textSegmentSize() { return get()->textSegmentSize; }
190
34d5b5e8 191 uintptr_t dataSize() { return TRAMPOLINE_PAGE_SIZE; }
13ba007e 192
66799735
A
193 uintptr_t impl() { return get()->impl.address(); }
194 uintptr_t start() { return get()->start.address(); }
195};
196
197static TrampolinePointerWrapper Trampolines;
8972963c
A
198
199// argument mode identifier
13ba007e
A
200// Some calculations assume that these modes are sequential starting from 0.
201// This order must match the order of the trampoline's assembly code.
8972963c
A
202typedef enum {
203 ReturnValueInRegisterArgumentMode,
8070259c 204#if SUPPORT_STRET
8972963c 205 ReturnValueOnStackArgumentMode,
8070259c 206#endif
8972963c 207
8070259c 208 ArgumentModeCount
8972963c
A
209} ArgumentMode;
210
8070259c
A
211// We must take care with our data layout on architectures that support
212// multiple page sizes.
213//
34d5b5e8
A
214// The trampoline template in __TEXT is sized and aligned with PAGE_MAX_SIZE,
215// except on i386 which is a weird special case that uses PAGE_MIN_SIZE.
216// The TRAMPOLINE_PAGE_SIZE macro handles this difference. On some platforms,
217// aligning to PAGE_MAX_SIZE requires additional linker flags.
8070259c 218//
34d5b5e8
A
219// When we allocate a page group, we use TRAMPOLINE_PAGE_SIZE size.
220// This allows trampoline code to find its data by subtracting TRAMPOLINE_PAGE_SIZE.
8070259c 221//
66799735 222// When we allocate a page group, we use the process's page alignment.
8070259c
A
223// This simplifies allocation because we don't need to force greater than
224// default alignment when running with small pages, but it also means
225// the trampoline code MUST NOT look for its data by masking with PAGE_MAX_MASK.
226
66799735 227struct TrampolineBlockPageGroup
8070259c 228{
66799735
A
229 TrampolineBlockPageGroup *nextPageGroup; // linked list of all pages
230 TrampolineBlockPageGroup *nextAvailablePage; // linked list of pages with available slots
13ba007e 231
8070259c 232 uintptr_t nextAvailable; // index of next available slot, endIndex() if no more available
13ba007e
A
233
234 const void * TrampolinePtrauth const text; // text VM region; stored only for the benefit of the leaks tool
235
236 TrampolineBlockPageGroup()
237 : nextPageGroup(nil)
238 , nextAvailablePage(nil)
239 , nextAvailable(startIndex())
240 , text((const void *)((uintptr_t)this + Trampolines.dataSize()))
241 { }
8070259c
A
242
243 // Payload data: block pointers and free list.
244 // Bytes parallel with trampoline header code are the fields above or unused
34d5b5e8 245 // uint8_t payloads[TRAMPOLINE_PAGE_SIZE - sizeof(TrampolineBlockPageGroup)]
66799735
A
246
247 // Code: Mach-O header, then trampoline header followed by trampolines.
248 // On platforms with struct return we have non-stret trampolines and
249 // stret trampolines. The stret and non-stret trampolines at a given
250 // index share the same data page.
34d5b5e8
A
251 // uint8_t macho[TRAMPOLINE_PAGE_SIZE];
252 // uint8_t trampolines[ArgumentModeCount][TRAMPOLINE_PAGE_SIZE];
8070259c 253
8972963c 254 // Per-trampoline block data format:
8070259c 255 // initial value is 0 while page data is filled sequentially
8972963c 256 // when filled, value is reference to Block_copy()d block
8070259c
A
257 // when empty, value is index of next available slot OR 0 if never used yet
258
259 union Payload {
260 id block;
261 uintptr_t nextAvailable; // free list
262 };
263
264 static uintptr_t headerSize() {
66799735 265 return (uintptr_t) (Trampolines.start() - Trampolines.impl());
8070259c
A
266 }
267
268 static uintptr_t slotSize() {
66799735 269 return SLOT_SIZE;
8070259c 270 }
8972963c 271
8070259c
A
272 static uintptr_t startIndex() {
273 // headerSize is assumed to be slot-aligned
274 return headerSize() / slotSize();
275 }
8972963c 276
8070259c 277 static uintptr_t endIndex() {
13ba007e 278 return (uintptr_t)Trampolines.dataSize() / slotSize();
8070259c 279 }
8972963c 280
8070259c
A
281 static bool validIndex(uintptr_t index) {
282 return (index >= startIndex() && index < endIndex());
283 }
8972963c 284
8070259c 285 Payload *payload(uintptr_t index) {
1807f628 286 ASSERT(validIndex(index));
8070259c
A
287 return (Payload *)((char *)this + index*slotSize());
288 }
8972963c 289
66799735 290 uintptr_t trampolinesForMode(int aMode) {
13ba007e
A
291 // Skip over the data area, one page of Mach-O headers,
292 // and one text page for each mode before this one.
293 return (uintptr_t)this + Trampolines.dataSize() +
34d5b5e8 294 TRAMPOLINE_PAGE_SIZE * (1 + aMode);
66799735
A
295 }
296
297 IMP trampoline(int aMode, uintptr_t index) {
1807f628 298 ASSERT(validIndex(index));
66799735
A
299 char *base = (char *)trampolinesForMode(aMode);
300 char *imp = base + index*slotSize();
8070259c
A
301#if __arm__
302 imp++; // trampoline is Thumb instructions
66799735
A
303#endif
304#if __has_feature(ptrauth_calls)
305 imp = ptrauth_sign_unauthenticated(imp,
306 ptrauth_key_function_pointer, 0);
cd5f04f5 307#endif
8070259c
A
308 return (IMP)imp;
309 }
8972963c 310
66799735
A
311 uintptr_t indexForTrampoline(uintptr_t tramp) {
312 for (int aMode = 0; aMode < ArgumentModeCount; aMode++) {
313 uintptr_t base = trampolinesForMode(aMode);
314 uintptr_t start = base + startIndex() * slotSize();
315 uintptr_t end = base + endIndex() * slotSize();
316 if (tramp >= start && tramp < end) {
317 return (uintptr_t)(tramp - base) / slotSize();
318 }
8070259c
A
319 }
320 return 0;
321 }
8972963c 322
8070259c 323 static void check() {
1807f628
A
324 ASSERT(TrampolineBlockPageGroup::headerSize() >= sizeof(TrampolineBlockPageGroup));
325 ASSERT(TrampolineBlockPageGroup::headerSize() % TrampolineBlockPageGroup::slotSize() == 0);
8070259c 326 }
8972963c 327
8070259c 328};
8972963c 329
66799735 330static TrampolineBlockPageGroup *HeadPageGroup;
8972963c 331
8070259c 332#pragma mark Utility Functions
8972963c 333
66799735
A
334#if !__OBJC2__
335#define runtimeLock classLock
8972963c 336#endif
8972963c
A
337
338#pragma mark Trampoline Management Functions
66799735 339static TrampolineBlockPageGroup *_allocateTrampolinesAndData()
8070259c 340{
66799735 341 runtimeLock.assertLocked();
8972963c
A
342
343 vm_address_t dataAddress;
344
66799735
A
345 TrampolineBlockPageGroup::check();
346
347 // Our final mapping will look roughly like this:
348 // r/w data
349 // r/o text mapped from libobjc-trampolines.dylib
350 // with fixed offsets from the text to the data embedded in the text.
351 //
352 // More precisely it will look like this:
353 // 1 page r/w data
354 // 1 page libobjc-trampolines.dylib Mach-O header
355 // N pages trampoline code, one for each ArgumentMode
356 // M pages for the rest of libobjc-trampolines' TEXT segment.
357 // The kernel requires that we remap the entire TEXT segment every time.
358 // We assume that our code begins on the second TEXT page, but are robust
359 // against other additions to the end of the TEXT segment.
360
1807f628 361 ASSERT(HeadPageGroup == nil || HeadPageGroup->nextAvailablePage == nil);
66799735
A
362
363 auto textSource = Trampolines.textSegment();
364 auto textSourceSize = Trampolines.textSegmentSize();
13ba007e 365 auto dataSize = Trampolines.dataSize();
66799735
A
366
367 // Allocate a single contiguous region big enough to hold data+text.
8070259c 368 kern_return_t result;
66799735
A
369 result = vm_allocate(mach_task_self(), &dataAddress,
370 dataSize + textSourceSize,
4a109af3
A
371 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_FOUNDATION));
372 if (result != KERN_SUCCESS) {
373 _objc_fatal("vm_allocate trampolines failed (%d)", result);
374 }
8972963c 375
66799735
A
376 // Remap libobjc-trampolines' TEXT segment atop all
377 // but the first of the pages we just allocated:
378 vm_address_t textDest = dataAddress + dataSize;
4a109af3 379 vm_prot_t currentProtection, maxProtection;
66799735
A
380 result = vm_remap(mach_task_self(), &textDest,
381 textSourceSize,
4a109af3 382 0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
66799735 383 mach_task_self(), textSource, TRUE,
4a109af3 384 &currentProtection, &maxProtection, VM_INHERIT_SHARE);
8070259c 385 if (result != KERN_SUCCESS) {
4a109af3 386 _objc_fatal("vm_remap trampolines failed (%d)", result);
8070259c 387 }
66799735 388
13ba007e 389 auto *pageGroup = new ((void*)dataAddress) TrampolineBlockPageGroup;
8972963c 390
66799735
A
391 if (HeadPageGroup) {
392 TrampolineBlockPageGroup *lastPageGroup = HeadPageGroup;
393 while(lastPageGroup->nextPageGroup) {
394 lastPageGroup = lastPageGroup->nextPageGroup;
4a109af3 395 }
66799735
A
396 lastPageGroup->nextPageGroup = pageGroup;
397 HeadPageGroup->nextAvailablePage = pageGroup;
8972963c 398 } else {
66799735 399 HeadPageGroup = pageGroup;
8972963c
A
400 }
401
66799735 402 return pageGroup;
8972963c
A
403}
404
66799735
A
405static TrampolineBlockPageGroup *
406getOrAllocatePageGroupWithNextAvailable()
8070259c 407{
66799735 408 runtimeLock.assertLocked();
8972963c 409
66799735
A
410 if (!HeadPageGroup)
411 return _allocateTrampolinesAndData();
8972963c 412
8070259c 413 // make sure head page is filled first
66799735
A
414 if (HeadPageGroup->nextAvailable != HeadPageGroup->endIndex())
415 return HeadPageGroup;
8972963c 416
66799735
A
417 if (HeadPageGroup->nextAvailablePage) // check if there is a page w/a hole
418 return HeadPageGroup->nextAvailablePage;
8972963c 419
66799735 420 return _allocateTrampolinesAndData(); // tack on a new one
8972963c
A
421}
422
66799735
A
423static TrampolineBlockPageGroup *
424pageAndIndexContainingIMP(IMP anImp, uintptr_t *outIndex)
8070259c 425{
66799735 426 runtimeLock.assertLocked();
8972963c 427
66799735
A
428 // Authenticate as a function pointer, returning an un-signed address.
429 uintptr_t trampAddress =
430 (uintptr_t)ptrauth_auth_data((const char *)anImp,
431 ptrauth_key_function_pointer, 0);
432
433 for (TrampolineBlockPageGroup *pageGroup = HeadPageGroup;
434 pageGroup;
435 pageGroup = pageGroup->nextPageGroup)
436 {
437 uintptr_t index = pageGroup->indexForTrampoline(trampAddress);
438 if (index) {
439 if (outIndex) *outIndex = index;
440 return pageGroup;
8972963c
A
441 }
442 }
443
7257e56c 444 return nil;
8972963c
A
445}
446
8070259c
A
447
448static ArgumentMode
66799735 449argumentModeForBlock(id block)
8070259c
A
450{
451 ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
452
453#if SUPPORT_STRET
454 if (_Block_has_signature(block) && _Block_use_stret(block))
455 aMode = ReturnValueOnStackArgumentMode;
456#else
1807f628 457 ASSERT(! (_Block_has_signature(block) && _Block_use_stret(block)));
8070259c
A
458#endif
459
460 return aMode;
461}
462
1807f628
A
463/// Initialize the trampoline machinery. Normally this does nothing, as
464/// everything is initialized lazily, but for certain processes we eagerly load
465/// the trampolines dylib.
466void
467_imp_implementationWithBlock_init(void)
468{
469#if TARGET_OS_OSX
470 // Eagerly load libobjc-trampolines.dylib in certain processes. Some
471 // programs (most notably QtWebEngineProcess used by older versions of
472 // embedded Chromium) enable a highly restrictive sandbox profile which
473 // blocks access to that dylib. If anything calls
474 // imp_implementationWithBlock (as AppKit has started doing) then we'll
475 // crash trying to load it. Loading it here sets it up before the sandbox
476 // profile is enabled and blocks it.
477 //
478 // This fixes EA Origin (rdar://problem/50813789)
479 // and Steam (rdar://problem/55286131)
480 if (__progname &&
481 (strcmp(__progname, "QtWebEngineProcess") == 0 ||
482 strcmp(__progname, "Steam Helper") == 0)) {
483 Trampolines.Initialize();
484 }
485#endif
486}
487
8070259c 488
8972963c 489// `block` must already have been copied
8070259c
A
490IMP
491_imp_implementationWithBlockNoCopy(id block)
8972963c 492{
66799735 493 runtimeLock.assertLocked();
8972963c 494
66799735
A
495 TrampolineBlockPageGroup *pageGroup =
496 getOrAllocatePageGroupWithNextAvailable();
8070259c 497
66799735 498 uintptr_t index = pageGroup->nextAvailable;
1807f628 499 ASSERT(index >= pageGroup->startIndex() && index < pageGroup->endIndex());
66799735 500 TrampolineBlockPageGroup::Payload *payload = pageGroup->payload(index);
8070259c
A
501
502 uintptr_t nextAvailableIndex = payload->nextAvailable;
503 if (nextAvailableIndex == 0) {
504 // First time through (unused slots are zero). Fill sequentially.
505 // If the page is now full this will now be endIndex(), handled below.
506 nextAvailableIndex = index + 1;
507 }
66799735
A
508 pageGroup->nextAvailable = nextAvailableIndex;
509 if (nextAvailableIndex == pageGroup->endIndex()) {
510 // PageGroup is now full (free list or wilderness exhausted)
8070259c 511 // Remove from available page linked list
66799735
A
512 TrampolineBlockPageGroup *iterator = HeadPageGroup;
513 while(iterator && (iterator->nextAvailablePage != pageGroup)) {
8070259c
A
514 iterator = iterator->nextAvailablePage;
515 }
516 if (iterator) {
66799735
A
517 iterator->nextAvailablePage = pageGroup->nextAvailablePage;
518 pageGroup->nextAvailablePage = nil;
8972963c 519 }
8972963c
A
520 }
521
8070259c 522 payload->block = block;
66799735 523 return pageGroup->trampoline(argumentModeForBlock(block), index);
8972963c
A
524}
525
8972963c
A
526
527#pragma mark Public API
cd5f04f5 528IMP imp_implementationWithBlock(id block)
8972963c 529{
66799735
A
530 // Block object must be copied outside runtimeLock
531 // because it performs arbitrary work.
8972963c 532 block = Block_copy(block);
66799735
A
533
534 // Trampolines must be initialized outside runtimeLock
535 // because it calls dlopen().
536 Trampolines.Initialize();
537
538 mutex_locker_t lock(runtimeLock);
539
540 return _imp_implementationWithBlockNoCopy(block);
8972963c
A
541}
542
543
cd5f04f5 544id imp_getBlock(IMP anImp) {
8070259c 545 uintptr_t index;
66799735 546 TrampolineBlockPageGroup *pageGroup;
8972963c 547
7257e56c 548 if (!anImp) return nil;
8972963c 549
66799735 550 mutex_locker_t lock(runtimeLock);
8972963c 551
66799735 552 pageGroup = pageAndIndexContainingIMP(anImp, &index);
8972963c 553
66799735 554 if (!pageGroup) {
7257e56c 555 return nil;
8972963c 556 }
8070259c 557
66799735 558 TrampolineBlockPageGroup::Payload *payload = pageGroup->payload(index);
8972963c 559
66799735 560 if (payload->nextAvailable <= TrampolineBlockPageGroup::endIndex()) {
8070259c 561 // unallocated
7257e56c 562 return nil;
8972963c
A
563 }
564
8070259c 565 return payload->block;
8972963c
A
566}
567
568BOOL imp_removeBlock(IMP anImp) {
8972963c
A
569
570 if (!anImp) return NO;
8070259c 571
66799735 572 id block;
8972963c 573
8070259c 574 {
66799735 575 mutex_locker_t lock(runtimeLock);
8972963c 576
66799735
A
577 uintptr_t index;
578 TrampolineBlockPageGroup *pageGroup =
579 pageAndIndexContainingIMP(anImp, &index);
580
581 if (!pageGroup) {
582 return NO;
583 }
584
585 TrampolineBlockPageGroup::Payload *payload = pageGroup->payload(index);
586 block = payload->block;
587 // block is released below, outside the lock
588
589 payload->nextAvailable = pageGroup->nextAvailable;
590 pageGroup->nextAvailable = index;
591
592 // make sure this page is on available linked list
593 TrampolineBlockPageGroup *pageGroupIterator = HeadPageGroup;
594
595 // see if page is the next available page for any existing pages
596 while (pageGroupIterator->nextAvailablePage &&
597 pageGroupIterator->nextAvailablePage != pageGroup)
598 {
599 pageGroupIterator = pageGroupIterator->nextAvailablePage;
600 }
601
602 if (! pageGroupIterator->nextAvailablePage) {
603 // if iteration stopped because nextAvail was nil
604 // add to end of list.
605 pageGroupIterator->nextAvailablePage = pageGroup;
606 pageGroup->nextAvailablePage = nil;
607 }
8972963c 608 }
66799735
A
609
610 // do this AFTER dropping the lock
8972963c
A
611 Block_release(block);
612 return YES;
613}