2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
27 Copyright 2004, Apple Computer, Inc.
30 Implementation of the "objc runtime pages", an fixed area
31 in high memory that can be reached via an absolute branch.
35 #import "objc-private.h"
44 static void rtp_set_up_objc_msgSend(uintptr_t address, size_t maxsize);
45 static void rtp_set_up_other(uintptr_t address, size_t maxsize, const char *name, void *gc_code, void *non_gc_code);
48 // from Libc, but no prototype yet (#3850825)
49 extern void sys_icache_invalidate(const void * newcode, size_t len);
51 static size_t rtp_copy_code(unsigned* dest, unsigned* source, size_t max_insns);
57 __private_extern__ void rtp_init(void)
60 _objc_inform("RTP: no rtp implementation for this platform");
66 /**********************************************************************
68 * Allocate and initialize the Objective-C runtime pages.
69 * Kills the process if something goes wrong.
70 **********************************************************************/
71 __private_extern__ void rtp_init(void)
74 vm_address_t objcRTPages = (vm_address_t)(kRTPagesHi - kRTPagesSize);
77 _objc_inform("RTP: initializing rtp at [%p..%p)",
78 objcRTPages, kRTPagesHi);
81 // unprotect the ObjC runtime pages for writing
82 ret = vm_protect(mach_task_self(),
83 objcRTPages, kRTPagesSize,
84 FALSE, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);
86 if (ret != KERN_SUCCESS) {
88 _objc_inform("RTP: warning: libSystem/kernel did not allocate Objective-C runtime pages; continuing anyway");
93 // initialize code in ObjC runtime pages
94 rtp_set_up_objc_msgSend(kRTAddress_objc_msgSend, kRTSize_objc_msgSend);
96 rtp_set_up_other(kRTAddress_objc_assign_ivar, kRTSize_objc_assign_ivar,
97 "objc_assign_ivar", objc_assign_ivar_gc, objc_assign_ivar_non_gc);
99 rtp_set_up_other(kRTAddress_objc_assign_global, kRTSize_objc_assign_global,
100 "objc_assign_global", objc_assign_global_gc, objc_assign_global_non_gc);
102 rtp_set_up_other(kRTAddress_objc_assign_strongCast, kRTSize_objc_assign_strongCast,
103 "objc_assign_strongCast", objc_assign_strongCast_gc, objc_assign_strongCast_non_gc);
105 // initialize data in ObjC runtime pages
106 memset((char *)kRTAddress_zero, 0, 16);
107 strcpy((char *)kIgnore, "<ignored selector>");
109 // re-protect the ObjC runtime pages for execution
110 ret = vm_protect(mach_task_self(),
111 objcRTPages, kRTPagesSize,
112 FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
113 if (ret != KERN_SUCCESS) {
114 _objc_inform("RTP: Could not re-protect Objective-C runtime pages!");
119 /**********************************************************************
120 * rtp_set_up_objc_msgSend
122 * Construct the objc runtime page version of objc_msgSend
123 * address is the entry point of the new implementation.
124 * maxsize is the number of bytes available for the new implementation.
125 **********************************************************************/
126 static void rtp_set_up_objc_msgSend(uintptr_t address, size_t maxsize)
129 // Location in the runtime pages of the new function.
130 unsigned *buffer = (unsigned *)address;
132 // Location of the original implementation.
133 // objc_msgSend is simple enough to copy directly
134 unsigned *code = (unsigned *)objc_msgSend;
136 // If building an instrumented or profiled runtime, simply branch
137 // directly to the full implementation.
138 #if defined(OBJC_INSTRUMENTED) || defined(PROFILE)
139 unsigned written = objc_write_branch(buffer, code);
140 sys_icache_invalidate(buffer, written*4);
142 _objc_inform("RTP: instrumented or profiled libobjc - objc_msgSend "
143 "in RTP at %p is a %d instruction branch",
149 // If function interposing is enabled, call the full implementation
150 // via a dyld-recognizable stub.
151 if (AllowInterposing) {
152 extern void objc_msgSend_stub(void);
153 unsigned written = objc_write_branch(buffer, objc_msgSend_stub);
154 sys_icache_invalidate(buffer, written*4);
156 _objc_inform("RTP: interposing enabled - objc_msgSend "
157 "in RTP at %p is a %d instruction branch",
164 _objc_inform("RTP: writing objc_msgSend at [%p..%p) ...",
165 address, address+maxsize);
168 // Copy instructions from function to runtime pages
169 // i is the number of INSTRUCTIONS written so far
170 size_t max_insns = maxsize / sizeof(unsigned);
171 size_t i = rtp_copy_code(buffer, code, max_insns);
173 // objc_msgSend didn't fit in the alloted space.
174 // Branch to ordinary objc_msgSend instead so the program won't crash.
175 i = objc_write_branch(buffer, code);
176 sys_icache_invalidate(buffer, i*4);
177 _objc_inform("RTP: objc_msgSend is too large to fit in the "
178 "runtime pages (%d bytes available)", maxsize);
183 // Replace load of _objc_nilReceiver.
184 // This assumes that the load of _objc_nilReceiver
185 // immediately follows the LAST `mflr r0` in objc_msgSend,
186 // and that the original load sequence is six instructions long.
188 // instructions used to load _objc_nilReceiver
189 const unsigned op_mflr_r0 = 0x7c0802a6u;
190 const unsigned op_lis_r11 = 0x3d600000u;
191 const unsigned op_lwz_r11 = 0x816b0000u;
192 const unsigned op_nop = 0x60000000u;
194 // get address of _objc_nilReceiver, and its lo and hi halves
195 unsigned address = (unsigned)&_objc_nilReceiver;
196 signed lo = (signed short)address;
197 signed ha = (address - lo) >> 16;
199 // search for mflr instruction
201 for (j = i; j-- != 0; ) {
202 if (buffer[j] == op_mflr_r0) {
203 // replace with lis lwz nop nop sequence
204 buffer[j + 0] = op_lis_r11 | (ha & 0xffff);
205 buffer[j + 1] = op_nop;
206 buffer[j + 2] = op_nop;
207 buffer[j + 3] = op_lwz_r11 | (lo & 0xffff);
208 buffer[j + 4] = op_nop;
209 buffer[j + 5] = op_nop;
215 // branch to the cache miss code
216 i += objc_write_branch(buffer + i, code + i);
218 // flush the instruction cache
219 sys_icache_invalidate(buffer, i*4);
222 _objc_inform("RTP: wrote objc_msgSend at [%p..%p)",
223 address, address + i*sizeof(unsigned));
226 #elif defined(__i386__)
227 #warning needs implementation
229 #error unknown architecture
234 /**********************************************************************
237 * construct the objc runtime page version of the supplied code
238 * address is the entry point of the new implementation.
239 * maxsize is the number of bytes available for the new implementation.
240 * name is the c string name of the routine being set up.
241 * gc_code is the code to use if collecting is enabled (assumed to be large and requiring a branch.)
242 * non_gc_code is the code to use if collecting is not enabled (assumed to be small enough to copy.)
243 **********************************************************************/
244 static void rtp_set_up_other(uintptr_t address, size_t maxsize, const char *name, void *gc_code, void *non_gc_code) {
246 // location in the runtime pages of this function
247 unsigned *buffer = (unsigned *)address;
249 // Location of the original implementation.
250 unsigned *code = (unsigned *)(objc_collecting_enabled() ? gc_code : non_gc_code);
252 if (objc_collecting_enabled()) {
253 unsigned written = objc_write_branch(buffer, code);
254 sys_icache_invalidate(buffer, written*4);
256 _objc_inform("RTP: %s in RTP at %p is a %d instruction branch",
257 name, buffer, written);
263 _objc_inform("RTP: writing %s at [%p..%p) ...",
264 name, address, address + maxsize);
267 // Copy instructions from function to runtime pages
268 // i is the number of INSTRUCTIONS written so far
269 unsigned max_insns = maxsize / sizeof(unsigned);
270 unsigned i = rtp_copy_code(buffer, code, max_insns);
272 // code didn't fit in the alloted space.
273 // Branch to ordinary objc_assign_ivar instead so the program won't crash.
274 i = objc_write_branch(buffer, code);
275 sys_icache_invalidate(buffer, i*4);
276 _objc_inform("RTP: %s is too large to fit in the "
277 "runtime pages (%d bytes available)", name, maxsize);
281 // flush the instruction cache
282 sys_icache_invalidate(buffer, i*4);
285 _objc_inform("RTP: wrote %s at [%p..%p)",
286 name, address, address + i * sizeof(unsigned));
289 #elif defined(__i386__)
290 #warning needs implementation
291 #else // defined(architecture)
292 #error unknown architecture
293 #endif // defined(architecture)
299 /**********************************************************************
302 * Copy blr-terminated PPC instructions from source to dest.
303 * If a blr is reached then that blr is copied, and the return value is
304 * the number of instructions copied ( <= max_insns )
305 * If no blr is reached then exactly max_insns instructions are copied,
306 * and the return value is max_insns+1.
307 **********************************************************************/
308 static size_t rtp_copy_code(unsigned* dest, unsigned* source, size_t max_insns)
310 const unsigned op_blr = 0x4e800020u;
313 // copy instructions until blr is found
314 for (i = 0; i < max_insns; i++) {
316 if (source[i] == op_blr) break;
319 // return number of instructions copied
320 // OR max_insns+1 if no blr was found
327 // defined(__ppc__) || defined(__i386__)