]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-rtp.m
objc4-437.3.tar.gz
[apple/objc4.git] / runtime / objc-rtp.m
1 /*
2 * Copyright (c) 2004-2007 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /*
24 Implementation of the "objc runtime pages", an fixed area
25 in high memory that can be reached via an absolute branch.
26 */
27
28 #import "objc-private.h"
29 #import <objc/message.h>
30
31
32 #if defined(__ppc__)
33
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);
37
38 /**********************************************************************
39 * rtp_init
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)
44 {
45 kern_return_t ret;
46 vm_address_t objcRTPages = (vm_address_t)(kRTPagesHi - kRTPagesSize);
47
48 if (PrintRTP) {
49 _objc_inform("RTP: initializing rtp at [%p..%p)",
50 (void *)objcRTPages, (void *)kRTPagesHi);
51 }
52
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);
57
58 if (ret != KERN_SUCCESS) {
59 if (PrintRTP) {
60 _objc_inform("RTP: warning: libSystem/kernel did not allocate Objective-C runtime pages; continuing anyway");
61 }
62 return;
63 }
64
65 // initialize code in ObjC runtime pages
66 rtp_set_up_objc_msgSend(kRTAddress_objc_msgSend, kRTSize_objc_msgSend);
67 #ifdef NO_GC
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
71 #endif
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);
74
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);
77
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);
80
81 // initialize data in ObjC runtime pages
82 memset((char *)kRTAddress_zero, 0, 16);
83 strlcpy((char *)kIgnore, "<ignored selector>", OBJC_SIZE_T(19));
84
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!");
91 }
92 }
93
94
95 /**********************************************************************
96 * rtp_set_up_objc_msgSend
97 *
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)
103 {
104 // Location in the runtime pages of the new function.
105 unsigned *buffer = (unsigned *)address;
106
107 // Location of the original implementation.
108 // objc_msgSend is simple enough to copy directly
109 unsigned *code = (unsigned *)objc_msgSend;
110
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);
116 if (PrintRTP) {
117 _objc_inform("RTP: instrumented or profiled libobjc - objc_msgSend "
118 "in RTP at %p is a %zu instruction branch",
119 buffer, written);
120 }
121 return;
122 #endif
123
124 if (PrintRTP) {
125 _objc_inform("RTP: writing objc_msgSend at [%p..%p) ...",
126 (void *)address, (void *)(address+maxsize));
127 }
128
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);
140 return;
141 }
142
143 {
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.
148
149 // instructions used to load _objc_nilReceiver
150 const unsigned op_mflr_r0 = 0x7c0802a6u;
151 const unsigned op_nop = 0x60000000u;
152
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;
160 #endif
161
162 // search for mflr instruction
163 size_t j;
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;
168 #if defined(__ppc__)
169 // lis r11, ha
170 // lwz r11, lo(r11)
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;
181 // lis r11, hi3
182 // ori r11, r11, hi2
183 // sldi r11, r11, 32
184 // oris r11, r11, ha
185 // lwz r11, lo(r11)
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;
192 #endif
193 break;
194 }
195 }
196 }
197
198 // branch to the cache miss code
199 i += objc_write_branch(buffer + i, code + i);
200
201 // flush the instruction cache
202 sys_icache_invalidate(buffer, i*4);
203
204 if (PrintRTP) {
205 _objc_inform("RTP: wrote objc_msgSend at [%p..%p)",
206 (void *)address, (void *)(address + i*sizeof(unsigned)));
207 }
208 }
209
210
211 /**********************************************************************
212 * rtp_set_up_other
213 *
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;
224
225 // Location of the original implementation.
226 unsigned *code = (unsigned *)(objc_collecting_enabled() ? gc_code : non_gc_code);
227
228 if (objc_collecting_enabled()) {
229 size_t written = objc_write_branch(buffer, code);
230 sys_icache_invalidate(buffer, written*4);
231 if (PrintRTP) {
232 _objc_inform("RTP: %s in RTP at %p is a %zu instruction branch",
233 name, buffer, written);
234 }
235 return;
236 }
237
238 if (PrintRTP) {
239 _objc_inform("RTP: writing %s at [%p..%p) ...",
240 name, (void *)address, (void *)(address + maxsize));
241 }
242
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);
247 if (i > 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);
254 return;
255 }
256
257 // flush the instruction cache
258 sys_icache_invalidate(buffer, i*4);
259
260 if (PrintRTP) {
261 _objc_inform("RTP: wrote %s at [%p..%p)",
262 name, (void *)address,
263 (void *)(address + i * sizeof(unsigned)));
264 }
265 }
266
267
268 /**********************************************************************
269 * rtp_copy_code
270 *
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)
278 {
279 const unsigned op_blr = 0x4e800020u;
280 size_t i;
281
282 // copy instructions until blr is found
283 for (i = 0; i < max_insns; i++) {
284 dest[i] = source[i];
285 if (source[i] == op_blr) break;
286 }
287
288 // return number of instructions copied
289 // OR max_insns+1 if no blr was found
290 return i + 1;
291 }
292
293
294 #elif defined(__i386__)
295
296
297 /**********************************************************************
298 * rtp_swap_imp
299 *
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)
305 {
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);
309 else
310 {
311 objc_write_branch(address, (unsigned*)code);
312
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);
316 }
317 }
318
319
320 __private_extern__ void rtp_init(void)
321 {
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.
324 #ifndef NO_GC
325 if (UseGC)
326 {
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");
333 }
334 else
335 #endif
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_*.");
340 }
341 }
342
343
344 #else
345
346
347 __private_extern__ void rtp_init(void)
348 {
349 if (PrintRTP) {
350 _objc_inform("RTP: no rtp implementation for this platform");
351 }
352 }
353
354
355 #endif
356