]>
Commit | Line | Data |
---|---|---|
13d88034 | 1 | /* |
b3962a83 | 2 | * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved. |
390d5862 | 3 | * |
b3962a83 | 4 | * @APPLE_LICENSE_HEADER_START@ |
390d5862 A |
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 | * | |
13d88034 | 13 | * The Original Code and all software distributed under the License are |
390d5862 | 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
13d88034 A |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
390d5862 A |
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 | * | |
13d88034 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | /*********************************************************************** | |
41c8faa5 A |
24 | * objc-runtime.m |
25 | * Copyright 1988-1996, NeXT Software, Inc. | |
26 | * Author: s. naroff | |
27 | * | |
28 | **********************************************************************/ | |
13d88034 | 29 | |
2bfd4448 A |
30 | |
31 | ||
13d88034 | 32 | /*********************************************************************** |
41c8faa5 A |
33 | * Imports. |
34 | **********************************************************************/ | |
13d88034 | 35 | |
13d88034 A |
36 | #include <mach-o/ldsyms.h> |
37 | #include <mach-o/dyld.h> | |
2bfd4448 | 38 | #include <mach-o/dyld_gdb.h> |
b3962a83 | 39 | #include <mach-o/dyld_priv.h> |
2bfd4448 A |
40 | #include <mach/mach.h> |
41 | #include <mach/mach_error.h> | |
13d88034 A |
42 | #include <sys/time.h> |
43 | #include <sys/resource.h> | |
2bfd4448 A |
44 | #include <sys/types.h> |
45 | #include <sys/stat.h> | |
46 | #include <fcntl.h> | |
b3962a83 A |
47 | #include <crt_externs.h> |
48 | #include <libgen.h> | |
2bfd4448 | 49 | |
b3962a83 A |
50 | #include <crt_externs.h> |
51 | #include <libgen.h> | |
41c8faa5 | 52 | |
b3962a83 A |
53 | #import "objc-private.h" |
54 | #import "hashtable2.h" | |
55 | #import "maptable.h" | |
56 | #import "Object.h" | |
57 | #import "objc-rtp.h" | |
58 | #import "objc-auto.h" | |
59 | #import "objc-loadmethod.h" | |
2bfd4448 | 60 | |
b3962a83 | 61 | OBJC_EXPORT Class getOriginalClassForPosingClass(Class); |
2bfd4448 A |
62 | |
63 | ||
64 | /*********************************************************************** | |
b3962a83 | 65 | * Exports. |
2bfd4448 | 66 | **********************************************************************/ |
2bfd4448 | 67 | |
b3962a83 A |
68 | // Settings from environment variables |
69 | __private_extern__ int PrintImages = -1; // env OBJC_PRINT_IMAGES | |
70 | __private_extern__ int PrintLoading = -1; // env OBJC_PRINT_LOAD_METHODS | |
71 | __private_extern__ int PrintInitializing = -1; // env OBJC_PRINT_INITIALIZE_METHODS | |
72 | __private_extern__ int PrintResolving = -1; // env OBJC_PRINT_RESOLVED_METHODS | |
73 | __private_extern__ int PrintConnecting = -1; // env OBJC_PRINT_CLASS_SETUP | |
74 | __private_extern__ int PrintProtocols = -1; // env OBJC_PRINT_PROTOCOL_SETUP | |
75 | __private_extern__ int PrintIvars = -1; // env OBJC_PRINT_IVAR_SETUP | |
76 | __private_extern__ int PrintFuture = -1; // env OBJC_PRINT_FUTURE_CLASSES | |
77 | __private_extern__ int PrintRTP = -1; // env OBJC_PRINT_RTP | |
78 | __private_extern__ int PrintGC = -1; // env OBJC_PRINT_GC | |
79 | __private_extern__ int PrintSharing = -1; // env OBJC_PRINT_SHARING | |
80 | __private_extern__ int PrintCxxCtors = -1; // env OBJC_PRINT_CXX_CTORS | |
81 | __private_extern__ int PrintExceptions = -1; // env OBJC_PRINT_EXCEPTIONS | |
82 | __private_extern__ int PrintAltHandlers = -1; // env OBJC_PRINT_ALT_HANDLERS | |
83 | __private_extern__ int PrintDeprecation = -1;// env OBJC_PRINT_DEPRECATION_WARNINGS | |
84 | __private_extern__ int PrintReplacedMethods = -1; // env OBJC_PRINT_REPLACED_METHODS | |
85 | __private_extern__ int PrintCacheCollection = -1; // env OBJC_PRINT_CACHE_COLLECTION | |
2bfd4448 | 86 | |
b3962a83 A |
87 | __private_extern__ int UseInternalZone = -1; // env OBJC_USE_INTERNAL_ZONE |
88 | __private_extern__ int AllowInterposing = -1;// env OBJC_ALLOW_INTERPOSING | |
2bfd4448 | 89 | |
b3962a83 A |
90 | __private_extern__ int DebugUnload = -1; // env OBJC_DEBUG_UNLOAD |
91 | __private_extern__ int DebugFragileSuperclasses = -1; // env OBJC_DEBUG_FRAGILE_SUPERCLASSES | |
92 | __private_extern__ int DebugNilSync = -1; // env OBJC_DEBUG_NIL_SYNC | |
2bfd4448 | 93 | |
b3962a83 A |
94 | __private_extern__ int DisableGC = -1; // env OBJC_DISABLE_GC |
95 | __private_extern__ int DebugFinalizers = -1; // env OBJC_DEBUG_FINALIZERS | |
2bfd4448 | 96 | |
2bfd4448 | 97 | |
b3962a83 A |
98 | // objc's key for pthread_getspecific |
99 | static pthread_key_t _objc_pthread_key = 0; | |
2bfd4448 | 100 | |
b3962a83 A |
101 | // Selectors for which @selector() doesn't work |
102 | __private_extern__ SEL cxx_construct_sel = NULL; | |
103 | __private_extern__ SEL cxx_destruct_sel = NULL; | |
104 | __private_extern__ const char *cxx_construct_name = ".cxx_construct"; | |
105 | __private_extern__ const char *cxx_destruct_name = ".cxx_destruct"; | |
2bfd4448 A |
106 | |
107 | ||
108 | /*********************************************************************** | |
b3962a83 | 109 | * Function prototypes internal to this module. |
2bfd4448 | 110 | **********************************************************************/ |
2bfd4448 | 111 | |
b3962a83 | 112 | static void _objc_unmap_image(header_info *hi); |
2bfd4448 | 113 | |
2bfd4448 | 114 | |
b3962a83 A |
115 | /*********************************************************************** |
116 | * Static data internal to this module. | |
117 | **********************************************************************/ | |
2bfd4448 | 118 | |
b3962a83 A |
119 | // we keep a linked list of header_info's describing each image as told to us by dyld |
120 | static header_info *FirstHeader NOBSS = 0; // NULL means empty list | |
121 | static header_info *LastHeader NOBSS = 0; // NULL means invalid; recompute it | |
122 | static int HeaderCount NOBSS = 0; | |
13d88034 | 123 | |
2bfd4448 | 124 | |
13d88034 | 125 | /*********************************************************************** |
b3962a83 A |
126 | * objc_getClass. Return the id of the named class. If the class does |
127 | * not exist, call _objc_classLoader and then objc_classHandler, either of | |
128 | * which may create a new class. | |
129 | * Warning: doesn't work if aClassName is the name of a posed-for class's isa! | |
41c8faa5 | 130 | **********************************************************************/ |
b3962a83 | 131 | id objc_getClass(const char *aClassName) |
13d88034 | 132 | { |
b3962a83 | 133 | if (!aClassName) return Nil; |
41c8faa5 | 134 | |
b3962a83 A |
135 | // NO unconnected, YES class handler |
136 | return look_up_class(aClassName, NO, YES); | |
13d88034 A |
137 | } |
138 | ||
2bfd4448 | 139 | |
13d88034 | 140 | /*********************************************************************** |
b3962a83 A |
141 | * objc_getRequiredClass. |
142 | * Same as objc_getClass, but kills the process if the class is not found. | |
143 | * This is used by ZeroLink, where failing to find a class would be a | |
144 | * compile-time link error without ZeroLink. | |
41c8faa5 | 145 | **********************************************************************/ |
b3962a83 | 146 | id objc_getRequiredClass(const char *aClassName) |
13d88034 | 147 | { |
b3962a83 A |
148 | id cls = objc_getClass(aClassName); |
149 | if (!cls) _objc_fatal("link error: class '%s' not found.", aClassName); | |
150 | return cls; | |
13d88034 A |
151 | } |
152 | ||
2bfd4448 | 153 | |
13d88034 | 154 | /*********************************************************************** |
b3962a83 A |
155 | * objc_lookUpClass. Return the id of the named class. |
156 | * If the class does not exist, call _objc_classLoader, which may create | |
157 | * a new class. | |
158 | * | |
159 | * Formerly objc_getClassWithoutWarning () | |
41c8faa5 | 160 | **********************************************************************/ |
b3962a83 | 161 | id objc_lookUpClass(const char *aClassName) |
13d88034 | 162 | { |
b3962a83 | 163 | if (!aClassName) return Nil; |
13d88034 | 164 | |
b3962a83 A |
165 | // NO unconnected, NO class handler |
166 | return look_up_class(aClassName, NO, NO); | |
167 | } | |
2bfd4448 A |
168 | |
169 | /*********************************************************************** | |
b3962a83 A |
170 | * objc_getFutureClass. Return the id of the named class. |
171 | * If the class does not exist, return an uninitialized class | |
172 | * structure that will be used for the class when and if it | |
173 | * does get loaded. | |
174 | * Not thread safe. | |
2bfd4448 | 175 | **********************************************************************/ |
b3962a83 | 176 | Class objc_getFutureClass(const char *name) |
2bfd4448 | 177 | { |
b3962a83 | 178 | Class cls; |
2bfd4448 | 179 | |
b3962a83 A |
180 | // YES unconnected, NO class handler |
181 | // (unconnected is OK because it will someday be the real class) | |
182 | cls = look_up_class(name, YES, NO); | |
183 | if (cls) { | |
184 | if (PrintFuture) { | |
185 | _objc_inform("FUTURE: found %p already in use for %s", cls, name); | |
2bfd4448 | 186 | } |
b3962a83 A |
187 | return cls; |
188 | } | |
189 | ||
190 | // No class or future class with that name yet. Make one. | |
191 | // fixme not thread-safe with respect to | |
192 | // simultaneous library load or getFutureClass. | |
193 | return _objc_allocateFutureClass(name); | |
2bfd4448 A |
194 | } |
195 | ||
196 | ||
13d88034 | 197 | /*********************************************************************** |
b3962a83 A |
198 | * objc_getMetaClass. Return the id of the meta class the named class. |
199 | * Warning: doesn't work if aClassName is the name of a posed-for class's isa! | |
41c8faa5 | 200 | **********************************************************************/ |
b3962a83 | 201 | id objc_getMetaClass(const char *aClassName) |
41c8faa5 | 202 | { |
b3962a83 | 203 | Class cls; |
41c8faa5 | 204 | |
b3962a83 | 205 | if (!aClassName) return Nil; |
390d5862 | 206 | |
b3962a83 A |
207 | cls = objc_getClass (aClassName); |
208 | if (!cls) | |
41c8faa5 | 209 | { |
b3962a83 A |
210 | _objc_inform ("class `%s' not linked into application", aClassName); |
211 | return Nil; | |
41c8faa5 | 212 | } |
2bfd4448 | 213 | |
b3962a83 | 214 | return ((id)cls)->isa; |
13d88034 A |
215 | } |
216 | ||
217 | ||
b3962a83 A |
218 | #if !__LP64__ |
219 | // Not updated for 64-bit ABI | |
220 | ||
13d88034 | 221 | /*********************************************************************** |
b3962a83 A |
222 | * _headerForAddress. |
223 | * addr can be a class or a category | |
41c8faa5 | 224 | **********************************************************************/ |
b3962a83 | 225 | static const header_info *_headerForAddress(void *addr) |
13d88034 | 226 | { |
b3962a83 A |
227 | unsigned long size; |
228 | unsigned long seg; | |
229 | header_info * hInfo; | |
390d5862 | 230 | |
b3962a83 A |
231 | // Check all headers in the vector |
232 | for (hInfo = FirstHeader; hInfo != NULL; hInfo = hInfo->next) | |
41c8faa5 | 233 | { |
b3962a83 A |
234 | // Locate header data, if any |
235 | if (!hInfo->objcSegmentHeader) continue; | |
236 | seg = hInfo->objcSegmentHeader->vmaddr + hInfo->image_slide; | |
237 | size = hInfo->objcSegmentHeader->filesize; | |
41c8faa5 | 238 | |
b3962a83 A |
239 | // Is the class in this header? |
240 | if ((seg <= (unsigned long) addr) && | |
241 | ((unsigned long) addr < (seg + size))) | |
242 | return hInfo; | |
41c8faa5 | 243 | } |
390d5862 | 244 | |
b3962a83 A |
245 | // Not found |
246 | return 0; | |
13d88034 A |
247 | } |
248 | ||
b3962a83 | 249 | |
13d88034 | 250 | /*********************************************************************** |
b3962a83 A |
251 | * _headerForClass |
252 | * Return the image header containing this class, or NULL. | |
253 | * Returns NULL on runtime-constructed classes, and the NSCF classes. | |
41c8faa5 | 254 | **********************************************************************/ |
b3962a83 A |
255 | __private_extern__ const header_info *_headerForClass(Class cls) |
256 | { | |
257 | return _headerForAddress(cls); | |
258 | } | |
259 | ||
260 | // !__LP64__ | |
261 | #endif | |
262 | ||
13d88034 A |
263 | |
264 | /*********************************************************************** | |
b3962a83 | 265 | * _nameForHeader. |
41c8faa5 | 266 | **********************************************************************/ |
b3962a83 | 267 | __private_extern__ const char *_nameForHeader(const headerType *header) |
13d88034 | 268 | { |
b3962a83 A |
269 | return _getObjcHeaderName ((headerType *) header); |
270 | } | |
41c8faa5 | 271 | |
41c8faa5 | 272 | |
b3962a83 A |
273 | /*********************************************************************** |
274 | * _gcForHInfo. | |
275 | **********************************************************************/ | |
276 | __private_extern__ const char *_gcForHInfo(const header_info *hinfo) | |
277 | { | |
278 | if (_objcHeaderRequiresGC(hinfo)) return "requires GC"; | |
279 | else if (_objcHeaderSupportsGC(hinfo)) return "supports GC"; | |
280 | else return "does not support GC"; | |
281 | } | |
282 | __private_extern__ const char *_gcForHInfo2(const header_info *hinfo) | |
283 | { | |
284 | if (_objcHeaderRequiresGC(hinfo)) return " (requires GC)"; | |
285 | else if (_objcHeaderSupportsGC(hinfo)) return " (supports GC)"; | |
286 | else return ""; | |
287 | } | |
41c8faa5 | 288 | |
41c8faa5 | 289 | |
b3962a83 A |
290 | /*********************************************************************** |
291 | * bad_magic. | |
292 | * Return YES if the header has invalid Mach-o magic. | |
293 | **********************************************************************/ | |
294 | static BOOL bad_magic(const headerType *mhdr) | |
295 | { | |
296 | return (mhdr->magic != MH_MAGIC && mhdr->magic != MH_MAGIC_64 && | |
297 | mhdr->magic != MH_CIGAM && mhdr->magic != MH_CIGAM_64); | |
13d88034 A |
298 | } |
299 | ||
b3962a83 | 300 | |
13d88034 | 301 | /*********************************************************************** |
41c8faa5 A |
302 | * _objc_headerStart. Return what headers we know about. |
303 | **********************************************************************/ | |
b3962a83 | 304 | __private_extern__ header_info *_objc_headerStart(void) |
13d88034 | 305 | { |
41c8faa5 A |
306 | // Take advatage of our previous work |
307 | return FirstHeader; | |
13d88034 A |
308 | } |
309 | ||
41c8faa5 | 310 | |
2bfd4448 A |
311 | /*********************************************************************** |
312 | * _objc_addHeader. | |
b3962a83 | 313 | * Returns NULL if the header has no ObjC metadata. |
2bfd4448 | 314 | **********************************************************************/ |
41c8faa5 | 315 | |
2bfd4448 A |
316 | // tested with 2; typical case is 4, but OmniWeb & Mail push it towards 20 |
317 | #define HINFO_SIZE 16 | |
390d5862 | 318 | |
2bfd4448 A |
319 | static int HeaderInfoCounter NOBSS = 0; |
320 | static header_info HeaderInfoTable[HINFO_SIZE] NOBSS = { {0} }; | |
13d88034 | 321 | |
b3962a83 | 322 | static header_info * _objc_addHeader(const headerType *header) |
390d5862 | 323 | { |
b3962a83 A |
324 | size_t info_size = 0; |
325 | const segmentType *objc_segment; | |
326 | const segmentType *objc2_segment; | |
327 | const objc_image_info *image_info; | |
328 | const segmentType *data_segment; | |
2bfd4448 | 329 | header_info *result; |
b3962a83 A |
330 | ptrdiff_t image_slide; |
331 | ||
332 | // Weed out duplicates | |
333 | for (result = FirstHeader; result; result = result->next) { | |
334 | if (header == result->mhdr) return NULL; | |
335 | } | |
41c8faa5 | 336 | |
2bfd4448 | 337 | // Locate the __OBJC segment |
b3962a83 A |
338 | image_slide = _getImageSlide(header); |
339 | image_info = _getObjcImageInfo(header, image_slide, &info_size); | |
2bfd4448 | 340 | objc_segment = getsegbynamefromheader(header, SEG_OBJC); |
b3962a83 A |
341 | objc2_segment = getsegbynamefromheader(header, SEG_OBJC2); |
342 | data_segment = getsegbynamefromheader(header, SEG_DATA); | |
343 | if (!objc_segment && !image_info && !objc2_segment) return NULL; | |
41c8faa5 | 344 | |
2bfd4448 A |
345 | // Find or allocate a header_info entry. |
346 | if (HeaderInfoCounter < HINFO_SIZE) { | |
347 | result = &HeaderInfoTable[HeaderInfoCounter++]; | |
348 | } else { | |
349 | result = _malloc_internal(sizeof(header_info)); | |
1f20c7a7 | 350 | } |
1f20c7a7 | 351 | |
2bfd4448 A |
352 | // Set up the new header_info entry. |
353 | result->mhdr = header; | |
b3962a83 | 354 | result->image_slide = image_slide; |
2bfd4448 | 355 | result->objcSegmentHeader = objc_segment; |
b3962a83 A |
356 | result->dataSegmentHeader = data_segment; |
357 | #if !__OBJC2__ | |
358 | result->mod_count = 0; | |
359 | result->mod_ptr = _getObjcModules(header, result->image_slide, &result->mod_count); | |
360 | #endif | |
361 | result->info = image_info; | |
362 | dladdr(result->mhdr, &result->dl_info); | |
363 | result->allClassesRealized = NO; | |
364 | ||
365 | // dylibs are not allowed to unload | |
366 | if (result->mhdr->filetype == MH_DYLIB) { | |
367 | dlopen(result->dl_info.dli_fname, RTLD_NOLOAD); | |
2bfd4448 | 368 | } |
1f20c7a7 | 369 | |
2bfd4448 A |
370 | // Make sure every copy of objc_image_info in this image is the same. |
371 | // This means same version and same bitwise contents. | |
372 | if (result->info) { | |
b3962a83 A |
373 | const objc_image_info *start = result->info; |
374 | const objc_image_info *end = | |
2bfd4448 | 375 | (objc_image_info *)(info_size + (uint8_t *)start); |
b3962a83 | 376 | const objc_image_info *info = start; |
2bfd4448 A |
377 | while (info < end) { |
378 | // version is byte size, except for version 0 | |
379 | size_t struct_size = info->version; | |
380 | if (struct_size == 0) struct_size = 2 * sizeof(uint32_t); | |
381 | if (info->version != start->version || | |
382 | 0 != memcmp(info, start, struct_size)) | |
383 | { | |
b3962a83 | 384 | _objc_inform("'%s' has inconsistently-compiled Objective-C " |
2bfd4448 A |
385 | "code. Please recompile all code in it.", |
386 | _nameForHeader(header)); | |
387 | } | |
388 | info = (objc_image_info *)(struct_size + (uint8_t *)info); | |
389 | } | |
390d5862 | 390 | } |
2bfd4448 A |
391 | |
392 | // Add the header to the header list. | |
393 | // The header is appended to the list, to preserve the bottom-up order. | |
b3962a83 | 394 | HeaderCount++; |
2bfd4448 A |
395 | result->next = NULL; |
396 | if (!FirstHeader) { | |
397 | // list is empty | |
398 | FirstHeader = LastHeader = result; | |
399 | } else { | |
400 | if (!LastHeader) { | |
401 | // list is not empty, but LastHeader is invalid - recompute it | |
402 | LastHeader = FirstHeader; | |
403 | while (LastHeader->next) LastHeader = LastHeader->next; | |
390d5862 | 404 | } |
2bfd4448 A |
405 | // LastHeader is now valid |
406 | LastHeader->next = result; | |
407 | LastHeader = result; | |
390d5862 A |
408 | } |
409 | ||
2bfd4448 | 410 | return result; |
390d5862 A |
411 | } |
412 | ||
2bfd4448 | 413 | |
390d5862 | 414 | /*********************************************************************** |
2bfd4448 A |
415 | * _objc_RemoveHeader |
416 | * Remove the given header from the header list. | |
417 | * FirstHeader is updated. | |
418 | * LastHeader is set to NULL. Any code that uses LastHeader must | |
419 | * detect this NULL and recompute LastHeader by traversing the list. | |
390d5862 | 420 | **********************************************************************/ |
2bfd4448 | 421 | static void _objc_removeHeader(header_info *hi) |
390d5862 | 422 | { |
2bfd4448 A |
423 | header_info **hiP; |
424 | ||
425 | for (hiP = &FirstHeader; *hiP != NULL; hiP = &(**hiP).next) { | |
426 | if (*hiP == hi) { | |
427 | header_info *deadHead = *hiP; | |
428 | ||
429 | // Remove from the linked list (updating FirstHeader if necessary). | |
430 | *hiP = (**hiP).next; | |
431 | ||
432 | // Update LastHeader if necessary. | |
433 | if (LastHeader == deadHead) { | |
434 | LastHeader = NULL; // will be recomputed next time it's used | |
435 | } | |
436 | ||
437 | // Free the memory, unless it was in the static HeaderInfoTable. | |
438 | if (deadHead < HeaderInfoTable || | |
439 | deadHead >= HeaderInfoTable + HINFO_SIZE) | |
440 | { | |
441 | _free_internal(deadHead); | |
442 | } | |
443 | ||
b3962a83 A |
444 | HeaderCount--; |
445 | ||
2bfd4448 | 446 | break; |
390d5862 A |
447 | } |
448 | } | |
390d5862 A |
449 | } |
450 | ||
451 | ||
13d88034 | 452 | /*********************************************************************** |
2bfd4448 A |
453 | * check_gc |
454 | * Check whether the executable supports or requires GC, and make sure | |
455 | * all already-loaded libraries support the executable's GC mode. | |
456 | * Returns TRUE if the executable wants GC on. | |
41c8faa5 | 457 | **********************************************************************/ |
2bfd4448 | 458 | static BOOL check_wants_gc(void) |
13d88034 | 459 | { |
2bfd4448 A |
460 | const header_info *hi; |
461 | BOOL appWantsGC; | |
462 | ||
463 | // Environment variables can override the following. | |
b3962a83 A |
464 | if (DisableGC) { |
465 | _objc_inform("GC: forcing GC OFF because OBJC_DISABLE_GC is set"); | |
2bfd4448 | 466 | appWantsGC = NO; |
41c8faa5 A |
467 | } |
468 | else { | |
2bfd4448 A |
469 | // Find the executable and check its GC bits. |
470 | // If the executable cannot be found, default to NO. | |
471 | // (The executable will not be found if the executable contains | |
472 | // no Objective-C code.) | |
473 | appWantsGC = NO; | |
474 | for (hi = FirstHeader; hi != NULL; hi = hi->next) { | |
475 | if (hi->mhdr->filetype == MH_EXECUTE) { | |
476 | appWantsGC = _objcHeaderSupportsGC(hi) ? YES : NO; | |
477 | if (PrintGC) { | |
b3962a83 A |
478 | _objc_inform("GC: executable '%s' %s", |
479 | _nameForHeader(hi->mhdr), _gcForHInfo(hi)); | |
2bfd4448 A |
480 | } |
481 | } | |
482 | } | |
390d5862 | 483 | } |
2bfd4448 | 484 | return appWantsGC; |
13d88034 A |
485 | } |
486 | ||
2bfd4448 A |
487 | /*********************************************************************** |
488 | * verify_gc_readiness | |
489 | * if we want gc, verify that every header describes files compiled | |
490 | * and presumably ready for gc. | |
491 | ************************************************************************/ | |
492 | ||
b3962a83 A |
493 | static void verify_gc_readiness(BOOL wantsGC, header_info **hList, |
494 | uint32_t hCount) | |
13d88034 | 495 | { |
2bfd4448 | 496 | BOOL busted = NO; |
b3962a83 | 497 | uint32_t i; |
2bfd4448 A |
498 | |
499 | // Find the libraries and check their GC bits against the app's request | |
b3962a83 A |
500 | for (i = 0; i < hCount; i++) { |
501 | header_info *hi = hList[i]; | |
2bfd4448 A |
502 | if (hi->mhdr->filetype == MH_EXECUTE) { |
503 | continue; | |
504 | } | |
505 | else if (hi->mhdr == &_mh_dylib_header) { | |
506 | // libobjc itself works with anything even though it is not | |
507 | // compiled with -fobjc-gc (fixme should it be?) | |
508 | } | |
509 | else if (wantsGC && ! _objcHeaderSupportsGC(hi)) { | |
510 | // App wants GC but library does not support it - bad | |
b3962a83 A |
511 | _objc_inform_now_and_on_crash |
512 | ("'%s' was not compiled with -fobjc-gc or -fobjc-gc-only, " | |
513 | "but the application requires GC", | |
514 | _nameForHeader(hi->mhdr)); | |
2bfd4448 A |
515 | busted = YES; |
516 | } | |
b3962a83 A |
517 | else if (!wantsGC && _objcHeaderRequiresGC(hi)) { |
518 | // App doesn't want GC but library requires it - bad | |
519 | _objc_inform_now_and_on_crash | |
520 | ("'%s' was compiled with -fobjc-gc-only, " | |
521 | "but the application does not support GC", | |
522 | _nameForHeader(hi->mhdr)); | |
523 | busted = YES; | |
524 | } | |
2bfd4448 A |
525 | |
526 | if (PrintGC) { | |
b3962a83 A |
527 | _objc_inform("GC: library '%s' %s", |
528 | _nameForHeader(hi->mhdr), _gcForHInfo(hi)); | |
2bfd4448 A |
529 | } |
530 | } | |
41c8faa5 | 531 | |
2bfd4448 A |
532 | if (busted) { |
533 | // GC state is not consistent. | |
534 | // Kill the process unless one of the forcing flags is set. | |
b3962a83 | 535 | if (!DisableGC) { |
2bfd4448 | 536 | _objc_fatal("*** GC capability of application and some libraries did not match"); |
41c8faa5 A |
537 | } |
538 | } | |
13d88034 A |
539 | } |
540 | ||
2bfd4448 | 541 | |
13d88034 | 542 | /*********************************************************************** |
2bfd4448 A |
543 | * objc_setConfiguration |
544 | * Read environment variables that affect the runtime. | |
545 | * Also print environment variable help, if requested. | |
41c8faa5 A |
546 | **********************************************************************/ |
547 | static void objc_setConfiguration() { | |
2bfd4448 A |
548 | int PrintHelp = (getenv("OBJC_HELP") != NULL); |
549 | int PrintOptions = (getenv("OBJC_PRINT_OPTIONS") != NULL); | |
b3962a83 A |
550 | int secure = issetugid(); |
551 | ||
552 | if (secure) { | |
553 | // All environment variables are ignored when setuid or setgid. | |
554 | if (PrintHelp) _objc_inform("OBJC_HELP ignored when running setuid or setgid"); | |
555 | if (PrintOptions) _objc_inform("OBJC_PRINT_OPTIONS ignored when running setuid or setgid"); | |
556 | } | |
557 | else { | |
558 | if (PrintHelp) { | |
559 | _objc_inform("OBJC_HELP: describe Objective-C runtime environment variables"); | |
560 | if (PrintOptions) { | |
561 | _objc_inform("OBJC_HELP is set"); | |
562 | } | |
563 | _objc_inform("OBJC_PRINT_OPTIONS: list which options are set"); | |
564 | } | |
2bfd4448 | 565 | if (PrintOptions) { |
b3962a83 | 566 | _objc_inform("OBJC_PRINT_OPTIONS is set"); |
2bfd4448 | 567 | } |
41c8faa5 | 568 | } |
2bfd4448 A |
569 | |
570 | #define OPTION(var, env, help) \ | |
571 | if ( var == -1 ) { \ | |
b3962a83 A |
572 | char *value = getenv(#env); \ |
573 | var = value != NULL && !strcmp("YES", value); \ | |
574 | if (secure) { \ | |
575 | if (var) _objc_inform(#env " ignored when running setuid or setgid"); \ | |
576 | var = 0; \ | |
577 | } else { \ | |
578 | if (PrintHelp) _objc_inform(#env ": " help); \ | |
579 | if (PrintOptions && var) _objc_inform(#env " is set"); \ | |
580 | } \ | |
390d5862 | 581 | } |
2bfd4448 A |
582 | |
583 | OPTION(PrintImages, OBJC_PRINT_IMAGES, | |
b3962a83 | 584 | "log image and library names as they are loaded"); |
2bfd4448 | 585 | OPTION(PrintLoading, OBJC_PRINT_LOAD_METHODS, |
b3962a83 A |
586 | "log calls to class and category +load methods"); |
587 | OPTION(PrintInitializing, OBJC_PRINT_INITIALIZE_METHODS, | |
588 | "log calls to class +initialize methods"); | |
589 | OPTION(PrintResolving, OBJC_PRINT_RESOLVED_METHODS, | |
590 | "log methods created by +resolveClassMethod: and +resolveInstanceMethod:"); | |
591 | OPTION(PrintConnecting, OBJC_PRINT_CLASS_SETUP, | |
592 | "log progress of class and category setup"); | |
593 | OPTION(PrintProtocols, OBJC_PRINT_PROTOCOL_SETUP, | |
594 | "log progresso of protocol setup"); | |
595 | OPTION(PrintIvars, OBJC_PRINT_IVAR_SETUP, | |
596 | "log processing of non-fragile ivars"); | |
597 | OPTION(PrintFuture, OBJC_PRINT_FUTURE_CLASSES, | |
598 | "log use of future classes for toll-free bridging"); | |
2bfd4448 A |
599 | OPTION(PrintRTP, OBJC_PRINT_RTP, |
600 | "log initialization of the Objective-C runtime pages"); | |
601 | OPTION(PrintGC, OBJC_PRINT_GC, | |
602 | "log some GC operations"); | |
603 | OPTION(PrintSharing, OBJC_PRINT_SHARING, | |
604 | "log cross-process memory sharing"); | |
605 | OPTION(PrintCxxCtors, OBJC_PRINT_CXX_CTORS, | |
606 | "log calls to C++ ctors and dtors for instance variables"); | |
b3962a83 A |
607 | OPTION(PrintExceptions, OBJC_PRINT_EXCEPTIONS, |
608 | "log exception handling"); | |
609 | OPTION(PrintAltHandlers, OBJC_PRINT_ALT_HANDLERS, | |
610 | "log processing of exception alt handlers"); | |
611 | OPTION(PrintReplacedMethods, OBJC_PRINT_REPLACED_METHODS, | |
612 | "log methods replaced by category implementations"); | |
613 | OPTION(PrintDeprecation, OBJC_PRINT_DEPRECATION_WARNINGS, | |
614 | "warn about calls to deprecated runtime functions"); | |
615 | OPTION(PrintCacheCollection, OBJC_PRINT_CACHE_COLLECTION, | |
616 | "log cleanup of stale method caches"); | |
2bfd4448 A |
617 | |
618 | OPTION(DebugUnload, OBJC_DEBUG_UNLOAD, | |
619 | "warn about poorly-behaving bundles when unloaded"); | |
620 | OPTION(DebugFragileSuperclasses, OBJC_DEBUG_FRAGILE_SUPERCLASSES, | |
621 | "warn about subclasses that may have been broken by subsequent changes to superclasses"); | |
b3962a83 A |
622 | OPTION(DebugFinalizers, OBJC_DEBUG_FINALIZERS, |
623 | "warn about classes that implement -dealloc but not -finalize"); | |
624 | OPTION(DebugNilSync, OBJC_DEBUG_NIL_SYNC, | |
625 | "warn about @synchronized(nil), which does no synchronization"); | |
2bfd4448 A |
626 | |
627 | OPTION(UseInternalZone, OBJC_USE_INTERNAL_ZONE, | |
628 | "allocate runtime data in a dedicated malloc zone"); | |
629 | OPTION(AllowInterposing, OBJC_ALLOW_INTERPOSING, | |
630 | "allow function interposing of objc_msgSend()"); | |
631 | ||
b3962a83 | 632 | OPTION(DisableGC, OBJC_DISABLE_GC, |
2bfd4448 | 633 | "force GC OFF, even if the executable wants it on"); |
b3962a83 | 634 | |
2bfd4448 | 635 | #undef OPTION |
41c8faa5 | 636 | } |
2bfd4448 A |
637 | |
638 | ||
41c8faa5 A |
639 | /*********************************************************************** |
640 | * objc_setMultithreaded. | |
641 | **********************************************************************/ | |
13d88034 A |
642 | void objc_setMultithreaded (BOOL flag) |
643 | { | |
b3962a83 A |
644 | OBJC_WARN_DEPRECATED; |
645 | ||
1f20c7a7 | 646 | // Nothing here. Thread synchronization in the runtime is always active. |
13d88034 | 647 | } |
1f20c7a7 A |
648 | |
649 | ||
b3962a83 A |
650 | /*********************************************************************** |
651 | * _objc_fetch_pthread_data | |
652 | * Fetch objc's pthread data for this thread. | |
653 | * If the data doesn't exist yet and create is NO, return NULL. | |
654 | * If the data doesn't exist yet and create is YES, allocate and return it. | |
655 | **********************************************************************/ | |
656 | __private_extern__ _objc_pthread_data *_objc_fetch_pthread_data(BOOL create) | |
657 | { | |
658 | _objc_pthread_data *data; | |
659 | ||
660 | data = pthread_getspecific(_objc_pthread_key); | |
661 | if (!data && create) { | |
662 | data = _calloc_internal(1, sizeof(_objc_pthread_data)); | |
663 | pthread_setspecific(_objc_pthread_key, data); | |
664 | } | |
665 | ||
666 | return data; | |
667 | } | |
668 | ||
1f20c7a7 A |
669 | |
670 | /*********************************************************************** | |
671 | * _objc_pthread_destroyspecific | |
672 | * Destructor for objc's per-thread data. | |
673 | * arg shouldn't be NULL, but we check anyway. | |
674 | **********************************************************************/ | |
675 | extern void _destroyInitializingClassList(struct _objc_initializing_classes *list); | |
b3962a83 | 676 | __private_extern__ void _objc_pthread_destroyspecific(void *arg) |
1f20c7a7 A |
677 | { |
678 | _objc_pthread_data *data = (_objc_pthread_data *)arg; | |
679 | if (data != NULL) { | |
680 | _destroyInitializingClassList(data->initializingClasses); | |
b3962a83 A |
681 | _destroyLockList(data->lockList); |
682 | _destroySyncCache(data->syncCache); | |
683 | _destroyAltHandlerList(data->handlerList); | |
1f20c7a7 A |
684 | |
685 | // add further cleanup here... | |
686 | ||
b3962a83 A |
687 | _free_internal(data); |
688 | } | |
689 | } | |
690 | ||
691 | ||
692 | /*********************************************************************** | |
693 | * _objcInit | |
694 | * Former library initializer. This function is now merely a placeholder | |
695 | * for external callers. All runtime initialization has now been moved | |
696 | * to map_images() and _objc_init. | |
697 | **********************************************************************/ | |
698 | void _objcInit(void) | |
699 | { | |
700 | // do nothing | |
701 | } | |
702 | ||
703 | ||
704 | /*********************************************************************** | |
705 | * gc_enforcer | |
706 | * Make sure that images about to be loaded by dyld are GC-acceptable. | |
707 | * Images linked to the executable are always permitted; they are | |
708 | * enforced inside map_images() itself. | |
709 | **********************************************************************/ | |
710 | static BOOL InitialDyldRegistration = NO; | |
711 | static const char *gc_enforcer(enum dyld_image_states state, | |
712 | uint32_t infoCount, | |
713 | const struct dyld_image_info info[]) | |
714 | { | |
715 | uint32_t i; | |
716 | ||
717 | // Linked images get a free pass | |
718 | if (InitialDyldRegistration) return NULL; | |
719 | ||
720 | if (PrintImages) { | |
721 | _objc_inform("IMAGES: checking %d images for compatibility...", | |
722 | infoCount); | |
723 | } | |
724 | ||
725 | for (i = 0; i < infoCount; i++) { | |
726 | const headerType *mhdr = (const headerType *)info[i].imageLoadAddress; | |
727 | if (bad_magic(mhdr)) continue; | |
728 | ||
729 | objc_image_info *image_info; | |
730 | size_t size; | |
731 | ||
732 | if (mhdr == &_mh_dylib_header) { | |
733 | // libobjc itself - OK | |
734 | continue; | |
735 | } | |
736 | ||
737 | #if !__LP64__ | |
738 | // 32-bit: __OBJC seg but no image_info means no GC support | |
739 | if (!getsegbynamefromheader(mhdr, SEG_OBJC)) { | |
740 | // not objc - assume OK | |
741 | continue; | |
742 | } | |
743 | image_info = _getObjcImageInfo(mhdr, _getImageSlide(mhdr), &size); | |
744 | if (!image_info) { | |
745 | // No image_info - assume GC unsupported | |
746 | if (!UseGC) { | |
747 | // GC is OFF - ok | |
748 | continue; | |
749 | } else { | |
750 | // GC is ON - bad | |
751 | if (PrintImages || PrintGC) { | |
752 | _objc_inform("IMAGES: rejecting %d images because %s doesn't support GC (no image_info)", infoCount, info[i].imageFilePath); | |
753 | } | |
754 | return "GC capability mismatch"; | |
755 | } | |
756 | } | |
757 | #else | |
758 | // 64-bit: no image_info means no objc at all | |
759 | image_info = _getObjcImageInfo(mhdr, _getImageSlide(mhdr), &size); | |
760 | if (!image_info) { | |
761 | // not objc - assume OK | |
762 | continue; | |
763 | } | |
764 | #endif | |
765 | ||
766 | if (UseGC && !_objcInfoSupportsGC(image_info)) { | |
767 | // GC is ON, but image does not support GC | |
768 | if (PrintImages || PrintGC) { | |
769 | _objc_inform("IMAGES: rejecting %d images because %s doesn't support GC", infoCount, info[i].imageFilePath); | |
770 | } | |
771 | return "GC capability mismatch"; | |
772 | } | |
773 | if (!UseGC && _objcInfoRequiresGC(image_info)) { | |
774 | // GC is OFF, but image requires GC | |
775 | if (PrintImages || PrintGC) { | |
776 | _objc_inform("IMAGES: rejecting %d images because %s requires GC", infoCount, info[i].imageFilePath); | |
777 | } | |
778 | return "GC capability mismatch"; | |
779 | } | |
1f20c7a7 | 780 | } |
1f20c7a7 | 781 | |
b3962a83 | 782 | return NULL; |
2bfd4448 A |
783 | } |
784 | ||
13d88034 | 785 | |
2bfd4448 A |
786 | /*********************************************************************** |
787 | * map_images | |
788 | * Process the given images which are being mapped in by dyld. | |
789 | * All class registration and fixups are performed (or deferred pending | |
790 | * discovery of missing superclasses etc), and +load methods are called. | |
791 | * | |
792 | * info[] is in bottom-up order i.e. libobjc will be earlier in the | |
793 | * array than any library that links to libobjc. | |
794 | **********************************************************************/ | |
b3962a83 A |
795 | static const char *map_images(enum dyld_image_states state, uint32_t infoCount, |
796 | const struct dyld_image_info infoList[]) | |
2bfd4448 A |
797 | { |
798 | static BOOL firstTime = YES; | |
799 | static BOOL wantsGC NOBSS = NO; | |
800 | uint32_t i; | |
41c8faa5 | 801 | header_info *hInfo; |
b3962a83 A |
802 | header_info *hList[infoCount]; |
803 | uint32_t hCount; | |
13d88034 | 804 | |
2bfd4448 A |
805 | // Perform first-time initialization if necessary. |
806 | // This function is called before ordinary library initializers. | |
807 | if (firstTime) { | |
b3962a83 A |
808 | extern SEL FwdSel; // in objc-msg-*.s |
809 | // workaround for rdar://5198739 | |
810 | pthread_key_t unused; | |
811 | pthread_key_create(&unused, NULL); | |
2bfd4448 A |
812 | pthread_key_create(&_objc_pthread_key, _objc_pthread_destroyspecific); |
813 | objc_setConfiguration(); // read environment variables | |
2bfd4448 A |
814 | // grab selectors for which @selector() doesn't work |
815 | cxx_construct_sel = sel_registerName(cxx_construct_name); | |
816 | cxx_destruct_sel = sel_registerName(cxx_destruct_name); | |
b3962a83 A |
817 | FwdSel = sel_registerName("forward::"); // in objc-msg-*.s |
818 | exception_init(); | |
819 | ||
820 | InitialDyldRegistration = YES; | |
821 | dyld_register_image_state_change_handler(dyld_image_state_mapped, 0 /* batch */, &gc_enforcer); | |
822 | InitialDyldRegistration = NO; | |
2bfd4448 | 823 | } |
13d88034 | 824 | |
2bfd4448 A |
825 | if (PrintImages) { |
826 | _objc_inform("IMAGES: processing %u newly-mapped images...\n", infoCount); | |
827 | } | |
41c8faa5 | 828 | |
41c8faa5 | 829 | |
b3962a83 A |
830 | // Find all images with Objective-C metadata. |
831 | hCount = 0; | |
832 | i = infoCount; | |
833 | while (i--) { | |
834 | const headerType *mhdr = (headerType *)infoList[i].imageLoadAddress; | |
835 | if (bad_magic(mhdr)) continue; | |
41c8faa5 | 836 | |
2bfd4448 A |
837 | hInfo = _objc_addHeader(mhdr); |
838 | if (!hInfo) { | |
839 | // no objc data in this entry | |
2bfd4448 A |
840 | continue; |
841 | } | |
41c8faa5 | 842 | |
b3962a83 | 843 | hList[hCount++] = hInfo; |
2bfd4448 A |
844 | |
845 | if (PrintImages) { | |
846 | _objc_inform("IMAGES: loading image for %s%s%s%s\n", | |
847 | _nameForHeader(mhdr), | |
848 | mhdr->filetype == MH_BUNDLE ? " (bundle)" : "", | |
849 | _objcHeaderIsReplacement(hInfo) ? " (replacement)":"", | |
b3962a83 | 850 | _gcForHInfo2(hInfo)); |
2bfd4448 A |
851 | } |
852 | } | |
41c8faa5 | 853 | |
2bfd4448 A |
854 | // Perform one-time runtime initialization that must be deferred until |
855 | // the executable itself is found. This needs to be done before | |
856 | // further initialization. | |
857 | // (The executable may not be present in this infoList if the | |
858 | // executable does not contain Objective-C code but Objective-C | |
859 | // is dynamically loaded later. In that case, check_wants_gc() | |
860 | // will do the right thing.) | |
861 | if (firstTime) { | |
862 | wantsGC = check_wants_gc(); | |
b3962a83 A |
863 | |
864 | verify_gc_readiness(wantsGC, hList, hCount); | |
865 | ||
2bfd4448 A |
866 | gc_init(wantsGC); // needs executable for GC decision |
867 | rtp_init(); // needs GC decision first | |
868 | } else { | |
b3962a83 | 869 | verify_gc_readiness(wantsGC, hList, hCount); |
2bfd4448 | 870 | } |
41c8faa5 | 871 | |
b3962a83 | 872 | _read_images(hList, hCount); |
41c8faa5 | 873 | |
2bfd4448 | 874 | firstTime = NO; |
41c8faa5 | 875 | |
b3962a83 | 876 | return NULL; |
2bfd4448 | 877 | } |
41c8faa5 | 878 | |
13d88034 | 879 | |
b3962a83 A |
880 | static const char *load_images(enum dyld_image_states state,uint32_t infoCount, |
881 | const struct dyld_image_info infoList[]) | |
2bfd4448 | 882 | { |
b3962a83 | 883 | BOOL found = NO; |
2bfd4448 | 884 | uint32_t i; |
13d88034 | 885 | |
b3962a83 A |
886 | i = infoCount; |
887 | while (i--) { | |
888 | header_info *hi; | |
889 | for (hi = FirstHeader; hi != NULL; hi = hi->next) { | |
890 | const headerType *mhdr = (headerType*)infoList[i].imageLoadAddress; | |
891 | if (hi->mhdr == mhdr) { | |
892 | prepare_load_methods(hi); | |
893 | found = YES; | |
2bfd4448 A |
894 | } |
895 | } | |
41c8faa5 | 896 | } |
13d88034 | 897 | |
b3962a83 | 898 | if (found) call_load_methods(); |
41c8faa5 | 899 | |
b3962a83 | 900 | return NULL; |
2bfd4448 | 901 | } |
41c8faa5 | 902 | |
2bfd4448 | 903 | /*********************************************************************** |
b3962a83 A |
904 | * unmap_image |
905 | * Process the given image which is about to be unmapped by dyld. | |
906 | * mh is mach_header instead of headerType because that's what | |
907 | * dyld_priv.h says even for 64-bit. | |
2bfd4448 | 908 | **********************************************************************/ |
b3962a83 | 909 | static void unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide) |
2bfd4448 | 910 | { |
b3962a83 A |
911 | if (PrintImages) { |
912 | _objc_inform("IMAGES: processing 1 newly-unmapped image...\n"); | |
41c8faa5 A |
913 | } |
914 | ||
b3962a83 | 915 | header_info *hi; |
2bfd4448 | 916 | |
b3962a83 A |
917 | // Find the runtime's header_info struct for the image |
918 | for (hi = FirstHeader; hi != NULL; hi = hi->next) { | |
919 | if (hi->mhdr == (const headerType *)mh) { | |
920 | _objc_unmap_image(hi); | |
921 | return; | |
13d88034 | 922 | } |
2bfd4448 | 923 | } |
b3962a83 A |
924 | |
925 | // no objc data for this image | |
2bfd4448 | 926 | } |
13d88034 | 927 | |
13d88034 | 928 | |
2bfd4448 | 929 | /*********************************************************************** |
b3962a83 A |
930 | * _objc_init |
931 | * Static initializer. Registers our image notifier with dyld. | |
932 | * fixme move map_images' firstTime code here - but GC code might need | |
933 | * another earlier image notifier | |
2bfd4448 | 934 | **********************************************************************/ |
b3962a83 A |
935 | static __attribute__((constructor)) |
936 | void _objc_init(void) | |
2bfd4448 | 937 | { |
b3962a83 A |
938 | // Register for unmap first, in case some +load unmaps something |
939 | _dyld_register_func_for_remove_image(&unmap_image); | |
940 | dyld_register_image_state_change_handler(dyld_image_state_bound, | |
941 | 1/*batch*/, &map_images); | |
942 | dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images); | |
13d88034 | 943 | } |
13d88034 | 944 | |
2bfd4448 | 945 | |
13d88034 | 946 | /*********************************************************************** |
41c8faa5 | 947 | * _objc_unmap_image. |
2bfd4448 A |
948 | * Destroy any Objective-C data for the given image, which is about to |
949 | * be unloaded by dyld. | |
950 | * Note: not thread-safe, but image loading isn't either. | |
41c8faa5 | 951 | **********************************************************************/ |
b3962a83 | 952 | static void _objc_unmap_image(header_info *hi) |
2bfd4448 | 953 | { |
2bfd4448 A |
954 | if (PrintImages) { |
955 | _objc_inform("IMAGES: unloading image for %s%s%s%s\n", | |
b3962a83 A |
956 | _nameForHeader(hi->mhdr), |
957 | hi->mhdr->filetype == MH_BUNDLE ? " (bundle)" : "", | |
2bfd4448 | 958 | _objcHeaderIsReplacement(hi) ? " (replacement)" : "", |
b3962a83 | 959 | _gcForHInfo2(hi)); |
2bfd4448 A |
960 | } |
961 | ||
b3962a83 | 962 | _unload_image(hi); |
2bfd4448 A |
963 | |
964 | // Remove header_info from header list | |
965 | _objc_removeHeader(hi); | |
13d88034 | 966 | } |
13d88034 | 967 | |
2bfd4448 | 968 | |
13d88034 | 969 | /*********************************************************************** |
390d5862 | 970 | * _objc_setNilReceiver |
41c8faa5 | 971 | **********************************************************************/ |
390d5862 | 972 | id _objc_setNilReceiver(id newNilReceiver) |
13d88034 | 973 | { |
390d5862 A |
974 | id oldNilReceiver; |
975 | ||
976 | oldNilReceiver = _objc_nilReceiver; | |
977 | _objc_nilReceiver = newNilReceiver; | |
978 | ||
979 | return oldNilReceiver; | |
13d88034 A |
980 | } |
981 | ||
982 | /*********************************************************************** | |
390d5862 | 983 | * _objc_getNilReceiver |
41c8faa5 | 984 | **********************************************************************/ |
390d5862 | 985 | id _objc_getNilReceiver(void) |
13d88034 | 986 | { |
390d5862 | 987 | return _objc_nilReceiver; |
13d88034 A |
988 | } |
989 | ||
13d88034 | 990 | |
390d5862 | 991 | /*********************************************************************** |
b3962a83 A |
992 | * objc_setForwardHandler |
993 | **********************************************************************/ | |
994 | void objc_setForwardHandler(void *fwd, void *fwd_stret) | |
995 | { | |
996 | _objc_forward_handler = fwd; | |
997 | _objc_forward_stret_handler = fwd_stret; | |
998 | } | |
999 | ||
1000 | ||
1001 | #if defined(__ppc__) || defined(__ppc64__) | |
1002 | ||
1003 | // Test to see if either the displacement or destination is within | |
1004 | // the +/- 2^25 range needed for a PPC branch immediate instruction. | |
1005 | // Shifting the high bit of the displacement (or destination) | |
1006 | // left 6 bits and then 6 bits arithmetically to the right does a | |
1007 | // sign extend of the 26th bit. If that result is equivalent to the | |
1008 | // original value, then the displacement (or destination) will fit | |
1009 | // into a simple branch. Otherwise a larger branch sequence is required. | |
1010 | // ppc64: max displacement is still +/- 2^25, but intptr_t is bigger | |
1011 | ||
1012 | // tiny: bc* | |
1013 | // small: b, ba (unconditional only) | |
1014 | // 32: bctr with lis+ori only | |
1015 | static BOOL ppc_tiny_displacement(intptr_t displacement) | |
1016 | { | |
1017 | size_t shift = sizeof(intptr_t) - 16; // ilp32=16, lp64=48 | |
1018 | return (((displacement << shift) >> shift) == displacement); | |
1019 | } | |
1020 | ||
1021 | static BOOL ppc_small_displacement(intptr_t displacement) | |
1022 | { | |
1023 | size_t shift = sizeof(intptr_t) - 26; // ilp32=6, lp64=38 | |
1024 | return (((displacement << shift) >> shift) == displacement); | |
1025 | } | |
1026 | ||
1027 | #if defined(__ppc64__) | |
1028 | // Same as ppc_small_displacement, but decides whether 32 bits is big enough. | |
1029 | static BOOL ppc_32bit_displacement(intptr_t displacement) | |
1030 | { | |
1031 | size_t shift = sizeof(intptr_t) - 32; | |
1032 | return (((displacement << shift) >> shift) == displacement); | |
1033 | } | |
1034 | #endif | |
1035 | ||
1036 | /********************************************************************** | |
1037 | * objc_branch_size | |
1038 | * Returns the number of instructions needed | |
1039 | * for a branch from entry to target. | |
390d5862 | 1040 | **********************************************************************/ |
b3962a83 | 1041 | __private_extern__ size_t objc_branch_size(void *entry, void *target) |
390d5862 | 1042 | { |
b3962a83 | 1043 | return objc_cond_branch_size(entry, target, COND_ALWAYS); |
390d5862 | 1044 | } |
2bfd4448 | 1045 | |
b3962a83 A |
1046 | __private_extern__ size_t |
1047 | objc_cond_branch_size(void *entry, void *target, unsigned cond) | |
1048 | { | |
1049 | intptr_t destination = (intptr_t)target; | |
1050 | intptr_t displacement = (intptr_t)destination - (intptr_t)entry; | |
2bfd4448 | 1051 | |
b3962a83 A |
1052 | if (cond == COND_ALWAYS && ppc_small_displacement(displacement)) { |
1053 | // fits in unconditional relative branch immediate | |
1054 | return 1; | |
1055 | } | |
1056 | if (cond == COND_ALWAYS && ppc_small_displacement(destination)) { | |
1057 | // fits in unconditional absolute branch immediate | |
1058 | return 1; | |
1059 | } | |
1060 | if (ppc_tiny_displacement(displacement)) { | |
1061 | // fits in conditional relative branch immediate | |
1062 | return 1; | |
1063 | } | |
1064 | if (ppc_tiny_displacement(destination)) { | |
1065 | // fits in conditional absolute branch immediate | |
1066 | return 1; | |
1067 | } | |
1068 | #if defined(__ppc64__) | |
1069 | if (!ppc_32bit_displacement(destination)) { | |
1070 | // fits in 64-bit absolute branch through CTR | |
1071 | return 7; | |
1072 | } | |
1073 | #endif | |
1074 | ||
1075 | // fits in 32-bit absolute branch through CTR | |
1076 | return 4; | |
1077 | } | |
2bfd4448 A |
1078 | |
1079 | /********************************************************************** | |
1080 | * objc_write_branch | |
1081 | * Writes at entry a PPC branch instruction sequence that branches to target. | |
b3962a83 | 1082 | * The sequence written will be objc_branch_size(entry, target) instructions. |
2bfd4448 A |
1083 | * Returns the number of instructions written. |
1084 | **********************************************************************/ | |
1085 | __private_extern__ size_t objc_write_branch(void *entry, void *target) | |
b3962a83 A |
1086 | { |
1087 | return objc_write_cond_branch(entry, target, COND_ALWAYS); | |
1088 | } | |
1089 | ||
1090 | __private_extern__ size_t | |
1091 | objc_write_cond_branch(void *entry, void *target, unsigned cond) | |
2bfd4448 A |
1092 | { |
1093 | unsigned *address = (unsigned *)entry; // location to store the 32 bit PPC instructions | |
1094 | intptr_t destination = (intptr_t)target; // destination as an absolute address | |
1095 | intptr_t displacement = (intptr_t)destination - (intptr_t)address; // destination as a branch relative offset | |
1096 | ||
b3962a83 A |
1097 | if (cond == COND_ALWAYS && ppc_small_displacement(displacement)) { |
1098 | // use unconditional relative branch with the displacement | |
1099 | address[0] = 0x48000000 | (unsigned)(displacement & 0x03fffffc); // b *+displacement | |
2bfd4448 A |
1100 | // issued 1 instruction |
1101 | return 1; | |
b3962a83 A |
1102 | } |
1103 | if (cond == COND_ALWAYS && ppc_small_displacement(destination)) { | |
1104 | // use unconditional absolute branch with the destination | |
1105 | address[0] = 0x48000000 | (unsigned)(destination & 0x03fffffc) | 2; // ba destination (2 is the absolute flag) | |
2bfd4448 A |
1106 | // issued 1 instruction |
1107 | return 1; | |
b3962a83 A |
1108 | } |
1109 | ||
1110 | if (ppc_tiny_displacement(displacement)) { | |
1111 | // use conditional relative branch with the displacement | |
1112 | address[0] = 0x40000000 | cond | (unsigned)(displacement & 0x0000fffc); // b *+displacement | |
1113 | // issued 1 instruction | |
1114 | return 1; | |
1115 | } | |
1116 | if (ppc_tiny_displacement(destination)) { | |
1117 | // use conditional absolute branch with the destination | |
1118 | address[0] = 0x40000000 | cond | (unsigned)(destination & 0x0000fffc) | 2; // ba destination (2 is the absolute flag) | |
1119 | // issued 1 instruction | |
1120 | return 1; | |
1121 | } | |
1122 | ||
1123 | ||
1124 | // destination is large and far away. | |
1125 | // Use an absolute branch via CTR. | |
1126 | ||
1127 | #if defined(__ppc64__) | |
1128 | if (!ppc_32bit_displacement(destination)) { | |
1129 | uint16_t lo = destination & 0xffff; | |
1130 | uint16_t hi = (destination >> 16) & 0xffff; | |
1131 | uint16_t hi2 = (destination >> 32) & 0xffff; | |
1132 | uint16_t hi3 = (destination >> 48) & 0xffff; | |
1133 | ||
1134 | address[0] = 0x3d800000 | hi3; // lis r12, hi3 | |
1135 | address[1] = 0x618c0000 | hi2; // ori r12, r12, hi2 | |
1136 | address[2] = 0x798c07c6; // sldi r12, r12, 32 | |
1137 | address[3] = 0x658c0000 | hi; // oris r12, r12, hi | |
1138 | address[4] = 0x618c0000 | lo; // ori r12, r12, lo | |
1139 | address[5] = 0x7d8903a6; // mtctr r12 | |
1140 | address[6] = 0x4c000420 | cond; // bctr | |
1141 | // issued 7 instructions | |
1142 | return 7; | |
1143 | } | |
1144 | #endif | |
1145 | ||
1146 | { | |
1147 | uint16_t lo = destination & 0xffff; | |
1148 | uint16_t hi = (destination >> 16) & 0xffff; | |
1149 | ||
1150 | address[0] = 0x3d800000 | hi; // lis r12,hi | |
1151 | address[1] = 0x618c0000 | lo; // ori r12,r12,lo | |
1152 | address[2] = 0x7d8903a6; // mtctr r12 | |
1153 | address[3] = 0x4c000420 | cond; // bctr | |
2bfd4448 A |
1154 | // issued 4 instructions |
1155 | return 4; | |
1156 | } | |
1157 | } | |
1158 | ||
b3962a83 A |
1159 | // defined(__ppc__) || defined(__ppc64__) |
1160 | #endif | |
1161 | ||
1162 | #if defined(__i386__) || defined(__x86_64__) | |
1163 | ||
1164 | /********************************************************************** | |
1165 | * objc_branch_size | |
1166 | * Returns the number of BYTES needed | |
1167 | * for a branch from entry to target. | |
1168 | **********************************************************************/ | |
1169 | __private_extern__ size_t objc_branch_size(void *entry, void *target) | |
1170 | { | |
1171 | return objc_cond_branch_size(entry, target, COND_ALWAYS); | |
1172 | } | |
1173 | ||
1174 | __private_extern__ size_t | |
1175 | objc_cond_branch_size(void *entry, void *target, unsigned cond) | |
1176 | { | |
1177 | // For simplicity, always use 32-bit relative jumps. | |
1178 | if (cond == COND_ALWAYS) return 5; | |
1179 | else return 6; | |
1180 | } | |
1181 | ||
1182 | /********************************************************************** | |
1183 | * objc_write_branch | |
1184 | * Writes at entry an i386 branch instruction sequence that branches to target. | |
1185 | * The sequence written will be objc_branch_size(entry, target) BYTES. | |
1186 | * Returns the number of BYTES written. | |
1187 | **********************************************************************/ | |
1188 | __private_extern__ size_t objc_write_branch(void *entry, void *target) | |
1189 | { | |
1190 | return objc_write_cond_branch(entry, target, COND_ALWAYS); | |
1191 | } | |
1192 | ||
1193 | __private_extern__ size_t | |
1194 | objc_write_cond_branch(void *entry, void *target, unsigned cond) | |
1195 | { | |
1196 | uint8_t *address = (uint8_t *)entry; // instructions written to here | |
1197 | intptr_t destination = (intptr_t)target; // branch dest as absolute address | |
1198 | intptr_t displacement = (intptr_t)destination - ((intptr_t)address + objc_cond_branch_size(entry, target, cond)); // branch dest as relative offset | |
1199 | ||
1200 | // For simplicity, always use 32-bit relative jumps | |
1201 | if (cond != COND_ALWAYS) { | |
1202 | *address++ = 0x0f; // Jcc prefix | |
1203 | } | |
1204 | *address++ = cond; | |
1205 | *address++ = displacement & 0xff; | |
1206 | *address++ = (displacement >> 8) & 0xff; | |
1207 | *address++ = (displacement >> 16) & 0xff; | |
1208 | *address++ = (displacement >> 24) & 0xff; | |
1209 | ||
1210 | return address - (uint8_t *)entry; | |
1211 | } | |
1212 | ||
1213 | // defined __i386__ | |
2bfd4448 A |
1214 | #endif |
1215 | ||
1216 | ||
1217 | /********************************************************************** | |
1218 | * secure_open | |
1219 | * Securely open a file from a world-writable directory (like /tmp) | |
1220 | * If the file does not exist, it will be atomically created with mode 0600 | |
1221 | * If the file exists, it must be, and remain after opening: | |
1222 | * 1. a regular file (in particular, not a symlink) | |
1223 | * 2. owned by euid | |
1224 | * 3. permissions 0600 | |
1225 | * 4. link count == 1 | |
1226 | * Returns a file descriptor or -1. Errno may or may not be set on error. | |
1227 | **********************************************************************/ | |
1228 | __private_extern__ int secure_open(const char *filename, int flags, uid_t euid) | |
1229 | { | |
1230 | struct stat fs, ls; | |
1231 | int fd = -1; | |
1232 | BOOL truncate = NO; | |
1233 | BOOL create = NO; | |
1234 | ||
1235 | if (flags & O_TRUNC) { | |
1236 | // Don't truncate the file until after it is open and verified. | |
1237 | truncate = YES; | |
1238 | flags &= ~O_TRUNC; | |
1239 | } | |
1240 | if (flags & O_CREAT) { | |
1241 | // Don't create except when we're ready for it | |
1242 | create = YES; | |
1243 | flags &= ~O_CREAT; | |
1244 | flags &= ~O_EXCL; | |
1245 | } | |
1246 | ||
1247 | if (lstat(filename, &ls) < 0) { | |
1248 | if (errno == ENOENT && create) { | |
1249 | // No such file - create it | |
1250 | fd = open(filename, flags | O_CREAT | O_EXCL, 0600); | |
1251 | if (fd >= 0) { | |
1252 | // File was created successfully. | |
1253 | // New file does not need to be truncated. | |
1254 | return fd; | |
1255 | } else { | |
1256 | // File creation failed. | |
1257 | return -1; | |
1258 | } | |
1259 | } else { | |
1260 | // lstat failed, or user doesn't want to create the file | |
1261 | return -1; | |
1262 | } | |
1263 | } else { | |
1264 | // lstat succeeded - verify attributes and open | |
1265 | if (S_ISREG(ls.st_mode) && // regular file? | |
1266 | ls.st_nlink == 1 && // link count == 1? | |
1267 | ls.st_uid == euid && // owned by euid? | |
1268 | (ls.st_mode & ALLPERMS) == (S_IRUSR | S_IWUSR)) // mode 0600? | |
1269 | { | |
1270 | // Attributes look ok - open it and check attributes again | |
1271 | fd = open(filename, flags, 0000); | |
1272 | if (fd >= 0) { | |
1273 | // File is open - double-check attributes | |
1274 | if (0 == fstat(fd, &fs) && | |
1275 | fs.st_nlink == ls.st_nlink && // link count == 1? | |
1276 | fs.st_uid == ls.st_uid && // owned by euid? | |
1277 | fs.st_mode == ls.st_mode && // regular file, 0600? | |
1278 | fs.st_ino == ls.st_ino && // same inode as before? | |
1279 | fs.st_dev == ls.st_dev) // same device as before? | |
1280 | { | |
1281 | // File is open and OK | |
1282 | if (truncate) ftruncate(fd, 0); | |
1283 | return fd; | |
1284 | } else { | |
1285 | // Opened file looks funny - close it | |
1286 | close(fd); | |
1287 | return -1; | |
1288 | } | |
1289 | } else { | |
1290 | // File didn't open | |
1291 | return -1; | |
1292 | } | |
1293 | } else { | |
1294 | // Unopened file looks funny - don't open it | |
1295 | return -1; | |
1296 | } | |
1297 | } | |
1298 | } | |
1299 | ||
1300 | ||
b3962a83 A |
1301 | #if !__OBJC2__ |
1302 | // GrP fixme | |
1303 | extern Class _objc_getOrigClass(const char *name); | |
1304 | #endif | |
1305 | const char *class_getImageName(Class cls) | |
1306 | { | |
1307 | int ok; | |
1308 | Dl_info info; | |
1309 | ||
1310 | if (!cls) return NULL; | |
1311 | ||
1312 | #if !__OBJC2__ | |
1313 | cls = _objc_getOrigClass(_class_getName(cls)); | |
1314 | #endif | |
1315 | ||
1316 | ok = dladdr(cls, &info); | |
1317 | if (ok) return info.dli_fname; | |
1318 | else return NULL; | |
1319 | } | |
1320 | ||
1321 | ||
1322 | const char **objc_copyImageNames(unsigned int *outCount) | |
1323 | { | |
1324 | header_info *hi; | |
1325 | int count = 0; | |
1326 | int max = HeaderCount; | |
1327 | const char **names = calloc(max+1, sizeof(char *)); | |
1328 | ||
1329 | for (hi = _objc_headerStart(); | |
1330 | hi != NULL && count < max; | |
1331 | hi = hi->next) | |
1332 | { | |
1333 | if (hi->dl_info.dli_fname) { | |
1334 | names[count++] = hi->dl_info.dli_fname; | |
1335 | } | |
1336 | } | |
1337 | names[count] = NULL; | |
1338 | ||
1339 | if (count == 0) { | |
1340 | // Return NULL instead of empty list if there are no images | |
1341 | free(names); | |
1342 | names = NULL; | |
1343 | } | |
1344 | ||
1345 | if (outCount) *outCount = count; | |
1346 | return names; | |
1347 | } | |
1348 | ||
1349 | ||
1350 | /********************************************************************** | |
1351 | * | |
1352 | **********************************************************************/ | |
1353 | const char ** | |
1354 | objc_copyClassNamesForImage(const char *image, unsigned int *outCount) | |
1355 | { | |
1356 | header_info *hi; | |
1357 | ||
1358 | if (!image) { | |
1359 | if (outCount) *outCount = 0; | |
1360 | return NULL; | |
1361 | } | |
1362 | ||
1363 | // Find the image. | |
1364 | for (hi = _objc_headerStart(); hi != NULL; hi = hi->next) { | |
1365 | if (0 == strcmp(image, hi->dl_info.dli_fname)) break; | |
1366 | } | |
1367 | ||
1368 | if (!hi) { | |
1369 | if (outCount) *outCount = 0; | |
1370 | return NULL; | |
1371 | } | |
1372 | ||
1373 | return _objc_copyClassNamesForImage(hi, outCount); | |
1374 | } | |
1375 | ||
1376 | ||
1377 | /********************************************************************** | |
1378 | * Fast Enumeration Support | |
1379 | **********************************************************************/ | |
1380 | ||
1381 | static void (*enumerationMutationHandler)(id); | |
1382 | ||
1383 | /********************************************************************** | |
1384 | * objc_enumerationMutation | |
1385 | * called by compiler when a mutation is detected during foreach iteration | |
1386 | **********************************************************************/ | |
1387 | void objc_enumerationMutation(id object) { | |
1388 | if (enumerationMutationHandler == nil) { | |
1389 | _objc_fatal("mutation detected during 'for(... in ...)' enumeration of object %p.", object); | |
1390 | } | |
1391 | (*enumerationMutationHandler)(object); | |
1392 | } | |
1393 | ||
1394 | ||
1395 | /********************************************************************** | |
1396 | * objc_setEnumerationMutationHandler | |
1397 | * an entry point to customize mutation error handing | |
1398 | **********************************************************************/ | |
1399 | void objc_setEnumerationMutationHandler(void (*handler)(id)) { | |
1400 | enumerationMutationHandler = handler; | |
1401 | } |