2 * Copyright (c) 2004-2007 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@
24 Implementation of the "objc runtime pages", an fixed area
25 in high memory that can be reached via an absolute branch.
28 #import "objc-private.h"
29 #import <objc/message.h>
34 static size_t rtp_copy_code(unsigned* dest, unsigned* source, size_t max_insns);
35 static void rtp_set_up_objc_msgSend(uintptr_t address, size_t maxsize);
36 static void rtp_set_up_other(uintptr_t address, size_t maxsize, const char *name, void *gc_code, void *non_gc_code);
38 /**********************************************************************
40 * Allocate and initialize the Objective-C runtime pages.
41 * Kills the process if something goes wrong.
42 **********************************************************************/
43 __private_extern__ void rtp_init(void)
46 vm_address_t objcRTPages = (vm_address_t)(kRTPagesHi - kRTPagesSize);
49 _objc_inform("RTP: initializing rtp at [%p..%p)",
50 (void *)objcRTPages, (void *)kRTPagesHi);
53 // unprotect the ObjC runtime pages for writing
54 ret = vm_protect(mach_task_self(),
55 objcRTPages, kRTPagesSize,
56 FALSE, VM_PROT_READ | VM_PROT_WRITE);
58 if (ret != KERN_SUCCESS) {
60 _objc_inform("RTP: warning: libSystem/kernel did not allocate Objective-C runtime pages; continuing anyway");
65 // initialize code in ObjC runtime pages
66 rtp_set_up_objc_msgSend(kRTAddress_objc_msgSend, kRTSize_objc_msgSend);
68 #define objc_assign_ivar_gc objc_assign_ivar_non_gc
69 #define objc_assign_global_gc objc_assign_global_non_gc
70 #define objc_assign_strongCast_gc objc_assign_strongCast_non_gc
72 rtp_set_up_other(kRTAddress_objc_assign_ivar, kRTSize_objc_assign_ivar,
73 "objc_assign_ivar", objc_assign_ivar_gc, objc_assign_ivar_non_gc);
75 rtp_set_up_other(kRTAddress_objc_assign_global, kRTSize_objc_assign_global,
76 "objc_assign_global", objc_assign_global_gc, objc_assign_global_non_gc);
78 rtp_set_up_other(kRTAddress_objc_assign_strongCast, kRTSize_objc_assign_strongCast,
79 "objc_assign_strongCast", objc_assign_strongCast_gc, objc_assign_strongCast_non_gc);
81 // initialize data in ObjC runtime pages
82 memset((char *)kRTAddress_zero, 0, 16);
83 strlcpy((char *)kIgnore, "<ignored selector>", OBJC_SIZE_T(19));
85 // re-protect the ObjC runtime pages for execution
86 ret = vm_protect(mach_task_self(),
87 objcRTPages, kRTPagesSize,
88 FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
89 if (ret != KERN_SUCCESS) {
90 _objc_inform("RTP: Could not re-protect Objective-C runtime pages!");
95 /**********************************************************************
96 * rtp_set_up_objc_msgSend
98 * Construct the objc runtime page version of objc_msgSend
99 * address is the entry point of the new implementation.
100 * maxsize is the number of bytes available for the new implementation.
101 **********************************************************************/
102 static void rtp_set_up_objc_msgSend(uintptr_t address, size_t maxsize)
104 // Location in the runtime pages of the new function.
105 unsigned *buffer = (unsigned *)address;
107 // Location of the original implementation.
108 // objc_msgSend is simple enough to copy directly
109 unsigned *code = (unsigned *)objc_msgSend;
111 // If building an instrumented or profiled runtime, simply branch
112 // directly to the full implementation.
113 #if defined(OBJC_INSTRUMENTED) || defined(PROFILE)
114 size_t written = objc_write_branch(buffer, code);
115 sys_icache_invalidate(buffer, written*4);
117 _objc_inform("RTP: instrumented or profiled libobjc - objc_msgSend "
118 "in RTP at %p is a %zu instruction branch",
125 _objc_inform("RTP: writing objc_msgSend at [%p..%p) ...",
126 (void *)address, (void *)(address+maxsize));
129 // Copy instructions from function to runtime pages
130 // i is the number of INSTRUCTIONS written so far
131 size_t max_insns = maxsize / sizeof(unsigned);
132 size_t i = rtp_copy_code(buffer, code, max_insns);
133 if (i + objc_branch_size(buffer + i, code + i) > max_insns) {
134 // objc_msgSend didn't fit in the alloted space.
135 // Branch to ordinary objc_msgSend instead so the program won't crash.
136 i = objc_write_branch(buffer, code);
137 sys_icache_invalidate(buffer, i*4);
138 _objc_inform("RTP: objc_msgSend is too large to fit in the "
139 "runtime pages (%zu bytes available)", maxsize);
144 // Replace load of _objc_nilReceiver into r11
145 // This assumes that the load of _objc_nilReceiver
146 // immediately follows the LAST `mflr r0` in objc_msgSend,
147 // and that the original load sequence is six instructions long.
149 // instructions used to load _objc_nilReceiver
150 const unsigned op_mflr_r0 = 0x7c0802a6u;
151 const unsigned op_nop = 0x60000000u;
153 // get address of _objc_nilReceiver, and its lo and hi halves
154 uintptr_t address = (uintptr_t)&_objc_nilReceiver;
155 uint16_t lo = address & 0xffff;
156 uint16_t ha = ((address - (int16_t)lo) >> 16) & 0xffff;
157 #if defined(__ppc64__)
158 uint16_t hi2 = (address >> 32) & 0xffff;
159 uint16_t hi3 = (address >> 48) & 0xffff;
162 // search for mflr instruction
164 for (j = i; j-- != 0; ) {
165 if (buffer[j] == op_mflr_r0) {
166 const unsigned op_lis_r11 = 0x3d600000u;
167 const unsigned op_lwz_r11 = 0x816b0000u;
171 buffer[j + 0] = op_lis_r11 | ha;
172 buffer[j + 1] = op_nop;
173 buffer[j + 2] = op_nop;
174 buffer[j + 3] = op_lwz_r11 | lo;
175 buffer[j + 4] = op_nop;
176 buffer[j + 5] = op_nop;
177 #elif defined(__ppc64__)
178 const unsigned op_ori_r11 = 0x616b0000u;
179 const unsigned op_oris_r11 = 0x656b0000u;
180 const unsigned op_sldi_r11 = 0x796b07c6u;
186 buffer[j + 0] = op_lis_r11 | hi3;
187 buffer[j + 1] = op_ori_r11 | hi2;
188 buffer[j + 2] = op_sldi_r11;
189 buffer[j + 3] = op_oris_r11 | ha;
190 buffer[j + 4] = op_lwz_r11 | lo;
191 buffer[j + 5] = op_nop;
198 // branch to the cache miss code
199 i += objc_write_branch(buffer + i, code + i);
201 // flush the instruction cache
202 sys_icache_invalidate(buffer, i*4);
205 _objc_inform("RTP: wrote objc_msgSend at [%p..%p)",
206 (void *)address, (void *)(address + i*sizeof(unsigned)));
211 /**********************************************************************
214 * construct the objc runtime page version of the supplied code
215 * address is the entry point of the new implementation.
216 * maxsize is the number of bytes available for the new implementation.
217 * name is the c string name of the routine being set up.
218 * gc_code is the code to use if collecting is enabled (assumed to be large and requiring a branch.)
219 * non_gc_code is the code to use if collecting is not enabled (assumed to be small enough to copy.)
220 **********************************************************************/
221 static void rtp_set_up_other(uintptr_t address, size_t maxsize, const char *name, void *gc_code, void *non_gc_code) {
222 // location in the runtime pages of this function
223 unsigned *buffer = (unsigned *)address;
225 // Location of the original implementation.
226 unsigned *code = (unsigned *)(objc_collecting_enabled() ? gc_code : non_gc_code);
228 if (objc_collecting_enabled()) {
229 size_t written = objc_write_branch(buffer, code);
230 sys_icache_invalidate(buffer, written*4);
232 _objc_inform("RTP: %s in RTP at %p is a %zu instruction branch",
233 name, buffer, written);
239 _objc_inform("RTP: writing %s at [%p..%p) ...",
240 name, (void *)address, (void *)(address + maxsize));
243 // Copy instructions from function to runtime pages
244 // i is the number of INSTRUCTIONS written so far
245 size_t max_insns = maxsize / sizeof(unsigned);
246 size_t i = rtp_copy_code(buffer, code, max_insns);
248 // code didn't fit in the alloted space.
249 // Branch to ordinary objc_assign_ivar instead so the program won't crash.
250 i = objc_write_branch(buffer, code);
251 sys_icache_invalidate(buffer, i*4);
252 _objc_inform("RTP: %s is too large to fit in the "
253 "runtime pages (%zu bytes available)", name, maxsize);
257 // flush the instruction cache
258 sys_icache_invalidate(buffer, i*4);
261 _objc_inform("RTP: wrote %s at [%p..%p)",
262 name, (void *)address,
263 (void *)(address + i * sizeof(unsigned)));
268 /**********************************************************************
271 * Copy blr-terminated PPC instructions from source to dest.
272 * If a blr is reached then that blr is copied, and the return value is
273 * the number of instructions copied ( <= max_insns )
274 * If no blr is reached then exactly max_insns instructions are copied,
275 * and the return value is max_insns+1.
276 **********************************************************************/
277 static size_t rtp_copy_code(unsigned* dest, unsigned* source, size_t max_insns)
279 const unsigned op_blr = 0x4e800020u;
282 // copy instructions until blr is found
283 for (i = 0; i < max_insns; i++) {
285 if (source[i] == op_blr) break;
288 // return number of instructions copied
289 // OR max_insns+1 if no blr was found
294 #elif defined(__i386__)
297 /**********************************************************************
300 * Swap a function's current implementation with a new one.
301 * The routine at 'address' is assumed to be at least as large as the
302 * jump instruction required to reach the new implementation.
303 **********************************************************************/
304 static void rtp_swap_imp(unsigned *address, void *code, const char *name)
306 if (vm_protect(mach_task_self(), (vm_address_t)address, 1,
307 FALSE, VM_PROT_READ | VM_PROT_WRITE) != KERN_SUCCESS)
308 _objc_fatal("Could not get write access to %s.", name);
311 objc_write_branch(address, (unsigned*)code);
313 if (vm_protect(mach_task_self(), (vm_address_t)address, 1,
314 FALSE, VM_PROT_READ | VM_PROT_EXECUTE) != KERN_SUCCESS)
315 _objc_fatal("Could not reprotect %s.", name);
320 __private_extern__ void rtp_init(void)
322 // At load time, the page on which the objc_assign_* routines live is not
323 // marked as executable. We fix that here, regardless of the GC choice.
327 rtp_swap_imp((unsigned*)objc_assign_ivar,
328 objc_assign_ivar_gc, "objc_assign_ivar");
329 rtp_swap_imp((unsigned*)objc_assign_global,
330 objc_assign_global_gc, "objc_assign_global");
331 rtp_swap_imp((unsigned*)objc_assign_strongCast,
332 objc_assign_strongCast_gc, "objc_assign_strongCast");
336 { // Not GC, just make the page executable.
337 if (vm_protect(mach_task_self(), (vm_address_t)objc_assign_ivar, 1,
338 FALSE, VM_PROT_READ | VM_PROT_EXECUTE) != KERN_SUCCESS)
339 _objc_fatal("Could not reprotect objc_assign_*.");
347 __private_extern__ void rtp_init(void)
350 _objc_inform("RTP: no rtp implementation for this platform");