/*
* Copyright (c) 2007 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/***********************************************************************
* objc-os.m
* OS portability layer.
**********************************************************************/
#include "objc-os.h"
#include "objc-private.h"
#include "objc-loadmethod.h"
#if TARGET_OS_WIN32
#include "objc-runtime-old.h"
#include "objcrt.h"
malloc_zone_t *_objc_internal_zone(void)
{
return NULL;
}
int monitor_init(monitor_t *c)
{
// fixme error checking
HANDLE mutex = CreateMutex(NULL, TRUE, NULL);
while (!c->mutex) {
// fixme memory barrier here?
if (0 == InterlockedCompareExchangePointer(&c->mutex, mutex, 0)) {
// we win - finish construction
c->waiters = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
c->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL);
InitializeCriticalSection(&c->waitCountLock);
c->waitCount = 0;
c->didBroadcast = 0;
ReleaseMutex(c->mutex);
return 0;
}
}
// someone else allocated the mutex and constructed the monitor
ReleaseMutex(mutex);
CloseHandle(mutex);
return 0;
}
void mutex_init(mutex_t *m)
{
while (!m->lock) {
CRITICAL_SECTION *newlock = malloc(sizeof(CRITICAL_SECTION));
InitializeCriticalSection(newlock);
// fixme memory barrier here?
if (0 == InterlockedCompareExchangePointer(&m->lock, newlock, 0)) {
return;
}
// someone else installed their lock first
DeleteCriticalSection(newlock);
free(newlock);
}
}
void recursive_mutex_init(recursive_mutex_t *m)
{
// fixme error checking
HANDLE newmutex = CreateMutex(NULL, FALSE, NULL);
while (!m->mutex) {
// fixme memory barrier here?
if (0 == InterlockedCompareExchangePointer(&m->mutex, newmutex, 0)) {
// we win
return;
}
}
// someone else installed their lock first
CloseHandle(newmutex);
}
WINBOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
environ_init();
tls_init();
lock_init();
sel_init(NO, 3500); // old selector heuristic
exception_init();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
OBJC_EXPORT void *_objc_init_image(HMODULE image, const objc_sections *sects)
{
header_info *hi = _malloc_internal(sizeof(header_info));
size_t count, i;
hi->mhdr = (const headerType *)image;
hi->info = sects->iiStart;
hi->allClassesRealized = NO;
hi->modules = sects->modStart ? (Module *)((void **)sects->modStart+1) : 0;
hi->moduleCount = (Module *)sects->modEnd - hi->modules;
hi->protocols = sects->protoStart ? (struct old_protocol **)((void **)sects->protoStart+1) : 0;
hi->protocolCount = (struct old_protocol **)sects->protoEnd - hi->protocols;
hi->imageinfo = NULL;
hi->imageinfoBytes = 0;
// hi->imageinfo = sects->iiStart ? (uint8_t *)((void **)sects->iiStart+1) : 0;;
// hi->imageinfoBytes = (uint8_t *)sects->iiEnd - hi->imageinfo;
hi->selrefs = sects->selrefsStart ? (SEL *)((void **)sects->selrefsStart+1) : 0;
hi->selrefCount = (SEL *)sects->selrefsEnd - hi->selrefs;
hi->clsrefs = sects->clsrefsStart ? (Class *)((void **)sects->clsrefsStart+1) : 0;
hi->clsrefCount = (Class *)sects->clsrefsEnd - hi->clsrefs;
count = 0;
for (i = 0; i < hi->moduleCount; i++) {
if (hi->modules[i]) count++;
}
hi->mod_count = 0;
hi->mod_ptr = 0;
if (count > 0) {
hi->mod_ptr = malloc(count * sizeof(struct objc_module));
for (i = 0; i < hi->moduleCount; i++) {
if (hi->modules[i]) memcpy(&hi->mod_ptr[hi->mod_count++], hi->modules[i], sizeof(struct objc_module));
}
}
hi->moduleName = malloc(MAX_PATH * sizeof(TCHAR));
GetModuleFileName((HMODULE)(hi->mhdr), hi->moduleName, MAX_PATH * sizeof(TCHAR));
appendHeader(hi);
if (PrintImages) {
_objc_inform("IMAGES: loading image for %s%s%s\n",
hi->fname,
headerIsBundle(hi) ? " (bundle)" : "",
_objcHeaderIsReplacement(hi) ? " (replacement)":"");
}
_read_images(&hi, 1);
return hi;
}
OBJC_EXPORT void _objc_load_image(HMODULE image, header_info *hinfo)
{
prepare_load_methods(hinfo);
call_load_methods();
}
OBJC_EXPORT void _objc_unload_image(HMODULE image, header_info *hinfo)
{
_objc_fatal("image unload not supported");
}
bool crashlog_header_name(header_info *hi)
{
return true;
}
// TARGET_OS_WIN32
#elif TARGET_OS_MAC
#if !__OBJC2__
#include "objc-file-old.h"
#endif
void mutex_init(mutex_t *m)
{
pthread_mutex_init(m, NULL);
}
void recursive_mutex_init(recursive_mutex_t *m)
{
// fixme error checking
pthread_mutex_t *newmutex;
// Build recursive mutex attributes, if needed
static pthread_mutexattr_t *attr;
if (!attr) {
pthread_mutexattr_t *newattr = (pthread_mutexattr_t *)
_malloc_internal(sizeof(pthread_mutexattr_t));
pthread_mutexattr_init(newattr);
pthread_mutexattr_settype(newattr, PTHREAD_MUTEX_RECURSIVE);
while (!attr) {
if (OSAtomicCompareAndSwapPtrBarrier(0, newattr, (void**)&attr)) {
// we win
goto attr_done;
}
}
// someone else built the attr first
_free_internal(newattr);
}
attr_done:
// Build the mutex itself
newmutex = (pthread_mutex_t *)_malloc_internal(sizeof(pthread_mutex_t));
pthread_mutex_init(newmutex, attr);
while (!m->mutex) {
if (OSAtomicCompareAndSwapPtrBarrier(0, newmutex, (void**)&m->mutex)) {
// we win
return;
}
}
// someone else installed their mutex first
pthread_mutex_destroy(newmutex);
}
/***********************************************************************
* bad_magic.
* Return YES if the header has invalid Mach-o magic.
**********************************************************************/
BOOL bad_magic(const headerType *mhdr)
{
return (mhdr->magic != MH_MAGIC && mhdr->magic != MH_MAGIC_64 &&
mhdr->magic != MH_CIGAM && mhdr->magic != MH_CIGAM_64);
}
static header_info * addHeader(const headerType *mhdr)
{
header_info *hi;
if (bad_magic(mhdr)) return NULL;
#if __OBJC2__
// Look for hinfo from the dyld shared cache.
hi = preoptimizedHinfoForHeader(mhdr);
if (hi) {
// Found an hinfo in the dyld shared cache.
// Weed out duplicates.
if (hi->loaded) {
return NULL;
}
// Initialize fields not set by the shared cache
// hi->next is set by appendHeader
hi->fname = dyld_image_path_containing_address(hi->mhdr);
hi->loaded = true;
hi->inSharedCache = true;
if (PrintPreopt) {
_objc_inform("PREOPTIMIZATION: honoring preoptimized header info at %p for %s", hi, hi->fname);
}
# if !NDEBUG
// Verify image_info
size_t info_size = 0;
const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size);
assert(image_info == hi->info);
# endif
}
else
#endif
{
// Didn't find an hinfo in the dyld shared cache.
// Weed out duplicates
for (hi = FirstHeader; hi; hi = hi->next) {
if (mhdr == hi->mhdr) return NULL;
}
// Locate the __OBJC segment
size_t info_size = 0;
unsigned long seg_size;
const objc_image_info *image_info = _getObjcImageInfo(mhdr,&info_size);
const uint8_t *objc_segment = getsegmentdata(mhdr,SEG_OBJC,&seg_size);
if (!objc_segment && !image_info) return NULL;
// Allocate a header_info entry.
hi = (header_info *)_calloc_internal(sizeof(header_info), 1);
// Set up the new header_info entry.
hi->mhdr = mhdr;
#if !__OBJC2__
// mhdr must already be set
hi->mod_count = 0;
hi->mod_ptr = _getObjcModules(hi, &hi->mod_count);
#endif
hi->info = image_info;
hi->fname = dyld_image_path_containing_address(hi->mhdr);
hi->loaded = true;
hi->inSharedCache = false;
hi->allClassesRealized = NO;
}
// dylibs are not allowed to unload
// ...except those with image_info and nothing else (5359412)
if (hi->mhdr->filetype == MH_DYLIB && _hasObjcContents(hi)) {
dlopen(hi->fname, RTLD_NOLOAD);
}
appendHeader(hi);
return hi;
}
#if !SUPPORT_GC
const char *_gcForHInfo(const header_info *hinfo)
{
return "";
}
const char *_gcForHInfo2(const header_info *hinfo)
{
return "";
}
#else
/***********************************************************************
* _gcForHInfo.
**********************************************************************/
const char *_gcForHInfo(const header_info *hinfo)
{
if (_objcHeaderRequiresGC(hinfo)) {
if (_objcHeaderSupportsCompaction(hinfo))
return "requires GC, supports compaction";
else
return "requires GC";
} else if (_objcHeaderSupportsGC(hinfo)) {
if (_objcHeaderSupportsCompaction(hinfo))
return "supports GC, supports compaction";
else
return "supports GC";
} else {
return "does not support GC";
}
}
const char *_gcForHInfo2(const header_info *hinfo)
{
if (_objcHeaderRequiresGC(hinfo)) {
if (_objcHeaderSupportsCompaction(hinfo))
return "(requires GC) (supports compaction)";
else
return "(requires GC)";
} else if (_objcHeaderSupportsGC(hinfo)) {
if (_objcHeaderSupportsCompaction(hinfo))
return "(supports GC) (supports compaction)";
else
return "(supports GC)";
}
return "";
}
/***********************************************************************
* check_gc
* Check whether the executable supports or requires GC, and make sure
* all already-loaded libraries support the executable's GC mode.
* Returns TRUE if the executable wants GC on.
**********************************************************************/
static void check_wants_gc(BOOL *appWantsGC, BOOL *appSupportsCompaction)
{
const header_info *hi;
// Environment variables can override the following.
if (DisableGC) {
_objc_inform_on_crash("GC: forcing GC OFF because OBJC_DISABLE_GC is set");
*appWantsGC = NO;
*appSupportsCompaction = NO;
}
else {
// Find the executable and check its GC bits.
// If the executable cannot be found, default to NO.
// (The executable will not be found if the executable contains
// no Objective-C code.)
*appWantsGC = NO;
*appSupportsCompaction = NO;
for (hi = FirstHeader; hi != NULL; hi = hi->next) {
if (hi->mhdr->filetype == MH_EXECUTE) {
*appWantsGC = _objcHeaderSupportsGC(hi) ? YES : NO;
*appSupportsCompaction = (*appWantsGC && _objcHeaderSupportsCompaction(hi)) ? YES : NO;
if (PrintGC) {
_objc_inform("GC: executable '%s' %s",
hi->fname, _gcForHInfo(hi));
}
}
}
}
}
/***********************************************************************
* verify_gc_readiness
* if we want gc, verify that every header describes files compiled
* and presumably ready for gc.
************************************************************************/
static void verify_gc_readiness(BOOL wantsGC, BOOL *wantsCompaction,
header_info **hList, uint32_t hCount)
{
BOOL busted = NO;
uint32_t i;
// Find the libraries and check their GC bits against the app's request
for (i = 0; i < hCount; i++) {
header_info *hi = hList[i];
if (hi->mhdr->filetype == MH_EXECUTE) {
continue;
}
else if (hi->mhdr == &_mh_dylib_header) {
// libobjc itself works with anything even though it is not
// compiled with -fobjc-gc (fixme should it be?)
}
else if (wantsGC && ! _objcHeaderSupportsGC(hi)) {
// App wants GC but library does not support it - bad
_objc_inform_now_and_on_crash
("'%s' was not compiled with -fobjc-gc or -fobjc-gc-only, "
"but the application requires GC",
hi->fname);
busted = YES;
}
else if (!wantsGC && _objcHeaderRequiresGC(hi)) {
// App doesn't want GC but library requires it - bad
_objc_inform_now_and_on_crash
("'%s' was compiled with -fobjc-gc-only, "
"but the application does not support GC",
hi->fname);
busted = YES;
}
if (*wantsCompaction && !_objcHeaderSupportsCompaction(hi)) {
// App supports compaction, but library doesn't.
_objc_inform_now_and_on_crash
("'%s' was not linked with -Xlinker -objc_gc_compaction, "
"but the application wants compaction.",
hi->fname);
// Simply warn for now until radars are filed. Eventually,
// objc_disableCompaction() will block until any current compaction completes.
objc_disableCompaction();
*wantsCompaction = NO;
}
if (PrintGC) {
_objc_inform("GC: library '%s' %s",
hi->fname, _gcForHInfo(hi));
}
}
if (busted) {
// GC state is not consistent.
// Kill the process unless one of the forcing flags is set.
if (!DisableGC) {
_objc_fatal("*** GC capability of application and some libraries did not match");
}
}
}
/***********************************************************************
* gc_enforcer
* Make sure that images about to be loaded by dyld are GC-acceptable.
* Images linked to the executable are always permitted; they are
* enforced inside map_images() itself.
**********************************************************************/
static BOOL InitialDyldRegistration = NO;
static const char *gc_enforcer(enum dyld_image_states state,
uint32_t infoCount,
const struct dyld_image_info info[])
{
uint32_t i;
// Linked images get a free pass
if (InitialDyldRegistration) return NULL;
if (PrintImages) {
_objc_inform("IMAGES: checking %d images for compatibility...",
infoCount);
}
for (i = 0; i < infoCount; i++) {
crashlog_header_name_string(info[i].imageFilePath);
const headerType *mhdr = (const headerType *)info[i].imageLoadAddress;
if (bad_magic(mhdr)) continue;
objc_image_info *image_info;
size_t size;
if (mhdr == &_mh_dylib_header) {
// libobjc itself - OK
continue;
}
#if !__OBJC2__
unsigned long seg_size;
// 32-bit: __OBJC seg but no image_info means no GC support
if (!getsegmentdata(mhdr, "__OBJC", &seg_size)) {
// not objc - assume OK
continue;
}
image_info = _getObjcImageInfo(mhdr, &size);
if (!image_info) {
// No image_info - assume GC unsupported
if (!UseGC) {
// GC is OFF - ok
continue;
} else {
// GC is ON - bad
if (PrintImages || PrintGC) {
_objc_inform("IMAGES: rejecting %d images because %s doesn't support GC (no image_info)", infoCount, info[i].imageFilePath);
}
goto reject;
}
}
#else
// 64-bit: no image_info means no objc at all
image_info = _getObjcImageInfo(mhdr, &size);
if (!image_info) {
// not objc - assume OK
continue;
}
#endif
if (UseGC && !_objcInfoSupportsGC(image_info)) {
// GC is ON, but image does not support GC
if (PrintImages || PrintGC) {
_objc_inform("IMAGES: rejecting %d images because %s doesn't support GC", infoCount, info[i].imageFilePath);
}
goto reject;
}
if (!UseGC && _objcInfoRequiresGC(image_info)) {
// GC is OFF, but image requires GC
if (PrintImages || PrintGC) {
_objc_inform("IMAGES: rejecting %d images because %s requires GC", infoCount, info[i].imageFilePath);
}
goto reject;
}
}
crashlog_header_name_string(NULL);
return NULL;
reject:
crashlog_header_name_string(NULL);
return "GC capability mismatch";
}
// SUPPORT_GC
#endif
/***********************************************************************
* map_images_nolock
* Process the given images which are being mapped in by dyld.
* All class registration and fixups are performed (or deferred pending
* discovery of missing superclasses etc), and +load methods are called.
*
* info[] is in bottom-up order i.e. libobjc will be earlier in the
* array than any library that links to libobjc.
*
* Locking: loadMethodLock(old) or runtimeLock(new) acquired by map_images.
**********************************************************************/
#if __OBJC2__
#include "objc-file.h"
#else
#include "objc-file-old.h"
#endif
const char *
map_images_nolock(enum dyld_image_states state, uint32_t infoCount,
const struct dyld_image_info infoList[])
{
static BOOL firstTime = YES;
static BOOL wantsGC = NO;
static BOOL wantsCompaction = NO;
uint32_t i;
header_info *hi;
header_info *hList[infoCount];
uint32_t hCount;
size_t selrefCount = 0;
// Perform first-time initialization if necessary.
// This function is called before ordinary library initializers.
// fixme defer initialization until an objc-using image is found?
if (firstTime) {
preopt_init();
#if SUPPORT_GC
InitialDyldRegistration = YES;
dyld_register_image_state_change_handler(dyld_image_state_mapped, 0 /* batch */, &gc_enforcer);
InitialDyldRegistration = NO;
#endif
}
if (PrintImages) {
_objc_inform("IMAGES: processing %u newly-mapped images...\n", infoCount);
}
// Find all images with Objective-C metadata.
hCount = 0;
i = infoCount;
while (i--) {
const headerType *mhdr = (headerType *)infoList[i].imageLoadAddress;
hi = addHeader(mhdr);
if (!hi) {
// no objc data in this entry
continue;
}
if (mhdr->filetype == MH_EXECUTE) {
#if __OBJC2__
size_t count;
_getObjc2SelectorRefs(hi, &count);
selrefCount += count;
_getObjc2MessageRefs(hi, &count);
selrefCount += count;
#else
_getObjcSelectorRefs(hi, &selrefCount);
#endif
}
hList[hCount++] = hi;
if (PrintImages) {
_objc_inform("IMAGES: loading image for %s%s%s%s%s\n",
hi->fname,
mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
_objcHeaderIsReplacement(hi) ? " (replacement)" : "",
_objcHeaderOptimizedByDyld(hi)?" (preoptimized)" : "",
_gcForHInfo2(hi));
}
}
// Perform one-time runtime initialization that must be deferred until
// the executable itself is found. This needs to be done before
// further initialization.
// (The executable may not be present in this infoList if the
// executable does not contain Objective-C code but Objective-C
// is dynamically loaded later. In that case, check_wants_gc()
// will do the right thing.)
#if SUPPORT_GC
if (firstTime) {
check_wants_gc(&wantsGC, &wantsCompaction);
verify_gc_readiness(wantsGC, &wantsCompaction, hList, hCount);
gc_init(wantsGC, wantsCompaction); // needs executable for GC decision
} else {
verify_gc_readiness(wantsGC, &wantsCompaction, hList, hCount);
}
if (wantsGC) {
// tell the collector about the data segment ranges.
for (i = 0; i < hCount; ++i) {
uint8_t *seg;
unsigned long seg_size;
hi = hList[i];
seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size);
if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
if (seg) gc_register_datasegment((uintptr_t)seg, seg_size);
// __OBJC contains no GC data, but pointers to it are
// used as associated reference values (rdar://6953570)
}
}
// Need to fixup barriers in all libraries that call into libobjc, whether GC is on or not.
for (i = 0; i < infoCount; ++i) {
gc_fixup_barrier_stubs(&infoList[i]);
}
#endif
if (firstTime) {
extern SEL FwdSel; // in objc-msg-*.s
sel_init(wantsGC, selrefCount);
FwdSel = sel_registerName("forward::");
arr_init();
}
_read_images(hList, hCount);
firstTime = NO;
return NULL;
}
/***********************************************************************
* load_images_nolock
* Prepares +load in the given images which are being mapped in by dyld.
* Returns YES if there are now +load methods to be called by call_load_methods.
*
* Locking: loadMethodLock(both) and runtimeLock(new) acquired by load_images
**********************************************************************/
BOOL
load_images_nolock(enum dyld_image_states state,uint32_t infoCount,
const struct dyld_image_info infoList[])
{
BOOL found = NO;
uint32_t i;
i = infoCount;
while (i--) {
header_info *hi;
for (hi = FirstHeader; hi != NULL; hi = hi->next) {
const headerType *mhdr = (headerType*)infoList[i].imageLoadAddress;
if (hi->mhdr == mhdr) {
prepare_load_methods(hi);
found = YES;
}
}
}
return found;
}
/***********************************************************************
* unmap_image_nolock
* Process the given image which is about to be unmapped by dyld.
* mh is mach_header instead of headerType because that's what
* dyld_priv.h says even for 64-bit.
*
* Locking: loadMethodLock(both) and runtimeLock(new) acquired by unmap_image.
**********************************************************************/
void
unmap_image_nolock(const struct mach_header *mh)
{
if (PrintImages) {
_objc_inform("IMAGES: processing 1 newly-unmapped image...\n");
}
header_info *hi;
// Find the runtime's header_info struct for the image
for (hi = FirstHeader; hi != NULL; hi = hi->next) {
if (hi->mhdr == (const headerType *)mh) {
break;
}
}
if (!hi) return;
if (PrintImages) {
_objc_inform("IMAGES: unloading image for %s%s%s%s\n",
hi->fname,
hi->mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
_objcHeaderIsReplacement(hi) ? " (replacement)" : "",
_gcForHInfo2(hi));
}
#if SUPPORT_GC
if (UseGC) {
uint8_t *seg;
unsigned long seg_size;
seg = getsegmentdata(hi->mhdr, "__DATA", &seg_size);
if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
seg = getsegmentdata(hi->mhdr, "__OBJC", &seg_size);
if (seg) gc_unregister_datasegment((uintptr_t)seg, seg_size);
}
#endif
_unload_image(hi);
// Remove header_info from header list
removeHeader(hi);
_free_internal(hi);
}
/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Old ABI: called by dyld as a library initializer
* New ABI: called by libSystem BEFORE library initialization time
**********************************************************************/
#if !__OBJC2__
static __attribute__((constructor))
#endif
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
lock_init();
exception_init();
// Register for unmap first, in case some +load unmaps something
_dyld_register_func_for_remove_image(&unmap_image);
dyld_register_image_state_change_handler(dyld_image_state_bound,
1/*batch*/, &map_images);
dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images);
}
/***********************************************************************
* _headerForAddress.
* addr can be a class or a category
**********************************************************************/
static const header_info *_headerForAddress(void *addr)
{
#if __OBJC2__
const char *segname = "__DATA";
#else
const char *segname = "__OBJC";
#endif
header_info *hi;
// Check all headers in the vector
for (hi = FirstHeader; hi != NULL; hi = hi->next)
{
uint8_t *seg;
unsigned long seg_size;
seg = getsegmentdata(hi->mhdr, segname, &seg_size);
if (!seg) continue;
// Is the class in this header?
if ((uint8_t *)addr >= seg && (uint8_t *)addr < seg + seg_size)
return hi;
}
// Not found
return 0;
}
/***********************************************************************
* _headerForClass
* Return the image header containing this class, or NULL.
* Returns NULL on runtime-constructed classes, and the NSCF classes.
**********************************************************************/
const header_info *_headerForClass(Class cls)
{
return _headerForAddress(cls);
}
/**********************************************************************
* secure_open
* Securely open a file from a world-writable directory (like /tmp)
* If the file does not exist, it will be atomically created with mode 0600
* If the file exists, it must be, and remain after opening:
* 1. a regular file (in particular, not a symlink)
* 2. owned by euid
* 3. permissions 0600
* 4. link count == 1
* Returns a file descriptor or -1. Errno may or may not be set on error.
**********************************************************************/
int secure_open(const char *filename, int flags, uid_t euid)
{
struct stat fs, ls;
int fd = -1;
BOOL truncate = NO;
BOOL create = NO;
if (flags & O_TRUNC) {
// Don't truncate the file until after it is open and verified.
truncate = YES;
flags &= ~O_TRUNC;
}
if (flags & O_CREAT) {
// Don't create except when we're ready for it
create = YES;
flags &= ~O_CREAT;
flags &= ~O_EXCL;
}
if (lstat(filename, &ls) < 0) {
if (errno == ENOENT && create) {
// No such file - create it
fd = open(filename, flags | O_CREAT | O_EXCL, 0600);
if (fd >= 0) {
// File was created successfully.
// New file does not need to be truncated.
return fd;
} else {
// File creation failed.
return -1;
}
} else {
// lstat failed, or user doesn't want to create the file
return -1;
}
} else {
// lstat succeeded - verify attributes and open
if (S_ISREG(ls.st_mode) && // regular file?
ls.st_nlink == 1 && // link count == 1?
ls.st_uid == euid && // owned by euid?
(ls.st_mode & ALLPERMS) == (S_IRUSR | S_IWUSR)) // mode 0600?
{
// Attributes look ok - open it and check attributes again
fd = open(filename, flags, 0000);
if (fd >= 0) {
// File is open - double-check attributes
if (0 == fstat(fd, &fs) &&
fs.st_nlink == ls.st_nlink && // link count == 1?
fs.st_uid == ls.st_uid && // owned by euid?
fs.st_mode == ls.st_mode && // regular file, 0600?
fs.st_ino == ls.st_ino && // same inode as before?
fs.st_dev == ls.st_dev) // same device as before?
{
// File is open and OK
if (truncate) ftruncate(fd, 0);
return fd;
} else {
// Opened file looks funny - close it
close(fd);
return -1;
}
} else {
// File didn't open
return -1;
}
} else {
// Unopened file looks funny - don't open it
return -1;
}
}
}
/***********************************************************************
* _objc_internal_zone.
* Malloc zone for internal runtime data.
* By default this is the default malloc zone, but a dedicated zone is
* used if environment variable OBJC_USE_INTERNAL_ZONE is set.
**********************************************************************/
malloc_zone_t *_objc_internal_zone(void)
{
static malloc_zone_t *z = (malloc_zone_t *)-1;
if (z == (malloc_zone_t *)-1) {
if (UseInternalZone) {
z = malloc_create_zone(vm_page_size, 0);
malloc_set_zone_name(z, "ObjC");
} else {
z = malloc_default_zone();
}
}
return z;
}
bool crashlog_header_name(header_info *hi)
{
return crashlog_header_name_string(hi ? hi->fname : NULL);
}
bool crashlog_header_name_string(const char *name)
{
CRSetCrashLogMessage2(name);
return true;
}
#if TARGET_OS_IPHONE
const char *__crashreporter_info__ = NULL;
const char *CRSetCrashLogMessage(const char *msg)
{
__crashreporter_info__ = msg;
return msg;
}
const char *CRGetCrashLogMessage(void)
{
return __crashreporter_info__;
}
const char *CRSetCrashLogMessage2(const char *msg)
{
// sorry
return msg;
}
#endif
// TARGET_OS_MAC
#else
#error unknown OS
#endif