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;
118 extern void *_a1a2_nexttramp;
119 extern void *_a2a3_firsttramp;
120 extern void *_a2a3_nexttramp;
122 // make sure thumb-edness of all trampolines match
123 assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
124 ((uintptr_t)&_a2a3_firsttramp) % 2);
125 assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
126 ((uintptr_t)&_a1a2_nexttramp) % 2);
127 assert(((uintptr_t)&_a1a2_firsttramp) % 2 ==
128 ((uintptr_t)&_a2a3_nexttramp) % 2);
130 return ((uintptr_t)&_a1a2_firsttramp) % 2;
133 static inline uint32_t _slotsPerPagePair() {
134 uint32_t slotSize = _slotSize();
135 uint32_t slotsPerPagePair = PAGE_SIZE / slotSize;
136 return slotsPerPagePair;
139 static inline uint32_t _paddingSlotCount() {
140 uint32_t headerSize = _headerSize();
141 uint32_t slotSize = _slotSize();
142 uint32_t paddingSlots = headerSize / slotSize;
146 static inline void **_payloadAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) {
147 uint32_t slotSize = _slotSize();
148 uintptr_t baseAddress = (uintptr_t) pagePair;
149 uintptr_t payloadAddress = baseAddress + (slotSize * index);
150 return (void **)payloadAddress;
153 static inline IMP _trampolineAddressAtIndex(TrampolineBlockPagePair *pagePair, uint32_t index) {
154 uint32_t slotSize = _slotSize();
155 uintptr_t baseAddress = (uintptr_t) &(pagePair->trampolines);
156 uintptr_t trampolineAddress = baseAddress + (slotSize * index);
159 if (trampolinesAreThumb()) trampolineAddress++;
162 return (IMP)trampolineAddress;
165 static inline void _lock() {
167 rwlock_write(&runtimeLock);
169 mutex_lock(&classLock);
173 static inline void _unlock() {
175 rwlock_unlock_write(&runtimeLock);
177 mutex_unlock(&classLock);
181 static inline void _assert_locked() {
183 rwlock_assert_writing(&runtimeLock);
185 mutex_assert_locked(&classLock);
189 #pragma mark Trampoline Management Functions
190 static TrampolineBlockPagePair *_allocateTrampolinesAndData(ArgumentMode aMode) {
193 vm_address_t dataAddress;
195 // make sure certain assumptions are met
196 assert(PAGE_SIZE == 4096);
197 assert(sizeof(TrampolineBlockPagePair) == 2*PAGE_SIZE);
198 assert(_slotSize() == 8);
199 assert(_headerSize() >= TRAMPOLINE_PAGE_PAIR_HEADER_SIZE);
200 assert((_headerSize() % _slotSize()) == 0);
202 assert(a1a2_tramphead() % PAGE_SIZE == 0);
203 assert(a1a2_tramphead() + PAGE_SIZE == a1a2_trampend());
204 assert(a2a3_tramphead() % PAGE_SIZE == 0);
205 assert(a2a3_tramphead() + PAGE_SIZE == a2a3_trampend());
207 TrampolineBlockPagePair *headPagePair = headPagePairs[aMode];
210 assert(headPagePair->nextAvailablePage == NULL);
214 kern_return_t result = KERN_FAILURE;
215 for(i = 0; i < 5; i++) {
216 result = vm_allocate(mach_task_self(), &dataAddress, PAGE_SIZE * 2, TRUE);
217 if (result != KERN_SUCCESS) {
218 mach_error("vm_allocate failed", result);
222 vm_address_t codeAddress = dataAddress + PAGE_SIZE;
223 result = vm_deallocate(mach_task_self(), codeAddress, PAGE_SIZE);
224 if (result != KERN_SUCCESS) {
225 mach_error("vm_deallocate failed", result);
231 case ReturnValueInRegisterArgumentMode:
232 codePage = a1a2_firsttramp() & ~(PAGE_MASK);
234 case ReturnValueOnStackArgumentMode:
235 codePage = a2a3_firsttramp() & ~(PAGE_MASK);
238 _objc_fatal("unknown return mode %d", (int)aMode);
241 vm_prot_t currentProtection, maxProtection;
242 result = vm_remap(mach_task_self(), &codeAddress, PAGE_SIZE, 0, FALSE, mach_task_self(),
243 codePage, TRUE, ¤tProtection, &maxProtection, VM_INHERIT_SHARE);
244 if (result != KERN_SUCCESS) {
245 result = vm_deallocate(mach_task_self(), dataAddress, PAGE_SIZE);
246 if (result != KERN_SUCCESS) {
247 mach_error("vm_deallocate for retry failed.", result);
254 if (result != KERN_SUCCESS)
257 TrampolineBlockPagePair *pagePair = (TrampolineBlockPagePair *) dataAddress;
258 pagePair->nextAvailable = _paddingSlotCount();
259 pagePair->nextPagePair = NULL;
260 pagePair->nextAvailablePage = NULL;
261 void **lastPageBlockPtr = _payloadAddressAtIndex(pagePair, _slotsPerPagePair() - 1);
262 *lastPageBlockPtr = (void*)(uintptr_t) LAST_SLOT_MARKER;
265 TrampolineBlockPagePair *lastPage = headPagePair;
266 while(lastPage->nextPagePair)
267 lastPage = lastPage->nextPagePair;
269 lastPage->nextPagePair = pagePair;
270 headPagePairs[aMode]->nextAvailablePage = pagePair;
272 headPagePairs[aMode] = pagePair;
278 static TrampolineBlockPagePair *_getOrAllocatePagePairWithNextAvailable(ArgumentMode aMode) {
281 TrampolineBlockPagePair *headPagePair = headPagePairs[aMode];
284 return _allocateTrampolinesAndData(aMode);
286 if (headPagePair->nextAvailable) // make sure head page is filled first
289 if (headPagePair->nextAvailablePage) // check if there is a page w/a hole
290 return headPagePair->nextAvailablePage;
292 return _allocateTrampolinesAndData(aMode); // tack on a new one
295 static TrampolineBlockPagePair *_pagePairAndIndexContainingIMP(IMP anImp, uint32_t *outIndex, TrampolineBlockPagePair **outHeadPagePair) {
298 uintptr_t impValue = (uintptr_t) anImp;
301 for(i = 0; i < ArgumentModeMax; i++) {
302 TrampolineBlockPagePair *pagePair = headPagePairs[i];
305 uintptr_t startOfTrampolines = (uintptr_t) &(pagePair->trampolines);
306 uintptr_t endOfTrampolines = ((uintptr_t) startOfTrampolines) + PAGE_SIZE;
308 if ( (impValue >=startOfTrampolines) && (impValue <= endOfTrampolines) ) {
310 *outIndex = (uint32_t) ((impValue - startOfTrampolines) / SLOT_SIZE);
312 if (outHeadPagePair) {
313 *outHeadPagePair = headPagePairs[i];
318 pagePair = pagePair->nextPagePair;
325 // `block` must already have been copied
326 static IMP _imp_implementationWithBlockNoCopy(ArgumentMode aMode, void *block)
330 TrampolineBlockPagePair *pagePair = _getOrAllocatePagePairWithNextAvailable(aMode);
331 if (!headPagePairs[aMode])
332 headPagePairs[aMode] = pagePair;
334 uint32_t index = pagePair->nextAvailable;
335 void **payloadAddress = _payloadAddressAtIndex(pagePair, index);
336 assert((index < 1024) || (index == LAST_SLOT_MARKER));
338 uint32_t nextAvailableIndex = (uint32_t) *((uintptr_t *) payloadAddress);
339 if (nextAvailableIndex == 0)
340 // first time through, slots are filled with zeros, fill sequentially
341 pagePair->nextAvailable = index + 1;
342 else if (nextAvailableIndex == LAST_SLOT_MARKER) {
343 // last slot is filled with this as marker
344 // page now full, remove from available page linked list
345 pagePair->nextAvailable = 0;
346 TrampolineBlockPagePair *iteratorPair = headPagePairs[aMode];
347 while(iteratorPair && (iteratorPair->nextAvailablePage != pagePair))
348 iteratorPair = iteratorPair->nextAvailablePage;
350 iteratorPair->nextAvailablePage = pagePair->nextAvailablePage;
351 pagePair->nextAvailablePage = NULL;
354 // empty slot at index contains pointer to next available index
355 pagePair->nextAvailable = nextAvailableIndex;
358 *payloadAddress = block;
359 IMP trampoline = _trampolineAddressAtIndex(pagePair, index);
364 static ArgumentMode _argumentModeForBlock(void *block) {
365 ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
367 if (_Block_has_signature(block) && _Block_use_stret(block))
368 aMode = ReturnValueOnStackArgumentMode;
373 #pragma mark Public API
374 IMP imp_implementationWithBlock(void *block)
376 block = Block_copy(block);
378 IMP returnIMP = _imp_implementationWithBlockNoCopy(_argumentModeForBlock(block), block);
384 void *imp_getBlock(IMP anImp) {
386 TrampolineBlockPagePair *pagePair;
388 if (!anImp) return NULL;
392 pagePair = _pagePairAndIndexContainingIMP(anImp, &index, NULL);
399 void *potentialBlock = *_payloadAddressAtIndex(pagePair, index);
401 if ((uintptr_t) potentialBlock == (uintptr_t) LAST_SLOT_MARKER) {
406 if ((uintptr_t) potentialBlock < (uintptr_t) _slotsPerPagePair()) {
413 return potentialBlock;
416 BOOL imp_removeBlock(IMP anImp) {
417 TrampolineBlockPagePair *pagePair;
418 TrampolineBlockPagePair *headPagePair;
421 if (!anImp) return NO;
424 pagePair = _pagePairAndIndexContainingIMP(anImp, &index, &headPagePair);
431 void **payloadAddress = _payloadAddressAtIndex(pagePair, index);
432 void *block = *payloadAddress;
433 // block is released below
435 if (pagePair->nextAvailable) {
436 *payloadAddress = (void *) (uintptr_t) pagePair->nextAvailable;
437 pagePair->nextAvailable = index;
439 *payloadAddress = (void *) (uintptr_t) LAST_SLOT_MARKER; // nada after this one is used
440 pagePair->nextAvailable = index;
443 // make sure this page is on available linked list
444 TrampolineBlockPagePair *pagePairIterator = headPagePair;
446 // see if pagePair is the next available page for any existing pages
447 while(pagePairIterator->nextAvailablePage && (pagePairIterator->nextAvailablePage != pagePair))
448 pagePairIterator = pagePairIterator->nextAvailablePage;
450 if (! pagePairIterator->nextAvailablePage) { // if iteration stopped because nextAvail was NULL
451 // add to end of list.
452 pagePairIterator->nextAvailablePage = pagePair;
453 pagePair->nextAvailablePage = NULL;
457 Block_release(block);