]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-rtp.m
objc4-274.tar.gz
[apple/objc4.git] / runtime / objc-rtp.m
1 /*
2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
7 *
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
13 * file.
14 *
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 objc-rtp.m
27 Copyright 2004, Apple Computer, Inc.
28 Author: Jim Laskey
29
30 Implementation of the "objc runtime pages", an fixed area
31 in high memory that can be reached via an absolute branch.
32 */
33
34 #import "objc-rtp.h"
35 #import "objc-private.h"
36 #import "objc-auto.h"
37
38 #import <stdint.h>
39 #import <mach/mach.h>
40
41
42 // Local prototypes
43
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);
46
47 #if defined(__ppc__)
48 // from Libc, but no prototype yet (#3850825)
49 extern void sys_icache_invalidate(const void * newcode, size_t len);
50
51 static size_t rtp_copy_code(unsigned* dest, unsigned* source, size_t max_insns);
52 #endif
53
54
55 #if !defined(__ppc__)
56
57 __private_extern__ void rtp_init(void)
58 {
59 if (PrintRTP) {
60 _objc_inform("RTP: no rtp implementation for this platform");
61 }
62 }
63
64 #else
65
66 /**********************************************************************
67 * rtp_init
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)
72 {
73 kern_return_t ret;
74 vm_address_t objcRTPages = (vm_address_t)(kRTPagesHi - kRTPagesSize);
75
76 if (PrintRTP) {
77 _objc_inform("RTP: initializing rtp at [%p..%p)",
78 objcRTPages, kRTPagesHi);
79 }
80
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);
85
86 if (ret != KERN_SUCCESS) {
87 if (PrintRTP) {
88 _objc_inform("RTP: warning: libSystem/kernel did not allocate Objective-C runtime pages; continuing anyway");
89 }
90 return;
91 }
92
93 // initialize code in ObjC runtime pages
94 rtp_set_up_objc_msgSend(kRTAddress_objc_msgSend, kRTSize_objc_msgSend);
95
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);
98
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);
101
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);
104
105 // initialize data in ObjC runtime pages
106 memset((char *)kRTAddress_zero, 0, 16);
107 strcpy((char *)kIgnore, "<ignored selector>");
108
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!");
115 }
116 }
117
118
119 /**********************************************************************
120 * rtp_set_up_objc_msgSend
121 *
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)
127 {
128 #if defined(__ppc__)
129 // Location in the runtime pages of the new function.
130 unsigned *buffer = (unsigned *)address;
131
132 // Location of the original implementation.
133 // objc_msgSend is simple enough to copy directly
134 unsigned *code = (unsigned *)objc_msgSend;
135
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);
141 if (PrintRTP) {
142 _objc_inform("RTP: instrumented or profiled libobjc - objc_msgSend "
143 "in RTP at %p is a %d instruction branch",
144 buffer, written);
145 }
146 return;
147 #endif
148
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);
155 if (PrintRTP) {
156 _objc_inform("RTP: interposing enabled - objc_msgSend "
157 "in RTP at %p is a %d instruction branch",
158 buffer, written);
159 }
160 return;
161 }
162
163 if (PrintRTP) {
164 _objc_inform("RTP: writing objc_msgSend at [%p..%p) ...",
165 address, address+maxsize);
166 }
167
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);
172 if (i > 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);
179 return;
180 }
181
182 {
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.
187
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;
193
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;
198
199 // search for mflr instruction
200 int j;
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;
210 break;
211 }
212 }
213 }
214
215 // branch to the cache miss code
216 i += objc_write_branch(buffer + i, code + i);
217
218 // flush the instruction cache
219 sys_icache_invalidate(buffer, i*4);
220
221 if (PrintRTP) {
222 _objc_inform("RTP: wrote objc_msgSend at [%p..%p)",
223 address, address + i*sizeof(unsigned));
224 }
225
226 #elif defined(__i386__)
227 #warning needs implementation
228 #else
229 #error unknown architecture
230 #endif
231 }
232
233
234 /**********************************************************************
235 * rtp_set_up_other
236 *
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) {
245 #if defined(__ppc__)
246 // location in the runtime pages of this function
247 unsigned *buffer = (unsigned *)address;
248
249 // Location of the original implementation.
250 unsigned *code = (unsigned *)(objc_collecting_enabled() ? gc_code : non_gc_code);
251
252 if (objc_collecting_enabled()) {
253 unsigned written = objc_write_branch(buffer, code);
254 sys_icache_invalidate(buffer, written*4);
255 if (PrintRTP) {
256 _objc_inform("RTP: %s in RTP at %p is a %d instruction branch",
257 name, buffer, written);
258 }
259 return;
260 }
261
262 if (PrintRTP) {
263 _objc_inform("RTP: writing %s at [%p..%p) ...",
264 name, address, address + maxsize);
265 }
266
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);
271 if (i > 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);
278 return;
279 }
280
281 // flush the instruction cache
282 sys_icache_invalidate(buffer, i*4);
283
284 if (PrintRTP) {
285 _objc_inform("RTP: wrote %s at [%p..%p)",
286 name, address, address + i * sizeof(unsigned));
287 }
288
289 #elif defined(__i386__)
290 #warning needs implementation
291 #else // defined(architecture)
292 #error unknown architecture
293 #endif // defined(architecture)
294 }
295
296
297 #if defined(__ppc__)
298
299 /**********************************************************************
300 * rtp_copy_code
301 *
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)
309 {
310 const unsigned op_blr = 0x4e800020u;
311 size_t i;
312
313 // copy instructions until blr is found
314 for (i = 0; i < max_insns; i++) {
315 dest[i] = source[i];
316 if (source[i] == op_blr) break;
317 }
318
319 // return number of instructions copied
320 // OR max_insns+1 if no blr was found
321 return i + 1;
322 }
323
324 // defined(__ppc__)
325 #endif
326
327 // defined(__ppc__) || defined(__i386__)
328 #endif