]> git.saurik.com Git - apple/objc4.git/blob - runtime/objc-rtp.m
objc4-371.1.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-rtp.h"
29 #import "objc-private.h"
30 #import "objc-auto.h"
31
32 #import <stdint.h>
33 #import <mach/mach.h>
34
35
36 // Local prototypes
37
38 #if defined(__ppc__) || defined(__ppc64__)
39 // from Libc, but no prototype yet (#3850825)
40 extern void sys_icache_invalidate(const void * newcode, size_t len);
41
42 static size_t rtp_copy_code(unsigned* dest, unsigned* source, size_t max_insns);
43 #endif
44
45
46 #if defined(__ppc64__)
47
48 __private_extern__ void rtp_init(void)
49 {
50 if (PrintRTP) {
51 _objc_inform("RTP: no rtp implementation for this platform");
52 }
53 }
54
55 #else
56
57 #if defined(__ppc__)
58 static void rtp_set_up_objc_msgSend(uintptr_t address, size_t maxsize);
59 static void rtp_set_up_other(uintptr_t address, size_t maxsize, const char *name, void *gc_code, void *non_gc_code);
60 #endif
61
62 #if defined(__i386__) || defined(__x86_64__)
63 static void rtp_swap_imp(unsigned *address, void *code, const char *name);
64 #endif
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 #if defined(__ppc__)
74
75 kern_return_t ret;
76 vm_address_t objcRTPages = (vm_address_t)(kRTPagesHi - kRTPagesSize);
77
78 if (PrintRTP) {
79 _objc_inform("RTP: initializing rtp at [%p..%p)",
80 (void *)objcRTPages, (void *)kRTPagesHi);
81 }
82
83 // unprotect the ObjC runtime pages for writing
84 ret = vm_protect(mach_task_self(),
85 objcRTPages, kRTPagesSize,
86 FALSE, VM_PROT_READ | VM_PROT_WRITE);
87
88 if (ret != KERN_SUCCESS) {
89 if (PrintRTP) {
90 _objc_inform("RTP: warning: libSystem/kernel did not allocate Objective-C runtime pages; continuing anyway");
91 }
92 return;
93 }
94
95 // initialize code in ObjC runtime pages
96 rtp_set_up_objc_msgSend(kRTAddress_objc_msgSend, kRTSize_objc_msgSend);
97
98 rtp_set_up_other(kRTAddress_objc_assign_ivar, kRTSize_objc_assign_ivar,
99 "objc_assign_ivar", objc_assign_ivar_gc, objc_assign_ivar_non_gc);
100
101 rtp_set_up_other(kRTAddress_objc_assign_global, kRTSize_objc_assign_global,
102 "objc_assign_global", objc_assign_global_gc, objc_assign_global_non_gc);
103
104 rtp_set_up_other(kRTAddress_objc_assign_strongCast, kRTSize_objc_assign_strongCast,
105 "objc_assign_strongCast", objc_assign_strongCast_gc, objc_assign_strongCast_non_gc);
106
107 // initialize data in ObjC runtime pages
108 memset((char *)kRTAddress_zero, 0, 16);
109 strcpy((char *)kIgnore, "<ignored selector>");
110
111 // re-protect the ObjC runtime pages for execution
112 ret = vm_protect(mach_task_self(),
113 objcRTPages, kRTPagesSize,
114 FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
115 if (ret != KERN_SUCCESS) {
116 _objc_inform("RTP: Could not re-protect Objective-C runtime pages!");
117 }
118
119 #elif defined(__i386__) || defined(__x86_64__)
120
121 // At load time, the page on which the objc_assign_* routines live is not
122 // marked as executable. We fix that here, regardless of the GC choice.
123 if (UseGC)
124 {
125 rtp_swap_imp((unsigned*)objc_assign_ivar,
126 objc_assign_ivar_gc, "objc_assign_ivar");
127 rtp_swap_imp((unsigned*)objc_assign_global,
128 objc_assign_global_gc, "objc_assign_global");
129 rtp_swap_imp((unsigned*)objc_assign_strongCast,
130 objc_assign_strongCast_gc, "objc_assign_strongCast");
131 }
132 else
133 { // Not GC, just make the page executable.
134 if (vm_protect(mach_task_self(), (vm_address_t)objc_assign_ivar, 1,
135 FALSE, VM_PROT_READ | VM_PROT_EXECUTE) != KERN_SUCCESS)
136 _objc_fatal("Could not reprotect objc_assign_*.");
137 }
138
139 #else
140 #error undefined architecture
141 #endif
142 }
143
144 #if defined(__ppc__)
145
146 /**********************************************************************
147 * rtp_set_up_objc_msgSend
148 *
149 * Construct the objc runtime page version of objc_msgSend
150 * address is the entry point of the new implementation.
151 * maxsize is the number of bytes available for the new implementation.
152 **********************************************************************/
153 static void rtp_set_up_objc_msgSend(uintptr_t address, size_t maxsize)
154 {
155 #if defined(__ppc__) || defined(__ppc64__)
156 // Location in the runtime pages of the new function.
157 unsigned *buffer = (unsigned *)address;
158
159 // Location of the original implementation.
160 // objc_msgSend is simple enough to copy directly
161 unsigned *code = (unsigned *)objc_msgSend;
162
163 // If building an instrumented or profiled runtime, simply branch
164 // directly to the full implementation.
165 #if defined(OBJC_INSTRUMENTED) || defined(PROFILE)
166 size_t written = objc_write_branch(buffer, code);
167 sys_icache_invalidate(buffer, written*4);
168 if (PrintRTP) {
169 _objc_inform("RTP: instrumented or profiled libobjc - objc_msgSend "
170 "in RTP at %p is a %zu instruction branch",
171 buffer, written);
172 }
173 return;
174 #endif
175
176 // If function interposing is enabled, call the full implementation
177 // via a dyld-recognizable stub.
178 if (AllowInterposing) {
179 extern void objc_msgSend_stub(void);
180 size_t written = objc_write_branch(buffer, objc_msgSend_stub);
181 sys_icache_invalidate(buffer, written*4);
182 if (PrintRTP) {
183 _objc_inform("RTP: interposing enabled - objc_msgSend "
184 "in RTP at %p is a %zu instruction branch",
185 buffer, written);
186 }
187 return;
188 }
189
190 if (PrintRTP) {
191 _objc_inform("RTP: writing objc_msgSend at [%p..%p) ...",
192 (void *)address, (void *)(address+maxsize));
193 }
194
195 // Copy instructions from function to runtime pages
196 // i is the number of INSTRUCTIONS written so far
197 size_t max_insns = maxsize / sizeof(unsigned);
198 size_t i = rtp_copy_code(buffer, code, max_insns);
199 if (i + objc_branch_size(buffer + i, code + i) > max_insns) {
200 // objc_msgSend didn't fit in the alloted space.
201 // Branch to ordinary objc_msgSend instead so the program won't crash.
202 i = objc_write_branch(buffer, code);
203 sys_icache_invalidate(buffer, i*4);
204 _objc_inform("RTP: objc_msgSend is too large to fit in the "
205 "runtime pages (%zu bytes available)", maxsize);
206 return;
207 }
208
209 {
210 // Replace load of _objc_nilReceiver into r11
211 // This assumes that the load of _objc_nilReceiver
212 // immediately follows the LAST `mflr r0` in objc_msgSend,
213 // and that the original load sequence is six instructions long.
214
215 // instructions used to load _objc_nilReceiver
216 const unsigned op_mflr_r0 = 0x7c0802a6u;
217 const unsigned op_nop = 0x60000000u;
218
219 // get address of _objc_nilReceiver, and its lo and hi halves
220 uintptr_t address = (uintptr_t)&_objc_nilReceiver;
221 uint16_t lo = address & 0xffff;
222 uint16_t ha = ((address - (int16_t)lo) >> 16) & 0xffff;
223 #if defined(__ppc64__)
224 uint16_t hi2 = (address >> 32) & 0xffff;
225 uint16_t hi3 = (address >> 48) & 0xffff;
226 #endif
227
228 // search for mflr instruction
229 size_t j;
230 for (j = i; j-- != 0; ) {
231 if (buffer[j] == op_mflr_r0) {
232 const unsigned op_lis_r11 = 0x3d600000u;
233 const unsigned op_lwz_r11 = 0x816b0000u;
234 #if defined(__ppc__)
235 // lis r11, ha
236 // lwz r11, lo(r11)
237 buffer[j + 0] = op_lis_r11 | ha;
238 buffer[j + 1] = op_nop;
239 buffer[j + 2] = op_nop;
240 buffer[j + 3] = op_lwz_r11 | lo;
241 buffer[j + 4] = op_nop;
242 buffer[j + 5] = op_nop;
243 #elif defined(__ppc64__)
244 const unsigned op_ori_r11 = 0x616b0000u;
245 const unsigned op_oris_r11 = 0x656b0000u;
246 const unsigned op_sldi_r11 = 0x796b07c6u;
247 // lis r11, hi3
248 // ori r11, r11, hi2
249 // sldi r11, r11, 32
250 // oris r11, r11, ha
251 // lwz r11, lo(r11)
252 buffer[j + 0] = op_lis_r11 | hi3;
253 buffer[j + 1] = op_ori_r11 | hi2;
254 buffer[j + 2] = op_sldi_r11;
255 buffer[j + 3] = op_oris_r11 | ha;
256 buffer[j + 4] = op_lwz_r11 | lo;
257 buffer[j + 5] = op_nop;
258 #endif
259 break;
260 }
261 }
262 }
263
264 // branch to the cache miss code
265 i += objc_write_branch(buffer + i, code + i);
266
267 // flush the instruction cache
268 sys_icache_invalidate(buffer, i*4);
269
270 if (PrintRTP) {
271 _objc_inform("RTP: wrote objc_msgSend at [%p..%p)",
272 (void *)address, (void *)(address + i*sizeof(unsigned)));
273 }
274
275 #elif defined(__i386__)
276 #warning needs implementation
277 #else
278 #error unknown architecture
279 #endif
280 }
281
282
283 /**********************************************************************
284 * rtp_set_up_other
285 *
286 * construct the objc runtime page version of the supplied code
287 * address is the entry point of the new implementation.
288 * maxsize is the number of bytes available for the new implementation.
289 * name is the c string name of the routine being set up.
290 * gc_code is the code to use if collecting is enabled (assumed to be large and requiring a branch.)
291 * non_gc_code is the code to use if collecting is not enabled (assumed to be small enough to copy.)
292 **********************************************************************/
293 static void rtp_set_up_other(uintptr_t address, size_t maxsize, const char *name, void *gc_code, void *non_gc_code) {
294 #if defined(__ppc__) || defined(__ppc64__)
295 // location in the runtime pages of this function
296 unsigned *buffer = (unsigned *)address;
297
298 // Location of the original implementation.
299 unsigned *code = (unsigned *)(objc_collecting_enabled() ? gc_code : non_gc_code);
300
301 if (objc_collecting_enabled()) {
302 size_t written = objc_write_branch(buffer, code);
303 sys_icache_invalidate(buffer, written*4);
304 if (PrintRTP) {
305 _objc_inform("RTP: %s in RTP at %p is a %zu instruction branch",
306 name, buffer, written);
307 }
308 return;
309 }
310
311 if (PrintRTP) {
312 _objc_inform("RTP: writing %s at [%p..%p) ...",
313 name, (void *)address, (void *)(address + maxsize));
314 }
315
316 // Copy instructions from function to runtime pages
317 // i is the number of INSTRUCTIONS written so far
318 size_t max_insns = maxsize / sizeof(unsigned);
319 size_t i = rtp_copy_code(buffer, code, max_insns);
320 if (i > max_insns) {
321 // code didn't fit in the alloted space.
322 // Branch to ordinary objc_assign_ivar instead so the program won't crash.
323 i = objc_write_branch(buffer, code);
324 sys_icache_invalidate(buffer, i*4);
325 _objc_inform("RTP: %s is too large to fit in the "
326 "runtime pages (%zu bytes available)", name, maxsize);
327 return;
328 }
329
330 // flush the instruction cache
331 sys_icache_invalidate(buffer, i*4);
332
333 if (PrintRTP) {
334 _objc_inform("RTP: wrote %s at [%p..%p)",
335 name, (void *)address,
336 (void *)(address + i * sizeof(unsigned)));
337 }
338
339 #elif defined(__i386__)
340 #warning needs implementation
341 #else // defined(architecture)
342 #error unknown architecture
343 #endif // defined(architecture)
344 }
345
346
347 /**********************************************************************
348 * rtp_copy_code
349 *
350 * Copy blr-terminated PPC instructions from source to dest.
351 * If a blr is reached then that blr is copied, and the return value is
352 * the number of instructions copied ( <= max_insns )
353 * If no blr is reached then exactly max_insns instructions are copied,
354 * and the return value is max_insns+1.
355 **********************************************************************/
356 static size_t rtp_copy_code(unsigned* dest, unsigned* source, size_t max_insns)
357 {
358 const unsigned op_blr = 0x4e800020u;
359 size_t i;
360
361 // copy instructions until blr is found
362 for (i = 0; i < max_insns; i++) {
363 dest[i] = source[i];
364 if (source[i] == op_blr) break;
365 }
366
367 // return number of instructions copied
368 // OR max_insns+1 if no blr was found
369 return i + 1;
370 }
371
372 // defined(__ppc__) || defined(__ppc64__)
373 #endif
374
375 #if defined(__i386__) || defined(__x86_64__)
376
377 /**********************************************************************
378 * rtp_swap_imp
379 *
380 * Swap a function's current implementation with a new one.
381 * The routine at 'address' is assumed to be at least as large as the
382 * jump instruction required to reach the new implementation.
383 **********************************************************************/
384 static void rtp_swap_imp(unsigned *address, void *code, const char *name)
385 {
386 if (vm_protect(mach_task_self(), (vm_address_t)address, 1,
387 FALSE, VM_PROT_READ | VM_PROT_WRITE) != KERN_SUCCESS)
388 _objc_fatal("Could not get write access to %s.", name);
389 else
390 {
391 objc_write_branch(address, (unsigned*)code);
392
393 if (vm_protect(mach_task_self(), (vm_address_t)address, 1,
394 FALSE, VM_PROT_READ | VM_PROT_EXECUTE) != KERN_SUCCESS)
395 _objc_fatal("Could not reprotect %s.", name);
396 }
397 }
398
399 // defined(__i386__) || defined(__x86_64__)
400 #endif
401
402 // !defined(__ppc64__)
403 #endif