2 * Copyright (c) 2010 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 /***********************************************************************
24 * objc-block-trampolines.m
27 **********************************************************************/
29 /***********************************************************************
31 **********************************************************************/
32 #include "objc-private.h"
36 #include <Block_private.h>
37 #include <mach/mach.h>
39 // symbols defined in assembly files
40 // Don't use the symbols directly; they're thumb-biased on some ARM archs.
41 #define TRAMP(tramp) \
42 static inline uintptr_t tramp(void) { \
43 extern void *_##tramp; \
44 return ((uintptr_t)&_##tramp) & ~1UL; \
47 TRAMP(a1a2_tramphead); // trampoline header code
48 TRAMP(a1a2_firsttramp); // first trampoline
49 TRAMP(a1a2_nexttramp); // second trampoline
50 TRAMP(a1a2_trampend); // after the last trampoline
53 TRAMP(a2a3_tramphead);
54 TRAMP(a2a3_firsttramp);
55 TRAMP(a2a3_nexttramp);
58 // argument mode identifier
60 ReturnValueInRegisterArgumentMode,
61 ReturnValueOnStackArgumentMode,
66 // slot size is 8 bytes on both i386 and x86_64 (because of bytes-per-call instruction is > 4 for both)
69 // unsigned value, any value, larger thna # of blocks that fit in the page pair
70 #define LAST_SLOT_MARKER 4241
72 #define TRAMPOLINE_PAGE_PAIR_HEADER_SIZE (sizeof(uint32_t) + sizeof(struct _TrampolineBlockPagePair *) + sizeof(struct _TrampolineBlockPagePair *))
73 typedef struct _TrampolineBlockPagePair {
74 struct _TrampolineBlockPagePair *nextPagePair; // linked list of all page pairs
75 struct _TrampolineBlockPagePair *nextAvailablePage; // linked list of pages with available slots
77 uint32_t nextAvailable; // index of next available slot, 0 if no more available
79 // Data: block pointers and free list.
80 // Bytes parallel with trampoline header are the fields above, or unused.
81 uint8_t blocks[ PAGE_SIZE - TRAMPOLINE_PAGE_PAIR_HEADER_SIZE ]
82 __attribute__((unavailable)) /* always use _headerSize() */;
84 // Code: trampoline header followed by trampolines.
85 uint8_t trampolines[PAGE_SIZE];
87 // Per-trampoline block data format:
88 // initial value is 0 while page pair is filled sequentially (last slot is LAST_SLOT_MARKER to indicate end of page)
89 // when filled, value is reference to Block_copy()d block
90 // when empty, value is index of next available slot OR LAST_SLOT_MARKER
92 } TrampolineBlockPagePair;
94 // two sets of trampoline page pairs; one for stack returns and one for register returns
95 static TrampolineBlockPagePair *headPagePairs[2];
97 #pragma mark Utility Functions
98 static inline uint32_t _headerSize() {
99 uint32_t headerSize = (uint32_t) (a1a2_firsttramp() - a1a2_tramphead());
101 // make sure stret and non-stret sizes match
102 assert(a2a3_firsttramp() - a2a3_tramphead() == headerSize);
107 static inline uint32_t _slotSize() {
108 uint32_t slotSize = (uint32_t) (a1a2_nexttramp() - a1a2_firsttramp());
110 // make sure stret and non-stret sizes match
111 assert(a2a3_nexttramp() - a2a3_firsttramp() == slotSize);
116 static inline bool trampolinesAreThumb(void) {
117 extern void *_a1a2_firsttramp;
119 extern void *_a1a2_nexttramp;
120 extern void *_a2a3_firsttramp;
121 extern void *_a2a3_nexttramp;
124 // make sure thumb-edness of all trampolines match
125 assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
126 ((uintptr_t)&_a2a3_firsttramp) % 2);
127 assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
128 ((uintptr_t)&_a1a2_nexttramp) % 2);
129 assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
130 ((uintptr_t)&_a2a3_nexttramp) % 2);
132 return ((uintptr_t)&_a1a2_firsttramp) % 2;
135 static inline uint32_t _slotsPerPagePair() {
136 uint32_t slotSize = _slotSize();
137 uint32_t slotsPerPagePair = PAGE_SIZE / slotSize;
138 return slotsPerPagePair;
141 static inline uint32_t _paddingSlotCount() {
142 uint32_t headerSize = _headerSize();
143 uint32_t slotSize = _slotSize();
144 uint32_t paddingSlots = headerSize / slotSize;
148 static inline id *_payloadAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) {
149 uint32_t slotSize = _slotSize();
150 uintptr_t baseAddress = (uintptr_t) pagePair;
151 uintptr_t payloadAddress = baseAddress + (slotSize * index);
152 return (id *)payloadAddress;
155 static inline IMP _trampolineAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) {
156 uint32_t slotSize = _slotSize();
157 uintptr_t baseAddress = (uintptr_t) &(pagePair->trampolines);
158 uintptr_t trampolineAddress = baseAddress + (slotSize * index);
161 if (trampolinesAreThumb()) trampolineAddress++;
164 return (IMP)trampolineAddress;
167 static inline void _lock() {
169 rwlock_write(&runtimeLock);
171 mutex_lock(&classLock);
175 static inline void _unlock() {
177 rwlock_unlock_write(&runtimeLock);
179 mutex_unlock(&classLock);
183 static inline void _assert_locked() {
185 rwlock_assert_writing(&runtimeLock);
187 mutex_assert_locked(&classLock);
191 #pragma mark Trampoline Management Functions
192 static TrampolineBlockPagePair *_allocateTrampolinesAndData(ArgumentMode aMode) {
195 vm_address_t dataAddress;
197 // make sure certain assumptions are met
198 assert(sizeof(TrampolineBlockPagePair) == 2*PAGE_SIZE);
199 assert(_slotSize() == 8);
200 assert(_headerSize() >= TRAMPOLINE_PAGE_PAIR_HEADER_SIZE);
201 assert((_headerSize() % _slotSize()) == 0);
203 assert(a1a2_tramphead() % PAGE_SIZE == 0);
204 assert(a1a2_tramphead() + PAGE_SIZE == a1a2_trampend());
205 assert(a2a3_tramphead() % PAGE_SIZE == 0);
206 assert(a2a3_tramphead() + PAGE_SIZE == a2a3_trampend());
208 TrampolineBlockPagePair *headPagePair = headPagePairs[aMode];
211 assert(headPagePair->nextAvailablePage == nil);
215 kern_return_t result = KERN_FAILURE;
216 for(i = 0; i < 5; i++) {
217 result = vm_allocate(mach_task_self(), &dataAddress, PAGE_SIZE * 2,
218 TRUE | VM_MAKE_TAG(VM_MEMORY_FOUNDATION));
219 if (result != KERN_SUCCESS) {
220 mach_error("vm_allocate failed", result);
224 vm_address_t codeAddress = dataAddress + PAGE_SIZE;
225 result = vm_deallocate(mach_task_self(), codeAddress, PAGE_SIZE);
226 if (result != KERN_SUCCESS) {
227 mach_error("vm_deallocate failed", result);
233 case ReturnValueInRegisterArgumentMode:
234 codePage = a1a2_firsttramp() & ~(PAGE_MASK);
236 case ReturnValueOnStackArgumentMode:
237 codePage = a2a3_firsttramp() & ~(PAGE_MASK);
240 _objc_fatal("unknown return mode %d", (int)aMode);
243 vm_prot_t currentProtection, maxProtection;
244 result = vm_remap(mach_task_self(), &codeAddress, PAGE_SIZE, 0, FALSE, mach_task_self(),
245 codePage, TRUE, ¤tProtection, &maxProtection, VM_INHERIT_SHARE);
246 if (result != KERN_SUCCESS) {
247 result = vm_deallocate(mach_task_self(), dataAddress, PAGE_SIZE);
248 if (result != KERN_SUCCESS) {
249 mach_error("vm_deallocate for retry failed.", result);
256 if (result != KERN_SUCCESS)
259 TrampolineBlockPagePair *pagePair = (TrampolineBlockPagePair *) dataAddress;
260 pagePair->nextAvailable = _paddingSlotCount();
261 pagePair->nextPagePair = nil;
262 pagePair->nextAvailablePage = nil;
263 id *lastPageBlockPtr = _payloadAddressAtIndex(pagePair, _slotsPerPagePair() - 1);
264 *lastPageBlockPtr = (id)(uintptr_t) LAST_SLOT_MARKER;
267 TrampolineBlockPagePair *lastPage = headPagePair;
268 while(lastPage->nextPagePair)
269 lastPage = lastPage->nextPagePair;
271 lastPage->nextPagePair = pagePair;
272 headPagePairs[aMode]->nextAvailablePage = pagePair;
274 headPagePairs[aMode] = pagePair;
280 static TrampolineBlockPagePair *_getOrAllocatePagePairWithNextAvailable(ArgumentMode aMode) {
283 TrampolineBlockPagePair *headPagePair = headPagePairs[aMode];
286 return _allocateTrampolinesAndData(aMode);
288 if (headPagePair->nextAvailable) // make sure head page is filled first
291 if (headPagePair->nextAvailablePage) // check if there is a page w/a hole
292 return headPagePair->nextAvailablePage;
294 return _allocateTrampolinesAndData(aMode); // tack on a new one
297 static TrampolineBlockPagePair *_pagePairAndIndexContainingIMP(IMP anImp, uint32_t *outIndex, TrampolineBlockPagePair **outHeadPagePair) {
300 uintptr_t impValue = (uintptr_t) anImp;
303 for(i = 0; i < ArgumentModeMax; i++) {
304 TrampolineBlockPagePair *pagePair = headPagePairs[i];
307 uintptr_t startOfTrampolines = (uintptr_t) &(pagePair->trampolines);
308 uintptr_t endOfTrampolines = ((uintptr_t) startOfTrampolines) + PAGE_SIZE;
310 if ( (impValue >=startOfTrampolines) && (impValue <= endOfTrampolines) ) {
312 *outIndex = (uint32_t) ((impValue - startOfTrampolines) / SLOT_SIZE);
314 if (outHeadPagePair) {
315 *outHeadPagePair = headPagePairs[i];
320 pagePair = pagePair->nextPagePair;
327 // `block` must already have been copied
328 static IMP _imp_implementationWithBlockNoCopy(ArgumentMode aMode, id block)
332 TrampolineBlockPagePair *pagePair = _getOrAllocatePagePairWithNextAvailable(aMode);
333 if (!headPagePairs[aMode])
334 headPagePairs[aMode] = pagePair;
336 uint32_t index = pagePair->nextAvailable;
337 id *payloadAddress = _payloadAddressAtIndex(pagePair, index);
338 assert((index < _slotsPerPagePair()) || (index == LAST_SLOT_MARKER));
340 uint32_t nextAvailableIndex = (uint32_t) *((uintptr_t *) payloadAddress);
341 if (nextAvailableIndex == 0)
342 // first time through, slots are filled with zeros, fill sequentially
343 pagePair->nextAvailable = index + 1;
344 else if (nextAvailableIndex == LAST_SLOT_MARKER) {
345 // last slot is filled with this as marker
346 // page now full, remove from available page linked list
347 pagePair->nextAvailable = 0;
348 TrampolineBlockPagePair *iteratorPair = headPagePairs[aMode];
349 while(iteratorPair && (iteratorPair->nextAvailablePage != pagePair))
350 iteratorPair = iteratorPair->nextAvailablePage;
352 iteratorPair->nextAvailablePage = pagePair->nextAvailablePage;
353 pagePair->nextAvailablePage = nil;
356 // empty slot at index contains pointer to next available index
357 pagePair->nextAvailable = nextAvailableIndex;
360 *payloadAddress = block;
361 IMP trampoline = _trampolineAddressAtIndex(pagePair, index);
366 static ArgumentMode _argumentModeForBlock(id block) {
367 ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
369 if (_Block_has_signature(block) && _Block_use_stret(block))
370 aMode = ReturnValueOnStackArgumentMode;
375 #pragma mark Public API
376 IMP imp_implementationWithBlock(id block)
378 block = Block_copy(block);
380 IMP returnIMP = _imp_implementationWithBlockNoCopy(_argumentModeForBlock(block), block);
386 id imp_getBlock(IMP anImp) {
388 TrampolineBlockPagePair *pagePair;
390 if (!anImp) return nil;
394 pagePair = _pagePairAndIndexContainingIMP(anImp, &index, nil);
401 id potentialBlock = *_payloadAddressAtIndex(pagePair, index);
403 if ((uintptr_t) potentialBlock == (uintptr_t) LAST_SLOT_MARKER) {
408 if ((uintptr_t) potentialBlock < (uintptr_t) _slotsPerPagePair()) {
415 return potentialBlock;
418 BOOL imp_removeBlock(IMP anImp) {
419 TrampolineBlockPagePair *pagePair;
420 TrampolineBlockPagePair *headPagePair;
423 if (!anImp) return NO;
426 pagePair = _pagePairAndIndexContainingIMP(anImp, &index, &headPagePair);
433 id *payloadAddress = _payloadAddressAtIndex(pagePair, index);
434 id block = *payloadAddress;
435 // block is released below
437 if (pagePair->nextAvailable) {
438 *payloadAddress = (id) (uintptr_t) pagePair->nextAvailable;
439 pagePair->nextAvailable = index;
441 *payloadAddress = (id) (uintptr_t) LAST_SLOT_MARKER; // nada after this one is used
442 pagePair->nextAvailable = index;
445 // make sure this page is on available linked list
446 TrampolineBlockPagePair *pagePairIterator = headPagePair;
448 // see if pagePair is the next available page for any existing pages
449 while(pagePairIterator->nextAvailablePage && (pagePairIterator->nextAvailablePage != pagePair))
450 pagePairIterator = pagePairIterator->nextAvailablePage;
452 if (! pagePairIterator->nextAvailablePage) { // if iteration stopped because nextAvail was nil
453 // add to end of list.
454 pagePairIterator->nextAvailablePage = pagePair;
455 pagePair->nextAvailablePage = nil;
459 Block_release(block);